1482 lines
39 KiB
C
1482 lines
39 KiB
C
/* Handle exceptional things in C++.
|
|
Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
|
|
Contributed by Michael Tiemann <tiemann@cygnus.com>
|
|
Rewritten by Mike Stump <mrs@cygnus.com>, based upon an
|
|
initial re-implementation courtesy Tad Hunt.
|
|
|
|
This file is part of GNU CC.
|
|
|
|
GNU CC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU CC; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
/* High-level class interface. */
|
|
|
|
#include "config.h"
|
|
#include "tree.h"
|
|
#include "rtl.h"
|
|
#include "cp-tree.h"
|
|
#include "flags.h"
|
|
#include "obstack.h"
|
|
#include "expr.h"
|
|
|
|
extern void (*interim_eh_hook) PROTO((tree));
|
|
|
|
/* holds the fndecl for __builtin_return_address () */
|
|
tree builtin_return_address_fndecl;
|
|
|
|
/* Define at your own risk! */
|
|
#ifndef CROSS_COMPILE
|
|
#ifdef sun
|
|
#ifdef sparc
|
|
#define TRY_NEW_EH
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef TRY_NEW_EH
|
|
|
|
static void
|
|
sorry_no_eh ()
|
|
{
|
|
static int warned = 0;
|
|
if (! warned)
|
|
{
|
|
sorry ("exception handling not supported");
|
|
warned = 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
build_exception_table ()
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_exception_blocks ()
|
|
{
|
|
}
|
|
|
|
void
|
|
start_protect ()
|
|
{
|
|
}
|
|
|
|
void
|
|
end_protect (finalization)
|
|
tree finalization;
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_start_try_stmts ()
|
|
{
|
|
sorry_no_eh ();
|
|
}
|
|
|
|
void
|
|
expand_end_try_stmts ()
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_start_all_catch ()
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_end_all_catch ()
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_start_catch_block (declspecs, declarator)
|
|
tree declspecs, declarator;
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_end_catch_block ()
|
|
{
|
|
}
|
|
|
|
void
|
|
init_exception_processing ()
|
|
{
|
|
}
|
|
|
|
void
|
|
expand_throw (exp)
|
|
tree exp;
|
|
{
|
|
sorry_no_eh ();
|
|
}
|
|
|
|
#else
|
|
|
|
static int
|
|
doing_eh (do_warn)
|
|
int do_warn;
|
|
{
|
|
if (! flag_handle_exceptions)
|
|
{
|
|
static int warned = 0;
|
|
if (! warned && do_warn)
|
|
{
|
|
error ("exception handling disabled, use -fhandle-exceptions to enable.");
|
|
warned = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
NO GNEWS IS GOOD GNEWS WITH GARRY GNUS: This version is much closer
|
|
to supporting exception handling as per Stroustrup's 2nd edition.
|
|
It is a complete rewrite of all the EH stuff that was here before
|
|
Shortcomings:
|
|
1. The type of the throw and catch must still match
|
|
exactly (no support yet for matching base classes)
|
|
2. Throw specifications of functions still doesnt't work.
|
|
Cool Things:
|
|
1. Destructors are called properly :-)
|
|
2. No overhead for the non-exception thrown case.
|
|
3. Fixing shortcomings 1 and 2 is simple.
|
|
-Tad Hunt (tad@mail.csh.rit.edu)
|
|
|
|
*/
|
|
|
|
/* A couple of backend routines from m88k.c */
|
|
|
|
/* used to cache a call to __builtin_return_address () */
|
|
static tree BuiltinReturnAddress;
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
/* XXX - Tad: for EH */
|
|
/* output an exception table entry */
|
|
|
|
static void
|
|
output_exception_table_entry (file, start_label, end_label, eh_label)
|
|
FILE *file;
|
|
rtx start_label, end_label, eh_label;
|
|
{
|
|
char label[100];
|
|
|
|
fprintf (file, "\t%s\t ", ASM_LONG);
|
|
if (GET_CODE (start_label) == CODE_LABEL)
|
|
{
|
|
ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (start_label));
|
|
assemble_name (file, label);
|
|
}
|
|
else if (GET_CODE (start_label) == SYMBOL_REF)
|
|
{
|
|
fprintf (stderr, "YYYYYYYYYEEEEEEEESSSSSSSSSSSS!!!!!!!!!!\n");
|
|
assemble_name (file, XSTR (start_label, 0));
|
|
}
|
|
putc ('\n', file);
|
|
|
|
fprintf (file, "\t%s\t ", ASM_LONG);
|
|
ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (end_label));
|
|
assemble_name (file, label);
|
|
putc ('\n', file);
|
|
|
|
fprintf (file, "\t%s\t ", ASM_LONG);
|
|
ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (eh_label));
|
|
assemble_name (file, label);
|
|
putc ('\n', file);
|
|
|
|
putc ('\n', file); /* blank line */
|
|
}
|
|
|
|
static void
|
|
easy_expand_asm (str)
|
|
char *str;
|
|
{
|
|
expand_asm (build_string (strlen (str)+1, str));
|
|
}
|
|
|
|
/* unwind the stack. */
|
|
static void
|
|
do_unwind (throw_label)
|
|
rtx throw_label;
|
|
{
|
|
#ifdef sparc
|
|
extern FILE *asm_out_file;
|
|
tree fcall;
|
|
tree params;
|
|
rtx return_val_rtx;
|
|
|
|
/* call to __builtin_return_address () */
|
|
params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
|
|
fcall = build_function_call (BuiltinReturnAddress, params);
|
|
return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
|
|
/* In the return, the new pc is pc+8, as the value comming in is
|
|
really the address of the call insn, not the next insn. */
|
|
emit_move_insn (return_val_rtx, plus_constant(gen_rtx (LABEL_REF,
|
|
Pmode,
|
|
throw_label), -8));
|
|
/* We use three values, PC, type, and value */
|
|
easy_expand_asm ("st %l0,[%fp]");
|
|
easy_expand_asm ("st %l1,[%fp+4]");
|
|
easy_expand_asm ("st %l2,[%fp+8]");
|
|
easy_expand_asm ("ret");
|
|
easy_expand_asm ("restore");
|
|
emit_barrier ();
|
|
#endif
|
|
#if m88k
|
|
rtx temp_frame = frame_pointer_rtx;
|
|
|
|
temp_frame = memory_address (Pmode, temp_frame);
|
|
temp_frame = copy_to_reg (gen_rtx (MEM, Pmode, temp_frame));
|
|
|
|
/* hopefully this will successfully pop the frame! */
|
|
emit_move_insn (frame_pointer_rtx, temp_frame);
|
|
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
|
|
emit_move_insn (arg_pointer_rtx, frame_pointer_rtx);
|
|
emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
|
|
(HOST_WIDE_INT)m88k_debugger_offset (stack_pointer_rtx, 0))));
|
|
|
|
#if 0
|
|
emit_insn (gen_add2_insn (arg_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
|
|
-(HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
|
|
|
|
emit_move_insn (stack_pointer_rtx, arg_pointer_rtx);
|
|
|
|
emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
|
|
(HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
/* This is the startup, and finish stuff per exception table. */
|
|
|
|
/* XXX - Tad: exception handling section */
|
|
#ifndef EXCEPT_SECTION_ASM_OP
|
|
#define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits"
|
|
#endif
|
|
|
|
#ifdef EXCEPT_SECTION_ASM_OP
|
|
typedef struct {
|
|
void *start_protect;
|
|
void *end_protect;
|
|
void *exception_handler;
|
|
} exception_table;
|
|
#endif /* EXCEPT_SECTION_ASM_OP */
|
|
|
|
#ifdef EXCEPT_SECTION_ASM_OP
|
|
|
|
/* on machines which support it, the exception table lives in another section,
|
|
but it needs a label so we can reference it... This sets up that
|
|
label! */
|
|
asm (EXCEPT_SECTION_ASM_OP);
|
|
exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 };
|
|
asm (TEXT_SECTION_ASM_OP);
|
|
|
|
#endif /* EXCEPT_SECTION_ASM_OP */
|
|
|
|
#ifdef EXCEPT_SECTION_ASM_OP
|
|
|
|
/* we need to know where the end of the exception table is... so this
|
|
is how we do it! */
|
|
|
|
asm (EXCEPT_SECTION_ASM_OP);
|
|
exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 };
|
|
asm (TEXT_SECTION_ASM_OP);
|
|
|
|
#endif /* EXCEPT_SECTION_ASM_OP */
|
|
|
|
#endif
|
|
|
|
void
|
|
exception_section ()
|
|
{
|
|
#ifdef ASM_OUTPUT_SECTION_NAME
|
|
named_section (".gcc_except_table");
|
|
#else
|
|
text_section ();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/* from: my-cp-except.c */
|
|
|
|
/* VI: ":set ts=4" */
|
|
#if 0
|
|
#include <stdio.h> */
|
|
#include "config.h"
|
|
#include "tree.h"
|
|
#include "rtl.h"
|
|
#include "cp-tree.h"
|
|
#endif
|
|
#include "decl.h"
|
|
#if 0
|
|
#include "flags.h"
|
|
#endif
|
|
#include "insn-flags.h"
|
|
#include "obstack.h"
|
|
#if 0
|
|
#include "expr.h"
|
|
#endif
|
|
|
|
/* ======================================================================
|
|
Briefly the algorithm works like this:
|
|
|
|
When a constructor or start of a try block is encountered,
|
|
push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a
|
|
new entry in the unwind protection stack and returns a label to
|
|
output to start the protection for that block.
|
|
|
|
When a destructor or end try block is encountered, pop_eh_entry
|
|
(&eh_stack) is called. Pop_eh_entry () returns the ehEntry it
|
|
created when push_eh_entry () was called. The ehEntry structure
|
|
contains three things at this point. The start protect label,
|
|
the end protect label, and the exception handler label. The end
|
|
protect label should be output before the call to the destructor
|
|
(if any). If it was a destructor, then its parse tree is stored
|
|
in the finalization variable in the ehEntry structure. Otherwise
|
|
the finalization variable is set to NULL to reflect the fact that
|
|
is the the end of a try block. Next, this modified ehEntry node
|
|
is enqueued in the finalizations queue by calling
|
|
enqueue_eh_entry (&queue,entry).
|
|
|
|
+---------------------------------------------------------------+
|
|
|XXX: Will need modification to deal with partially |
|
|
| constructed arrays of objects |
|
|
| |
|
|
| Basically, this consists of keeping track of how many |
|
|
| of the objects have been constructed already (this |
|
|
| should be in a register though, so that shouldn't be a |
|
|
| problem. |
|
|
+---------------------------------------------------------------+
|
|
|
|
When a catch block is encountered, there is a lot of work to be
|
|
done.
|
|
|
|
Since we don't want to generate the catch block inline with the
|
|
regular flow of the function, we need to have some way of doing
|
|
so. Luckily, we have a couple of routines "get_last_insn ()" and
|
|
"set_last_insn ()" provided. When the start of a catch block is
|
|
encountered, we save a pointer to the last insn generated. After
|
|
the catch block is generated, we save a pointer to the first
|
|
catch block insn and the last catch block insn with the routines
|
|
"NEXT_INSN ()" and "get_last_insn ()". We then set the last insn
|
|
to be the last insn generated before the catch block, and set the
|
|
NEXT_INSN (last_insn) to zero.
|
|
|
|
Since catch blocks might be nested inside other catch blocks, and
|
|
we munge the chain of generated insns after the catch block is
|
|
generated, we need to store the pointers to the last insn
|
|
generated in a stack, so that when the end of a catch block is
|
|
encountered, the last insn before the current catch block can be
|
|
popped and set to be the last insn, and the first and last insns
|
|
of the catch block just generated can be enqueue'd for output at
|
|
a later time.
|
|
|
|
Next we must insure that when the catch block is executed, all
|
|
finalizations for the matching try block have been completed. If
|
|
any of those finalizations throw an exception, we must call
|
|
terminate according to the ARM (section r.15.6.1). What this
|
|
means is that we need to dequeue and emit finalizations for each
|
|
entry in the ehQueue until we get to an entry with a NULL
|
|
finalization field. For any of the finalization entries, if it
|
|
is not a call to terminate (), we must protect it by giving it
|
|
another start label, end label, and exception handler label,
|
|
setting its finalization tree to be a call to terminate (), and
|
|
enqueue'ing this new ehEntry to be output at an outer level.
|
|
Finally, after all that is done, we can get around to outputting
|
|
the catch block which basically wraps all the "catch (...) {...}"
|
|
statements in a big if/then/else construct that matches the
|
|
correct block to call.
|
|
|
|
===================================================================== */
|
|
|
|
extern rtx emit_insn PROTO((rtx));
|
|
extern rtx gen_nop PROTO(());
|
|
|
|
/* local globals for function calls
|
|
====================================================================== */
|
|
|
|
/* used to cache "terminate ()", "unexpected ()", "set_terminate ()", and
|
|
"set_unexpected ()" after default_conversion. (lib-except.c) */
|
|
static tree Terminate, Unexpected, SetTerminate, SetUnexpected, CatchMatch;
|
|
|
|
/* used to cache __find_first_exception_table_match ()
|
|
for throw (lib-except.c) */
|
|
static tree FirstExceptionMatch;
|
|
|
|
/* used to cache a call to __unwind_function () (lib-except.c) */
|
|
static tree Unwind;
|
|
|
|
/* holds a ready to emit call to "terminate ()". */
|
|
static tree TerminateFunctionCall;
|
|
|
|
/* ====================================================================== */
|
|
|
|
|
|
|
|
/* data structures for my various quick and dirty stacks and queues
|
|
Eventually, most of this should go away, because I think it can be
|
|
integrated with stuff already built into the compiler. */
|
|
|
|
/* =================================================================== */
|
|
|
|
struct labelNode {
|
|
rtx label;
|
|
struct labelNode *chain;
|
|
};
|
|
|
|
|
|
/* this is the most important structure here. Basically this is how I store
|
|
an exception table entry internally. */
|
|
struct ehEntry {
|
|
rtx start_label;
|
|
rtx end_label;
|
|
rtx exception_handler_label;
|
|
|
|
tree finalization;
|
|
};
|
|
|
|
struct ehNode {
|
|
struct ehEntry *entry;
|
|
struct ehNode *chain;
|
|
};
|
|
|
|
struct ehStack {
|
|
struct ehNode *top;
|
|
};
|
|
|
|
struct ehQueue {
|
|
struct ehNode *head;
|
|
struct ehNode *tail;
|
|
};
|
|
|
|
struct exceptNode {
|
|
rtx catchstart;
|
|
rtx catchend;
|
|
|
|
struct exceptNode *chain;
|
|
};
|
|
|
|
struct exceptStack {
|
|
struct exceptNode *top;
|
|
};
|
|
/* ========================================================================= */
|
|
|
|
|
|
|
|
/* local globals - these local globals are for storing data necessary for
|
|
generating the exception table and code in the correct order.
|
|
|
|
========================================================================= */
|
|
|
|
/* Holds the pc for doing "throw" */
|
|
rtx saved_pc;
|
|
/* Holds the type of the thing being thrown. */
|
|
rtx saved_throw_type;
|
|
/* Holds the value being thrown. */
|
|
rtx saved_throw_value;
|
|
|
|
rtx throw_label;
|
|
|
|
static struct ehStack ehstack;
|
|
static struct ehQueue ehqueue;
|
|
static struct ehQueue eh_table_output_queue;
|
|
static struct exceptStack exceptstack;
|
|
static struct labelNode *false_label_stack = NULL;
|
|
static struct labelNode *caught_return_label_stack = NULL;
|
|
/* ========================================================================= */
|
|
|
|
/* function prototypes */
|
|
static struct ehEntry *pop_eh_entry PROTO((struct ehStack *stack));
|
|
static void enqueue_eh_entry PROTO((struct ehQueue *queue, struct ehEntry *entry));
|
|
static void push_except_stmts PROTO((struct exceptStack *exceptstack,
|
|
rtx catchstart, rtx catchend));
|
|
static int pop_except_stmts PROTO((struct exceptStack *exceptstack,
|
|
rtx *catchstart, rtx *catchend));
|
|
static rtx push_eh_entry PROTO((struct ehStack *stack));
|
|
static struct ehEntry *dequeue_eh_entry PROTO((struct ehQueue *queue));
|
|
static void new_eh_queue PROTO((struct ehQueue *queue));
|
|
static void new_eh_stack PROTO((struct ehStack *stack));
|
|
static void new_except_stack PROTO((struct exceptStack *queue));
|
|
static void push_last_insn PROTO(());
|
|
static rtx pop_last_insn PROTO(());
|
|
static void push_label_entry PROTO((struct labelNode **labelstack, rtx label));
|
|
static rtx pop_label_entry PROTO((struct labelNode **labelstack));
|
|
static rtx top_label_entry PROTO((struct labelNode **labelstack));
|
|
static struct ehEntry *copy_eh_entry PROTO((struct ehEntry *entry));
|
|
|
|
|
|
|
|
/* All my cheesy stack/queue/misc data structure handling routines
|
|
|
|
========================================================================= */
|
|
|
|
static void
|
|
push_label_entry (labelstack, label)
|
|
struct labelNode **labelstack;
|
|
rtx label;
|
|
{
|
|
struct labelNode *newnode=(struct labelNode*)xmalloc (sizeof (struct labelNode));
|
|
|
|
newnode->label = label;
|
|
newnode->chain = *labelstack;
|
|
*labelstack = newnode;
|
|
}
|
|
|
|
static rtx
|
|
pop_label_entry (labelstack)
|
|
struct labelNode **labelstack;
|
|
{
|
|
rtx label;
|
|
struct labelNode *tempnode;
|
|
|
|
if (! *labelstack) return NULL_RTX;
|
|
|
|
tempnode = *labelstack;
|
|
label = tempnode->label;
|
|
*labelstack = (*labelstack)->chain;
|
|
free (tempnode);
|
|
|
|
return label;
|
|
}
|
|
|
|
static rtx
|
|
top_label_entry (labelstack)
|
|
struct labelNode **labelstack;
|
|
{
|
|
if (! *labelstack) return NULL_RTX;
|
|
|
|
return (*labelstack)->label;
|
|
}
|
|
|
|
static void
|
|
push_except_stmts (exceptstack, catchstart, catchend)
|
|
struct exceptStack *exceptstack;
|
|
rtx catchstart, catchend;
|
|
{
|
|
struct exceptNode *newnode = (struct exceptNode*)
|
|
xmalloc (sizeof (struct exceptNode));
|
|
|
|
newnode->catchstart = catchstart;
|
|
newnode->catchend = catchend;
|
|
newnode->chain = exceptstack->top;
|
|
|
|
exceptstack->top = newnode;
|
|
}
|
|
|
|
static int
|
|
pop_except_stmts (exceptstack, catchstart, catchend)
|
|
struct exceptStack *exceptstack;
|
|
rtx *catchstart, *catchend;
|
|
{
|
|
struct exceptNode *tempnode;
|
|
|
|
if (!exceptstack->top) {
|
|
*catchstart = *catchend = NULL_RTX;
|
|
return 0;
|
|
}
|
|
|
|
tempnode = exceptstack->top;
|
|
exceptstack->top = exceptstack->top->chain;
|
|
|
|
*catchstart = tempnode->catchstart;
|
|
*catchend = tempnode->catchend;
|
|
free (tempnode);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Push to permanent obstack for rtl generation.
|
|
One level only! */
|
|
static struct obstack *saved_rtl_obstack;
|
|
void
|
|
push_rtl_perm ()
|
|
{
|
|
extern struct obstack permanent_obstack;
|
|
extern struct obstack *rtl_obstack;
|
|
|
|
saved_rtl_obstack = rtl_obstack;
|
|
rtl_obstack = &permanent_obstack;
|
|
}
|
|
|
|
/* Pop back to normal rtl handling. */
|
|
static void
|
|
pop_rtl_from_perm ()
|
|
{
|
|
extern struct obstack permanent_obstack;
|
|
extern struct obstack *rtl_obstack;
|
|
|
|
rtl_obstack = saved_rtl_obstack;
|
|
}
|
|
|
|
static rtx
|
|
push_eh_entry (stack)
|
|
struct ehStack *stack;
|
|
{
|
|
struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
|
|
struct ehEntry *entry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
|
|
|
|
if (stack == NULL) {
|
|
free (node);
|
|
free (entry);
|
|
return NULL_RTX;
|
|
}
|
|
|
|
/* These are saved for the exception table. */
|
|
push_rtl_perm ();
|
|
entry->start_label = gen_label_rtx ();
|
|
entry->end_label = gen_label_rtx ();
|
|
entry->exception_handler_label = gen_label_rtx ();
|
|
pop_rtl_from_perm ();
|
|
|
|
entry->finalization = NULL_TREE;
|
|
|
|
node->entry = entry;
|
|
node->chain = stack->top;
|
|
stack->top = node;
|
|
|
|
enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
|
|
|
|
return entry->start_label;
|
|
}
|
|
|
|
static struct ehEntry *
|
|
pop_eh_entry (stack)
|
|
struct ehStack *stack;
|
|
{
|
|
struct ehNode *tempnode;
|
|
struct ehEntry *tempentry;
|
|
|
|
if (stack && (tempnode = stack->top)) {
|
|
tempentry = tempnode->entry;
|
|
stack->top = stack->top->chain;
|
|
free (tempnode);
|
|
|
|
return tempentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct ehEntry *
|
|
copy_eh_entry (entry)
|
|
struct ehEntry *entry;
|
|
{
|
|
struct ehEntry *newentry;
|
|
|
|
newentry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
|
|
memcpy ((void*)newentry, (void*)entry, sizeof (struct ehEntry));
|
|
|
|
return newentry;
|
|
}
|
|
|
|
static void
|
|
enqueue_eh_entry (queue, entry)
|
|
struct ehQueue *queue;
|
|
struct ehEntry *entry;
|
|
{
|
|
struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
|
|
|
|
node->entry = entry;
|
|
node->chain = NULL;
|
|
|
|
if (queue->head == NULL)
|
|
{
|
|
queue->head = node;
|
|
}
|
|
else
|
|
{
|
|
queue->tail->chain = node;
|
|
}
|
|
queue->tail = node;
|
|
}
|
|
|
|
static struct ehEntry *
|
|
dequeue_eh_entry (queue)
|
|
struct ehQueue *queue;
|
|
{
|
|
struct ehNode *tempnode;
|
|
struct ehEntry *tempentry;
|
|
|
|
if (queue->head == NULL)
|
|
return NULL;
|
|
|
|
tempnode = queue->head;
|
|
queue->head = queue->head->chain;
|
|
|
|
tempentry = tempnode->entry;
|
|
free (tempnode);
|
|
|
|
return tempentry;
|
|
}
|
|
|
|
static void
|
|
new_eh_queue (queue)
|
|
struct ehQueue *queue;
|
|
{
|
|
queue->head = queue->tail = NULL;
|
|
}
|
|
|
|
static void
|
|
new_eh_stack (stack)
|
|
struct ehStack *stack;
|
|
{
|
|
stack->top = NULL;
|
|
}
|
|
|
|
static void
|
|
new_except_stack (stack)
|
|
struct exceptStack *stack;
|
|
{
|
|
stack->top = NULL;
|
|
}
|
|
/* ========================================================================= */
|
|
|
|
void
|
|
lang_interim_eh (finalization)
|
|
tree finalization;
|
|
{
|
|
if (finalization)
|
|
end_protect (finalization);
|
|
else
|
|
start_protect ();
|
|
}
|
|
|
|
/* sets up all the global eh stuff that needs to be initialized at the
|
|
start of compilation.
|
|
|
|
This includes:
|
|
- Setting up all the function call trees
|
|
- Initializing the ehqueue
|
|
- Initializing the eh_table_output_queue
|
|
- Initializing the ehstack
|
|
- Initializing the exceptstack
|
|
*/
|
|
|
|
void
|
|
init_exception_processing ()
|
|
{
|
|
extern tree define_function ();
|
|
tree unexpected_fndecl, terminate_fndecl;
|
|
tree set_unexpected_fndecl, set_terminate_fndecl;
|
|
tree catch_match_fndecl;
|
|
tree find_first_exception_match_fndecl;
|
|
tree unwind_fndecl;
|
|
tree temp, PFV;
|
|
|
|
interim_eh_hook = lang_interim_eh;
|
|
|
|
/* void (*)() */
|
|
PFV = build_pointer_type (build_function_type (void_type_node, void_list_node));
|
|
|
|
/* arg list for the build_function_type call for set_terminate () and
|
|
set_unexpected () */
|
|
temp = tree_cons (NULL_TREE, PFV, void_list_node);
|
|
|
|
push_lang_context (lang_name_c);
|
|
|
|
set_terminate_fndecl =
|
|
define_function ("set_terminate",
|
|
build_function_type (PFV, temp),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
set_unexpected_fndecl =
|
|
define_function ("set_unexpected",
|
|
build_function_type (PFV, temp),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
|
|
unexpected_fndecl =
|
|
define_function ("unexpected",
|
|
build_function_type (void_type_node, void_list_node),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
terminate_fndecl =
|
|
define_function ("terminate",
|
|
build_function_type (void_type_node, void_list_node),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
catch_match_fndecl =
|
|
define_function ("__throw_type_match",
|
|
build_function_type (integer_type_node,
|
|
tree_cons (NULL_TREE, string_type_node, tree_cons (NULL_TREE, ptr_type_node, void_list_node))),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
find_first_exception_match_fndecl =
|
|
define_function ("__find_first_exception_table_match",
|
|
build_function_type (ptr_type_node,
|
|
tree_cons (NULL_TREE, ptr_type_node,
|
|
void_list_node)),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
unwind_fndecl =
|
|
define_function ("__unwind_function",
|
|
build_function_type (void_type_node,
|
|
tree_cons (NULL_TREE, ptr_type_node, void_list_node)),
|
|
NOT_BUILT_IN,
|
|
pushdecl,
|
|
0);
|
|
|
|
Unexpected = default_conversion (unexpected_fndecl);
|
|
Terminate = default_conversion (terminate_fndecl);
|
|
SetTerminate = default_conversion (set_terminate_fndecl);
|
|
SetUnexpected = default_conversion (set_unexpected_fndecl);
|
|
CatchMatch = default_conversion (catch_match_fndecl);
|
|
FirstExceptionMatch = default_conversion (find_first_exception_match_fndecl);
|
|
Unwind = default_conversion (unwind_fndecl);
|
|
BuiltinReturnAddress = default_conversion (builtin_return_address_fndecl);
|
|
|
|
TerminateFunctionCall = build_function_call (Terminate, NULL_TREE);
|
|
|
|
pop_lang_context ();
|
|
throw_label = gen_label_rtx ();
|
|
saved_pc = gen_rtx (REG, Pmode, 16);
|
|
saved_throw_type = gen_rtx (REG, Pmode, 17);
|
|
saved_throw_value = gen_rtx (REG, Pmode, 18);
|
|
|
|
new_eh_queue (&ehqueue);
|
|
new_eh_queue (&eh_table_output_queue);
|
|
new_eh_stack (&ehstack);
|
|
new_except_stack (&exceptstack);
|
|
}
|
|
|
|
/* call this to begin a block of unwind protection (ie: when an object is
|
|
constructed) */
|
|
void
|
|
start_protect ()
|
|
{
|
|
if (doing_eh (0))
|
|
{
|
|
emit_label (push_eh_entry (&ehstack));
|
|
}
|
|
}
|
|
|
|
/* call this to end a block of unwind protection. the finalization tree is
|
|
the finalization which needs to be run in order to cleanly unwind through
|
|
this level of protection. (ie: call this when a scope is exited)*/
|
|
void
|
|
end_protect (finalization)
|
|
tree finalization;
|
|
{
|
|
struct ehEntry *entry = pop_eh_entry (&ehstack);
|
|
|
|
if (! doing_eh (0))
|
|
return;
|
|
|
|
emit_label (entry->end_label);
|
|
|
|
entry->finalization = finalization;
|
|
|
|
enqueue_eh_entry (&ehqueue, entry);
|
|
}
|
|
|
|
/* call this on start of a try block. */
|
|
void
|
|
expand_start_try_stmts ()
|
|
{
|
|
if (doing_eh (1))
|
|
{
|
|
start_protect ();
|
|
}
|
|
}
|
|
|
|
void
|
|
expand_end_try_stmts ()
|
|
{
|
|
end_protect (integer_zero_node);
|
|
}
|
|
|
|
struct insn_save_node {
|
|
rtx last;
|
|
struct insn_save_node *chain;
|
|
};
|
|
|
|
static struct insn_save_node *InsnSave = NULL;
|
|
|
|
|
|
/* Used to keep track of where the catch blocks start. */
|
|
static void
|
|
push_last_insn ()
|
|
{
|
|
struct insn_save_node *newnode = (struct insn_save_node*)
|
|
xmalloc (sizeof (struct insn_save_node));
|
|
|
|
newnode->last = get_last_insn ();
|
|
newnode->chain = InsnSave;
|
|
InsnSave = newnode;
|
|
}
|
|
|
|
/* Use to keep track of where the catch blocks start. */
|
|
static rtx
|
|
pop_last_insn ()
|
|
{
|
|
struct insn_save_node *tempnode;
|
|
rtx temprtx;
|
|
|
|
if (!InsnSave) return NULL_RTX;
|
|
|
|
tempnode = InsnSave;
|
|
temprtx = tempnode->last;
|
|
InsnSave = InsnSave->chain;
|
|
|
|
free (tempnode);
|
|
|
|
return temprtx;
|
|
}
|
|
|
|
/* call this to start processing of all the catch blocks. */
|
|
void
|
|
expand_start_all_catch ()
|
|
{
|
|
struct ehEntry *entry;
|
|
rtx label;
|
|
|
|
if (! doing_eh (1))
|
|
return;
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
label = gen_label_rtx ();
|
|
/* The label for the exception handling block we will save. */
|
|
emit_label (label);
|
|
|
|
push_label_entry (&caught_return_label_stack, label);
|
|
|
|
/* Remember where we started. */
|
|
push_last_insn ();
|
|
|
|
emit_insn (gen_nop ());
|
|
|
|
/* Will this help us not stomp on it? */
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
|
|
|
|
while (1)
|
|
{
|
|
entry = dequeue_eh_entry (&ehqueue);
|
|
emit_label (entry->exception_handler_label);
|
|
|
|
expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
|
|
|
|
/* When we get down to the matching entry, stop. */
|
|
if (entry->finalization == integer_zero_node)
|
|
break;
|
|
|
|
free (entry);
|
|
}
|
|
|
|
/* This goes when the below moves out of our way. */
|
|
#if 1
|
|
label = gen_label_rtx ();
|
|
emit_jump (label);
|
|
#endif
|
|
|
|
/* All this should be out of line, and saved back in the exception handler
|
|
block area. */
|
|
#if 1
|
|
entry->start_label = entry->exception_handler_label;
|
|
/* These are saved for the exception table. */
|
|
push_rtl_perm ();
|
|
entry->end_label = gen_label_rtx ();
|
|
entry->exception_handler_label = gen_label_rtx ();
|
|
entry->finalization = TerminateFunctionCall;
|
|
pop_rtl_from_perm ();
|
|
emit_label (entry->end_label);
|
|
|
|
enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
|
|
|
|
/* After running the finalization, continue on out to the next
|
|
cleanup, if we have nothing better to do. */
|
|
emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry->end_label));
|
|
/* Will this help us not stomp on it? */
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
|
|
emit_jump (throw_label);
|
|
emit_label (entry->exception_handler_label);
|
|
expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
|
|
emit_barrier ();
|
|
#endif
|
|
emit_label (label);
|
|
}
|
|
|
|
/* call this to end processing of all the catch blocks. */
|
|
void
|
|
expand_end_all_catch ()
|
|
{
|
|
rtx catchstart, catchend, last;
|
|
rtx label;
|
|
|
|
if (! doing_eh (1))
|
|
return;
|
|
|
|
/* Find the start of the catch block. */
|
|
last = pop_last_insn ();
|
|
catchstart = NEXT_INSN (last);
|
|
catchend = get_last_insn ();
|
|
|
|
NEXT_INSN (last) = 0;
|
|
set_last_insn (last);
|
|
|
|
/* this level of catch blocks is done, so set up the successful catch jump
|
|
label for the next layer of catch blocks. */
|
|
pop_label_entry (&caught_return_label_stack);
|
|
|
|
push_except_stmts (&exceptstack, catchstart, catchend);
|
|
|
|
/* Here we fall through into the continuation code. */
|
|
}
|
|
|
|
|
|
/* this is called from expand_exception_blocks () to expand the toplevel
|
|
finalizations for a function. */
|
|
void
|
|
expand_leftover_cleanups ()
|
|
{
|
|
struct ehEntry *entry;
|
|
rtx first_label = NULL_RTX;
|
|
|
|
if (! doing_eh (0))
|
|
return;
|
|
|
|
/* Will this help us not stomp on it? */
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
|
|
|
|
while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
|
|
{
|
|
if (! first_label)
|
|
first_label = entry->exception_handler_label;
|
|
emit_label (entry->exception_handler_label);
|
|
|
|
expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
|
|
|
|
/* leftover try block, opps. */
|
|
if (entry->finalization == integer_zero_node)
|
|
abort ();
|
|
|
|
free (entry);
|
|
}
|
|
if (first_label)
|
|
{
|
|
rtx label;
|
|
struct ehEntry entry;
|
|
/* These are saved for the exception table. */
|
|
push_rtl_perm ();
|
|
label = gen_label_rtx ();
|
|
entry.start_label = first_label;
|
|
entry.end_label = label;
|
|
entry.exception_handler_label = gen_label_rtx ();
|
|
entry.finalization = TerminateFunctionCall;
|
|
pop_rtl_from_perm ();
|
|
emit_label (label);
|
|
|
|
enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
|
|
|
|
/* After running the finalization, continue on out to the next
|
|
cleanup, if we have nothing better to do. */
|
|
emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry.end_label));
|
|
/* Will this help us not stomp on it? */
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
|
|
emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
|
|
emit_jump (throw_label);
|
|
emit_label (entry.exception_handler_label);
|
|
expand_expr (entry.finalization, const0_rtx, VOIDmode, 0);
|
|
emit_barrier ();
|
|
}
|
|
}
|
|
|
|
/* call this to start a catch block. Typename is the typename, and identifier
|
|
is the variable to place the object in or NULL if the variable doesn't
|
|
matter. If typename is NULL, that means its a "catch (...)" or catch
|
|
everything. In that case we don't need to do any type checking.
|
|
(ie: it ends up as the "else" clause rather than an "else if" clause) */
|
|
void
|
|
expand_start_catch_block (declspecs, declarator)
|
|
tree declspecs, declarator;
|
|
{
|
|
rtx false_label_rtx;
|
|
rtx protect_label_rtx;
|
|
tree type;
|
|
tree decl;
|
|
tree init;
|
|
|
|
if (! doing_eh (1))
|
|
return;
|
|
|
|
/* Create a binding level for the parm. */
|
|
expand_start_bindings (0);
|
|
|
|
if (declspecs)
|
|
{
|
|
tree init_type;
|
|
decl = grokdeclarator (declarator, declspecs, NORMAL, 1, NULL_TREE);
|
|
|
|
/* Figure out the type that the initializer is. */
|
|
init_type = TREE_TYPE (decl);
|
|
if (TREE_CODE (init_type) != REFERENCE_TYPE)
|
|
init_type = build_reference_type (init_type);
|
|
|
|
init = convert_from_reference (save_expr (make_tree (init_type, saved_throw_value)));
|
|
|
|
/* Do we need the below two lines? */
|
|
/* Let `finish_decl' know that this initializer is ok. */
|
|
DECL_INITIAL (decl) = init;
|
|
/* This needs to be preallocated under the try block,
|
|
in a union of all catch variables. */
|
|
pushdecl (decl);
|
|
type = TREE_TYPE (decl);
|
|
|
|
/* peel back references, so they match. */
|
|
if (TREE_CODE (type) == REFERENCE_TYPE)
|
|
type = TREE_TYPE (type);
|
|
}
|
|
else
|
|
type = NULL_TREE;
|
|
|
|
false_label_rtx = gen_label_rtx ();
|
|
push_label_entry (&false_label_stack, false_label_rtx);
|
|
|
|
/* This is saved for the exception table. */
|
|
push_rtl_perm ();
|
|
protect_label_rtx = gen_label_rtx ();
|
|
pop_rtl_from_perm ();
|
|
push_label_entry (&false_label_stack, protect_label_rtx);
|
|
|
|
if (type)
|
|
{
|
|
tree params;
|
|
char *typestring;
|
|
rtx call_rtx, return_value_rtx;
|
|
tree catch_match_fcall;
|
|
tree catchmatch_arg, argval;
|
|
|
|
typestring = build_overload_name (type, 1, 1);
|
|
|
|
params = tree_cons (NULL_TREE,
|
|
combine_strings (build_string (strlen (typestring)+1, typestring)),
|
|
tree_cons (NULL_TREE,
|
|
make_tree (ptr_type_node, saved_throw_type),
|
|
NULL_TREE));
|
|
catch_match_fcall = build_function_call (CatchMatch, params);
|
|
call_rtx = expand_call (catch_match_fcall, NULL_RTX, 0);
|
|
|
|
return_value_rtx =
|
|
hard_function_value (integer_type_node, catch_match_fcall);
|
|
|
|
/* did the throw type match function return TRUE? */
|
|
emit_cmp_insn (return_value_rtx, const0_rtx, NE, NULL_RTX,
|
|
GET_MODE (return_value_rtx), 0, 0);
|
|
|
|
/* if it returned FALSE, jump over the catch block, else fall into it */
|
|
emit_jump_insn (gen_bne (false_label_rtx));
|
|
finish_decl (decl, init, NULL_TREE, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Fall into the catch all section. */
|
|
}
|
|
|
|
/* This is the starting of something to protect. */
|
|
emit_label (protect_label_rtx);
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
}
|
|
|
|
|
|
/* Call this to end a catch block. Its responsible for emitting the
|
|
code to handle jumping back to the correct place, and for emitting
|
|
the label to jump to if this catch block didn't match. */
|
|
void expand_end_catch_block ()
|
|
{
|
|
if (doing_eh (1))
|
|
{
|
|
rtx start_protect_label_rtx;
|
|
rtx end_protect_label_rtx;
|
|
tree decls;
|
|
struct ehEntry entry;
|
|
|
|
/* label we jump to if we caught the exception */
|
|
emit_jump (top_label_entry (&caught_return_label_stack));
|
|
|
|
/* Code to throw out to outer context, if we get an throw from within
|
|
our catch handler. */
|
|
/* These are saved for the exception table. */
|
|
push_rtl_perm ();
|
|
entry.exception_handler_label = gen_label_rtx ();
|
|
pop_rtl_from_perm ();
|
|
emit_label (entry.exception_handler_label);
|
|
emit_move_insn (saved_pc, gen_rtx (LABEL_REF,
|
|
Pmode,
|
|
top_label_entry (&caught_return_label_stack)));
|
|
emit_jump (throw_label);
|
|
/* No associated finalization. */
|
|
entry.finalization = NULL_TREE;
|
|
|
|
/* Because we are reordered out of line, we have to protect this. */
|
|
/* label for the start of the protection region. */
|
|
start_protect_label_rtx = pop_label_entry (&false_label_stack);
|
|
|
|
/* Cleanup the EH paramater. */
|
|
expand_end_bindings (decls = getdecls (), decls != NULL_TREE, 0);
|
|
|
|
/* label we emit to jump to if this catch block didn't match. */
|
|
emit_label (end_protect_label_rtx = pop_label_entry (&false_label_stack));
|
|
|
|
/* Because we are reordered out of line, we have to protect this. */
|
|
entry.start_label = start_protect_label_rtx;
|
|
entry.end_label = end_protect_label_rtx;
|
|
|
|
/* These set up a call to throw the caught exception into the outer
|
|
context. */
|
|
enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
|
|
}
|
|
}
|
|
|
|
/* cheesyness to save some typing. returns the return value rtx */
|
|
rtx
|
|
do_function_call (func, params, return_type)
|
|
tree func, params, return_type;
|
|
{
|
|
tree func_call;
|
|
func_call = build_function_call (func, params);
|
|
expand_call (func_call, NULL_RTX, 0);
|
|
if (return_type != NULL_TREE)
|
|
return hard_function_value (return_type, func_call);
|
|
return NULL_RTX;
|
|
}
|
|
|
|
|
|
/* is called from expand_excpetion_blocks () to generate the code in a function
|
|
to "throw" if anything in the function needs to preform a throw.
|
|
|
|
expands "throw" as the following psuedo code:
|
|
|
|
throw:
|
|
eh = find_first_exception_match (saved_pc);
|
|
if (!eh) goto gotta_rethrow_it;
|
|
goto eh;
|
|
|
|
gotta_rethrow_it:
|
|
saved_pc = __builtin_return_address (0);
|
|
pop_to_previous_level ();
|
|
goto throw;
|
|
|
|
*/
|
|
static void
|
|
expand_builtin_throw ()
|
|
{
|
|
tree fcall;
|
|
tree params;
|
|
rtx return_val_rtx;
|
|
rtx gotta_rethrow_it = gen_label_rtx ();
|
|
rtx gotta_call_terminate = gen_label_rtx ();
|
|
rtx unwind_and_throw = gen_label_rtx ();
|
|
rtx goto_unwind_and_throw = gen_label_rtx ();
|
|
|
|
emit_label (throw_label);
|
|
|
|
/* search for an exception handler for the saved_pc */
|
|
return_val_rtx = do_function_call (FirstExceptionMatch,
|
|
tree_cons (NULL_TREE, make_tree (ptr_type_node, saved_pc), NULL_TREE),
|
|
ptr_type_node);
|
|
|
|
/* did we find one? */
|
|
emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
|
|
GET_MODE (return_val_rtx), 0, 0);
|
|
|
|
/* if not, jump to gotta_rethrow_it */
|
|
emit_jump_insn (gen_beq (gotta_rethrow_it));
|
|
|
|
/* we found it, so jump to it */
|
|
emit_indirect_jump (return_val_rtx);
|
|
|
|
/* code to deal with unwinding and looking for it again */
|
|
emit_label (gotta_rethrow_it);
|
|
|
|
/* call to __builtin_return_address () */
|
|
params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
|
|
fcall = build_function_call (BuiltinReturnAddress, params);
|
|
return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
|
|
|
|
/* did __builtin_return_address () return a valid address? */
|
|
emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
|
|
GET_MODE (return_val_rtx), 0, 0);
|
|
|
|
emit_jump_insn (gen_beq (gotta_call_terminate));
|
|
|
|
/* yes it did */
|
|
emit_move_insn (saved_pc, return_val_rtx);
|
|
do_unwind (throw_label);
|
|
emit_jump (throw_label);
|
|
|
|
/* no it didn't --> therefore we need to call terminate */
|
|
emit_label (gotta_call_terminate);
|
|
do_function_call (Terminate, NULL_TREE, NULL_TREE);
|
|
}
|
|
|
|
|
|
/* This is called to expand all the toplevel exception handling
|
|
finalization for a function. It should only be called once per
|
|
function. */
|
|
void
|
|
expand_exception_blocks ()
|
|
{
|
|
rtx catchstart, catchend;
|
|
rtx last;
|
|
static rtx funcend;
|
|
|
|
funcend = gen_label_rtx ();
|
|
emit_jump (funcend);
|
|
/* expand_null_return (); */
|
|
|
|
while (pop_except_stmts (&exceptstack, &catchstart, &catchend)) {
|
|
last = get_last_insn ();
|
|
NEXT_INSN (last) = catchstart;
|
|
PREV_INSN (catchstart) = last;
|
|
NEXT_INSN (catchend) = 0;
|
|
set_last_insn (catchend);
|
|
}
|
|
|
|
expand_leftover_cleanups ();
|
|
|
|
{
|
|
static int have_done = 0;
|
|
if (! have_done && TREE_PUBLIC (current_function_decl)
|
|
&& ! DECL_INLINE (current_function_decl))
|
|
{
|
|
have_done = 1;
|
|
expand_builtin_throw ();
|
|
}
|
|
}
|
|
emit_label (funcend);
|
|
}
|
|
|
|
|
|
/* call this to expand a throw statement. This follows the following
|
|
algorithm:
|
|
|
|
1. Allocate space to save the current PC onto the stack.
|
|
2. Generate and emit a label and save its address into the
|
|
newly allocate stack space since we can't save the pc directly.
|
|
3. If this is the first call to throw in this function:
|
|
generate a label for the throw block
|
|
4. jump to the throw block label. */
|
|
void
|
|
expand_throw (exp)
|
|
tree exp;
|
|
{
|
|
rtx label;
|
|
tree type;
|
|
|
|
if (! doing_eh (1))
|
|
return;
|
|
|
|
/* This is the label that represents where in the code we were, when
|
|
we got an exception. This needs to be updated when we rethrow an
|
|
exception, so that the matching routine knows to search out. */
|
|
label = gen_label_rtx ();
|
|
emit_label (label);
|
|
emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, label));
|
|
|
|
if (exp)
|
|
{
|
|
/* throw expression */
|
|
/* First, decay it. */
|
|
exp = default_conversion (exp);
|
|
type = TREE_TYPE (exp);
|
|
|
|
{
|
|
char *typestring = build_overload_name (type, 1, 1);
|
|
tree throw_type = build1 (ADDR_EXPR, ptr_type_node, combine_strings (build_string (strlen (typestring)+1, typestring)));
|
|
rtx throw_type_rtx = expand_expr (throw_type, NULL_RTX, VOIDmode, 0);
|
|
rtx throw_value_rtx;
|
|
|
|
emit_move_insn (saved_throw_type, throw_type_rtx);
|
|
exp = convert_to_reference (build_reference_type (build_type_variant (TREE_TYPE (exp), 1, 0)), exp, CONV_STATIC, LOOKUP_COMPLAIN, NULL_TREE);
|
|
if (exp == error_mark_node)
|
|
error (" in thrown expression");
|
|
throw_value_rtx = expand_expr (build_unary_op (ADDR_EXPR, exp, 0), NULL_RTX, VOIDmode, 0);
|
|
emit_move_insn (saved_throw_value, throw_value_rtx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* rethrow current exception */
|
|
/* This part is easy, as we dont' have to do anything else. */
|
|
}
|
|
|
|
emit_jump (throw_label);
|
|
}
|
|
|
|
|
|
/* output the exception table */
|
|
void
|
|
build_exception_table ()
|
|
{
|
|
extern FILE *asm_out_file;
|
|
struct ehEntry *entry;
|
|
|
|
if (! doing_eh (0))
|
|
return;
|
|
|
|
exception_section ();
|
|
|
|
/* Beginning marker for table. */
|
|
fprintf (asm_out_file, " .global ___EXCEPTION_TABLE__\n");
|
|
fprintf (asm_out_file, " .align 4\n");
|
|
fprintf (asm_out_file, "___EXCEPTION_TABLE__:\n");
|
|
fprintf (asm_out_file, " .word 0, 0, 0\n");
|
|
|
|
while (entry = dequeue_eh_entry (&eh_table_output_queue)) {
|
|
output_exception_table_entry (asm_out_file,
|
|
entry->start_label, entry->end_label, entry->exception_handler_label);
|
|
}
|
|
|
|
/* Ending marker for table. */
|
|
fprintf (asm_out_file, " .global ___EXCEPTION_END__\n");
|
|
fprintf (asm_out_file, "___EXCEPTION_END__:\n");
|
|
fprintf (asm_out_file, " .word -1, -1, -1\n");
|
|
}
|
|
|
|
/* end of: my-cp-except.c */
|
|
#endif
|
|
|
|
|
|
/* Build a throw expression. */
|
|
tree
|
|
build_throw (e)
|
|
tree e;
|
|
{
|
|
e = build1 (THROW_EXPR, void_type_node, e);
|
|
TREE_SIDE_EFFECTS (e) = 1;
|
|
return e;
|
|
}
|