+2004-07-08 Richard Henderson <rth@redhat.com>
+
+ * except.c (expand_eh_region_start, expand_eh_region_end,
+ expand_eh_handler, expand_eh_region_end_cleanup,
+ expand_start_all_catch, expand_start_catch, expand_end_catch,
+ expand_end_all_catch, expand_eh_region_end_allowed,
+ expand_eh_region_end_must_not_throw, expand_eh_region_end_throw,
+ expand_eh_region_end_fixup): Remove.
+ * stmt.c (struct nesting): Remove stack_level, innermost_stack_block,
+ cleanups, outer_cleanups, label_chain, exception_region.
+ (struct goto_fixup): Remove stack_level, cleanup_list_list.
+ (struct label_chain): Remove.
+ (struct stmt_status): Remove x_stack_block_stack.
+ (stack_block_stack, expand_goto_internal, expand_fixup, expand_fixups,
+ fixup_gotos, save_stack_pointer, expand_decl_cleanup,
+ expand_decl_cleanup_eh, expand_cleanups, start_cleanup_deferral,
+ end_cleanup_deferral, last_cleanup_this_contour,
+ containing_blocks_have_cleanups_or_stack_level,
+ any_pending_cleanups): Remove.
+ (expand_null_return_1): Take no arguments.
+ (expand_label, expand_naked_return, expand_return,
+ expand_start_bindings_and_block, expand_end_bindings, expand_decl,
+ expand_anon_union_decl, expand_start_case, pushcase, pushcase_range,
+ expand_end_case_type): Don't use any of them.
+ * calls.c (expand_call): Likewise.
+ * dojump.c (do_jump): Likewise.
+ * function.c (expand_function_end): Likewise.
+ * expr.c (store_expr, expand_expr_real_1): Likewise.
+ (safe_from_p): Don't handle WITH_CLEANUP_EXPR, CLEANUP_POINT_EXPR.
+ (expand_expr_real_1): Don't handle WITH_CLEANUP_EXPR,
+ CLEANUP_POINT_EXPR, TARGET_EXPR, TRY_CATCH_EXPR, CATCH_EXPR,
+ EH_FILTER_EXPR, TRY_FINALLY_EXPR, GOTO_SUBROUTINE_EXPR.
+ * fold-const.c (fold_checksum_tree): Use first_rtl_op.
+ * gengtype.c (adjust_field_tree_exp): Remove rtl op handling.
+ * gimplify.c (gimplify_cleanup_point_expr): Renumber operands
+ for WITH_CLEANUP_EXPR.
+ (gimple_push_cleanup): Likewise.
+ * integrate.c (copy_decl_for_inlining): Don't DECL_TOO_LATE.
+ * print-tree.c (print_node): Likewise.
+ * tree-pretty-print.c (dump_generic_node): Remove GOTO_SUBROUTINE_EXPR.
+ * tree.c (first_rtl_op): Always just TREE_CODE_LENGTH.
+ (has_cleanups): Remove GOTO_SUBROUTINE_EXPR.
+ * tree.def (WITH_CLEANUP_EXPR): Remove op1 and op2.
+ (GOTO_SUBROUTINE_EXPR): Remove.
+ * tree.h (WITH_CLEANUP_EXPR_RTL): Remove.
+ (DECL_TOO_LATE): Remove.
+ * except.h, tree.h: Update decls.
+
2004-07-08 Paolo Bonzini <bonzini@gnu.org>
* explow.c (optimize_save_area_alloca): Do not accept parameters.
+2004-07-08 Richard Henderson <rth@redhat.com>
+
+ * trans.c (gnat_to_gnu <N_Handled_Sequence_Of_Statements>): Update
+ commentary.
+
2004-07-06 Vincent Celier <celier@gnat.com>
* vms_conv.ads: Minor reformatting.
case N_Handled_Sequence_Of_Statements:
/* The GCC exception handling mechanism can handle both ZCX and SJLJ
- schemes and we have our own SJLJ mechanism. To call the GCC
- mechanism, we first call expand_eh_region_start if there is at least
- one handler associated with the region. We then generate code for
- the region and call expand_start_all_catch to announce that the
- associated handlers are going to be generated.
-
- For each handler we call expand_start_catch, generate code for the
- handler, and then call expand_end_catch.
-
- After all the handlers, we call expand_end_all_catch.
-
- Here we deal with the region level calls and the
- N_Exception_Handler branch deals with the handler level calls
- (start_catch/end_catch).
+ schemes and we have our own SJLJ mechanism. To call the GCC
+ mechanism, we call add_cleanup, and when we leave the binding,
+ end_stmt_group will create the TRY_FINALLY_EXPR.
??? The region level calls down there have been specifically put in
place for a ZCX context and currently the order in which things are
|| !flag_optimize_sibling_calls
|| !rtx_equal_function_value_matters
|| current_nesting_level () == 0
- || any_pending_cleanups ()
|| args_size.var
|| lookup_stmt_eh_region (exp) >= 0)
try_tail_call = 0;
/* Do the same for the function address if it is an expression. */
if (!fndecl)
addr = fix_unsafe_tree (addr);
- /* Expanding one of those dangerous arguments could have added
- cleanups, but otherwise give it a whirl. */
- if (any_pending_cleanups ())
- try_tail_call = 0;
}
/* If value type not void, return an rtx for the value. */
- /* If there are cleanups to be called, don't use a hard reg as target.
- We need to double check this and see if it matters anymore. */
- if (any_pending_cleanups ())
- {
- if (target && REG_P (target)
- && REGNO (target) < FIRST_PSEUDO_REGISTER)
- target = 0;
- sibcall_failure = 1;
- }
-
if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
|| ignore)
target = const0_rtx;
clear_pending_stack_adjust ();
emit_insn (gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx));
emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
- save_stack_pointer ();
}
return target;
+2004-07-08 Richard Henderson <rth@redhat.com>
+
+ * cp-tree.h (expand_eh_spec_block): Remove.
+
2004-07-07 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (saved_scope): Remove x_previous_class_type and
extern tree expand_start_catch_block (tree);
extern void expand_end_catch_block (void);
extern void expand_builtin_throw (void);
-extern void expand_eh_spec_block (tree);
extern void expand_exception_blocks (void);
extern tree build_exc_ptr (void);
extern tree build_throw (tree);
if (if_false_label == 0)
if_false_label = drop_through_label = gen_label_rtx ();
do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX);
- start_cleanup_deferral ();
do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
- end_cleanup_deferral ();
break;
case TRUTH_ORIF_EXPR:
if (if_true_label == 0)
if_true_label = drop_through_label = gen_label_rtx ();
do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label);
- start_cleanup_deferral ();
do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
- end_cleanup_deferral ();
break;
case COMPOUND_EXPR:
do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX);
- start_cleanup_deferral ();
/* Now the THEN-expression. */
do_jump (TREE_OPERAND (exp, 1),
if_false_label ? if_false_label : drop_through_label,
do_jump (TREE_OPERAND (exp, 2),
if_false_label ? if_false_label : drop_through_label,
if_true_label ? if_true_label : drop_through_label);
- end_cleanup_deferral ();
}
break;
static void add_type_for_runtime (tree);
static tree lookup_type_for_runtime (tree);
-static struct eh_region *expand_eh_region_end (void);
-
static void resolve_fixup_regions (void);
static void remove_fixup_regions (void);
static void remove_unreachable_regions (rtx);
region->tree_label = lab;
}
\f
-/* Start an exception handling region. All instructions emitted
- after this point are considered to be part of the region until
- expand_eh_region_end is invoked. */
-
-void
-expand_eh_region_start (void)
-{
- struct eh_region *new;
- rtx note;
-
- if (! doing_eh (0))
- return;
-
- new = gen_eh_region (ERT_UNKNOWN, cfun->eh->cur_region);
- cfun->eh->cur_region = new;
-
- /* Create a note marking the start of this region. */
- note = emit_note (NOTE_INSN_EH_REGION_BEG);
- NOTE_EH_HANDLER (note) = new->region_number;
-}
-
-/* Common code to end a region. Returns the region just ended. */
-
-static struct eh_region *
-expand_eh_region_end (void)
-{
- struct eh_region *cur_region = cfun->eh->cur_region;
- rtx note;
-
- /* Create a note marking the end of this region. */
- note = emit_note (NOTE_INSN_EH_REGION_END);
- NOTE_EH_HANDLER (note) = cur_region->region_number;
-
- /* Pop. */
- cfun->eh->cur_region = cur_region->outer;
-
- return cur_region;
-}
-
-/* Expand HANDLER, which is the operand 1 of a TRY_CATCH_EXPR. Catch
- blocks and C++ exception-specifications are handled specially. */
-
-void
-expand_eh_handler (tree handler)
-{
- tree inner = expr_first (handler);
-
- switch (TREE_CODE (inner))
- {
- case CATCH_EXPR:
- expand_start_all_catch ();
- expand_expr (handler, const0_rtx, VOIDmode, 0);
- expand_end_all_catch ();
- break;
-
- case EH_FILTER_EXPR:
- if (EH_FILTER_MUST_NOT_THROW (handler))
- expand_eh_region_end_must_not_throw (EH_FILTER_FAILURE (handler));
- else
- expand_eh_region_end_allowed (EH_FILTER_TYPES (handler),
- EH_FILTER_FAILURE (handler));
- break;
-
- default:
- expand_eh_region_end_cleanup (handler);
- break;
- }
-}
-
-/* End an exception handling region for a cleanup. HANDLER is an
- expression to expand for the cleanup. */
-
-void
-expand_eh_region_end_cleanup (tree handler)
-{
- struct eh_region *region;
- tree protect_cleanup_actions;
- rtx around_label;
- rtx data_save[2];
-
- if (! doing_eh (0))
- return;
-
- region = expand_eh_region_end ();
- region->type = ERT_CLEANUP;
- region->label = gen_label_rtx ();
- region->u.cleanup.exp = handler;
- region->u.cleanup.prev_try = cfun->eh->try_region;
-
- around_label = gen_label_rtx ();
- emit_jump (around_label);
-
- emit_label (region->label);
-
- if (flag_non_call_exceptions || region->may_contain_throw)
- {
- /* Give the language a chance to specify an action to be taken if an
- exception is thrown that would propagate out of the HANDLER. */
- protect_cleanup_actions
- = (lang_protect_cleanup_actions
- ? (*lang_protect_cleanup_actions) ()
- : NULL_TREE);
-
- if (protect_cleanup_actions)
- expand_eh_region_start ();
-
- /* In case this cleanup involves an inline destructor with a try block in
- it, we need to save the EH return data registers around it. */
- data_save[0] = gen_reg_rtx (ptr_mode);
- emit_move_insn (data_save[0], get_exception_pointer (cfun));
- data_save[1] = gen_reg_rtx (word_mode);
- emit_move_insn (data_save[1], get_exception_filter (cfun));
-
- expand_expr (handler, const0_rtx, VOIDmode, 0);
-
- emit_move_insn (cfun->eh->exc_ptr, data_save[0]);
- emit_move_insn (cfun->eh->filter, data_save[1]);
-
- if (protect_cleanup_actions)
- expand_eh_region_end_must_not_throw (protect_cleanup_actions);
-
- /* We need any stack adjustment complete before the around_label. */
- do_pending_stack_adjust ();
- }
-
- /* We delay the generation of the _Unwind_Resume until we generate
- landing pads. We emit a marker here so as to get good control
- flow data in the meantime. */
- region->resume
- = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
- emit_barrier ();
-
- emit_label (around_label);
-}
-
void
expand_resx_expr (tree exp)
{
emit_barrier ();
}
-/* End an exception handling region for a try block, and prepares
- for subsequent calls to expand_start_catch. */
-
-void
-expand_start_all_catch (void)
-{
- struct eh_region *region;
-
- if (! doing_eh (1))
- return;
-
- region = expand_eh_region_end ();
- region->type = ERT_TRY;
- region->u.try.prev_try = cfun->eh->try_region;
- region->u.try.continue_label = gen_label_rtx ();
-
- cfun->eh->try_region = region;
-
- emit_jump (region->u.try.continue_label);
-}
-
-/* Begin a catch clause. TYPE is the type caught, a list of such
- types, (in the case of Java) an ADDR_EXPR which points to the
- runtime type to match, or null if this is a catch-all
- clause. Providing a type list enables to associate the catch region
- with potentially several exception types, which is useful e.g. for
- Ada. */
-
-void
-expand_start_catch (tree type_or_list)
-{
- struct eh_region *c;
- rtx note;
-
- if (! doing_eh (0))
- return;
-
- c = gen_eh_region_catch (cfun->eh->try_region, type_or_list);
- cfun->eh->cur_region = c;
-
- c->label = gen_label_rtx ();
- emit_label (c->label);
-
- note = emit_note (NOTE_INSN_EH_REGION_BEG);
- NOTE_EH_HANDLER (note) = c->region_number;
-}
-
-/* End a catch clause. Control will resume after the try/catch block. */
-
-void
-expand_end_catch (void)
-{
- if (! doing_eh (0))
- return;
-
- expand_eh_region_end ();
- emit_jump (cfun->eh->try_region->u.try.continue_label);
-}
-
-/* End a sequence of catch handlers for a try block. */
-
-void
-expand_end_all_catch (void)
-{
- struct eh_region *try_region;
-
- if (! doing_eh (0))
- return;
-
- try_region = cfun->eh->try_region;
- cfun->eh->try_region = try_region->u.try.prev_try;
-
- emit_label (try_region->u.try.continue_label);
-}
-
-/* End an exception region for an exception type filter. ALLOWED is a
- TREE_LIST of types to be matched by the runtime. FAILURE is an
- expression to invoke if a mismatch occurs.
-
- ??? We could use these semantics for calls to rethrow, too; if we can
- see the surrounding catch clause, we know that the exception we're
- rethrowing satisfies the "filter" of the catch type. */
-
-void
-expand_eh_region_end_allowed (tree allowed, tree failure)
-{
- struct eh_region *region;
- rtx around_label;
-
- if (! doing_eh (0))
- return;
-
- region = expand_eh_region_end ();
- region->type = ERT_ALLOWED_EXCEPTIONS;
- region->u.allowed.type_list = allowed;
- region->label = gen_label_rtx ();
-
- for (; allowed ; allowed = TREE_CHAIN (allowed))
- add_type_for_runtime (TREE_VALUE (allowed));
-
- /* We must emit the call to FAILURE here, so that if this function
- throws a different exception, that it will be processed by the
- correct region. */
-
- around_label = gen_label_rtx ();
- emit_jump (around_label);
-
- emit_label (region->label);
- expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* We must adjust the stack before we reach the AROUND_LABEL because
- the call to FAILURE does not occur on all paths to the
- AROUND_LABEL. */
- do_pending_stack_adjust ();
-
- emit_label (around_label);
-}
-
-/* End an exception region for a must-not-throw filter. FAILURE is an
- expression invoke if an uncaught exception propagates this far.
-
- This is conceptually identical to expand_eh_region_end_allowed with
- an empty allowed list (if you passed "std::terminate" instead of
- "__cxa_call_unexpected"), but they are represented differently in
- the C++ LSDA. */
-
-void
-expand_eh_region_end_must_not_throw (tree failure)
-{
- struct eh_region *region;
- rtx around_label;
-
- if (! doing_eh (0))
- return;
-
- region = expand_eh_region_end ();
- region->type = ERT_MUST_NOT_THROW;
- region->label = gen_label_rtx ();
-
- /* We must emit the call to FAILURE here, so that if this function
- throws a different exception, that it will be processed by the
- correct region. */
-
- around_label = gen_label_rtx ();
- emit_jump (around_label);
-
- emit_label (region->label);
- expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- emit_label (around_label);
-}
-
-/* End an exception region for a throw. No handling goes on here,
- but it's the easiest way for the front-end to indicate what type
- is being thrown. */
-
-void
-expand_eh_region_end_throw (tree type)
-{
- struct eh_region *region;
-
- if (! doing_eh (0))
- return;
-
- region = expand_eh_region_end ();
- region->type = ERT_THROW;
- region->u.throw.type = type;
-}
-
-/* End a fixup region. Within this region the cleanups for the immediately
- enclosing region are _not_ run. This is used for goto cleanup to avoid
- destroying an object twice.
-
- This would be an extraordinarily simple prospect, were it not for the
- fact that we don't actually know what the immediately enclosing region
- is. This surprising fact is because expand_cleanups is currently
- generating a sequence that it will insert somewhere else. We collect
- the proper notion of "enclosing" in convert_from_eh_region_ranges. */
-
-void
-expand_eh_region_end_fixup (tree handler)
-{
- struct eh_region *fixup;
-
- if (! doing_eh (0))
- return;
-
- fixup = expand_eh_region_end ();
- fixup->type = ERT_FIXUP;
- fixup->u.fixup.cleanup_exp = handler;
-}
-
/* Note that the current EH region (if any) may contain a throw, or a
call to a function which itself may contain a throw. */
\f
/* This section is for the exception handling specific optimization pass. */
-/* Random access the exception region tree. It's just as simple to
- collect the regions this way as in expand_eh_region_start, but
- without having to realloc memory. */
+/* Random access the exception region tree. */
void
collect_eh_region_array (void)
/* Test: is exception handling turned on? */
extern int doing_eh (int);
-/* Start an exception handling region. All instructions emitted after
- this point are considered to be part of the region until an
- expand_eh_region_end variant is invoked. */
-extern void expand_eh_region_start (void);
-
-/* End an exception handling region for a cleanup. HANDLER is an
- expression to expand for the cleanup. */
-extern void expand_eh_region_end_cleanup (tree);
-
-/* End an exception handling region for a try block, and prepares
- for subsequent calls to expand_start_catch. */
-extern void expand_start_all_catch (void);
-
-/* Begin a catch clause. TYPE is an object to be matched by the
- runtime, or a list of such objects, or null if this is a catch-all
- clause. */
-extern void expand_start_catch (tree);
-
-/* End a catch clause. Control will resume after the try/catch block. */
-extern void expand_end_catch (void);
-
-/* End a sequence of catch handlers for a try block. */
-extern void expand_end_all_catch (void);
-
-/* End an exception region for an exception type filter. ALLOWED is a
- TREE_LIST of TREE_VALUE objects to be matched by the runtime.
- FAILURE is a function to invoke if a mismatch occurs. */
-extern void expand_eh_region_end_allowed (tree, tree);
-
-/* End an exception region for a must-not-throw filter. FAILURE is a
- function to invoke if an uncaught exception propagates this far. */
-extern void expand_eh_region_end_must_not_throw (tree);
-
-/* End an exception region for a throw. No handling goes on here,
- but it's the easiest way for the front-end to indicate what type
- is being thrown. */
-extern void expand_eh_region_end_throw (tree);
-
-/* End a fixup region. Within this region the cleanups for the immediately
- enclosing region are _not_ run. This is used for goto cleanup to avoid
- destroying an object twice. */
-extern void expand_eh_region_end_fixup (tree);
-
-/* End some sort of EH region, depending on the argument. */
-extern void expand_eh_handler (tree);
-
/* Note that the current EH region (if any) may contain a throw, or a
call to a function which itself may contain a throw. */
extern void note_eh_region_may_contain_throw (struct eh_region *);
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1);
- start_cleanup_deferral ();
store_expr (TREE_OPERAND (exp, 1), target, want_value & 2);
- end_cleanup_deferral ();
emit_queue ();
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
- start_cleanup_deferral ();
store_expr (TREE_OPERAND (exp, 2), target, want_value & 2);
- end_cleanup_deferral ();
emit_queue ();
emit_label (lab2);
OK_DEFER_POP;
break;
case WITH_CLEANUP_EXPR:
- exp_rtl = WITH_CLEANUP_EXPR_RTL (exp);
- break;
-
case CLEANUP_POINT_EXPR:
+ /* Lowered by gimplify.c. */
+ abort ();
+
case SAVE_EXPR:
return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
case OBJ_TYPE_REF:
return expand_expr (OBJ_TYPE_REF_EXPR (exp), target, tmode, modifier);
- case WITH_CLEANUP_EXPR:
- if (WITH_CLEANUP_EXPR_RTL (exp) == 0)
- {
- WITH_CLEANUP_EXPR_RTL (exp)
- = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
- expand_decl_cleanup_eh (NULL_TREE, TREE_OPERAND (exp, 1),
- CLEANUP_EH_ONLY (exp));
-
- /* That's it for this cleanup. */
- TREE_OPERAND (exp, 1) = 0;
- }
- return WITH_CLEANUP_EXPR_RTL (exp);
-
- case CLEANUP_POINT_EXPR:
- {
- /* Start a new binding layer that will keep track of all cleanup
- actions to be performed. */
- expand_start_bindings (2);
-
- target_temp_slot_level = temp_slot_level;
-
- op0 = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
- /* If we're going to use this value, load it up now. */
- if (! ignore)
- op0 = force_not_mem (op0);
- preserve_temp_slots (op0);
- expand_end_bindings (NULL_TREE, 0, 0);
- }
- return op0;
-
case CALL_EXPR:
/* Check for a built-in function. */
if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
tree then_ = TREE_OPERAND (exp, 1);
tree else_ = TREE_OPERAND (exp, 2);
- /* If we do not have any pending cleanups or stack_levels
- to restore, and at least one arm of the COND_EXPR is a
- GOTO_EXPR to a local label, then we can emit more efficient
- code by using jumpif/jumpifnot instead of the 'if' machinery. */
- if (! optimize
- || containing_blocks_have_cleanups_or_stack_level ())
- ;
- else if (TREE_CODE (then_) == GOTO_EXPR
- && TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL)
+ if (TREE_CODE (then_) == GOTO_EXPR
+ && TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL)
{
jumpif (pred, label_rtx (GOTO_DESTINATION (then_)));
return expand_expr (else_, const0_rtx, VOIDmode, 0);
/* Just use the 'if' machinery. */
expand_start_cond (pred, 0);
- start_cleanup_deferral ();
expand_expr (then_, const0_rtx, VOIDmode, 0);
exp = else_;
expand_start_else ();
expand_expr (exp, const0_rtx, VOIDmode, 0);
}
- end_cleanup_deferral ();
expand_end_cond ();
return const0_rtx;
}
else
jumpifnot (TREE_OPERAND (exp, 0), op0);
- start_cleanup_deferral ();
if (binary_op && temp == 0)
/* Just touch the other operand. */
expand_expr (TREE_OPERAND (binary_op, 1),
modifier == EXPAND_STACK_PARM ? 2 : 0);
jumpif (TREE_OPERAND (exp, 0), op0);
- start_cleanup_deferral ();
if (TREE_TYPE (TREE_OPERAND (exp, 2)) != void_type_node)
store_expr (TREE_OPERAND (exp, 2), temp,
modifier == EXPAND_STACK_PARM ? 2 : 0);
modifier == EXPAND_STACK_PARM ? 2 : 0);
jumpifnot (TREE_OPERAND (exp, 0), op0);
- start_cleanup_deferral ();
if (TREE_TYPE (TREE_OPERAND (exp, 1)) != void_type_node)
store_expr (TREE_OPERAND (exp, 1), temp,
modifier == EXPAND_STACK_PARM ? 2 : 0);
op1 = gen_label_rtx ();
jumpifnot (TREE_OPERAND (exp, 0), op0);
- start_cleanup_deferral ();
-
/* One branch of the cond can be void, if it never returns. For
example A ? throw : E */
if (temp != 0
else
expand_expr (TREE_OPERAND (exp, 1),
ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
- end_cleanup_deferral ();
emit_queue ();
emit_jump_insn (gen_jump (op1));
emit_barrier ();
emit_label (op0);
- start_cleanup_deferral ();
if (temp != 0
&& TREE_TYPE (TREE_OPERAND (exp, 2)) != void_type_node)
store_expr (TREE_OPERAND (exp, 2), temp,
ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
}
- end_cleanup_deferral ();
-
emit_queue ();
emit_label (op1);
OK_DEFER_POP;
return temp;
}
- case TARGET_EXPR:
- {
- /* Something needs to be initialized, but we didn't know
- where that thing was when building the tree. For example,
- it could be the return value of a function, or a parameter
- to a function which lays down in the stack, or a temporary
- variable which must be passed by reference.
-
- We guarantee that the expression will either be constructed
- or copied into our original target. */
-
- tree slot = TREE_OPERAND (exp, 0);
- tree cleanups = NULL_TREE;
- tree exp1;
-
- if (TREE_CODE (slot) != VAR_DECL)
- abort ();
-
- if (! ignore)
- target = original_target;
-
- /* Set this here so that if we get a target that refers to a
- register variable that's already been used, put_reg_into_stack
- knows that it should fix up those uses. */
- TREE_USED (slot) = 1;
-
- if (target == 0)
- {
- if (DECL_RTL_SET_P (slot))
- {
- target = DECL_RTL (slot);
- /* If we have already expanded the slot, so don't do
- it again. (mrs) */
- if (TREE_OPERAND (exp, 1) == NULL_TREE)
- return target;
- }
- else
- {
- target = assign_temp (type, 2, 0, 1);
- SET_DECL_RTL (slot, target);
-
- /* Since SLOT is not known to the called function
- to belong to its stack frame, we must build an explicit
- cleanup. This case occurs when we must build up a reference
- to pass the reference as an argument. In this case,
- it is very likely that such a reference need not be
- built here. */
-
- if (TREE_OPERAND (exp, 2) == 0)
- TREE_OPERAND (exp, 2)
- = lang_hooks.maybe_build_cleanup (slot);
- cleanups = TREE_OPERAND (exp, 2);
- }
- }
- else
- {
- /* This case does occur, when expanding a parameter which
- needs to be constructed on the stack. The target
- is the actual stack address that we want to initialize.
- The function we call will perform the cleanup in this case. */
-
- /* If we have already assigned it space, use that space,
- not target that we were passed in, as our target
- parameter is only a hint. */
- if (DECL_RTL_SET_P (slot))
- {
- target = DECL_RTL (slot);
- /* If we have already expanded the slot, so don't do
- it again. (mrs) */
- if (TREE_OPERAND (exp, 1) == NULL_TREE)
- return target;
- }
- else
- SET_DECL_RTL (slot, target);
- }
-
- exp1 = TREE_OPERAND (exp, 3) = TREE_OPERAND (exp, 1);
- /* Mark it as expanded. */
- TREE_OPERAND (exp, 1) = NULL_TREE;
-
- if (VOID_TYPE_P (TREE_TYPE (exp1)))
- /* If the initializer is void, just expand it; it will initialize
- the object directly. */
- expand_expr (exp1, const0_rtx, VOIDmode, 0);
- else
- store_expr (exp1, target, modifier == EXPAND_STACK_PARM ? 2 : 0);
-
- expand_decl_cleanup_eh (NULL_TREE, cleanups, CLEANUP_EH_ONLY (exp));
-
- return target;
- }
-
case INIT_EXPR:
{
tree lhs = TREE_OPERAND (exp, 0);
return const0_rtx;
case TRY_CATCH_EXPR:
- {
- tree handler = TREE_OPERAND (exp, 1);
-
- expand_eh_region_start ();
- op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
- expand_eh_handler (handler);
-
- return op0;
- }
-
case CATCH_EXPR:
- expand_start_catch (CATCH_TYPES (exp));
- expand_expr (CATCH_BODY (exp), const0_rtx, VOIDmode, 0);
- expand_end_catch ();
- return const0_rtx;
-
case EH_FILTER_EXPR:
- /* Should have been handled in expand_eh_handler. */
- abort ();
-
case TRY_FINALLY_EXPR:
- {
- tree try_block = TREE_OPERAND (exp, 0);
- tree finally_block = TREE_OPERAND (exp, 1);
-
- if ((!optimize && lang_protect_cleanup_actions == NULL)
- || unsafe_for_reeval (finally_block) > 1)
- {
- /* In this case, wrapping FINALLY_BLOCK in an UNSAVE_EXPR
- is not sufficient, so we cannot expand the block twice.
- So we play games with GOTO_SUBROUTINE_EXPR to let us
- expand the thing only once. */
- /* When not optimizing, we go ahead with this form since
- (1) user breakpoints operate more predictably without
- code duplication, and
- (2) we're not running any of the global optimizers
- that would explode in time/space with the highly
- connected CFG created by the indirect branching. */
-
- rtx finally_label = gen_label_rtx ();
- rtx done_label = gen_label_rtx ();
- rtx return_link = gen_reg_rtx (Pmode);
- tree cleanup = build (GOTO_SUBROUTINE_EXPR, void_type_node,
- (tree) finally_label, (tree) return_link);
- TREE_SIDE_EFFECTS (cleanup) = 1;
-
- /* Start a new binding layer that will keep track of all cleanup
- actions to be performed. */
- expand_start_bindings (2);
- target_temp_slot_level = temp_slot_level;
-
- expand_decl_cleanup (NULL_TREE, cleanup);
- op0 = expand_expr (try_block, target, tmode, modifier);
-
- preserve_temp_slots (op0);
- expand_end_bindings (NULL_TREE, 0, 0);
- emit_jump (done_label);
- emit_label (finally_label);
- expand_expr (finally_block, const0_rtx, VOIDmode, 0);
- emit_indirect_jump (return_link);
- emit_label (done_label);
- }
- else
- {
- expand_start_bindings (2);
- target_temp_slot_level = temp_slot_level;
-
- expand_decl_cleanup (NULL_TREE, finally_block);
- op0 = expand_expr (try_block, target, tmode, modifier);
-
- preserve_temp_slots (op0);
- expand_end_bindings (NULL_TREE, 0, 0);
- }
-
- return op0;
- }
+ /* Lowered by tree-eh.c. */
+ abort ();
- case GOTO_SUBROUTINE_EXPR:
- {
- rtx subr = (rtx) TREE_OPERAND (exp, 0);
- rtx return_link = *(rtx *) &TREE_OPERAND (exp, 1);
- rtx return_address = gen_label_rtx ();
- emit_move_insn (return_link,
- gen_rtx_LABEL_REF (Pmode, return_address));
- emit_jump (subr);
- emit_label (return_address);
- return const0_rtx;
- }
+ case WITH_CLEANUP_EXPR:
+ case CLEANUP_POINT_EXPR:
+ case TARGET_EXPR:
+ /* Lowered by gimplify.c. */
+ abort ();
case VA_ARG_EXPR:
return expand_builtin_va_arg (TREE_OPERAND (exp, 0), type);
fold_checksum_tree (TREE_TYPE (expr), ctx, ht);
if (TREE_CODE_CLASS (code) != 't' && TREE_CODE_CLASS (code) != 'd')
fold_checksum_tree (TREE_CHAIN (expr), ctx, ht);
- len = TREE_CODE_LENGTH (code);
switch (TREE_CODE_CLASS (code))
{
case 'c':
}
break;
case 'e':
- switch (code)
- {
- case GOTO_SUBROUTINE_EXPR: len = 0; break;
- case WITH_CLEANUP_EXPR: len = 2; break;
- default: break;
- }
- /* Fall through. */
case 'r':
case '<':
case '1':
case '2':
case 's':
+ len = first_rtl_op (code);
for (i = 0; i < len; ++i)
fold_checksum_tree (TREE_OPERAND (expr, i), ctx, ht);
break;
sh mach_dep_reorg) that still try and compute their own lifetime info
instead of using the general framework. */
use_return_register ();
-
- /* Fix up any gotos that jumped out to the outermost
- binding level of the function.
- Must follow emitting RETURN_LABEL. */
-
- /* If you have any cleanups to do at this point,
- and they need to create temporary variables,
- then you will lose. */
- expand_fixups (get_insns ());
}
rtx
{
pair_p flds;
options_p nodot;
- size_t i;
- static const struct {
- const char *name;
- int first_rtl;
- int num_rtl;
- } data[] = {
- { "GOTO_SUBROUTINE_EXPR", 0, 2 },
- { "WITH_CLEANUP_EXPR", 2, 1 },
- };
if (t->kind != TYPE_ARRAY)
{
flds->opt->info = "";
}
- for (i = 0; i < ARRAY_SIZE (data); i++)
- {
- pair_p old_flds = flds;
- pair_p subfields = NULL;
- int r_index;
- const char *sname;
-
- for (r_index = 0;
- r_index < data[i].first_rtl + data[i].num_rtl;
- r_index++)
- {
- pair_p old_subf = subfields;
- subfields = xmalloc (sizeof (*subfields));
- subfields->next = old_subf;
- subfields->name = xasprintf ("[%d]", r_index);
- if (r_index < data[i].first_rtl)
- subfields->type = t->u.a.p;
- else
- subfields->type = create_pointer (find_structure ("rtx_def", 0));
- subfields->line.file = __FILE__;
- subfields->line.line = __LINE__;
- subfields->opt = nodot;
- }
-
- flds = xmalloc (sizeof (*flds));
- flds->next = old_flds;
- flds->name = "";
- sname = xasprintf ("tree_exp_%s", data[i].name);
- new_structure (sname, 0, &lexer_line, subfields, NULL);
- flds->type = find_structure (sname, 0);
- flds->line.file = __FILE__;
- flds->line.line = __LINE__;
- flds->opt = xmalloc (sizeof (*flds->opt));
- flds->opt->next = nodot;
- flds->opt->name = "tag";
- flds->opt->info = data[i].name;
- }
-
new_structure ("tree_exp_subunion", 1, &lexer_line, flds, nodot);
return find_structure ("tree_exp_subunion", 1);
}
{
if (tsi_one_before_end_p (iter))
{
- tsi_link_before (&iter, TREE_OPERAND (wce, 1), TSI_SAME_STMT);
+ tsi_link_before (&iter, TREE_OPERAND (wce, 0), TSI_SAME_STMT);
tsi_delink (&iter);
break;
}
sl = tsi_split_statement_list_after (&iter);
tfe = build (TRY_FINALLY_EXPR, void_type_node, sl, NULL_TREE);
- append_to_statement_list (TREE_OPERAND (wce, 1),
- &TREE_OPERAND (tfe, 1));
+ append_to_statement_list (TREE_OPERAND (wce, 0),
+ &TREE_OPERAND (tfe, 1));
*wce_p = tfe;
iter = tsi_start (sl);
}
tree ftrue = build (MODIFY_EXPR, void_type_node, flag,
boolean_true_node);
cleanup = build (COND_EXPR, void_type_node, flag, cleanup, NULL);
- wce = build (WITH_CLEANUP_EXPR, void_type_node, NULL_TREE,
- cleanup, NULL_TREE);
+ wce = build (WITH_CLEANUP_EXPR, void_type_node, cleanup);
append_to_statement_list (ffalse, &gimplify_ctxp->conditional_cleanups);
append_to_statement_list (wce, &gimplify_ctxp->conditional_cleanups);
append_to_statement_list (ftrue, pre_p);
}
else
{
- wce = build (WITH_CLEANUP_EXPR, void_type_node, NULL_TREE,
- cleanup, NULL_TREE);
+ wce = build (WITH_CLEANUP_EXPR, void_type_node, cleanup);
append_to_statement_list (wce, pre_p);
}
- gimplify_stmt (&TREE_OPERAND (wce, 1));
+ gimplify_stmt (&TREE_OPERAND (wce, 0));
}
/* Gimplify a TARGET_EXPR which doesn't appear on the rhs of an INIT_EXPR. */
if (TREE_CODE (copy) == LABEL_DECL)
{
TREE_ADDRESSABLE (copy) = 0;
- DECL_TOO_LATE (copy) = 0;
}
}
+2004-07-08 Richard Henderson <rth@redhat.com>
+
+ * expr.c (case_identity, get_primitive_array_vtable,
+ java_expand_expr, emit_init_test_initialization): Remove.
+ * java-tree.h (java_expand_expr): Remove.
+ * lang.c (LANG_HOOKS_EXPAND_EXPR): Remove.
+
2004-07-07 Per Bothner <per@bothner.com>
* class.c (build_static_field_ref): Add a NOP_EXPR; otherwise we
static void java_stack_pop (int);
static tree build_java_throw_out_of_bounds_exception (tree);
static tree build_java_check_indexed_type (tree, tree);
-static tree case_identity (tree, tree);
static unsigned char peek_opcode_at_pc (struct JCF *, int, int);
-static int emit_init_test_initialization (void **entry, void * ptr);
static GTY(()) tree operand_type[59];
type_map [i] = TREE_VEC_ELT (vec, i);
}
-/* Do the expansion of a Java switch. With Gcc, switches are front-end
- dependent things, but they rely on gcc routines. This function is
- placed here because it uses things defined locally in parse.y. */
-
-static tree
-case_identity (tree t __attribute__ ((__unused__)), tree v)
-{
- return v;
-}
-
-/* Return the name of the vtable for an array of a given primitive
- type. */
-static tree
-get_primitive_array_vtable (tree elt)
-{
- tree r;
- if (elt == boolean_type_node)
- r = boolean_array_vtable;
- else if (elt == byte_type_node)
- r = byte_array_vtable;
- else if (elt == char_type_node)
- r = char_array_vtable;
- else if (elt == short_type_node)
- r = short_array_vtable;
- else if (elt == int_type_node)
- r = int_array_vtable;
- else if (elt == long_type_node)
- r = long_array_vtable;
- else if (elt == float_type_node)
- r = float_array_vtable;
- else if (elt == double_type_node)
- r = double_array_vtable;
- else
- abort ();
- return build_address_of (r);
-}
-
-struct rtx_def *
-java_expand_expr (tree exp, rtx target, enum machine_mode tmode,
- int modifier /* Actually an enum expand_modifier. */,
- rtx *alt_rtl ATTRIBUTE_UNUSED)
-{
- tree current;
-
- abort ();
-
- switch (TREE_CODE (exp))
- {
-
- case EXPR_WITH_FILE_LOCATION:
- {
- rtx to_return;
- const char *saved_input_filename = input_filename;
- int saved_lineno = input_line;
- input_filename = EXPR_WFL_FILENAME (exp);
- input_line = EXPR_WFL_LINENO (exp);
- if (EXPR_WFL_EMIT_LINE_NOTE (exp))
- emit_line_note (input_location);
- /* Possibly avoid switching back and forth here. */
- to_return = expand_expr (EXPR_WFL_NODE (exp), target, tmode, modifier);
- input_filename = saved_input_filename;
- input_line = saved_lineno;
- return to_return;
- }
-
- case NEW_ARRAY_INIT:
- {
- rtx tmp;
- tree array_type = TREE_TYPE (TREE_TYPE (exp));
- tree element_type = TYPE_ARRAY_ELEMENT (array_type);
- tree data_fld = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (array_type)));
- HOST_WIDE_INT ilength = java_array_type_length (array_type);
- tree length = build_int_2 (ilength, 0);
- tree init = TREE_OPERAND (exp, 0);
- tree array_decl;
-
- /* See if we can generate the array statically. */
- if (TREE_CONSTANT (init) && TREE_STATIC (exp)
- && JPRIMITIVE_TYPE_P (element_type))
- {
- tree temp, value, init_decl;
- struct rtx_def *r;
- START_RECORD_CONSTRUCTOR (temp, object_type_node);
- PUSH_FIELD_VALUE (temp, "vtable",
- get_primitive_array_vtable (element_type));
- if (! flag_hash_synchronization)
- PUSH_FIELD_VALUE (temp, "sync_info", null_pointer_node);
- FINISH_RECORD_CONSTRUCTOR (temp);
- START_RECORD_CONSTRUCTOR (value, array_type);
- PUSH_SUPER_VALUE (value, temp);
- PUSH_FIELD_VALUE (value, "length", length);
- PUSH_FIELD_VALUE (value, "data", init);
- FINISH_RECORD_CONSTRUCTOR (value);
-
- init_decl = build_decl (VAR_DECL, generate_name (), array_type);
- pushdecl_top_level (init_decl);
- TREE_STATIC (init_decl) = 1;
- DECL_INITIAL (init_decl) = value;
- DECL_IGNORED_P (init_decl) = 1;
- TREE_READONLY (init_decl) = 1;
- /* Hash synchronization requires at least 64-bit alignment. */
- if (flag_hash_synchronization && POINTER_SIZE < 64)
- DECL_ALIGN (init_decl) = 64;
- rest_of_decl_compilation (init_decl, NULL, 1, 0);
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (init_decl)) = 1;
- init = build1 (ADDR_EXPR, TREE_TYPE (exp), init_decl);
- r = expand_expr (init, target, tmode, modifier);
- return r;
- }
-
- array_decl = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
- expand_decl (array_decl);
- tmp = expand_assignment (array_decl,
- build_new_array (element_type, length),
- 1);
- if (TREE_CONSTANT (init)
- && ilength >= 10 && JPRIMITIVE_TYPE_P (element_type))
- {
- tree init_decl;
- init_decl = build_decl (VAR_DECL, generate_name (),
- TREE_TYPE (init));
- pushdecl_top_level (init_decl);
- TREE_STATIC (init_decl) = 1;
- DECL_INITIAL (init_decl) = init;
- DECL_IGNORED_P (init_decl) = 1;
- TREE_READONLY (init_decl) = 1;
- rest_of_decl_compilation (init_decl, NULL, 1, 0);
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (init_decl)) = 1;
- init = init_decl;
- }
- expand_assignment (build (COMPONENT_REF, TREE_TYPE (data_fld),
- build_java_indirect_ref (array_type,
- array_decl, flag_check_references),
- data_fld, NULL_TREE),
- init, 0);
- return tmp;
- }
- case BLOCK:
- if (BLOCK_EXPR_BODY (exp))
- {
- tree local;
- rtx last;
- tree body = BLOCK_EXPR_BODY (exp);
- /* Set to 1 or more when we found a static class
- initialization flag. */
- int found_class_initialization_flag = 0;
-
- pushlevel (2); /* 2 and above */
- expand_start_bindings (0);
- local = BLOCK_EXPR_DECLS (exp);
- while (local)
- {
- tree next = TREE_CHAIN (local);
- found_class_initialization_flag +=
- LOCAL_CLASS_INITIALIZATION_FLAG_P (local);
- layout_decl (local, 0);
- expand_decl (pushdecl (local));
- local = next;
- }
-
- /* Emit initialization code for test flags if we saw one. */
- if (! always_initialize_class_p
- && current_function_decl
- && found_class_initialization_flag)
- htab_traverse
- (DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl),
- emit_init_test_initialization, NULL);
-
- /* Avoid deep recursion for long block. */
- while (TREE_CODE (body) == COMPOUND_EXPR)
- {
- expand_expr (TREE_OPERAND (body, 0), const0_rtx, VOIDmode, 0);
- emit_queue ();
- body = TREE_OPERAND (body, 1);
- }
- last = expand_expr (body, NULL_RTX, VOIDmode, 0);
- emit_queue ();
- expand_end_bindings (getdecls (), 1, 0);
- poplevel (1, 1, 0);
- return last;
- }
- return const0_rtx;
-
- case CASE_EXPR:
- {
- tree duplicate;
- if (pushcase (TREE_OPERAND (exp, 0), case_identity,
- build_decl (LABEL_DECL, NULL_TREE, NULL_TREE),
- &duplicate) == 2)
- {
- EXPR_WFL_LINECOL (wfl_operator) = EXPR_WFL_LINECOL (exp);
- parse_error_context
- (wfl_operator, "Duplicate case label: `%s'",
- print_int_node (TREE_OPERAND (exp, 0)));
- }
- return const0_rtx;
- }
-
- case DEFAULT_EXPR:
- pushcase (NULL_TREE, 0,
- build_decl (LABEL_DECL, NULL_TREE, NULL_TREE), NULL);
- return const0_rtx;
-
- case TRY_EXPR:
- /* We expand a try[-catch] block */
-
- /* Expand the try block */
- expand_eh_region_start ();
- expand_expr_stmt (TREE_OPERAND (exp, 0));
- expand_start_all_catch ();
-
- /* Expand all catch clauses (EH handlers) */
- for (current = TREE_OPERAND (exp, 1); current;
- current = TREE_CHAIN (current))
- {
- tree catch = TREE_OPERAND (current, 0);
- tree decl = BLOCK_EXPR_DECLS (catch);
- tree type = (decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE);
-
- expand_start_catch (prepare_eh_table_type (type));
- expand_expr_stmt (TREE_OPERAND (current, 0));
- expand_end_catch ();
- }
- expand_end_all_catch ();
- return const0_rtx;
-
- case JAVA_EXC_OBJ_EXPR:
- return expand_expr (build_exception_object_ref (TREE_TYPE (exp)),
- target, tmode, modifier);
-
- default:
- internal_error ("can't expand %s", tree_code_name [TREE_CODE (exp)]);
- }
-}
-
/* Go over METHOD's bytecode and note instruction starts in
instruction_bits[]. */
return node;
}
-/* Called for every element in DECL_FUNCTION_INIT_TEST_TABLE of a
- method in order to emit initialization code for each test flag. */
-
-static int
-emit_init_test_initialization (void **entry, void *x ATTRIBUTE_UNUSED)
-{
- struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry;
- tree klass = build_class_ref (ite->key);
- tree rhs;
-
- /* If the DECL_INITIAL of the test flag is set to true, it
- means that the class is already initialized the time it
- is in use. */
- if (DECL_INITIAL (ite->value) == boolean_true_node)
- rhs = boolean_true_node;
- /* Otherwise, we initialize the class init check variable by looking
- at the `state' field of the class to see if it is already
- initialized. This makes things a bit faster if the class is
- already initialized, which should be the common case. */
- else
- rhs = build (GE_EXPR, boolean_type_node,
- build (COMPONENT_REF, byte_type_node,
- build1 (INDIRECT_REF, class_type_node, klass),
- lookup_field (&class_type_node,
- get_identifier ("state")),
- NULL_TREE),
- build_int_2 (JV_STATE_DONE, 0));
-
- expand_expr_stmt (build (MODIFY_EXPR, boolean_type_node,
- ite->value, rhs));
- return true;
-}
-
/* EXPR_WITH_FILE_LOCATION are used to keep track of the exact
location where an expression or an identifier were encountered. It
is necessary for languages where the frontend parser will handle
extern void java_mark_class_local (tree);
-#if defined(RTX_CODE) && defined (HAVE_MACHINE_MODES)
-struct rtx_def * java_expand_expr (tree, rtx, enum machine_mode, int, rtx *);
-#endif
extern void java_inlining_merge_static_initializers (tree, void *);
extern void java_inlining_map_static_initializers (tree, void *);
#define LANG_HOOKS_UNSAFE_FOR_REEVAL java_unsafe_for_reeval
#undef LANG_HOOKS_MARK_ADDRESSABLE
#define LANG_HOOKS_MARK_ADDRESSABLE java_mark_addressable
-#undef LANG_HOOKS_EXPAND_EXPR
-#define LANG_HOOKS_EXPAND_EXPR java_expand_expr
#undef LANG_HOOKS_TRUTHVALUE_CONVERSION
#define LANG_HOOKS_TRUTHVALUE_CONVERSION java_truthvalue_conversion
#undef LANG_HOOKS_DUP_LANG_SPECIFIC_DECL
if (TREE_CODE (node) == FIELD_DECL && DECL_NONADDRESSABLE_P (node))
fputs (" nonaddressable", file);
- if (TREE_CODE (node) == LABEL_DECL && DECL_TOO_LATE (node))
- fputs (" too-late", file);
if (TREE_CODE (node) == LABEL_DECL && DECL_ERROR_ISSUED (node))
fputs (" error-issued", file);
/* Sequence number of this binding contour within the function,
in order of entry. */
int block_start_count;
- /* Nonzero => value to restore stack to on exit. */
- rtx stack_level;
/* The NOTE that starts this contour.
Used by expand_goto to check whether the destination
is within each contour or not. */
rtx first_insn;
- /* Innermost containing binding contour that has a stack level. */
- struct nesting *innermost_stack_block;
- /* List of cleanups to be run on exit from this contour.
- This is a list of expressions to be evaluated.
- The TREE_PURPOSE of each link is the ..._DECL node
- which the cleanup pertains to. */
- tree cleanups;
- /* List of cleanup-lists of blocks containing this block,
- as they were at the locus where this block appears.
- There is an element for each containing block,
- ordered innermost containing block first.
- The tail of this list can be 0,
- if all remaining elements would be empty lists.
- The element's TREE_VALUE is the cleanup-list of that block,
- which may be null. */
- tree outer_cleanups;
- /* Chain of labels defined inside this binding contour.
- For contours that have stack levels or cleanups. */
- struct label_chain *label_chain;
- /* Nonzero if this is associated with an EH region. */
- int exception_region;
/* The saved target_temp_slot_level from our outer block.
We may reset target_temp_slot_level to be the level of
this block, if that is done, target_temp_slot_level
cond_stack = cond_stack->next; \
if (block_stack == this) \
block_stack = block_stack->next; \
- if (stack_block_stack == this) \
- stack_block_stack = stack_block_stack->next; \
if (case_stack == this) \
case_stack = case_stack->next; \
nesting_depth = nesting_stack->depth - 1; \
/* Number of binding contours started in current function
before the label reference. */
int block_start_count;
- /* The outermost stack level that should be restored for this jump.
- Each time a binding contour that resets the stack is exited,
- if the target label is *not* yet defined, this slot is updated. */
- rtx stack_level;
- /* List of lists of cleanup expressions to be run by this goto.
- There is one element for each block that this goto is within.
- The tail of this list can be 0,
- if all remaining elements would be empty.
- The TREE_VALUE contains the cleanup list of that block as of the
- time this goto was seen.
- The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */
- tree cleanup_list_list;
-};
-
-/* Within any binding contour that must restore a stack level,
- all labels are recorded with a chain of these structures. */
-
-struct label_chain GTY(())
-{
- /* Points to following fixup. */
- struct label_chain *next;
- tree label;
};
struct stmt_status GTY(())
/* If any new stacks are added here, add them to POPSTACKS too. */
- /* Chain of all pending binding contours that restore stack levels
- or have cleanups. */
- struct nesting * x_stack_block_stack;
-
/* Chain of all pending conditional statements. */
struct nesting * x_cond_stack;
};
#define block_stack (cfun->stmt->x_block_stack)
-#define stack_block_stack (cfun->stmt->x_stack_block_stack)
#define cond_stack (cfun->stmt->x_cond_stack)
#define case_stack (cfun->stmt->x_case_stack)
#define nesting_stack (cfun->stmt->x_nesting_stack)
static int n_occurrences (int, const char *);
static bool decl_conflicts_with_clobbers_p (tree, const HARD_REG_SET);
-static void expand_goto_internal (tree, rtx, rtx);
-static int expand_fixup (tree, rtx, rtx);
static void expand_nl_goto_receiver (void);
-static void fixup_gotos (struct nesting *, rtx, tree, rtx, int);
static bool check_operand_nalternatives (tree, tree);
static bool check_unique_operand_names (tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree);
-static void expand_null_return_1 (rtx);
+static void expand_null_return_1 (void);
static enum br_predictor return_prediction (rtx);
static rtx shift_return_value (rtx);
static void expand_value_return (rtx);
-static void expand_cleanups (tree, int, int);
static void do_jump_if_equal (rtx, rtx, rtx, int);
static int estimate_case_costs (case_node_ptr);
static bool same_case_target_p (rtx, rtx);
void
expand_label (tree label)
{
- struct label_chain *p;
rtx label_r = label_rtx (label);
do_pending_stack_adjust ();
if (DECL_NONLOCAL (label) || FORCED_LABEL (label))
maybe_set_first_label_num (label_r);
-
- if (stack_block_stack != 0)
- {
- p = ggc_alloc (sizeof (struct label_chain));
- p->next = stack_block_stack->data.block.label_chain;
- stack_block_stack->data.block.label_chain = p;
- p->label = label;
- }
}
/* Generate RTL code for a `goto' statement with target label LABEL.
abort ();
#endif
- expand_goto_internal (label, label_rtx (label), NULL_RTX);
-}
-
-/* Generate RTL code for a `goto' statement with target label BODY.
- LABEL should be a LABEL_REF.
- LAST_INSN, if non-0, is the rtx we should consider as the last
- insn emitted (for the purposes of cleaning up a return). */
-
-static void
-expand_goto_internal (tree body, rtx label, rtx last_insn)
-{
- struct nesting *block;
- rtx stack_level = 0;
-
- if (GET_CODE (label) != CODE_LABEL)
- abort ();
-
- /* If label has already been defined, we can tell now
- whether and how we must alter the stack level. */
-
- if (PREV_INSN (label) != 0)
- {
- /* Find the innermost pending block that contains the label.
- (Check containment by comparing insn-uids.)
- Then restore the outermost stack level within that block,
- and do cleanups of all blocks contained in it. */
- for (block = block_stack; block; block = block->next)
- {
- if (INSN_UID (block->data.block.first_insn) < INSN_UID (label))
- break;
- if (block->data.block.stack_level != 0)
- stack_level = block->data.block.stack_level;
- /* Execute the cleanups for blocks we are exiting. */
- if (block->data.block.cleanups != 0)
- {
- expand_cleanups (block->data.block.cleanups, 1, 1);
- do_pending_stack_adjust ();
- }
- }
-
- if (stack_level)
- {
- /* Ensure stack adjust isn't done by emit_jump, as this
- would clobber the stack pointer. This one should be
- deleted as dead by flow. */
- clear_pending_stack_adjust ();
- do_pending_stack_adjust ();
-
- /* Don't do this adjust if it's to the end label and this function
- is to return with a depressed stack pointer. */
- if (label == return_label
- && (((TREE_CODE (TREE_TYPE (current_function_decl))
- == FUNCTION_TYPE)
- && (TYPE_RETURNS_STACK_DEPRESSED
- (TREE_TYPE (current_function_decl))))))
- ;
- else
- emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX);
- }
-
- if (body != 0 && DECL_TOO_LATE (body))
- error ("jump to `%s' invalidly jumps into binding contour",
- IDENTIFIER_POINTER (DECL_NAME (body)));
- }
- /* Label not yet defined: may need to put this goto
- on the fixup list. */
- else if (! expand_fixup (body, label, last_insn))
- {
- /* No fixup needed. Record that the label is the target
- of at least one goto that has no fixup. */
- if (body != 0)
- TREE_ADDRESSABLE (body) = 1;
- }
-
- emit_jump (label);
-}
-\f
-/* Generate if necessary a fixup for a goto
- whose target label in tree structure (if any) is TREE_LABEL
- and whose target in rtl is RTL_LABEL.
-
- If LAST_INSN is nonzero, we pretend that the jump appears
- after insn LAST_INSN instead of at the current point in the insn stream.
-
- The fixup will be used later to insert insns just before the goto.
- Those insns will restore the stack level as appropriate for the
- target label, and will (in the case of C++) also invoke any object
- destructors which have to be invoked when we exit the scopes which
- are exited by the goto.
-
- Value is nonzero if a fixup is made. */
-
-static int
-expand_fixup (tree tree_label, rtx rtl_label, rtx last_insn)
-{
- struct nesting *block, *end_block;
-
- /* See if we can recognize which block the label will be output in.
- This is possible in some very common cases.
- If we succeed, set END_BLOCK to that block.
- Otherwise, set it to 0. */
-
- if (cond_stack
- && (rtl_label == cond_stack->data.cond.endif_label
- || rtl_label == cond_stack->data.cond.next_label))
- end_block = cond_stack;
- else
- end_block = 0;
-
- /* Now set END_BLOCK to the binding level to which we will return. */
-
- if (end_block)
- {
- struct nesting *next_block = end_block->all;
- block = block_stack;
-
- /* First see if the END_BLOCK is inside the innermost binding level.
- If so, then no cleanups or stack levels are relevant. */
- while (next_block && next_block != block)
- next_block = next_block->all;
-
- if (next_block)
- return 0;
-
- /* Otherwise, set END_BLOCK to the innermost binding level
- which is outside the relevant control-structure nesting. */
- next_block = block_stack->next;
- for (block = block_stack; block != end_block; block = block->all)
- if (block == next_block)
- next_block = next_block->next;
- end_block = next_block;
- }
-
- /* Does any containing block have a stack level or cleanups?
- If not, no fixup is needed, and that is the normal case
- (the only case, for standard C). */
- for (block = block_stack; block != end_block; block = block->next)
- if (block->data.block.stack_level != 0
- || block->data.block.cleanups != 0)
- break;
-
- if (block != end_block)
- {
- /* Ok, a fixup is needed. Add a fixup to the list of such. */
- struct goto_fixup *fixup = ggc_alloc (sizeof (struct goto_fixup));
- /* In case an old stack level is restored, make sure that comes
- after any pending stack adjust. */
- /* ?? If the fixup isn't to come at the present position,
- doing the stack adjust here isn't useful. Doing it with our
- settings at that location isn't useful either. Let's hope
- someone does it! */
- if (last_insn == 0)
- do_pending_stack_adjust ();
- fixup->target = tree_label;
- fixup->target_rtl = rtl_label;
-
- /* Create a BLOCK node and a corresponding matched set of
- NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes at
- this point. The notes will encapsulate any and all fixup
- code which we might later insert at this point in the insn
- stream. Also, the BLOCK node will be the parent (i.e. the
- `SUPERBLOCK') of any other BLOCK nodes which we might create
- later on when we are expanding the fixup code.
-
- Note that optimization passes might move the *_BLOCK notes away,
- so we use a NOTE_INSN_DELETED as a placeholder. */
-
- {
- rtx original_before_jump
- = last_insn ? last_insn : get_last_insn ();
- rtx start;
- rtx end;
- tree block;
-
- block = make_node (BLOCK);
- TREE_USED (block) = 1;
-
- BLOCK_CHAIN (block)
- = BLOCK_CHAIN (DECL_INITIAL (current_function_decl));
- BLOCK_CHAIN (DECL_INITIAL (current_function_decl))
- = block;
-
- start_sequence ();
- start = emit_note (NOTE_INSN_BLOCK_BEG);
- NOTE_BLOCK (start) = block;
- fixup->before_jump = emit_note (NOTE_INSN_DELETED);
- end = emit_note (NOTE_INSN_BLOCK_END);
- NOTE_BLOCK (end) = block;
- fixup->context = block;
- end_sequence ();
- emit_insn_after (start, original_before_jump);
- }
-
- fixup->block_start_count = current_block_start_count;
- fixup->stack_level = 0;
- fixup->cleanup_list_list
- = ((block->data.block.outer_cleanups
- || block->data.block.cleanups)
- ? tree_cons (NULL_TREE, block->data.block.cleanups,
- block->data.block.outer_cleanups)
- : 0);
- fixup->next = goto_fixup_chain;
- goto_fixup_chain = fixup;
- }
-
- return block != 0;
-}
-\f
-/* Expand any needed fixups in the outputmost binding level of the
- function. FIRST_INSN is the first insn in the function. */
-
-void
-expand_fixups (rtx first_insn)
-{
- fixup_gotos (NULL, NULL_RTX, NULL_TREE, first_insn, 0);
-}
-
-/* When exiting a binding contour, process all pending gotos requiring fixups.
- THISBLOCK is the structure that describes the block being exited.
- STACK_LEVEL is the rtx for the stack level to restore exiting this contour.
- CLEANUP_LIST is a list of expressions to evaluate on exiting this contour.
- FIRST_INSN is the insn that began this contour.
-
- Gotos that jump out of this contour must restore the
- stack level and do the cleanups before actually jumping.
-
- DONT_JUMP_IN positive means report error if there is a jump into this
- contour from before the beginning of the contour. This is also done if
- STACK_LEVEL is nonzero unless DONT_JUMP_IN is negative. */
-
-static void
-fixup_gotos (struct nesting *thisblock, rtx stack_level,
- tree cleanup_list, rtx first_insn, int dont_jump_in)
-{
- struct goto_fixup *f, *prev;
-
- /* F is the fixup we are considering; PREV is the previous one. */
- /* We run this loop in two passes so that cleanups of exited blocks
- are run first, and blocks that are exited are marked so
- afterwards. */
-
- for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
- {
- /* Test for a fixup that is inactive because it is already handled. */
- if (f->before_jump == 0)
- {
- /* Delete inactive fixup from the chain, if that is easy to do. */
- if (prev != 0)
- prev->next = f->next;
- }
- /* Has this fixup's target label been defined?
- If so, we can finalize it. */
- else if (PREV_INSN (f->target_rtl) != 0)
- {
- rtx cleanup_insns;
-
- /* If this fixup jumped into this contour from before the beginning
- of this contour, report an error. This code used to use
- the first non-label insn after f->target_rtl, but that's
- wrong since such can be added, by things like put_var_into_stack
- and have INSN_UIDs that are out of the range of the block. */
- /* ??? Bug: this does not detect jumping in through intermediate
- blocks that have stack levels or cleanups.
- It detects only a problem with the innermost block
- around the label. */
- if (f->target != 0
- && (dont_jump_in > 0 || (dont_jump_in == 0 && stack_level)
- || cleanup_list)
- && INSN_UID (first_insn) < INSN_UID (f->target_rtl)
- && INSN_UID (first_insn) > INSN_UID (f->before_jump)
- && ! DECL_ERROR_ISSUED (f->target))
- {
- error ("%Jlabel '%D' used before containing binding contour",
- f->target, f->target);
- /* Prevent multiple errors for one label. */
- DECL_ERROR_ISSUED (f->target) = 1;
- }
-
- /* We will expand the cleanups into a sequence of their own and
- then later on we will attach this new sequence to the insn
- stream just ahead of the actual jump insn. */
-
- start_sequence ();
-
- /* Temporarily restore the lexical context where we will
- logically be inserting the fixup code. We do this for the
- sake of getting the debugging information right. */
-
- lang_hooks.decls.pushlevel (0);
- lang_hooks.decls.set_block (f->context);
-
- /* Expand the cleanups for blocks this jump exits. */
- if (f->cleanup_list_list)
- {
- tree lists;
- for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists))
- /* Marked elements correspond to blocks that have been closed.
- Do their cleanups. */
- if (TREE_ADDRESSABLE (lists)
- && TREE_VALUE (lists) != 0)
- {
- expand_cleanups (TREE_VALUE (lists), 1, 1);
- /* Pop any pushes done in the cleanups,
- in case function is about to return. */
- do_pending_stack_adjust ();
- }
- }
-
- /* Restore stack level for the biggest contour that this
- jump jumps out of. */
- if (f->stack_level
- && ! (f->target_rtl == return_label
- && ((TREE_CODE (TREE_TYPE (current_function_decl))
- == FUNCTION_TYPE)
- && (TYPE_RETURNS_STACK_DEPRESSED
- (TREE_TYPE (current_function_decl))))))
- emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
-
- /* Finish up the sequence containing the insns which implement the
- necessary cleanups, and then attach that whole sequence to the
- insn stream just ahead of the actual jump insn. Attaching it
- at that point insures that any cleanups which are in fact
- implicit C++ object destructions (which must be executed upon
- leaving the block) appear (to the debugger) to be taking place
- in an area of the generated code where the object(s) being
- destructed are still "in scope". */
-
- cleanup_insns = get_insns ();
- lang_hooks.decls.poplevel (1, 0, 0);
-
- end_sequence ();
- emit_insn_after (cleanup_insns, f->before_jump);
-
- f->before_jump = 0;
- }
- }
-
- /* For any still-undefined labels, do the cleanups for this block now.
- We must do this now since items in the cleanup list may go out
- of scope when the block ends. */
- for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
- if (f->before_jump != 0
- && PREV_INSN (f->target_rtl) == 0
- /* Label has still not appeared. If we are exiting a block with
- a stack level to restore, that started before the fixup,
- mark this stack level as needing restoration
- when the fixup is later finalized. */
- && thisblock != 0
- /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared, it
- means the label is undefined. That's erroneous, but possible. */
- && (thisblock->data.block.block_start_count
- <= f->block_start_count))
- {
- tree lists = f->cleanup_list_list;
- rtx cleanup_insns;
-
- for (; lists; lists = TREE_CHAIN (lists))
- /* If the following elt. corresponds to our containing block
- then the elt. must be for this block. */
- if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups)
- {
- start_sequence ();
- lang_hooks.decls.pushlevel (0);
- lang_hooks.decls.set_block (f->context);
- expand_cleanups (TREE_VALUE (lists), 1, 1);
- do_pending_stack_adjust ();
- cleanup_insns = get_insns ();
- lang_hooks.decls.poplevel (1, 0, 0);
- end_sequence ();
- if (cleanup_insns != 0)
- f->before_jump
- = emit_insn_after (cleanup_insns, f->before_jump);
-
- f->cleanup_list_list = TREE_CHAIN (lists);
- }
-
- if (stack_level)
- f->stack_level = stack_level;
- }
+ emit_jump (label_rtx (label));
}
\f
/* Return the number of times character C occurs in string S. */
void
expand_null_return (void)
{
- rtx last_insn;
-
- last_insn = get_last_insn ();
-
/* If this function was declared to return a value, but we
didn't, clobber the return registers so that they are not
propagated live to the rest of the function. */
clobber_return_register ();
- expand_null_return_1 (last_insn);
+ expand_null_return_1 ();
}
/* Generate RTL to return directly from the current function.
void
expand_naked_return (void)
{
- rtx last_insn, end_label;
-
- last_insn = get_last_insn ();
- end_label = naked_return_label;
+ rtx end_label;
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
+ end_label = naked_return_label;
if (end_label == 0)
end_label = naked_return_label = gen_label_rtx ();
- expand_goto_internal (NULL_TREE, end_label, last_insn);
+
+ emit_jump (end_label);
}
/* Try to guess whether the value of return means error code. */
static void
expand_value_return (rtx val)
{
- rtx last_insn;
rtx return_reg;
enum br_predictor pred;
}
- last_insn = get_last_insn ();
return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
/* Copy the value to the return location
emit_move_insn (return_reg, val);
}
- expand_null_return_1 (last_insn);
+ expand_null_return_1 ();
}
-/* Output a return with no value. If LAST_INSN is nonzero,
- pretend that the return takes place after LAST_INSN. */
+/* Output a return with no value. */
static void
-expand_null_return_1 (rtx last_insn)
+expand_null_return_1 (void)
{
- rtx end_label = return_label;
+ rtx end_label;
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
+ end_label = return_label;
if (end_label == 0)
end_label = return_label = gen_label_rtx ();
- expand_goto_internal (NULL_TREE, end_label, last_insn);
+ emit_jump (end_label);
}
\f
/* Generate RTL to evaluate the expression RETVAL and return it
void
expand_return (tree retval)
{
- /* If there are any cleanups to be performed, then they will
- be inserted following LAST_INSN. It is desirable
- that the last_insn, for such purposes, should be the
- last insn before computing the return value. Otherwise, cleanups
- which call functions can clobber the return value. */
- /* ??? rms: I think that is erroneous, because in C++ it would
- run destructors on variables that might be used in the subsequent
- computation of the return value. */
- rtx last_insn = 0;
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
}
else if (TREE_CODE (retval) == RESULT_DECL)
retval_rhs = retval;
- else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR)
+ else if ((TREE_CODE (retval) == MODIFY_EXPR
+ || TREE_CODE (retval) == INIT_EXPR)
&& TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
retval_rhs = TREE_OPERAND (retval, 1);
else
retval_rhs = retval;
- last_insn = get_last_insn ();
-
result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));
/* If the result is an aggregate that is being returned in one (or more)
copying a BLKmode value into registers. We could put this code in a
more general area (for use by everyone instead of just function
call/return), but until this feature is generally usable it is kept here
- (and in expand_call). The value must go into a pseudo in case there
- are cleanups that will clobber the real return register. */
+ (and in expand_call). */
if (retval_rhs != 0
&& TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
val = expand_expr (retval_rhs, val, GET_MODE (val), 0);
val = force_not_mem (val);
emit_queue ();
- /* Return the calculated value, doing cleanups first. */
+ /* Return the calculated value. */
expand_value_return (shift_return_value (val));
}
else
{
- /* No cleanups or no hard reg used;
- calculate value into hard return reg. */
+ /* No hard reg used; calculate value into hard return reg. */
expand_expr (retval, const0_rtx, VOIDmode, 0);
emit_queue ();
expand_value_return (result_rtl);
thisblock->next = block_stack;
thisblock->all = nesting_stack;
thisblock->depth = ++nesting_depth;
- thisblock->data.block.stack_level = 0;
- thisblock->data.block.cleanups = 0;
- thisblock->data.block.exception_region = 0;
thisblock->data.block.block_target_temp_slot_level = target_temp_slot_level;
thisblock->data.block.conditional_code = 0;
never the last instruction. */
emit_note (NOTE_INSN_DELETED);
- if (block_stack
- && !(block_stack->data.block.cleanups == NULL_TREE
- && block_stack->data.block.outer_cleanups == NULL_TREE))
- thisblock->data.block.outer_cleanups
- = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
- block_stack->data.block.outer_cleanups);
- else
- thisblock->data.block.outer_cleanups = 0;
- thisblock->data.block.label_chain = 0;
- thisblock->data.block.innermost_stack_block = stack_block_stack;
thisblock->data.block.first_insn = note;
thisblock->data.block.block_start_count = ++current_block_start_count;
thisblock->exit_label = exit_flag ? gen_label_rtx () : 0;
void
expand_end_bindings (tree vars, int mark_ends ATTRIBUTE_UNUSED,
- int dont_jump_in)
+ int dont_jump_in ATTRIBUTE_UNUSED)
{
struct nesting *thisblock = block_stack;
emit_label (thisblock->exit_label);
}
- /* Don't allow jumping into a block that has a stack level.
- Cleanups are allowed, though. */
- if (dont_jump_in > 0
- || (dont_jump_in == 0 && thisblock->data.block.stack_level != 0))
- {
- struct label_chain *chain;
-
- /* Any labels in this block are no longer valid to go to.
- Mark them to cause an error message. */
- for (chain = thisblock->data.block.label_chain; chain; chain = chain->next)
- {
- DECL_TOO_LATE (chain->label) = 1;
- /* If any goto without a fixup came to this label,
- that must be an error, because gotos without fixups
- come from outside all saved stack-levels. */
- if (TREE_ADDRESSABLE (chain->label))
- error ("%Jlabel '%D' used before containing binding contour",
- chain->label, chain->label);
- }
- }
-
- /* Restore stack level in effect before the block
- (only if variable-size objects allocated). */
- /* Perform any cleanups associated with the block. */
-
- if (thisblock->data.block.stack_level != 0
- || thisblock->data.block.cleanups != 0)
- {
- int reachable;
- rtx insn;
-
- /* Only clean up here if this point can actually be reached. */
- insn = get_last_insn ();
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- reachable = (! insn || GET_CODE (insn) != BARRIER);
-
- /* Do the cleanups. */
- expand_cleanups (thisblock->data.block.cleanups, 0, reachable);
- if (reachable)
- do_pending_stack_adjust ();
-
- /* Restore the stack level. */
-
- if (reachable && thisblock->data.block.stack_level != 0)
- {
- emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
- thisblock->data.block.stack_level, NULL_RTX);
- if (cfun->nonlocal_goto_save_area)
- update_nonlocal_goto_save_area ();
- }
-
- /* Any gotos out of this block must also do these things.
- Also report any gotos with fixups that came to labels in this
- level. */
- fixup_gotos (thisblock,
- thisblock->data.block.stack_level,
- thisblock->data.block.cleanups,
- thisblock->data.block.first_insn,
- dont_jump_in);
- }
-
- /* Mark the beginning and end of the scope if requested.
- We do this now, after running cleanups on the variables
- just going out of scope, so they are in scope for their cleanups. */
+ /* Mark the beginning and end of the scope if requested. */
/* Get rid of the beginning-mark if we don't make an end-mark. */
NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
/* Restore block_stack level for containing block. */
- stack_block_stack = thisblock->data.block.innermost_stack_block;
POPSTACK (block_stack);
/* Pop the stack slot nesting and free any slots at this level. */
pop_temp_slots ();
}
\f
-/* Generate code to save the stack pointer at the start of the current block
- and set up to restore it on exit. */
-
-void
-save_stack_pointer (void)
-{
- struct nesting *thisblock = block_stack;
-
- if (thisblock->data.block.stack_level == 0)
- {
- emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
- &thisblock->data.block.stack_level,
- thisblock->data.block.first_insn);
- stack_block_stack = thisblock;
- }
-}
-\f
/* Generate RTL for the automatic variable declaration DECL.
(Other kinds of declarations are simply ignored if seen here.) */
/* Record the stack pointer on entry to block, if have
not already done so. */
do_pending_stack_adjust ();
- save_stack_pointer ();
/* Compute the variable's size, in bytes. This will expand any
needed SAVE_EXPRs for the first time. */
pop_temp_slots ();
}
-/* CLEANUP is an expression to be executed at exit from this binding contour;
- for example, in C++, it might call the destructor for this variable.
-
- We wrap CLEANUP in an UNSAVE_EXPR node, so that we can expand the
- CLEANUP multiple times, and have the correct semantics. This
- happens in exception handling, for gotos, returns, breaks that
- leave the current scope.
-
- If CLEANUP is nonzero and DECL is zero, we record a cleanup
- that is not associated with any particular variable. */
-
-int
-expand_decl_cleanup (tree decl, tree cleanup)
-{
- struct nesting *thisblock;
-
- /* Error if we are not in any block. */
- if (cfun == 0 || block_stack == 0)
- return 0;
-
- thisblock = block_stack;
-
- /* Record the cleanup if there is one. */
-
- if (cleanup != 0)
- {
- tree t;
- rtx seq;
- tree *cleanups = &thisblock->data.block.cleanups;
- int cond_context = conditional_context ();
-
- if (cond_context)
- {
- rtx flag = gen_reg_rtx (word_mode);
- rtx set_flag_0;
- tree cond;
-
- start_sequence ();
- emit_move_insn (flag, const0_rtx);
- set_flag_0 = get_insns ();
- end_sequence ();
-
- thisblock->data.block.last_unconditional_cleanup
- = emit_insn_after (set_flag_0,
- thisblock->data.block.last_unconditional_cleanup);
-
- emit_move_insn (flag, const1_rtx);
-
- cond = build_decl (VAR_DECL, NULL_TREE,
- lang_hooks.types.type_for_mode (word_mode, 1));
- SET_DECL_RTL (cond, flag);
-
- /* Conditionalize the cleanup. */
- cleanup = build (COND_EXPR, void_type_node,
- lang_hooks.truthvalue_conversion (cond),
- cleanup, integer_zero_node);
- cleanup = fold (cleanup);
-
- cleanups = &thisblock->data.block.cleanups;
- }
-
- cleanup = unsave_expr (cleanup);
-
- t = *cleanups = tree_cons (decl, cleanup, *cleanups);
-
- if (! cond_context)
- /* If this block has a cleanup, it belongs in stack_block_stack. */
- stack_block_stack = thisblock;
-
- if (cond_context)
- {
- start_sequence ();
- }
-
- if (! using_eh_for_cleanups_p)
- TREE_ADDRESSABLE (t) = 1;
- else
- expand_eh_region_start ();
-
- if (cond_context)
- {
- seq = get_insns ();
- end_sequence ();
- if (seq)
- thisblock->data.block.last_unconditional_cleanup
- = emit_insn_after (seq,
- thisblock->data.block.last_unconditional_cleanup);
- }
- else
- {
- thisblock->data.block.last_unconditional_cleanup
- = get_last_insn ();
- /* When we insert instructions after the last unconditional cleanup,
- we don't adjust last_insn. That means that a later add_insn will
- clobber the instructions we've just added. The easiest way to
- fix this is to just insert another instruction here, so that the
- instructions inserted after the last unconditional cleanup are
- never the last instruction. */
- emit_note (NOTE_INSN_DELETED);
- }
- }
- return 1;
-}
-
-/* Like expand_decl_cleanup, but maybe only run the cleanup if an exception
- is thrown. */
-
-int
-expand_decl_cleanup_eh (tree decl, tree cleanup, int eh_only)
-{
- int ret = expand_decl_cleanup (decl, cleanup);
- if (cleanup && ret)
- {
- tree node = block_stack->data.block.cleanups;
- CLEANUP_EH_ONLY (node) = eh_only;
- }
- return ret;
-}
\f
/* DECL is an anonymous union. CLEANUP is a cleanup for DECL.
DECL_ELTS is the list of elements that belong to DECL's type.
In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */
void
-expand_anon_union_decl (tree decl, tree cleanup, tree decl_elts)
+expand_anon_union_decl (tree decl, tree cleanup ATTRIBUTE_UNUSED,
+ tree decl_elts)
{
- struct nesting *thisblock = cfun == 0 ? 0 : block_stack;
rtx x;
tree t;
}
expand_decl (decl);
- expand_decl_cleanup (decl, cleanup);
x = DECL_RTL (decl);
/* Go through the elements, assigning RTL to each. */
for (t = decl_elts; t; t = TREE_CHAIN (t))
{
tree decl_elt = TREE_VALUE (t);
- tree cleanup_elt = TREE_PURPOSE (t);
enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
/* If any of the elements are addressable, so is the entire
}
else
abort ();
-
- /* Record the cleanup if there is one. */
-
- if (cleanup != 0)
- thisblock->data.block.cleanups
- = tree_cons (decl_elt, cleanup_elt,
- thisblock->data.block.cleanups);
}
}
\f
-/* Expand a list of cleanups LIST.
- Elements may be expressions or may be nested lists.
-
- If IN_FIXUP is nonzero, we are generating this cleanup for a fixup
- goto and handle protection regions specially in that case.
-
- If REACHABLE, we emit code, otherwise just inform the exception handling
- code about this finalization. */
-
-static void
-expand_cleanups (tree list, int in_fixup, int reachable)
-{
- tree tail;
- for (tail = list; tail; tail = TREE_CHAIN (tail))
- if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
- expand_cleanups (TREE_VALUE (tail), in_fixup, reachable);
- else
- {
- if (! in_fixup && using_eh_for_cleanups_p)
- expand_eh_region_end_cleanup (TREE_VALUE (tail));
-
- if (reachable && !CLEANUP_EH_ONLY (tail))
- {
- /* Cleanups may be run multiple times. For example,
- when exiting a binding contour, we expand the
- cleanups associated with that contour. When a goto
- within that binding contour has a target outside that
- contour, it will expand all cleanups from its scope to
- the target. Though the cleanups are expanded multiple
- times, the control paths are non-overlapping so the
- cleanups will not be executed twice. */
-
- /* We may need to protect from outer cleanups. */
- if (in_fixup && using_eh_for_cleanups_p)
- {
- expand_eh_region_start ();
-
- expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
-
- expand_eh_region_end_fixup (TREE_VALUE (tail));
- }
- else
- expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
-
- free_temp_slots ();
- }
- }
-}
-
-/* Mark when the context we are emitting RTL for as a conditional
- context, so that any cleanup actions we register with
- expand_decl_init will be properly conditionalized when those
- cleanup actions are later performed. Must be called before any
- expression (tree) is expanded that is within a conditional context. */
-
-void
-start_cleanup_deferral (void)
-{
- /* block_stack can be NULL if we are inside the parameter list. It is
- OK to do nothing, because cleanups aren't possible here. */
- if (block_stack)
- ++block_stack->data.block.conditional_code;
-}
-
-/* Mark the end of a conditional region of code. Because cleanup
- deferrals may be nested, we may still be in a conditional region
- after we end the currently deferred cleanups, only after we end all
- deferred cleanups, are we back in unconditional code. */
-
-void
-end_cleanup_deferral (void)
-{
- /* block_stack can be NULL if we are inside the parameter list. It is
- OK to do nothing, because cleanups aren't possible here. */
- if (block_stack)
- --block_stack->data.block.conditional_code;
-}
-
-tree
-last_cleanup_this_contour (void)
-{
- if (block_stack == 0)
- return 0;
-
- return block_stack->data.block.cleanups;
-}
-
-
-/* Return nonzero if any containing block has a stack level or
- cleanups. */
-
-int
-containing_blocks_have_cleanups_or_stack_level (void)
-{
- struct nesting *block;
-
- for (block = block_stack; block; block = block->next)
- if (block->data.block.stack_level != 0
- || block->data.block.cleanups != 0)
- return 1;
-
- return 0;
-}
-
-/* Return 1 if there are any pending cleanups at this point.
- Check the current contour as well as contours that enclose
- the current contour. */
-
-int
-any_pending_cleanups (void)
-{
- struct nesting *block;
-
- if (cfun == NULL || cfun->stmt == NULL || block_stack == 0)
- return 0;
-
- if (block_stack->data.block.cleanups != NULL)
- return 1;
-
- if (block_stack->data.block.outer_cleanups == 0)
- return 0;
-
- for (block = block_stack->next; block; block = block->next)
- if (block->data.block.cleanups != 0)
- return 1;
-
- return 0;
-}
-\f
/* Enter a case (Pascal) or switch (C) statement.
Push a block onto case_stack and nesting_stack
to accumulate the case-labels that are seen
emit_note (NOTE_INSN_DELETED);
thiscase->data.case_stmt.start = get_last_insn ();
-
- start_cleanup_deferral ();
}
/* Accumulate one case or default label inside a case or switch statement.
If VALUE is a duplicate or overlaps, return 2 and do nothing
except store the (first) duplicate node in *DUPLICATE.
If VALUE is out of range, return 3 and do nothing.
- If we are jumping into the scope of a cleanup or var-sized array, return 5.
Return 0 on success.
Extended to handle range statements. */
if (! (case_stack && case_stack->data.case_stmt.start))
return 1;
- if (stack_block_stack
- && stack_block_stack->depth > case_stack->depth)
- return 5;
-
index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
nominal_type = case_stack->data.case_stmt.nominal_type;
if (! (case_stack && case_stack->data.case_stmt.start))
return 1;
- if (stack_block_stack
- && stack_block_stack->depth > case_stack->depth)
- return 5;
-
index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
nominal_type = case_stack->data.case_stmt.nominal_type;
if (count != 0)
range = fold (build (MINUS_EXPR, index_type, maxval, minval));
- end_cleanup_deferral ();
-
if (count == 0)
{
expand_expr (index_expr, const0_rtx, VOIDmode, 0);
reorder_insns (before_case, end,
thiscase->data.case_stmt.start);
}
- else
- end_cleanup_deferral ();
if (thiscase->exit_label && !exit_done)
emit_label (thiscase->exit_label);
is_expr = false;
break;
- case GOTO_SUBROUTINE_EXPR:
- NIY;
- break;
-
case LABEL_EXPR:
op0 = TREE_OPERAND (node, 0);
/* If this is for break or continue, don't bother printing it. */
{
switch (code)
{
- case GOTO_SUBROUTINE_EXPR:
- return 0;
- case WITH_CLEANUP_EXPR:
- return 2;
default:
return TREE_CODE_LENGTH (code);
}
switch (TREE_CODE (exp))
{
case TARGET_EXPR:
- case GOTO_SUBROUTINE_EXPR:
case WITH_CLEANUP_EXPR:
return 1;
DEFTREECODE (CALL_EXPR, "call_expr", 'e', 3)
/* Specify a value to compute along with its corresponding cleanup.
- Operand 0 argument is an expression whose value needs a cleanup.
- Operand 1 is the cleanup expression for the object.
- Operand 2 is unused.
- The cleanup is executed by the first enclosing CLEANUP_POINT_EXPR, if
- it exists, otherwise it is the responsibility of the caller to manually
- call expand_start_target_temps/expand_end_target_temps, as needed.
-
- This differs from TRY_CATCH_EXPR in that operand 2 is always
- evaluated when an exception isn't thrown when cleanups are run. */
-DEFTREECODE (WITH_CLEANUP_EXPR, "with_cleanup_expr", 'e', 3)
+ Operand 0 is the cleanup expression.
+ The cleanup is executed by the first enclosing CLEANUP_POINT_EXPR,
+ which must exist. This differs from TRY_CATCH_EXPR in that operand 1
+ is always evaluated when cleanups are run. */
+DEFTREECODE (WITH_CLEANUP_EXPR, "with_cleanup_expr", 'e', 1)
/* Specify a cleanup point.
Operand 0 is an expression that may have cleanups. If it does, those
The type should be void and the value should be ignored. */
DEFTREECODE (GOTO_EXPR, "goto_expr", 's', 1)
-/* Used internally for cleanups in the implementation of TRY_FINALLY_EXPR.
- (Specifically, it is created by expand_expr, not front-ends.)
- Operand 0 is the rtx for the start of the subroutine we need to call.
- Operand 1 is the rtx for a variable in which to store the address
- of where the subroutine should return to. */
-DEFTREECODE (GOTO_SUBROUTINE_EXPR, "goto_subroutine", 's', 2)
-
/* RETURN. Evaluates operand 0, then returns from the current function.
Presumably that operand is an assignment that stores into the
RESULT_DECL that hold the value to be returned.
&& VOID_TYPE_P (TREE_TYPE (NODE)) \
&& integer_zerop (TREE_OPERAND (NODE, 0)))
-/* In a WITH_CLEANUP_EXPR node. */
-#define WITH_CLEANUP_EXPR_RTL(NODE) \
- TREE_RTL_OPERAND_CHECK (NODE, WITH_CLEANUP_EXPR, 2)
-
/* In a CONSTRUCTOR node. */
#define CONSTRUCTOR_ELTS(NODE) TREE_OPERAND_CHECK_CODE (NODE, CONSTRUCTOR, 0)
specially. */
#define DECL_BIT_FIELD(NODE) (FIELD_DECL_CHECK (NODE)->decl.bit_field_flag)
-/* In a LABEL_DECL, nonzero means label was defined inside a binding
- contour that restored a stack level and which is now exited. */
-#define DECL_TOO_LATE(NODE) (LABEL_DECL_CHECK (NODE)->decl.bit_field_flag)
-
/* Unused in FUNCTION_DECL. */
/* In a VAR_DECL that's static,
\f
/* In stmt.c */
-extern void expand_fixups (rtx);
extern void expand_expr_stmt (tree);
extern void expand_expr_stmt_value (tree, int, int);
extern int warn_if_unused_value (tree, location_t);
expand_start_bindings_and_block(flags, NULL_TREE)
extern void expand_end_bindings (tree, int, int);
extern void warn_about_unused_variables (tree);
-extern void start_cleanup_deferral (void);
-extern void end_cleanup_deferral (void);
extern int is_body_block (tree);
extern int conditional_context (void);
extern struct nesting * current_nesting_level (void);
-extern tree last_cleanup_this_contour (void);
extern void expand_start_case (int, tree, tree, const char *);
extern void expand_end_case_type (tree, tree);
#define expand_end_case(cond) expand_end_case_type (cond, NULL)
extern void expand_asm_expr (tree);
extern bool asm_op_is_mem_input (tree, tree);
extern tree resolve_asm_operand_names (tree, tree, tree);
-extern int any_pending_cleanups (void);
extern void init_stmt_for_function (void);
extern void expand_start_target_temps (void);
extern void expand_end_target_temps (void);
extern void expand_elseif (tree);
-extern void save_stack_pointer (void);
extern void expand_decl (tree);
-extern int expand_decl_cleanup (tree, tree);
-extern int expand_decl_cleanup_eh (tree, tree, int);
extern void expand_anon_union_decl (tree, tree, tree);
-extern int containing_blocks_have_cleanups_or_stack_level (void);
/* In gimplify.c. */
extern tree create_artificial_label (void);