#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;
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 ();
========================================================================= */
-/* holds the pc for doing "throw" */
+/* Holds the pc for doing "throw" */
rtx saved_pc;
-/* holds the type of the thing being thrown. */
+/* Holds the type of the thing being thrown. */
rtx saved_throw_type;
+/* Holds the value being thrown. */
+rtx saved_throw_value;
rtx throw_label;
}
/* ========================================================================= */
+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.
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));
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);
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)
{
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
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);
push_except_stmts (&exceptstack, catchstart, catchend);
- /* Here was fall through into the continuation code. */
+ /* Here we fall through into the continuation code. */
}
/* 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)
{
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);
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)
{
- decl = grokdeclarator (declarator, declspecs, PARM, 0, NULL_TREE);
+ 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;
/* 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);
}
{
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 (pop_label_entry (&false_label_stack));
+ 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));
}
}
expand_throw (exp)
tree exp;
{
- tree raiseid = NULL_TREE;
- rtx temp_size;
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 variable */
+ /* 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
- type = void_type_node;
-
- {
- 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);
- emit_move_insn (saved_throw_type, throw_type_rtx);
- }
+ {
+ /* rethrow current exception */
+ /* This part is easy, as we dont' have to do anything else. */
+ }
emit_jump (throw_label);
}
@node Exception Handling, Free Store, Copying Objects, Top
@section Exception Handling
+Note, exception handling in g++ is still under development.
+
This section describes the mapping of C++ exceptions in the C++
front-end, into the back-end exception handling framework.
Unfortunately, the standard allows standard type conversions on throw
parameters so they can match catch handlers. This means we need a
-mechanism to handle type conversion at run time, ICK.
+mechanism to handle type conversion at run time, ICK. I read this part
+again, and it appears that we only have to be able to do a few of the
+conversions at run time, so we should be ok.
In C++, all cleanups should be protected by exception regions. The
region starts just after the reason why the cleanup is created has
actions to perform. I hope they add this logic into the back-end, as it
would be nice to get that alternative approach in C++.
+On an rs6000, xlC stores exception objects on that stack, under the try
+block. When is unwinds down into a handler, the frame pointer is
+adjusted back to the normal value for the frame in which the handler
+resides, and the stack pointer is left unchanged from the time at which
+the object was throwed. This is so that there is always someplace for
+the exception object, and nothing can overwrite it, once we start
+throwing. The only bad part, is that the stack remains large.
+
+Flaws in g++'s exception handling. The stack pointer is restored from
+stack, we want to match rs6000, and propagate the stack pointer from
+time of throw, down, to the catch place.
+
+Only exact type matching of throw types works (references work also),
+catch variables cannot be used. Only works on a Sun sparc running SunOS
+4.1.x. Unwinding to outer catch clauses works. All temps and local
+variables are cleaned up in all unwinded scopes. Completed parts of
+partially constructed objects are not cleaned up. Don't expect
+exception handling to work right if you optimize, in fact the compiler
+will probably core dump. You can only have one source file worth of
+exception handling code. If two EH regions are the exact same size, the
+backend cannot tell which one is first. It punts by picking the last
+one, if they tie. This is usually right. We really should stick in a
+nop, if they are the same size.
+
+If we fall off the end of a series of catch blocks, we return to the
+flow of control in a normal fasion. But this is wrong, we should
+rethrow.
+
+When we invoke the copy constructor for an exception object because it
+is passed by value, and if we take a hit (exception) inside the copy
+constructor someplace, where do we go? I have tentatively choosen to
+not catch throws by the outer block at the same unwind level, if one
+exists, but rather to allow the frame to unwind into the next series of
+handlers, if any. If this is the wrong way to do it, we will need to
+protect the rest of the handler in some fashion. Maybe just changing
+the handler's handler to protect the whole series of handlers is the
+right way to go.
+
+The EH object is copied like it should be, if it is passed by value,
+otherwise we get a reference directly to it.
+
+EH objects make it through unwinding, but are subject to being
+overwritten as they are still past the top of stack.
+
+Exceptions in catch handlers now go to outer block.
+
@node Free Store, Concept Index, Exception Handling, Top
@section Free Store