diff options
author | Richard Henderson <rth@redhat.com> | 2009-09-14 12:18:58 -0700 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2009-09-14 12:18:58 -0700 |
commit | 1d65f45cfaefa060737af130c3fc69afb3030980 (patch) | |
tree | 2fcbbb5f99b13293753d83230cf9f4e0893a9b51 /gcc | |
parent | 0c433c31b31f25e3f18e58bd8d404c02722d7f7c (diff) | |
download | gcc-1d65f45cfaefa060737af130c3fc69afb3030980.zip gcc-1d65f45cfaefa060737af130c3fc69afb3030980.tar.gz gcc-1d65f45cfaefa060737af130c3fc69afb3030980.tar.bz2 |
Squash commit of EH in gimple
From-SVN: r151696
Diffstat (limited to 'gcc')
91 files changed, 4408 insertions, 4731 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 75333c1..cc04831 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,343 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * except.h: Update declarations. + (struct pointer_map_t): Forward declare. + (ERT_UNKNOWN, ERT_THROW, ERT_CATCH): Remove. + (struct eh_landing_pad_d, eh_landing_pad): New. + (struct eh_catch_d, eh_catch): New. + (struct eh_region_d): Remove next_region_sharing_label, aka, + label, tree_label, landing_pad, post_landing_pad, resume, + may_contain_throw. Rename region_number to index. Remove + u.eh_catch, u.eh_throw. Rename u.eh_try.eh_catch to first_catch. + Add u.must_not_throw, landing_pads, exc_ptr_reg, filter_reg. + (VEC(eh_landing_pad,gc)): New. + (struct eh_status): Remove last_region_number. Add lp_array, + throw_stmt_table, ttype_data, ehspec_data. + (ehr_next, FOR_ALL_EH_REGION_AT): New. + (FOR_ALL_EH_REGION_FN, FOR_ALL_EH_REGION): New. + * except.c (lang_protect_cleanup_actions): Return tree. + (struct ehl_map_entry): Remove. + (init_eh_for_function): Push zero entries for region and lp_array. + (gen_eh_region): Add to region_array immediately. + (gen_eh_region_catch): Operate on eh_catch objects. + (gen_eh_landing_pad): New. + (get_eh_region_may_contain_throw, get_eh_region_tree_label): Remove. + (get_eh_region_no_tree_label, set_eh_region_tree_label): Remove. + (get_eh_region_from_number, get_eh_region_from_number_fn): New. + (get_eh_landing_pad_from_number_fn): New. + (get_eh_landing_pad_from_number): New. + (get_eh_region_from_lp_number_fn): New. + (get_eh_region_from_lp_number): New. + (expand_resx_stmt, note_eh_region_may_contain_throw): Remove. + (get_exception_pointer, get_exception_filter): Remove. + (collect_eh_region_array, can_be_reached_by_runtime): Remove. + (current_function_has_exception_handlers): Simplify. + (bring_to_root, eh_region_replaceable_by_p): Remove. + (replace_region, hash_type_list, hash_eh_region): Remove. + (eh_regions_equal_p, merge_peers, remove_unreachable_regions): Remove. + (label_to_region_map, num_eh_regions): Remove. + (get_next_region_sharing_label, must_not_throw_labels): Remove. + (find_exception_handler_labels): Remove. + (duplicate_eh_regions_0, find_prev_try): Remove. + (struct duplicate_eh_regions_data): New. + (duplicate_eh_regions_1): Rewrite. + (duplicate_eh_regions): Return a pointer map instead of an + integer offset. + (copy_eh_region_1, copy_eh_region, push_reachable_handler): Remove. + (redirect_eh_edge_to_label): Remove. + (eh_region_outermost): Rewrite using eh_region pointers + instead of integers. + (add_ttypes_entry): Update for ttype_data move to eh_status. + (add_ehspec_entry): Rewrite with VEC instead of varray. + (assign_filter_values): Likewise. Export. + (build_post_landing_pads, connect_post_landing_pads): Remove. + (dw2_build_landing_pads): Rewrite to use lp_array. + (struct sjlj_lp_info, sjlj_find_directly_reachable_regions): Remove. + (sjlj_assign_call_site_values): Rewrite to use lp_array. + (sjlj_emit_dispatch_table, sjlj_build_landing_pads): Likewise. + (sjlj_mark_call_sites): Update for landing pad numbers. + (finish_eh_generation): Rewrite. + (gate_handle_eh): Do nothing for no eh tree. + (pass_rtl_eh): Move up near finish_eh_generation. + (remove_eh_landing_pad): New. + (remove_eh_handler): Export. + (remove_eh_region, remove_eh_handler_and_replace): Remove. + (for_each_eh_label): Rewrite to use lp_array. + (make_reg_eh_region_note): New. + (make_reg_eh_region_note_nothrow_nononlocal): New. + (insn_could_throw_p): New. + (copy_reg_eh_region_note_forward): New. + (copy_reg_eh_region_note_backward): New. + (check_handled, add_reachable_handler): Remove. + (reachable_next_level, foreach_reachable_handler): Remove. + (arh_to_landing_pad, arh_to_label, reachable_handlers): Remove. + (get_eh_region_and_lp_from_rtx): New. + (get_eh_region_from_rtx): New. + (can_throw_internal_1, can_throw_external_1): Remove. + (can_throw_internal): Use get_eh_region_from_rtx. + (can_throw_external): Use get_eh_region_and_lp_from_rtx. + (insn_nothrow_p, can_nonlocal_goto): New. + (expand_builtin_eh_common, expand_builtin_eh_pointer): New. + (expand_builtin_eh_filter, expand_builtin_eh_copy_values): New. + (add_action_record): Use VEC not varray. + (collect_one_action_chain): Update for eh_region changes. + (convert_to_eh_region_ranges): Make static. Use VEC not varray. + Use get_eh_region_and_lp_from_rtx. + (gate_convert_to_eh_region_ranges): New. + (pass_convert_to_eh_region_ranges): Use it. + (push_uleb128, push_sleb128): Use VEC not varray. + (output_one_function_exception_table): Likewise. + (dump_eh_tree): Update for eh_region changes. + (verify_eh_tree): Likewise. + (verify_eh_region, default_init_unwind_resume_libfunc): Remove. + * tree-eh.c: Include target.h. + (add_stmt_to_eh_lp_fn): Rename from add_stmt_to_eh_region_fn. + Don't disallow GIMPLE_RESX; adjust argument check. + (add_stmt_to_eh_lp): Rename from add_stmt_to_eh_region. + (record_stmt_eh_region): Update for landing pad numbers; + generate a landing pad if necessary. + (remove_stmt_from_eh_lp): Rename from remove_stmt_from_eh_region. + (remove_stmt_from_eh_lp_fn): Similarly. + (lookup_stmt_eh_lp_fn): Rename from lookup_stmt_eh_region_fn. + Update for lp numbers; don't special case missing throw_stmt_table. + (lookup_expr_eh_lp): Similarly. + (lookup_stmt_eh_lp): Rename from lookup_stmt_eh_region. + (eh_seq, eh_region_may_contain_throw): New. + (struct leh_state): Add ehp_region. + (struct leh_tf_state): Remove eh_label. + (emit_post_landing_pad): New. + (emit_resx, emit_eh_dispatch): New. + (note_eh_region_may_contain_throw): New. + (frob_into_branch_around): Take eh_region not eh label; + emit eh code into eh_seq. + (honor_protect_cleanup_actions): Early exit for no actions. Don't + handle EXC_PTR_EXPR, FILTER_EXPR. Use gimple_build_eh_must_not_throw, + lower_eh_must_not_throw. Emit code to eh_seq. + (lower_try_finally_nofallthru): Emit eh code to eh_seq. + (lower_try_finally_onedest): Likewise. + (lower_try_finally_copy): Likewise. + (lower_try_finally_switch): Likewise. + (lower_try_finally): Initialize ehp_region. + (lower_catch): Update for eh_catch objects. + (lower_eh_filter): Don't handle must_not_throw. + (lower_eh_must_not_throw): New. + (lower_cleanup): Don't set eh_label. + (lower_eh_constructs_2): Resolve eh builtins. + Handle GIMPLE_EH_MUST_NOT_THROW. + (lower_eh_constructs): Initialize eh_region_may_contain_throw. + Add eh_seq to the end of the function body. + (make_eh_dispatch_edges): New. + (make_eh_edge): Remove. + (make_eh_edges): Simplify for landing pads. + (redirect_eh_edge_1): New. + (redirect_eh_edge): Use it. + (redirect_eh_dispatch_edge): New. + (stmt_could_throw_p): Use a switch. Allow RESX. + (stmt_can_throw_external): Use lookup_stmt_eh_lp. + (stmt_can_throw_internal): Likewise. + (maybe_clean_eh_stmt_fn, maybe_clean_eh_stmt): New. + (maybe_clean_or_replace_eh_stmt): Update for landing pads. + (maybe_duplicate_eh_stmt_fn, maybe_duplicate_eh_stmt): New. + (gate_refactor_eh): New. + (pass_refactor_eh): Use it. + (lower_resx, execute_lower_resx, pass_lower_resx): New. + (lower_eh_dispatch, execute_lower_eh_dispatch): New. + (gate_lower_ehcontrol, pass_lower_eh_dispatch): New. + (remove_unreachable_handlers): Rename from + tree_remove_unreachable_handlers; rewrite for landing pads; + call remove_eh_handler directly. + (remove_unreachable_handlers_no_lp): New. + (unsplit_eh, unsplit_all_eh): New. + (tree_empty_eh_handler_p, all_phis_safe_to_merge): Remove. + (cleanup_empty_eh_merge_phis, cleanup_empty_eh_move_lp): New. + (cleanup_empty_eh_unsplit): New. + (cleanup_empty_eh): Rewrite. + (cleanup_all_empty_eh): New. + (execute_cleanup_eh): Rename from cleanup_eh. Remove unreachable + handlers first. Use unsplit_all_eh, cleanup_all_empty_eh. + (gate_cleanup_eh): New. + (pass_cleanup_eh): Use it. + (verify_eh_edges): Move later in file. Expect one EH edge. + (verify_eh_dispatch_edge): New. + + * Makefile.in (FUNCTION_H): Use vecprim.h, not varray.h. + (gtype-desc.o): Add TARGET_H. + (tree.o): Use EXCEPT_H, not except.h. + (cfgbuild.o): Add EXPR_H. + (GTFILES): Add vecprim.h. + * builtins.c (expand_builtin): Handle BUILT_IN_EH_POINTER, + BUILT_IN_EH_FILTER, BUILT_IN_EH_COPY_VALUES. + * builtins.def (BUILT_IN_UNWIND_RESUME, BUILT_IN_EH_POINTER, + BUILT_IN_EH_FILTER, BUILT_IN_EH_COPY_VALUES): New. + * calls.c (emit_call_1): Use make_reg_eh_region_note. + * cfgbuild.c (control_flow_insn_p): Use can_nonlocal_goto; tidy + calls to can_throw_internal. + (rtl_make_eh_edge): Use get_eh_landing_pad_from_rtx. + (make_edges): Don't handle RESX; use can_nonlocal_goto. + * cfgexpand.c (expand_gimple_stmt_1): Don't handle RESX. + (expand_gimple_stmt): Use make_reg_eh_region_note. + (expand_debug_expr): Don't handle EXC_PTR_EXPR and FILTER_EXPR. + (gimple_expand_cfg): Don't call convert_from_eh_region_ranges, + or find_exception_handler_labels. + * cfgrtl.c (rtl_verify_flow_info_1): Don't handle RESX. Assert + there is exacly one EH edge. Use can_nonlocal_goto and + can_throw_internal. + * cgraphunit.c (update_call_expr): Use maybe_clean_eh_stmt_fn. + (cgraph_materialize_all_clones): Use maybe_clean_or_replace_eh_stmt. + * combine.c (can_combine_p, try_combine): Use insn_nothrow_p. + * cse.c (count_reg_usage, insn_live_p): Use insn_could_throw_p. + * dce.c (deletable_insn_p_1): Don't test may_trap_p. + (deletable_insn_p): Use insn_nothrow_p; reorder nonjump insn test. + * dse.c (scan_insn): Use insn_could_throw_p. + * emit-rtl.c (try_split): Use copy_reg_eh_region_note_backward. + * expr.c (expand_expr_real): Use make_reg_eh_region_note. + (expand_expr_real_1): Don't handle RESX, EXC_PTR, or FILTER_EXPR. + * fold-const.c (tree_expr_nonnegative_warnv_p): Don't handle + EXC_PTR_EXPR or FILTER_EXPR. + (tree_expr_nonzero_warnv_p): Likewise. + * function.h: Include vecprim.h, not varray.h + (struct rtl_eh): Remove filter, exc_ptr, built_landing_pad members; + move ttype_data and ehspec_data members to struct eh_status; change + action_record_data member to a VEC. + * gcse.c (hash_scan_set): Use can_throw_internal. + * gengtype.c (open_base_files): Add target.h to gtype-desc.c. + * gimple-iterator.c (gsi_replace): Use maybe_clean_or_replace_eh_stmt. + * gimple-low.c (lower_stmt): Handle GIMPLE_EH_MUST_NOT_THROW. + (block_may_fallthru): Don't handle RESX_EXPR. + * gimple-pretty-print.c (dump_gimple_label): Dump EH_LANDING_PAD_NR. + (dump_gimple_eh_must_not_throw, dump_gimple_eh_dispatch): New. + (dump_gimple_stmt): Dump landing pad information with TDF_EH; + handle GIMPLE_EH_MUST_NOT_THROW, GIMPLE_EH_DISPATCH. + * gimple.c (gss_for_code): Handle GIMPLE_EH_MUST_NOT_THROW, + GIMPLE_EH_DISPATCH, GIMPLE_RESX. + (gimple_size): Likewise. + (gimple_build_eh_dispatch, gimple_build_eh_must_not_throw): New. + (gimple_build_resx): Use gimple_build_with_ops. + (DEFTREECODE): Don't handle EXC_PTR_EXPR, FILTER_EXPR. + (is_gimple_val): Likewise. + (is_gimple_stmt): Remove RESX_EXPR. + * gimple.def (GIMPLE_EH_MUST_NOT_THROW, GIMPLE_EH_DISPATCH): New. + (GIMPLE_RESX): Reorder with other EH constructs. + * gimple.h (struct gimple_statement_eh_mnt): New. + (struct gimple_statement_eh_ctrl): Rename from gimple_statement_resx. + (gimple_eh_filter_must_not_throw): Remove. + (gimple_eh_filter_set_must_not_throw): Remove. + (gimple_eh_must_not_throw_fndecl): New. + (gimple_eh_dispatch_region, gimple_eh_dispatch_set_region): New. + (is_gimple_resx): New. + * gimplify.c (gimplify_expr): Don't handle EXC_PTR_EXPR, RESX_EXPR. + Don't copy EH_FILTER_MUST_NOT_THROW. + * gsstruct.def (GSS_EH_MNT, GSS_EHCONTROL): New. + * ipa-inline.c (estimate_function_body_sizes): Don't try to + handle must_not_throw_labels specially. + * ipa-pure-const.c (check_call): Update debug statement for LP. + * ipa-type-escape.c (check_operand): Don't handle EXC_PTR or FILTER. + * ipa-utils.c (get_base_var): Likewise. + * libfunc.h (LTI_unwind_resume, unwind_resume_libfunc): Remove. + * lower-subreg.c (move_eh_region_note): Remove. + (resolve_simple_move): Use copy_reg_eh_region_note_forward. + * omp-low.c (new_omp_context): Update for eh_lp_nr. + (create_task_copyfn): Likewise. + (maybe_catch_exception): Use gimple_build_eh_filter. + * optabs.c (emit_libcall_block): Update test for no-nonlocal-goto + REG_EH_REGION. Use make_reg_eh_region_note_nothrow_nononlocal. + * passes.c (init_optimization_passes): Add pass_lower_eh_dispatch + and pass_lower_resx. + * print-tree.c (print_node): Dump EH_LANDING_PAD_NR. + * recog.c (peephole2_optimize): Use copy_reg_eh_region_note_backward, + can_throw_internal, can_nonlocal_goto. + * reload1.c (fixup_eh_region_note): Use insn_could_throw_p, + copy_reg_eh_region_note_forward. + (emit_input_reload_insns): Use copy_reg_eh_region_note_forward. + (emit_output_reload_insns): Likewise. + (copy_eh_notes): Remove. + * rtl.def (RESX): Remove. + * rtl.h: Update declarations. + * sese.c (graphite_copy_stmts_from_block): Use maybe_duplicate_eh_stmt. + * tree-cfg.c (make_edges): Handle GIMPLE_EH_DISPATCH. + (update_eh_label): Remove. + (cleanup_dead_labels_eh): New. + (cleanup_deal_labels): Use it instead of update_eh_label. + (gimple_merge_blocks): Update landing pad data structure when + removing a landing pad label. + (remove_useless_stmts_tc): Remove gimple_eh_filter_must_not_throw + test; handle GIMPLE_EH_MUST_NOT_THROW. + (is_ctrl_altering_stmt): Handle GIMPLE_EH_DISPATCH. + (verify_gimple_assign_single): Don't handle EXC_PTR or FILTER_EXPR. + (verify_types_in_gimple_stmt): Handle GIMPLE_EH_DISPATCH. + (verify_stmt): Likewise. Verify landing pads. + (gimple_redirect_edge_and_branch): Handle GIMPLE_EH_DISPATCH. + (gimple_duplicate_bb): Use maybe_duplicate_eh_stmt. + (struct move_stmt_d): Add eh_map. + (move_stmt_eh_region_nr, move_stmt_eh_region_tree_nr): New. + (move_stmt_r): Remap eh region numbers in builtin calls, + resx and eh_dispatch. + (move_block_to_fn): Remove eh_offset parameter. Use + maybe_duplicate_eh_stmt_fn. + (find_outermost_region_in_block): Operate on eh_region pointers + instead of region numbers. + (move_sese_region_to_fn): Expect eh_map instead of eh_offset from + duplicate_eh_regions. + * tree-cfgcleanup.c (tree_forwarder_block_p): Move entry block edge + test earlier. Disallow EH landing pads. + * tree-cfa.c (create_tree_common_ann): Don't set ann->rn. + * tree-flow.h: Update declarations. + (struct tree_ann_common_d): Replace rn with lp_nr. + * tree-inline.c (copy_tree_body_r): Don't handle RESX_EXPR. + (remap_eh_region_nr, remap_eh_region_tree_nr): New. + (remap_gimple_stmt): Remap eh region numbers in builtin calls, + resx and eh_dispatch. + (copy_bb): Use maybe_duplicate_eh_stmt_fn. + (copy_edges_for_bb): Use make_eh_dispatch_edges. + (copy_cfg_body): Expect eh_map instead of eh_region_offset + from duplicate_eh_regions. + (estimate_num_insns): Don't handle EXC_PTR_EXPR or FILTER_EXPR; + update RESX; handle EH_DISPATCH. + (expand_call_inline): Set eh_lp_nr, not eh_region. + (maybe_inline_call_in_expr): Likewise. + * tree-inline.h (struct copy_body_data): Replace eh_region with + eh_lp_nr, eh_region_offset with eh_map. + * tree-optimize.c (execute_fixup_cfg): Use maybe_clean_eh_stmt. + * tree-pass.h (pass_lower_eh_dispatch, pass_lower_resx): New. + * tree-pretty-print.c (dump_generic_node): Don't handle + EXC_PTR_EXPR, FILTER_EXPR, RESX_EXPR. + * tree-sra.c (scan_function): Use maybe_clean_eh_stmt. + * tree-ssa-alias.c (ref_maybe_used_by_call_p_1): Don't handle + EXC_PTR_EXPR, FILTER_EXPR. + * tree-ssa-operands.c (get_expr_operands): Likewise. + * tree-ssa-propagate.c (valid_gimple_rhs_p): Likewise. + * tree-ssa-sccvn.c (copy_reference_ops_from_ref): Likewise. + (ao_ref_init_from_vn_reference): Likewise. + * tree-ssa-sink.c (statement_sink_location): Likewise. + * tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Likewise. + (mark_virtual_phi_result_for_renaming): Export. Tidy. + * tree-ssa-pre.c (get_or_alloc_expr_for): Don't handle + EXC_PTR_EXPR, FILTER_EXPR. + (is_exception_related): Remove. + (compute_avail): Don't call it. + * tree-ssa-structalias.c: Remove VEC definitions for int and unsigned. + * tree.c (find_decls_types_in_eh_region): Update for eh_region changes. + (find_decls_types_in_node): Use FOR_ALL_EH_REGION_FN. + (build_common_builtin_nodes): Add enable_cxa_end_cleanup parameter. + Build EH builtins. + (build_resx): Remove. + * tree.def (EXC_PTR_EXPR, FILTER_EXPR, RESX_EXPR): Remove. + * tree.h: Update declarations. + (EH_FILTER_MUST_NOT_THROW): Remove. + (struct tree_label_decl): Add eh_landing_pad_nr. + (EH_LANDING_PAD_NR): New. + * value-prof.c (gimple_ic): Tidy variable names. Update for + landing pad numbers. + (gimple_stringop_fixed_value): Tidy variable names. Assert + that neither call stmt can throw. + * vecprim.h (uchar): New. + (VEC(uchar,heap), VEC(uchar,gc)): New. + + * c-common.c (c_define_builtins): Update call to + build_common_builtin_nodes. + * c-parser.c (c_parse_file): Don't call + default_init_unwind_resume_libfunc. + 2009-09-14 Richard Sandiford <rdsandiford@googlemail.com> * config/mips/mips-protos.h (mips_cfun_has_cprestore_slot_p): Declare. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index d89fb2a..2e406b6 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -856,7 +856,7 @@ RECOG_H = recog.h ALIAS_H = alias.h coretypes.h EMIT_RTL_H = emit-rtl.h FLAGS_H = flags.h options.h -FUNCTION_H = function.h $(TREE_H) $(HASHTAB_H) varray.h +FUNCTION_H = function.h $(TREE_H) $(HASHTAB_H) vecprim.h EXPR_H = expr.h insn-config.h $(FUNCTION_H) $(RTL_H) $(FLAGS_H) $(TREE_H) $(MACHMODE_H) $(EMIT_RTL_H) OPTABS_H = optabs.h insn-codes.h REGS_H = regs.h varray.h $(MACHMODE_H) $(OBSTACK_H) $(BASIC_BLOCK_H) $(FUNCTION_H) @@ -2127,7 +2127,7 @@ gtype-desc.o: gtype-desc.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ hard-reg-set.h $(BASIC_BLOCK_H) cselib.h $(INSN_ADDR_H) $(OPTABS_H) \ libfuncs.h debug.h $(GGC_H) $(CGRAPH_H) $(TREE_FLOW_H) reload.h \ $(CPP_ID_DATA_H) tree-chrec.h $(CFGLAYOUT_H) $(EXCEPT_H) output.h \ - $(CFGLOOP_H) + $(CFGLOOP_H) $(TARGET_H) ggc-common.o: ggc-common.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(GGC_H) $(HASHTAB_H) $(TOPLEV_H) $(PARAMS_H) hosthooks.h \ @@ -2163,10 +2163,11 @@ langhooks.o : langhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ intl.h $(GIMPLE_H) tree.o : tree.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ all-tree.def $(FLAGS_H) $(FUNCTION_H) $(PARAMS_H) \ - $(TOPLEV_H) $(GGC_H) $(HASHTAB_H) $(TARGET_H) output.h $(TM_P_H) langhooks.h \ - $(REAL_H) gt-tree.h $(TREE_INLINE_H) tree-iterator.h $(BASIC_BLOCK_H) \ - $(TREE_FLOW_H) $(OBSTACK_H) pointer-set.h fixed-value.h tree-pass.h \ - langhooks-def.h $(DIAGNOSTIC_H) $(CGRAPH_H) $(TIMEVAR_H) except.h debug.h + $(TOPLEV_H) $(GGC_H) $(HASHTAB_H) $(TARGET_H) output.h $(TM_P_H) \ + langhooks.h $(REAL_H) gt-tree.h $(TREE_INLINE_H) tree-iterator.h \ + $(BASIC_BLOCK_H) $(TREE_FLOW_H) $(OBSTACK_H) pointer-set.h fixed-value.h \ + tree-pass.h langhooks-def.h $(DIAGNOSTIC_H) $(CGRAPH_H) $(TIMEVAR_H) \ + $(EXCEPT_H) debug.h tree-dump.o: tree-dump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_H) langhooks.h $(TOPLEV_H) $(SPLAY_TREE_H) $(TREE_DUMP_H) \ tree-iterator.h $(TREE_PASS_H) $(DIAGNOSTIC_H) $(REAL_H) fixed-value.h @@ -2972,7 +2973,7 @@ cfganal.o : cfganal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TIMEVAR_H) $(OBSTACK_H) $(TOPLEV_H) vecprim.h cfgbuild.o : cfgbuild.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h $(TOPLEV_H) \ - $(FUNCTION_H) $(EXCEPT_H) $(TIMEVAR_H) $(TREE_H) + $(FUNCTION_H) $(EXCEPT_H) $(TIMEVAR_H) $(TREE_H) $(EXPR_H) cfgcleanup.o : cfgcleanup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TIMEVAR_H) hard-reg-set.h output.h $(FLAGS_H) $(RECOG_H) \ $(TOPLEV_H) insn-config.h cselib.h $(TARGET_H) $(TM_P_H) $(PARAMS_H) \ @@ -3460,6 +3461,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(host_xm_file_list) \ $(tm_file_list) $(HASHTAB_H) $(SPLAY_TREE_H) $(srcdir)/bitmap.h \ $(srcdir)/alias.h $(srcdir)/coverage.c $(srcdir)/rtl.h \ + $(srcdir)/vecprim.h \ $(srcdir)/optabs.h $(srcdir)/tree.h $(srcdir)/varray.h $(srcdir)/libfuncs.h $(SYMTAB_H) \ $(srcdir)/real.h $(srcdir)/function.h $(srcdir)/insn-addr.h $(srcdir)/hwint.h \ $(srcdir)/fixed-value.h \ diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index 368d494..5747227 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,12 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * gcc-interface/misc.c (gnat_init_gcc_eh): Don't call + default_init_unwind_resume_libfunc. + * gcc-interface/trans.c (Exception_Handler_to_gnu_zcx): Use + __builtin_eh_pointer. + * gcc-interface/utils.c (gnat_install_builtins): Update call + to build_common_builtin_nodes. + 2009-09-13 Richard Guenther <rguenther@suse.de> Rafael Avila de Espindola <espindola@google.com> diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c index 261351f..26df68d 100644 --- a/gcc/ada/gcc-interface/misc.c +++ b/gcc/ada/gcc-interface/misc.c @@ -435,7 +435,6 @@ gnat_init_gcc_eh (void) using_eh_for_cleanups (); lang_eh_type_covers = gnat_eh_type_covers; - default_init_unwind_resume_libfunc (); /* Turn on -fexceptions and -fnon-call-exceptions. The first one triggers the generation of the necessary exception runtime tables. The second one diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c index 29ab72a..61a3aea 100644 --- a/gcc/ada/gcc-interface/trans.c +++ b/gcc/ada/gcc-interface/trans.c @@ -3304,7 +3304,7 @@ Exception_Handler_to_gnu_zcx (Node_Id gnat_node) a new occurrence on top of the stack, which means that this top does not necessarily match the occurrence this handler was dealing with. - The EXC_PTR_EXPR object references the exception occurrence being + __builtin_eh_pointer references the exception occurrence being propagated. Upon handler entry, this is the exception for which the handler is triggered. This might not be the case upon handler exit, however, as we might have a new occurrence propagated by the handler's @@ -3312,7 +3312,10 @@ Exception_Handler_to_gnu_zcx (Node_Id gnat_node) We use a local variable to retrieve the incoming value at handler entry time, and reuse it to feed the end_handler hook's argument at exit. */ - gnu_current_exc_ptr = build0 (EXC_PTR_EXPR, ptr_type_node); + + gnu_current_exc_ptr + = build_call_expr (built_in_decls [BUILT_IN_EH_POINTER], + 1, integer_zero_node); gnu_incoming_exc_ptr = create_var_decl (get_identifier ("EXPTR"), NULL_TREE, ptr_type_node, gnu_current_exc_ptr, false, false, false, false, NULL, diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c index 9748caf..bd6a840 100644 --- a/gcc/ada/gcc-interface/utils.c +++ b/gcc/ada/gcc-interface/utils.c @@ -5439,7 +5439,7 @@ gnat_install_builtins (void) know about internal specificities and control attributes accordingly, for instance __builtin_alloca vs no-throw and -fstack-check. We will ignore the generic definition from builtins.def. */ - build_common_builtin_nodes (); + build_common_builtin_nodes (false); /* Now, install the target specific builtins, such as the AltiVec family on ppc, and the common set as exposed by builtins.def. */ diff --git a/gcc/builtins.c b/gcc/builtins.c index d4801b1..ee6417d6 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -6940,6 +6940,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, #endif case BUILT_IN_EXTEND_POINTER: return expand_builtin_extend_pointer (CALL_EXPR_ARG (exp, 0)); + case BUILT_IN_EH_POINTER: + return expand_builtin_eh_pointer (exp); + case BUILT_IN_EH_FILTER: + return expand_builtin_eh_filter (exp); + case BUILT_IN_EH_COPY_VALUES: + return expand_builtin_eh_copy_values (exp); case BUILT_IN_VA_START: return expand_builtin_va_start (exp); diff --git a/gcc/builtins.def b/gcc/builtins.def index 8d16936..00287c7 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -759,6 +759,12 @@ DEF_BUILTIN (BUILT_IN_EMUTLS_REGISTER_COMMON, true, true, true, ATTR_NOTHROW_LIST, false, !targetm.have_tls) +/* Exception support. */ +DEF_BUILTIN_STUB (BUILT_IN_UNWIND_RESUME, "__builtin_unwind_resume") +DEF_BUILTIN_STUB (BUILT_IN_EH_POINTER, "__builtin_eh_pointer") +DEF_BUILTIN_STUB (BUILT_IN_EH_FILTER, "__builtin_eh_filter") +DEF_BUILTIN_STUB (BUILT_IN_EH_COPY_VALUES, "__builtin_eh_copy_values") + /* Synchronization Primitives. */ #include "sync-builtins.def" diff --git a/gcc/c-common.c b/gcc/c-common.c index a19489c..25c0c01 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -4574,7 +4574,7 @@ c_define_builtins (tree va_list_ref_type_node, tree va_list_arg_type_node) targetm.init_builtins (); - build_common_builtin_nodes (); + build_common_builtin_nodes (c_dialect_cxx ()); if (flag_mudflap) mudflap_init (); diff --git a/gcc/c-parser.c b/gcc/c-parser.c index ddb81e1..feec8a4 100644 --- a/gcc/c-parser.c +++ b/gcc/c-parser.c @@ -8604,10 +8604,7 @@ c_parse_file (void) /* Initialize EH, if we've been told to do so. */ if (flag_exceptions) - { - default_init_unwind_resume_libfunc (); - using_eh_for_cleanups (); - } + using_eh_for_cleanups (); c_parser_translation_unit (the_parser); the_parser = NULL; diff --git a/gcc/calls.c b/gcc/calls.c index 2063909..16229cc 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -376,10 +376,8 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU if (ecf_flags & ECF_LOOPING_CONST_OR_PURE) RTL_LOOPING_CONST_OR_PURE_CALL_P (call_insn) = 1; - /* If this call can't throw, attach a REG_EH_REGION reg note to that - effect. */ - if (ecf_flags & ECF_NOTHROW) - add_reg_note (call_insn, REG_EH_REGION, const0_rtx); + /* Create a nothrow REG_EH_REGION note, if needed. */ + make_reg_eh_region_note (call_insn, ecf_flags, 0); if (ecf_flags & ECF_NORETURN) add_reg_note (call_insn, REG_NORETURN, const0_rtx); diff --git a/gcc/cfgbuild.c b/gcc/cfgbuild.c index 6e941bf..7d87a7a 100644 --- a/gcc/cfgbuild.c +++ b/gcc/cfgbuild.c @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "output.h" #include "function.h" #include "except.h" +#include "expr.h" #include "toplev.h" #include "timevar.h" @@ -80,8 +81,6 @@ inside_basic_block_p (const_rtx insn) bool control_flow_insn_p (const_rtx insn) { - rtx note; - switch (GET_CODE (insn)) { case NOTE: @@ -101,21 +100,20 @@ control_flow_insn_p (const_rtx insn) || find_reg_note (insn, REG_NORETURN, 0)) && GET_CODE (PATTERN (insn)) != COND_EXEC) return true; + /* Call insn may return to the nonlocal goto handler. */ - return ((nonlocal_goto_handler_labels - && (0 == (note = find_reg_note (insn, REG_EH_REGION, - NULL_RTX)) - || INTVAL (XEXP (note, 0)) >= 0)) - /* Or may trap. */ - || can_throw_internal (insn)); + if (can_nonlocal_goto (insn)) + return true; + break; case INSN: /* Treat trap instructions like noreturn calls (same provision). */ if (GET_CODE (PATTERN (insn)) == TRAP_IF && XEXP (PATTERN (insn), 0) == const1_rtx) return true; - - return (flag_non_call_exceptions && can_throw_internal (insn)); + if (!flag_non_call_exceptions) + return false; + break; case BARRIER: /* It is nonsense to reach barrier when looking for the @@ -126,6 +124,8 @@ control_flow_insn_p (const_rtx insn) default: gcc_unreachable (); } + + return can_throw_internal (insn); } @@ -155,16 +155,23 @@ make_label_edge (sbitmap edge_cache, basic_block src, rtx label, int flags) void rtl_make_eh_edge (sbitmap edge_cache, basic_block src, rtx insn) { - int is_call = CALL_P (insn) ? EDGE_ABNORMAL_CALL : 0; - rtx handlers, i; + eh_landing_pad lp = get_eh_landing_pad_from_rtx (insn); - handlers = reachable_handlers (insn); + if (lp) + { + rtx label = lp->landing_pad; - for (i = handlers; i; i = XEXP (i, 1)) - make_label_edge (edge_cache, src, XEXP (i, 0), - EDGE_ABNORMAL | EDGE_EH | is_call); + /* During initial rtl generation, use the post_landing_pad. */ + if (label == NULL) + { + gcc_assert (lp->post_landing_pad); + label = label_rtx (lp->post_landing_pad); + } - free_INSN_LIST_list (&handlers); + make_label_edge (edge_cache, src, label, + EDGE_ABNORMAL | EDGE_EH + | (CALL_P (insn) ? EDGE_ABNORMAL_CALL : 0)); + } } /* States of basic block as seen by find_many_sub_basic_blocks. */ @@ -253,13 +260,9 @@ make_edges (basic_block min, basic_block max, int update_p) { rtx tmp; - /* Recognize exception handling placeholders. */ - if (GET_CODE (PATTERN (insn)) == RESX) - rtl_make_eh_edge (edge_cache, bb, insn); - /* Recognize a non-local goto as a branch outside the current function. */ - else if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX)) + if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX)) ; /* Recognize a tablejump and do the right thing. */ @@ -333,12 +336,7 @@ make_edges (basic_block min, basic_block max, int update_p) gotos do not have their addresses taken, then only calls to those functions or to other nested functions that use them could possibly do nonlocal gotos. */ - - /* We do know that a REG_EH_REGION note with a value less - than 0 is guaranteed not to perform a non-local goto. */ - rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - - if (!note || INTVAL (XEXP (note, 0)) >= 0) + if (can_nonlocal_goto (insn)) for (x = nonlocal_goto_handler_labels; x; x = XEXP (x, 1)) make_label_edge (edge_cache, bb, XEXP (x, 0), EDGE_ABNORMAL | EDGE_ABNORMAL_CALL); @@ -446,8 +444,10 @@ find_bb_boundaries (basic_block bb) { enum rtx_code code = GET_CODE (insn); - /* On code label, split current basic block. */ - if (code == CODE_LABEL) + /* In case we've previously seen an insn that effects a control + flow transfer, split the block. */ + if ((flow_transfer_insn || code == CODE_LABEL) + && inside_basic_block_p (insn)) { fallthru = split_block (bb, PREV_INSN (insn)); if (flow_transfer_insn) @@ -465,36 +465,10 @@ find_bb_boundaries (basic_block bb) bb = fallthru->dest; remove_edge (fallthru); flow_transfer_insn = NULL_RTX; - if (LABEL_ALT_ENTRY_P (insn)) + if (code == CODE_LABEL && LABEL_ALT_ENTRY_P (insn)) make_edge (ENTRY_BLOCK_PTR, bb, 0); } - /* __builtin_unreachable () may cause a barrier to be emitted in - the middle of a BB. We need to split it in the same manner - as if the barrier were preceded by a control_flow_insn_p - insn. */ - if (code == BARRIER && !flow_transfer_insn) - flow_transfer_insn = prev_nonnote_insn_bb (insn); - - /* In case we've previously seen an insn that effects a control - flow transfer, split the block. */ - if (flow_transfer_insn && inside_basic_block_p (insn)) - { - fallthru = split_block (bb, PREV_INSN (insn)); - BB_END (bb) = flow_transfer_insn; - - /* Clean up the bb field for the insns between the blocks. */ - for (x = NEXT_INSN (flow_transfer_insn); - x != BB_HEAD (fallthru->dest); - x = NEXT_INSN (x)) - if (!BARRIER_P (x)) - set_block_for_insn (x, NULL); - - bb = fallthru->dest; - remove_edge (fallthru); - flow_transfer_insn = NULL_RTX; - } - if (control_flow_insn_p (insn)) flow_transfer_insn = insn; if (insn == end) diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index d1c2be2..0ed6bd5 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -1820,9 +1820,6 @@ expand_gimple_stmt_1 (gimple stmt) case GIMPLE_NOP: case GIMPLE_PREDICT: break; - case GIMPLE_RESX: - expand_resx_stmt (stmt); - break; case GIMPLE_SWITCH: expand_case (stmt); break; @@ -1961,7 +1958,7 @@ expand_gimple_stmt_1 (gimple stmt) static rtx expand_gimple_stmt (gimple stmt) { - int rn = -1; + int lp_nr = 0; rtx last = NULL; location_t saved_location = input_location; @@ -1993,8 +1990,8 @@ expand_gimple_stmt (gimple stmt) input_location = saved_location; /* Mark all insns that may trap. */ - rn = lookup_stmt_eh_region (stmt); - if (rn >= 0) + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr) { rtx insn; for (insn = next_real_insn (last); insn; @@ -2005,9 +2002,8 @@ expand_gimple_stmt (gimple stmt) may_trap_p instruction may throw. */ && GET_CODE (PATTERN (insn)) != CLOBBER && GET_CODE (PATTERN (insn)) != USE - && (CALL_P (insn) - || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))))) - add_reg_note (insn, REG_EH_REGION, GEN_INT (rn)); + && insn_could_throw_p (insn)) + make_reg_eh_region_note (insn, 0, lp_nr); } } @@ -2540,15 +2536,6 @@ expand_debug_expr (tree exp) op0, GEN_INT (bitsize), GEN_INT (bitpos)); } - case EXC_PTR_EXPR: - /* ??? Do not call get_exception_pointer(), we don't want to gen - it if it hasn't been created yet. */ - return get_exception_pointer (); - - case FILTER_EXPR: - /* Likewise get_exception_filter(). */ - return get_exception_filter (); - case ABS_EXPR: return gen_rtx_ABS (mode, op0); @@ -3556,12 +3543,10 @@ gimple_expand_cfg (void) set_curr_insn_block (DECL_INITIAL (current_function_decl)); insn_locators_finalize (); - /* Convert tree EH labels to RTL EH labels and zap the tree EH table. */ - convert_from_eh_region_ranges (); + /* Zap the tree EH table. */ set_eh_throw_stmt_table (cfun, NULL); rebuild_jump_labels (get_insns ()); - find_exception_handler_labels (); FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb) { diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c index 4c4b3b7..a7e93dd 100644 --- a/gcc/cfgrtl.c +++ b/gcc/cfgrtl.c @@ -1873,12 +1873,16 @@ rtl_verify_flow_info_1 (void) n_abnormal++; } - if (n_eh && GET_CODE (PATTERN (BB_END (bb))) != RESX - && !find_reg_note (BB_END (bb), REG_EH_REGION, NULL_RTX)) + if (n_eh && !find_reg_note (BB_END (bb), REG_EH_REGION, NULL_RTX)) { error ("missing REG_EH_REGION note in the end of bb %i", bb->index); err = 1; } + if (n_eh > 1) + { + error ("too many eh edges %i", bb->index); + err = 1; + } if (n_branch && (!JUMP_P (BB_END (bb)) || (n_branch > 1 && (any_uncondjump_p (BB_END (bb)) @@ -1894,7 +1898,8 @@ rtl_verify_flow_info_1 (void) } if (n_branch != 1 && any_uncondjump_p (BB_END (bb))) { - error ("wrong amount of branch edges after unconditional jump %i", bb->index); + error ("wrong number of branch edges after unconditional jump %i", + bb->index); err = 1; } if (n_branch != 1 && any_condjump_p (BB_END (bb)) @@ -2217,39 +2222,33 @@ purge_dead_edges (basic_block bb) /* Cleanup abnormal edges caused by exceptions or non-local gotos. */ for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) { + bool remove = false; + /* There are three types of edges we need to handle correctly here: EH edges, abnormal call EH edges, and abnormal call non-EH edges. The latter can appear when nonlocal gotos are used. */ - if (e->flags & EDGE_EH) + if (e->flags & EDGE_ABNORMAL_CALL) { - if (can_throw_internal (insn) - /* If this is a call edge, verify that this is a call insn. */ - && (! (e->flags & EDGE_ABNORMAL_CALL) - || CALL_P (insn))) - { - ei_next (&ei); - continue; - } + if (!CALL_P (insn)) + remove = true; + else if (can_nonlocal_goto (insn)) + ; + else if ((e->flags & EDGE_EH) && can_throw_internal (insn)) + ; + else + remove = true; } - else if (e->flags & EDGE_ABNORMAL_CALL) + else if (e->flags & EDGE_EH) + remove = !can_throw_internal (insn); + + if (remove) { - if (CALL_P (insn) - && (! (note = find_reg_note (insn, REG_EH_REGION, NULL)) - || INTVAL (XEXP (note, 0)) >= 0)) - { - ei_next (&ei); - continue; - } + remove_edge (e); + df_set_bb_dirty (bb); + purged = true; } else - { - ei_next (&ei); - continue; - } - - remove_edge (e); - df_set_bb_dirty (bb); - purged = true; + ei_next (&ei); } if (JUMP_P (insn)) diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 5551c72..2ad0718 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1561,10 +1561,7 @@ update_call_expr (struct cgraph_node *new_version) { struct function *inner_function = DECL_STRUCT_FUNCTION (e->caller->decl); gimple_call_set_fndecl (e->call_stmt, new_version->decl); - /* Update EH information too, just in case. */ - if (!stmt_could_throw_p (e->call_stmt) - && lookup_stmt_eh_region_fn (inner_function, e->call_stmt)) - remove_stmt_from_eh_region_fn (inner_function, e->call_stmt); + maybe_clean_eh_stmt_fn (inner_function, e->call_stmt); } } @@ -1909,9 +1906,7 @@ cgraph_materialize_all_clones (void) gsi_replace (&gsi, new_stmt, true); /* Update EH information too, just in case. */ - if (!stmt_could_throw_p (new_stmt) - && lookup_stmt_eh_region (new_stmt)) - remove_stmt_from_eh_region (new_stmt); + maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt); cgraph_set_call_stmt_including_clones (node, e->call_stmt, new_stmt); diff --git a/gcc/combine.c b/gcc/combine.c index 3437216..6b507c2 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -1562,7 +1562,6 @@ can_combine_p (rtx insn, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ, for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) { rtx elt = XVECEXP (PATTERN (insn), 0, i); - rtx note; switch (GET_CODE (elt)) { @@ -1613,9 +1612,8 @@ can_combine_p (rtx insn, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ, /* Ignore SETs whose result isn't used but not those that have side-effects. */ if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt)) - && (!(note = find_reg_note (insn, REG_EH_REGION, NULL_RTX)) - || INTVAL (XEXP (note, 0)) <= 0) - && ! side_effects_p (elt)) + && insn_nothrow_p (insn) + && !side_effects_p (elt)) break; /* If we have already found a SET, this is a second one and @@ -3108,15 +3106,13 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) { rtx set0 = XVECEXP (newpat, 0, 0); rtx set1 = XVECEXP (newpat, 0, 1); - rtx note; if (((REG_P (SET_DEST (set1)) && find_reg_note (i3, REG_UNUSED, SET_DEST (set1))) || (GET_CODE (SET_DEST (set1)) == SUBREG && find_reg_note (i3, REG_UNUSED, SUBREG_REG (SET_DEST (set1))))) - && (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX)) - || INTVAL (XEXP (note, 0)) <= 0) - && ! side_effects_p (SET_SRC (set1))) + && insn_nothrow_p (i3) + && !side_effects_p (SET_SRC (set1))) { newpat = set0; insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); @@ -3127,9 +3123,8 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) || (GET_CODE (SET_DEST (set0)) == SUBREG && find_reg_note (i3, REG_UNUSED, SUBREG_REG (SET_DEST (set0))))) - && (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX)) - || INTVAL (XEXP (note, 0)) <= 0) - && ! side_effects_p (SET_SRC (set0))) + && insn_nothrow_p (i3) + && !side_effects_p (SET_SRC (set0))) { newpat = set1; insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a50e9fc..4a17a77 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * except.c (init_exception_processing): Don't call + default_init_unwind_resume_libfunc. + (cp_protect_cleanup_actions): Return the decl to call. + (build_exc_ptr): Use __builtin_eh_pointer. + * optimize.c (clone_body): Set eh_lp_nr, not eh_region. + 2009-09-13 Richard Guenther <rguenther@suse.de> Rafael Avila de Espindola <espindola@google.com> diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 588c2ee..1b13819 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -53,7 +53,7 @@ static tree wrap_cleanups_r (tree *, int *, void *); static int complete_ptr_ref_or_void_ptr_p (tree, tree); static bool is_admissible_throw_operand (tree); static int can_convert_eh (tree, tree); -static gimple cp_protect_cleanup_actions (void); +static tree cp_protect_cleanup_actions (void); /* Sets up all the global eh stuff that needs to be initialized at the start of compilation. */ @@ -77,25 +77,20 @@ init_exception_processing (void) call_unexpected_node = push_throw_library_fn (get_identifier ("__cxa_call_unexpected"), tmp); - if (targetm.arm_eabi_unwinder) - unwind_resume_libfunc = init_one_libfunc ("__cxa_end_cleanup"); - else - default_init_unwind_resume_libfunc (); - lang_protect_cleanup_actions = &cp_protect_cleanup_actions; } /* Returns an expression to be executed if an unhandled exception is propagated out of a cleanup region. */ -static gimple +static tree cp_protect_cleanup_actions (void) { /* [except.terminate] When the destruction of an object during stack unwinding exits using an exception ... void terminate(); is called. */ - return gimple_build_call (terminate_node, 0); + return terminate_node; } static tree @@ -154,7 +149,8 @@ build_eh_type_type (tree type) tree build_exc_ptr (void) { - return build0 (EXC_PTR_EXPR, ptr_type_node); + return build_call_n (built_in_decls [BUILT_IN_EH_POINTER], + 1, integer_zero_node); } /* Declare a function NAME, returning RETURN_TYPE, taking a single diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c index abd38f8..58d5b90 100644 --- a/gcc/cp/optimize.c +++ b/gcc/cp/optimize.c @@ -99,7 +99,7 @@ clone_body (tree clone, tree fn, void *arg_map) id.transform_lang_insert_block = NULL; /* We're not inside any EH region. */ - id.eh_region = -1; + id.eh_lp_nr = 0; stmts = DECL_SAVED_TREE (fn); walk_tree (&stmts, copy_tree_body_r, &id, NULL); @@ -6544,9 +6544,9 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr) case CALL_INSN: case INSN: case JUMP_INSN: - /* We expect dest to be NULL_RTX here. If the insn may trap, mark - this fact by setting DEST to pc_rtx. */ - if (flag_non_call_exceptions && may_trap_p (PATTERN (x))) + /* We expect dest to be NULL_RTX here. If the insn may trap, mark + this fact by setting DEST to pc_rtx. */ + if (insn_could_throw_p (x)) dest = pc_rtx; if (code == CALL_INSN) count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr); @@ -6658,7 +6658,7 @@ static bool insn_live_p (rtx insn, int *counts) { int i; - if (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) + if (insn_could_throw_p (insn)) return true; else if (GET_CODE (PATTERN (insn)) == SET) return set_live_p (PATTERN (insn), insn, counts); @@ -79,13 +79,7 @@ deletable_insn_p_1 (rtx body) return false; default: - if (volatile_refs_p (body)) - return false; - - if (flag_non_call_exceptions && may_trap_p (body)) - return false; - - return true; + return !volatile_refs_p (body); } } @@ -99,6 +93,14 @@ deletable_insn_p (rtx insn, bool fast, bitmap arg_stores) rtx body, x; int i; + /* Don't delete jumps, notes and the like. */ + if (!NONJUMP_INSN_P (insn)) + return false; + + /* Don't delete insns that can throw. */ + if (!insn_nothrow_p (insn)) + return false; + if (CALL_P (insn) /* We cannot delete calls inside of the recursive dce because this may cause basic blocks to be deleted and this messes up @@ -113,13 +115,6 @@ deletable_insn_p (rtx insn, bool fast, bitmap arg_stores) && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))) return find_call_stack_args (insn, false, fast, arg_stores); - if (!NONJUMP_INSN_P (insn)) - return false; - - /* Similarly, we cannot delete other insns that can throw either. */ - if (df_in_progress && flag_non_call_exceptions && can_throw_internal (insn)) - return false; - body = PATTERN (insn); switch (GET_CODE (body)) { @@ -2531,7 +2531,7 @@ scan_insn (bb_info_t bb_info, rtx insn) them. */ if ((GET_CODE (PATTERN (insn)) == CLOBBER) || volatile_refs_p (PATTERN (insn)) - || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) + || insn_could_throw_p (insn) || (RTX_FRAME_RELATED_P (insn)) || find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX)) insn_info->cannot_delete = true; diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 65022fc..9ed36b3 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -3492,13 +3492,7 @@ try_split (rtx pat, rtx trial, int last) switch (REG_NOTE_KIND (note)) { case REG_EH_REGION: - for (insn = insn_last; insn != NULL_RTX; insn = PREV_INSN (insn)) - { - if (CALL_P (insn) - || (flag_non_call_exceptions && INSN_P (insn) - && may_trap_p (PATTERN (insn)))) - add_reg_note (insn, REG_EH_REGION, XEXP (note, 0)); - } + copy_reg_eh_region_note_backward (note, insn_last, NULL); break; case REG_NORETURN: diff --git a/gcc/except.c b/gcc/except.c index 9b6c24e..c916a18 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -21,30 +21,94 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -/* An exception is an event that can be signaled from within a - function. This event can then be "caught" or "trapped" by the - callers of this function. This potentially allows program flow to - be transferred to any arbitrary code associated with a function call - several levels up the stack. - - The intended use for this mechanism is for signaling "exceptional - events" in an out-of-band fashion, hence its name. The C++ language - (and many other OO-styled or functional languages) practically - requires such a mechanism, as otherwise it becomes very difficult - or even impossible to signal failure conditions in complex - situations. The traditional C++ example is when an error occurs in - the process of constructing an object; without such a mechanism, it - is impossible to signal that the error occurs without adding global - state variables and error checks around every object construction. - - The act of causing this event to occur is referred to as "throwing - an exception". (Alternate terms include "raising an exception" or - "signaling an exception".) The term "throw" is used because control - is returned to the callers of the function that is signaling the - exception, and thus there is the concept of "throwing" the - exception up the call stack. - - [ Add updated documentation on how to use this. ] */ +/* An exception is an event that can be "thrown" from within a + function. This event can then be "caught" by the callers of + the function. + + The representation of exceptions changes several times during + the compilation process: + + In the beginning, in the front end, we have the GENERIC trees + TRY_CATCH_EXPR, TRY_FINALLY_EXPR, WITH_CLEANUP_EXPR, + CLEANUP_POINT_EXPR, CATCH_EXPR, and EH_FILTER_EXPR. + + During initial gimplification (gimplify.c) these are lowered + to the GIMPLE_TRY, GIMPLE_CATCH, and GIMPLE_EH_FILTER nodes. + The WITH_CLEANUP_EXPR and CLEANUP_POINT_EXPR nodes are converted + into GIMPLE_TRY_FINALLY nodes; the others are a more direct 1-1 + conversion. + + During pass_lower_eh (tree-eh.c) we record the nested structure + of the TRY nodes in EH_REGION nodes in CFUN->EH->REGION_TREE. + We expand the lang_protect_cleanup_actions hook into MUST_NOT_THROW + regions at this time. We can then flatten the statements within + the TRY nodes to straight-line code. Statements that had been within + TRY nodes that can throw are recorded within CFUN->EH->THROW_STMT_TABLE, + so that we may remember what action is supposed to be taken if + a given statement does throw. During this lowering process, + we create an EH_LANDING_PAD node for each EH_REGION that has + some code within the function that needs to be executed if a + throw does happen. We also create RESX statements that are + used to transfer control from an inner EH_REGION to an outer + EH_REGION. We also create EH_DISPATCH statements as placeholders + for a runtime type comparison that should be made in order to + select the action to perform among different CATCH and EH_FILTER + regions. + + During pass_lower_eh_dispatch (tree-eh.c), which is run after + all inlining is complete, we are able to run assign_filter_values, + which allows us to map the set of types manipulated by all of the + CATCH and EH_FILTER regions to a set of integers. This set of integers + will be how the exception runtime communicates with the code generated + within the function. We then expand the GIMPLE_EH_DISPATCH statements + to a switch or conditional branches that use the argument provided by + the runtime (__builtin_eh_filter) and the set of integers we computed + in assign_filter_values. + + During pass_lower_resx (tree-eh.c), which is run near the end + of optimization, we expand RESX statements. If the eh region + that is outer to the RESX statement is a MUST_NOT_THROW, then + the RESX expands to some form of abort statement. If the eh + region that is outer to the RESX statement is within the current + function, then the RESX expands to a bookkeeping call + (__builtin_eh_copy_values) and a goto. Otherwise, the next + handler for the exception must be within a function somewhere + up the call chain, so we call back into the exception runtime + (__builtin_unwind_resume). + + During pass_expand (cfgexpand.c), we generate REG_EH_REGION notes + that create an rtl to eh_region mapping that corresponds to the + gimple to eh_region mapping that had been recorded in the + THROW_STMT_TABLE. + + During pass_rtl_eh (except.c), we generate the real landing pads + to which the runtime will actually transfer control. These new + landing pads perform whatever bookkeeping is needed by the target + backend in order to resume execution within the current function. + Each of these new landing pads falls through into the post_landing_pad + label which had been used within the CFG up to this point. All + exception edges within the CFG are redirected to the new landing pads. + If the target uses setjmp to implement exceptions, the various extra + calls into the runtime to register and unregister the current stack + frame are emitted at this time. + + During pass_convert_to_eh_region_ranges (except.c), we transform + the REG_EH_REGION notes attached to individual insns into + non-overlapping ranges of insns bounded by NOTE_INSN_EH_REGION_BEG + and NOTE_INSN_EH_REGION_END. Each insn within such ranges has the + same associated action within the exception region tree, meaning + that (1) the exception is caught by the same landing pad within the + current function, (2) the exception is blocked by the runtime with + a MUST_NOT_THROW region, or (3) the exception is not handled at all + within the current function. + + Finally, during assembly generation, we call + output_function_exception_table (except.c) to emit the tables with + which the exception runtime can determine if a given stack frame + handles a given exception, and if so what filter value to provide + to the function when the non-local control transfer is effected. + If the target uses dwarf2 unwinding to implement exceptions, then + output_call_frame_info (dwarf2out.c) emits the required unwind data. */ #include "config.h" @@ -87,18 +151,11 @@ along with GCC; see the file COPYING3. If not see /* Protect cleanup actions with must-not-throw regions, with a call to the given failure handler. */ -gimple (*lang_protect_cleanup_actions) (void); +tree (*lang_protect_cleanup_actions) (void); /* Return true if type A catches type B. */ int (*lang_eh_type_covers) (tree a, tree b); -/* A hash table of label to region number. */ - -struct GTY(()) ehl_map_entry { - rtx label; - struct eh_region_d *region; -}; - static GTY(()) int call_site_base; static GTY ((param_is (union tree_node))) htab_t type_to_runtime_map; @@ -118,6 +175,9 @@ struct GTY(()) call_site_record_d int action; }; +static bool get_eh_region_and_lp_from_rtx (const_rtx, eh_region *, + eh_landing_pad *); + static int t2r_eq (const void *, const void *); static hashval_t t2r_hash (const void *); @@ -127,49 +187,16 @@ static int ehspec_filter_eq (const void *, const void *); static hashval_t ehspec_filter_hash (const void *); static int add_ttypes_entry (htab_t, tree); static int add_ehspec_entry (htab_t, htab_t, tree); -static void assign_filter_values (void); -static void build_post_landing_pads (void); -static void connect_post_landing_pads (void); static void dw2_build_landing_pads (void); -struct sjlj_lp_info; -static bool sjlj_find_directly_reachable_regions (struct sjlj_lp_info *); -static void sjlj_assign_call_site_values (rtx, struct sjlj_lp_info *); -static void sjlj_mark_call_sites (struct sjlj_lp_info *); -static void sjlj_emit_function_enter (rtx); -static void sjlj_emit_function_exit (void); -static void sjlj_emit_dispatch_table (rtx, struct sjlj_lp_info *); -static void sjlj_build_landing_pads (void); - -static void remove_eh_handler (struct eh_region_d *); -static void remove_eh_handler_and_replace (struct eh_region_d *, - struct eh_region_d *, bool); - -/* The return value of reachable_next_level. */ -enum reachable_code -{ - /* The given exception is not processed by the given region. */ - RNL_NOT_CAUGHT, - /* The given exception may need processing by the given region. */ - RNL_MAYBE_CAUGHT, - /* The given exception is completely processed by the given region. */ - RNL_CAUGHT, - /* The given exception is completely processed by the runtime. */ - RNL_BLOCKED -}; - -struct reachable_info; -static enum reachable_code reachable_next_level (struct eh_region_d *, tree, - struct reachable_info *, bool); - static int action_record_eq (const void *, const void *); static hashval_t action_record_hash (const void *); static int add_action_record (htab_t, int, int); -static int collect_one_action_chain (htab_t, struct eh_region_d *); +static int collect_one_action_chain (htab_t, eh_region); static int add_call_site (rtx, int, int); -static void push_uleb128 (varray_type *, unsigned int); -static void push_sleb128 (varray_type *, int); +static void push_uleb128 (VEC (uchar, gc) **, unsigned int); +static void push_sleb128 (VEC (uchar, gc) **, int); #ifndef HAVE_AS_LEB128 static int dw2_size_of_call_site_table (int); static int sjlj_size_of_call_site_table (void); @@ -305,16 +332,20 @@ void init_eh_for_function (void) { cfun->eh = GGC_CNEW (struct eh_status); + + /* Make sure zero'th entries are used. */ + VEC_safe_push (eh_region, gc, cfun->eh->region_array, NULL); + VEC_safe_push (eh_landing_pad, gc, cfun->eh->lp_array, NULL); } /* Routines to generate the exception tree somewhat directly. These are used from tree-eh.c when processing exception related nodes during tree optimization. */ -static struct eh_region_d * -gen_eh_region (enum eh_region_type type, struct eh_region_d *outer) +static eh_region +gen_eh_region (enum eh_region_type type, eh_region outer) { - struct eh_region_d *new_eh; + eh_region new_eh; #ifdef ENABLE_CHECKING gcc_assert (doing_eh (0)); @@ -335,30 +366,32 @@ gen_eh_region (enum eh_region_type type, struct eh_region_d *outer) cfun->eh->region_tree = new_eh; } - new_eh->region_number = ++cfun->eh->last_region_number; + new_eh->index = VEC_length (eh_region, cfun->eh->region_array); + VEC_safe_push (eh_region, gc, cfun->eh->region_array, new_eh); return new_eh; } -struct eh_region_d * -gen_eh_region_cleanup (struct eh_region_d *outer) +eh_region +gen_eh_region_cleanup (eh_region outer) { - struct eh_region_d *cleanup = gen_eh_region (ERT_CLEANUP, outer); - return cleanup; + return gen_eh_region (ERT_CLEANUP, outer); } -struct eh_region_d * -gen_eh_region_try (struct eh_region_d *outer) +eh_region +gen_eh_region_try (eh_region outer) { return gen_eh_region (ERT_TRY, outer); } -struct eh_region_d * -gen_eh_region_catch (struct eh_region_d *t, tree type_or_list) +eh_catch +gen_eh_region_catch (eh_region t, tree type_or_list) { - struct eh_region_d *c, *l; + eh_catch c, l; tree type_list, type_node; + gcc_assert (t->type == ERT_TRY); + /* Ensure to always end up with a type list to normalize further processing, then register each type against the runtime types map. */ type_list = type_or_list; @@ -372,23 +405,23 @@ gen_eh_region_catch (struct eh_region_d *t, tree type_or_list) add_type_for_runtime (TREE_VALUE (type_node)); } - c = gen_eh_region (ERT_CATCH, t->outer); - c->u.eh_catch.type_list = type_list; + c = GGC_CNEW (struct eh_catch_d); + c->type_list = type_list; l = t->u.eh_try.last_catch; - c->u.eh_catch.prev_catch = l; + c->prev_catch = l; if (l) - l->u.eh_catch.next_catch = c; + l->next_catch = c; else - t->u.eh_try.eh_catch = c; + t->u.eh_try.first_catch = c; t->u.eh_try.last_catch = c; return c; } -struct eh_region_d * -gen_eh_region_allowed (struct eh_region_d *outer, tree allowed) +eh_region +gen_eh_region_allowed (eh_region outer, tree allowed) { - struct eh_region_d *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer); + eh_region region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer); region->u.allowed.type_list = allowed; for (; allowed ; allowed = TREE_CHAIN (allowed)) @@ -397,1271 +430,226 @@ gen_eh_region_allowed (struct eh_region_d *outer, tree allowed) return region; } -struct eh_region_d * -gen_eh_region_must_not_throw (struct eh_region_d *outer) +eh_region +gen_eh_region_must_not_throw (eh_region outer) { return gen_eh_region (ERT_MUST_NOT_THROW, outer); } -int -get_eh_region_number (struct eh_region_d *region) +eh_landing_pad +gen_eh_landing_pad (eh_region region) { - return region->region_number; -} + eh_landing_pad lp = GGC_CNEW (struct eh_landing_pad_d); -bool -get_eh_region_may_contain_throw (struct eh_region_d *region) -{ - return region->may_contain_throw; -} + lp->next_lp = region->landing_pads; + lp->region = region; + lp->index = VEC_length (eh_landing_pad, cfun->eh->lp_array); + region->landing_pads = lp; -tree -get_eh_region_tree_label (struct eh_region_d *region) -{ - return region->tree_label; -} + VEC_safe_push (eh_landing_pad, gc, cfun->eh->lp_array, lp); -tree -get_eh_region_no_tree_label (int region) -{ - return VEC_index (eh_region, cfun->eh->region_array, region)->tree_label; + return lp; } -void -set_eh_region_tree_label (struct eh_region_d *region, tree lab) -{ - region->tree_label = lab; -} - -void -expand_resx_stmt (gimple stmt) +eh_region +get_eh_region_from_number_fn (struct function *ifun, int i) { - int region_nr = gimple_resx_region (stmt); - rtx insn; - struct eh_region_d *reg = VEC_index (eh_region, - cfun->eh->region_array, region_nr); - - do_pending_stack_adjust (); - insn = emit_jump_insn (gen_rtx_RESX (VOIDmode, region_nr)); - if (reg->resume) - reg->resume = gen_rtx_INSN_LIST (VOIDmode, insn, reg->resume); - else - reg->resume = insn; - emit_barrier (); + return VEC_index (eh_region, ifun->eh->region_array, i); } -/* Note that the current EH region (if any) may contain a throw, or a - call to a function which itself may contain a throw. */ - -void -note_eh_region_may_contain_throw (struct eh_region_d *region) +eh_region +get_eh_region_from_number (int i) { - while (region && !region->may_contain_throw) - { - region->may_contain_throw = 1; - region = region->outer; - } + return get_eh_region_from_number_fn (cfun, i); } - -/* Return an rtl expression for a pointer to the exception object - within a handler. */ - -rtx -get_exception_pointer (void) +eh_landing_pad +get_eh_landing_pad_from_number_fn (struct function *ifun, int i) { - if (! crtl->eh.exc_ptr) - crtl->eh.exc_ptr = gen_reg_rtx (ptr_mode); - return crtl->eh.exc_ptr; + return VEC_index (eh_landing_pad, ifun->eh->lp_array, i); } -/* Return an rtl expression for the exception dispatch filter - within a handler. */ - -rtx -get_exception_filter (void) +eh_landing_pad +get_eh_landing_pad_from_number (int i) { - if (! crtl->eh.filter) - crtl->eh.filter = gen_reg_rtx (targetm.eh_return_filter_mode ()); - return crtl->eh.filter; + return get_eh_landing_pad_from_number_fn (cfun, i); } - -/* This section is for the exception handling specific optimization pass. */ - -/* Random access the exception region tree. */ -void -collect_eh_region_array (void) +eh_region +get_eh_region_from_lp_number_fn (struct function *ifun, int i) { - struct eh_region_d *i; - - i = cfun->eh->region_tree; - if (! i) - return; - - VEC_safe_grow (eh_region, gc, cfun->eh->region_array, - cfun->eh->last_region_number + 1); - VEC_replace (eh_region, cfun->eh->region_array, 0, 0); - - while (1) + if (i < 0) + return VEC_index (eh_region, ifun->eh->region_array, -i); + else if (i == 0) + return NULL; + else { - VEC_replace (eh_region, cfun->eh->region_array, i->region_number, i); - - /* If there are sub-regions, process them. */ - if (i->inner) - i = i->inner; - /* If there are peers, process them. */ - else if (i->next_peer) - i = i->next_peer; - /* Otherwise, step back up the tree to the next peer. */ - else - { - do { - i = i->outer; - if (i == NULL) - return; - } while (i->next_peer == NULL); - i = i->next_peer; - } + eh_landing_pad lp; + lp = VEC_index (eh_landing_pad, ifun->eh->lp_array, i); + return lp->region; } } -/* R is MUST_NOT_THROW region that is not reachable via local - RESX instructions. It still must be kept in the tree in case runtime - can unwind through it, or we will eliminate out terminate call - runtime would do otherwise. Return TRUE if R contains throwing statements - or some of the exceptions in inner regions can be unwound up to R. - - CONTAINS_STMT is bitmap of all regions that contains some throwing - statements. - - Function looks O(^3) at first sight. In fact the function is called at most - once for every MUST_NOT_THROW in EH tree from remove_unreachable_regions - Because the outer loop walking subregions does not dive in MUST_NOT_THROW, - the outer loop examines every region at most once. The inner loop - is doing unwinding from the throwing statement same way as we do during - CFG construction, so it is O(^2) in size of EH tree, but O(n) in size - of CFG. In practice Eh trees are wide, not deep, so this is not - a problem. */ - -static bool -can_be_reached_by_runtime (sbitmap contains_stmt, struct eh_region_d *r) +eh_region +get_eh_region_from_lp_number (int i) { - struct eh_region_d *i = r->inner; - unsigned n; - bitmap_iterator bi; - - if (TEST_BIT (contains_stmt, r->region_number)) - return true; - if (r->aka) - EXECUTE_IF_SET_IN_BITMAP (r->aka, 0, n, bi) - if (TEST_BIT (contains_stmt, n)) - return true; - if (!i) - return false; - while (1) - { - /* It is pointless to look into MUST_NOT_THROW - or dive into subregions. They never unwind up. */ - if (i->type != ERT_MUST_NOT_THROW) - { - bool found = TEST_BIT (contains_stmt, i->region_number); - if (!found && i->aka) - EXECUTE_IF_SET_IN_BITMAP (i->aka, 0, n, bi) - if (TEST_BIT (contains_stmt, n)) - { - found = true; - break; - } - /* We have nested region that contains throwing statement. - See if resuming might lead up to the resx or we get locally - caught sooner. If we get locally caught sooner, we either - know region R is not reachable or it would have direct edge - from the EH resx and thus consider region reachable at - firest place. */ - if (found) - { - struct eh_region_d *i1 = i; - tree type_thrown = NULL_TREE; - - if (i1->type == ERT_THROW) - { - type_thrown = i1->u.eh_throw.type; - i1 = i1->outer; - } - for (; i1 != r; i1 = i1->outer) - if (reachable_next_level (i1, type_thrown, NULL, - false) >= RNL_CAUGHT) - break; - if (i1 == r) - return true; - } - } - /* If there are sub-regions, process them. */ - if (i->type != ERT_MUST_NOT_THROW && i->inner) - i = i->inner; - /* If there are peers, process them. */ - else if (i->next_peer) - i = i->next_peer; - /* Otherwise, step back up the tree to the next peer. */ - else - { - do - { - i = i->outer; - if (i == r) - return false; - } - while (i->next_peer == NULL); - i = i->next_peer; - } - } + return get_eh_region_from_lp_number_fn (cfun, i); } + +/* Returns true if the current function has exception handling regions. */ -/* Bring region R to the root of tree. */ - -static void -bring_to_root (struct eh_region_d *r) +bool +current_function_has_exception_handlers (void) { - struct eh_region_d **pp; - struct eh_region_d *outer = r->outer; - if (!r->outer) - return; - for (pp = &outer->inner; *pp != r; pp = &(*pp)->next_peer) - continue; - *pp = r->next_peer; - r->outer = NULL; - r->next_peer = cfun->eh->region_tree; - cfun->eh->region_tree = r; + return cfun->eh->region_tree != NULL; } + +/* A subroutine of duplicate_eh_regions. Copy the eh_region tree at OLD. + Root it at OUTER, and apply LP_OFFSET to the lp numbers. */ -/* Return true if region R2 can be replaced by R1. */ - -static bool -eh_region_replaceable_by_p (const struct eh_region_d *r1, - const struct eh_region_d *r2) +struct duplicate_eh_regions_data { - /* Regions are semantically same if they are of same type, - have same label and type. */ - if (r1->type != r2->type) - return false; - if (r1->tree_label != r2->tree_label) - return false; - - /* Verify that also region type dependent data are the same. */ - switch (r1->type) - { - case ERT_MUST_NOT_THROW: - case ERT_CLEANUP: - break; - case ERT_TRY: - { - struct eh_region_d *c1, *c2; - for (c1 = r1->u.eh_try.eh_catch, - c2 = r2->u.eh_try.eh_catch; - c1 && c2; - c1 = c1->u.eh_catch.next_catch, - c2 = c2->u.eh_catch.next_catch) - if (!eh_region_replaceable_by_p (c1, c2)) - return false; - if (c1 || c2) - return false; - } - break; - case ERT_CATCH: - if (!list_equal_p (r1->u.eh_catch.type_list, r2->u.eh_catch.type_list)) - return false; - if (!list_equal_p (r1->u.eh_catch.filter_list, - r2->u.eh_catch.filter_list)) - return false; - break; - case ERT_ALLOWED_EXCEPTIONS: - if (!list_equal_p (r1->u.allowed.type_list, r2->u.allowed.type_list)) - return false; - if (r1->u.allowed.filter != r2->u.allowed.filter) - return false; - break; - case ERT_THROW: - if (r1->u.eh_throw.type != r2->u.eh_throw.type) - return false; - break; - default: - gcc_unreachable (); - } - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Regions %i and %i match\n", r1->region_number, - r2->region_number); - return true; -} - -/* Replace region R2 by R1. */ + duplicate_eh_regions_map label_map; + void *label_map_data; + struct pointer_map_t *eh_map; +}; static void -replace_region (struct eh_region_d *r1, struct eh_region_d *r2) +duplicate_eh_regions_1 (struct duplicate_eh_regions_data *data, + eh_region old_r, eh_region outer) { - struct eh_region_d *next1 = r1->u.eh_try.eh_catch; - struct eh_region_d *next2 = r2->u.eh_try.eh_catch; - bool is_try = r1->type == ERT_TRY; - - gcc_assert (r1->type != ERT_CATCH); - remove_eh_handler_and_replace (r2, r1, false); - if (is_try) - { - while (next1) - { - r1 = next1; - r2 = next2; - gcc_assert (next1->type == ERT_CATCH); - gcc_assert (next2->type == ERT_CATCH); - next1 = next1->u.eh_catch.next_catch; - next2 = next2->u.eh_catch.next_catch; - remove_eh_handler_and_replace (r2, r1, false); - } - } -} - -/* Return hash value of type list T. */ - -static hashval_t -hash_type_list (tree t) -{ - hashval_t val = 0; - for (; t; t = TREE_CHAIN (t)) - val = iterative_hash_hashval_t (TREE_HASH (TREE_VALUE (t)), val); - return val; -} - -/* Hash EH regions so semantically same regions get same hash value. */ + eh_landing_pad old_lp, new_lp; + eh_region new_r; + void **slot; -static hashval_t -hash_eh_region (const void *r) -{ - const struct eh_region_d *region = (const struct eh_region_d *) r; - hashval_t val = region->type; + new_r = gen_eh_region (old_r->type, outer); + slot = pointer_map_insert (data->eh_map, (void *)old_r); + gcc_assert (*slot == NULL); + *slot = (void *)new_r; - if (region->tree_label) - val = iterative_hash_hashval_t (LABEL_DECL_UID (region->tree_label), val); - switch (region->type) + switch (old_r->type) { - case ERT_MUST_NOT_THROW: - case ERT_CLEANUP: - break; - case ERT_TRY: - { - struct eh_region_d *c; - for (c = region->u.eh_try.eh_catch; - c; c = c->u.eh_catch.next_catch) - val = iterative_hash_hashval_t (hash_eh_region (c), val); - } - break; - case ERT_CATCH: - val = iterative_hash_hashval_t (hash_type_list - (region->u.eh_catch.type_list), val); - break; - case ERT_ALLOWED_EXCEPTIONS: - val = iterative_hash_hashval_t - (hash_type_list (region->u.allowed.type_list), val); - val = iterative_hash_hashval_t (region->u.allowed.filter, val); - break; - case ERT_THROW: - val |= iterative_hash_hashval_t (TYPE_UID (region->u.eh_throw.type), val); - break; - default: - gcc_unreachable (); - } - return val; -} - -/* Return true if regions R1 and R2 are equal. */ - -static int -eh_regions_equal_p (const void *r1, const void *r2) -{ - return eh_region_replaceable_by_p ((const struct eh_region_d *) r1, - (const struct eh_region_d *) r2); -} - -/* Walk all peers of REGION and try to merge those regions - that are semantically equivalent. Look into subregions - recursively too. */ - -static bool -merge_peers (struct eh_region_d *region) -{ - struct eh_region_d *r1, *r2, *outer = NULL, *next; - bool merged = false; - int num_regions = 0; - if (region) - outer = region->outer; - else - return false; + case ERT_CLEANUP: + break; - /* First see if there is inner region equivalent to region - in question. EH control flow is acyclic so we know we - can merge them. */ - if (outer) - for (r1 = region; r1; r1 = next) + case ERT_TRY: { - next = r1->next_peer; - if (r1->type == ERT_CATCH) - continue; - if (eh_region_replaceable_by_p (r1->outer, r1)) + eh_catch oc, nc; + for (oc = old_r->u.eh_try.first_catch; oc ; oc = oc->next_catch) { - replace_region (r1->outer, r1); - merged = true; + /* We should be doing all our region duplication before and + during inlining, which is before filter lists are created. */ + gcc_assert (oc->filter_list == NULL); + nc = gen_eh_region_catch (new_r, oc->type_list); + nc->label = data->label_map (oc->label, data->label_map_data); } - else - num_regions ++; } + break; - /* Get new first region and try to match the peers - for equivalence. */ - if (outer) - region = outer->inner; - else - region = cfun->eh->region_tree; + case ERT_ALLOWED_EXCEPTIONS: + new_r->u.allowed.type_list = old_r->u.allowed.type_list; + new_r->u.allowed.label + = data->label_map (old_r->u.allowed.label, data->label_map_data); + break; - /* There are few regions to inspect: - N^2 loop matching each region with each region - will do the job well. */ - if (num_regions < 10) - { - for (r1 = region; r1; r1 = r1->next_peer) - { - if (r1->type == ERT_CATCH) - continue; - for (r2 = r1->next_peer; r2; r2 = next) - { - next = r2->next_peer; - if (eh_region_replaceable_by_p (r1, r2)) - { - replace_region (r1, r2); - merged = true; - } - } - } - } - /* Or use hashtable to avoid N^2 behaviour. */ - else - { - htab_t hash; - hash = htab_create (num_regions, hash_eh_region, - eh_regions_equal_p, NULL); - for (r1 = region; r1; r1 = next) - { - void **slot; - - next = r1->next_peer; - if (r1->type == ERT_CATCH) - continue; - slot = htab_find_slot (hash, r1, INSERT); - if (!*slot) - *slot = r1; - else - replace_region ((struct eh_region_d *) *slot, r1); - } - htab_delete (hash); + case ERT_MUST_NOT_THROW: + new_r->u.must_not_throw = old_r->u.must_not_throw; + break; } - for (r1 = region; r1; r1 = r1->next_peer) - merged |= merge_peers (r1->inner); - return merged; -} - -/* Remove all regions whose labels are not reachable. - REACHABLE is bitmap of all regions that are used by the function - CONTAINS_STMT is bitmap of all regions that contains stmt (or NULL). */ -void -remove_unreachable_regions (sbitmap reachable, sbitmap contains_stmt) -{ - int i; - struct eh_region_d *r; - VEC(eh_region,heap) *must_not_throws = VEC_alloc (eh_region, heap, 16); - struct eh_region_d *local_must_not_throw = NULL; - struct eh_region_d *first_must_not_throw = NULL; - - for (i = cfun->eh->last_region_number; i > 0; --i) + for (old_lp = old_r->landing_pads; old_lp ; old_lp = old_lp->next_lp) { - r = VEC_index (eh_region, cfun->eh->region_array, i); - if (!r || r->region_number != i) + /* Don't bother copying unused landing pads. */ + if (old_lp->post_landing_pad == NULL) continue; - if (!TEST_BIT (reachable, i) && !r->resume) - { - bool kill_it = true; - - r->tree_label = NULL; - switch (r->type) - { - case ERT_THROW: - /* Don't remove ERT_THROW regions if their outer region - is reachable. */ - if (r->outer && TEST_BIT (reachable, r->outer->region_number)) - kill_it = false; - break; - case ERT_MUST_NOT_THROW: - /* MUST_NOT_THROW regions are implementable solely in the - runtime, but we need them when inlining function. - - Keep them if outer region is not MUST_NOT_THROW a well - and if they contain some statement that might unwind through - them. */ - if ((!r->outer || r->outer->type != ERT_MUST_NOT_THROW) - && (!contains_stmt - || can_be_reached_by_runtime (contains_stmt, r))) - kill_it = false; - break; - case ERT_TRY: - { - /* TRY regions are reachable if any of its CATCH regions - are reachable. */ - struct eh_region_d *c; - for (c = r->u.eh_try.eh_catch; c; - c = c->u.eh_catch.next_catch) - if (TEST_BIT (reachable, c->region_number)) - { - kill_it = false; - break; - } - break; - } - - default: - break; - } - - if (kill_it) - { - if (dump_file) - fprintf (dump_file, "Removing unreachable eh region %i\n", - r->region_number); - remove_eh_handler (r); - } - else if (r->type == ERT_MUST_NOT_THROW) - { - if (!first_must_not_throw) - first_must_not_throw = r; - VEC_safe_push (eh_region, heap, must_not_throws, r); - } - } - else - if (r->type == ERT_MUST_NOT_THROW) - { - if (!local_must_not_throw) - local_must_not_throw = r; - if (r->outer) - VEC_safe_push (eh_region, heap, must_not_throws, r); - } - } - - /* MUST_NOT_THROW regions without local handler are all the same; they - trigger terminate call in runtime. - MUST_NOT_THROW handled locally can differ in debug info associated - to std::terminate () call or if one is coming from Java and other - from C++ whether they call terminate or abort. - - We merge all MUST_NOT_THROW regions handled by the run-time into one. - We alsobring all local MUST_NOT_THROW regions to the roots of EH tree - (since unwinding never continues to the outer region anyway). - If MUST_NOT_THROW with local handler is present in the tree, we use - that region to merge into, since it will remain in tree anyway; - otherwise we use first MUST_NOT_THROW. - - Merging of locally handled regions needs changes to the CFG. Crossjumping - should take care of this, by looking at the actual code and - ensuring that the cleanup actions are really the same. */ - - if (local_must_not_throw) - first_must_not_throw = local_must_not_throw; - - for (i = 0; VEC_iterate (eh_region, must_not_throws, i, r); i++) - { - if (!r->label && !r->tree_label && r != first_must_not_throw) - { - if (dump_file) - fprintf (dump_file, "Replacing MUST_NOT_THROW region %i by %i\n", - r->region_number, - first_must_not_throw->region_number); - remove_eh_handler_and_replace (r, first_must_not_throw, false); - first_must_not_throw->may_contain_throw |= r->may_contain_throw; - } - else - bring_to_root (r); - } - merge_peers (cfun->eh->region_tree); -#ifdef ENABLE_CHECKING - verify_eh_tree (cfun); -#endif - VEC_free (eh_region, heap, must_not_throws); -} - -/* Return array mapping LABEL_DECL_UID to region such that region's tree_label - is identical to label. */ - -VEC (int, heap) * -label_to_region_map (void) -{ - VEC (int, heap) * label_to_region = NULL; - int i; - int idx; - - VEC_safe_grow_cleared (int, heap, label_to_region, - cfun->cfg->last_label_uid + 1); - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region_d *r = VEC_index (eh_region, cfun->eh->region_array, i); - if (r && r->region_number == i - && r->tree_label && LABEL_DECL_UID (r->tree_label) >= 0) - { - if ((idx = VEC_index (int, label_to_region, - LABEL_DECL_UID (r->tree_label))) != 0) - r->next_region_sharing_label = - VEC_index (eh_region, cfun->eh->region_array, idx); - else - r->next_region_sharing_label = NULL; - VEC_replace (int, label_to_region, LABEL_DECL_UID (r->tree_label), - i); - } - } - return label_to_region; -} - -/* Return number of EH regions. */ -int -num_eh_regions (void) -{ - return cfun->eh->last_region_number + 1; -} - -/* Return next region sharing same label as REGION. */ - -int -get_next_region_sharing_label (int region) -{ - struct eh_region_d *r; - if (!region) - return 0; - r = VEC_index (eh_region, cfun->eh->region_array, region); - if (!r || !r->next_region_sharing_label) - return 0; - return r->next_region_sharing_label->region_number; -} - -/* Return bitmap of all labels that are handlers of must not throw regions. */ - -bitmap -must_not_throw_labels (void) -{ - struct eh_region_d *i; - bitmap labels = BITMAP_ALLOC (NULL); - i = cfun->eh->region_tree; - if (! i) - return labels; + new_lp = gen_eh_landing_pad (new_r); + slot = pointer_map_insert (data->eh_map, (void *)old_lp); + gcc_assert (*slot == NULL); + *slot = (void *)new_lp; - while (1) - { - if (i->type == ERT_MUST_NOT_THROW && i->tree_label - && LABEL_DECL_UID (i->tree_label) >= 0) - bitmap_set_bit (labels, LABEL_DECL_UID (i->tree_label)); - - /* If there are sub-regions, process them. */ - if (i->inner) - i = i->inner; - /* If there are peers, process them. */ - else if (i->next_peer) - i = i->next_peer; - /* Otherwise, step back up the tree to the next peer. */ - else - { - do { - i = i->outer; - if (i == NULL) - return labels; - } while (i->next_peer == NULL); - i = i->next_peer; - } + new_lp->post_landing_pad + = data->label_map (old_lp->post_landing_pad, data->label_map_data); + EH_LANDING_PAD_NR (new_lp->post_landing_pad) = new_lp->index; } -} - -/* Set up EH labels for RTL. */ -void -convert_from_eh_region_ranges (void) -{ - int i, n = cfun->eh->last_region_number; - - /* Most of the work is already done at the tree level. All we need to - do is collect the rtl labels that correspond to the tree labels that - collect the rtl labels that correspond to the tree labels - we allocated earlier. */ - for (i = 1; i <= n; ++i) - { - struct eh_region_d *region; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (region && region->tree_label) - region->label = DECL_RTL_IF_SET (region->tree_label); - } + for (old_r = old_r->inner; old_r ; old_r = old_r->next_peer) + duplicate_eh_regions_1 (data, old_r, new_r); } -void -find_exception_handler_labels (void) -{ - int i; +/* Duplicate the EH regions from IFUN rooted at COPY_REGION into + the current function and root the tree below OUTER_REGION. + The special case of COPY_REGION of NULL means all regions. + Remap labels using MAP/MAP_DATA callback. Return a pointer map + that allows the caller to remap uses of both EH regions and + EH landing pads. */ - if (cfun->eh->region_tree == NULL) - return; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region_d *region; - rtx lab; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (! region || region->region_number != i) - continue; - if (crtl->eh.built_landing_pads) - lab = region->landing_pad; - else - lab = region->label; - } -} - -/* Returns true if the current function has exception handling regions. */ - -bool -current_function_has_exception_handlers (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region_d *region; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (region - && region->region_number == i - && region->type != ERT_THROW) - return true; - } - - return false; -} - -/* A subroutine of duplicate_eh_regions. Search the region tree under O - for the minimum and maximum region numbers. Update *MIN and *MAX. */ - -static void -duplicate_eh_regions_0 (eh_region o, int *min, int *max) +struct pointer_map_t * +duplicate_eh_regions (struct function *ifun, + eh_region copy_region, int outer_lp, + duplicate_eh_regions_map map, void *map_data) { - int i; + struct duplicate_eh_regions_data data; + eh_region outer_region; - if (o->aka) - { - i = bitmap_first_set_bit (o->aka); - if (i < *min) - *min = i; - i = bitmap_last_set_bit (o->aka); - if (i > *max) - *max = i; - } - if (o->region_number < *min) - *min = o->region_number; - if (o->region_number > *max) - *max = o->region_number; - - if (o->inner) - { - o = o->inner; - duplicate_eh_regions_0 (o, min, max); - while (o->next_peer) - { - o = o->next_peer; - duplicate_eh_regions_0 (o, min, max); - } - } -} - -/* A subroutine of duplicate_eh_regions. Copy the region tree under OLD. - Root it at OUTER, and apply EH_OFFSET to the region number. Don't worry - about the other internal pointers just yet, just the tree-like pointers. */ - -static eh_region -duplicate_eh_regions_1 (eh_region old, eh_region outer, int eh_offset) -{ - eh_region ret, n; - - ret = n = GGC_NEW (struct eh_region_d); - - *n = *old; - n->outer = outer; - n->next_peer = NULL; - if (old->aka) - { - unsigned i; - bitmap_iterator bi; - n->aka = BITMAP_GGC_ALLOC (); - - EXECUTE_IF_SET_IN_BITMAP (old->aka, 0, i, bi) - { - bitmap_set_bit (n->aka, i + eh_offset); - VEC_replace (eh_region, cfun->eh->region_array, i + eh_offset, n); - } - } - - n->region_number += eh_offset; - VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n); - - if (old->inner) - { - old = old->inner; - n = n->inner = duplicate_eh_regions_1 (old, ret, eh_offset); - while (old->next_peer) - { - old = old->next_peer; - n = n->next_peer = duplicate_eh_regions_1 (old, ret, eh_offset); - } - } - - return ret; -} - -/* Look for first outer region of R (or R itself) that is - TRY region. Return NULL if none. */ - -static struct eh_region_d * -find_prev_try (struct eh_region_d * r) -{ - for (; r && r->type != ERT_TRY; r = r->outer) - if (r->type == ERT_MUST_NOT_THROW - || (r->type == ERT_ALLOWED_EXCEPTIONS - && !r->u.allowed.type_list)) - { - r = NULL; - break; - } - return r; -} - -/* Duplicate the EH regions of IFUN, rooted at COPY_REGION, into current - function and root the tree below OUTER_REGION. Remap labels using MAP - callback. The special case of COPY_REGION of 0 means all regions. */ - -int -duplicate_eh_regions (struct function *ifun, duplicate_eh_regions_map map, - void *data, int copy_region, int outer_region) -{ - eh_region cur, outer, *splice; - int i, min_region, max_region, eh_offset, cfun_last_region_number; - int num_regions; - - if (!ifun->eh) - return 0; #ifdef ENABLE_CHECKING verify_eh_tree (ifun); #endif - /* Find the range of region numbers to be copied. The interface we - provide here mandates a single offset to find new number from old, - which means we must look at the numbers present, instead of the - count or something else. */ - if (copy_region > 0) - { - min_region = INT_MAX; - max_region = 0; - - cur = VEC_index (eh_region, ifun->eh->region_array, copy_region); - duplicate_eh_regions_0 (cur, &min_region, &max_region); - } - else - { - min_region = 1; - max_region = ifun->eh->last_region_number; - } - num_regions = max_region - min_region + 1; - cfun_last_region_number = cfun->eh->last_region_number; - eh_offset = cfun_last_region_number + 1 - min_region; - - /* If we've not yet created a region array, do so now. */ - cfun->eh->last_region_number = cfun_last_region_number + num_regions; - VEC_safe_grow_cleared (eh_region, gc, cfun->eh->region_array, - cfun->eh->last_region_number + 1); - - /* Locate the spot at which to insert the new tree. */ - if (outer_region > 0) - { - outer = VEC_index (eh_region, cfun->eh->region_array, outer_region); - if (outer) - splice = &outer->inner; - else - splice = &cfun->eh->region_tree; - } - else - { - outer = NULL; - splice = &cfun->eh->region_tree; - } - while (*splice) - splice = &(*splice)->next_peer; - - if (!ifun->eh->region_tree) - { - if (outer) - for (i = cfun_last_region_number + 1; - i <= cfun->eh->last_region_number; i++) - { - VEC_replace (eh_region, cfun->eh->region_array, i, outer); - if (outer->aka == NULL) - outer->aka = BITMAP_GGC_ALLOC (); - bitmap_set_bit (outer->aka, i); - } - return eh_offset; - } + data.label_map = map; + data.label_map_data = map_data; + data.eh_map = pointer_map_create (); + outer_region = get_eh_region_from_lp_number (outer_lp); + /* Copy all the regions in the subtree. */ - if (copy_region > 0) - { - cur = VEC_index (eh_region, ifun->eh->region_array, copy_region); - *splice = duplicate_eh_regions_1 (cur, outer, eh_offset); - } + if (copy_region) + duplicate_eh_regions_1 (&data, copy_region, outer_region); else { - eh_region n; - - cur = ifun->eh->region_tree; - *splice = n = duplicate_eh_regions_1 (cur, outer, eh_offset); - while (cur->next_peer) - { - cur = cur->next_peer; - n = n->next_peer = duplicate_eh_regions_1 (cur, outer, eh_offset); - } + eh_region r; + for (r = ifun->eh->region_tree; r ; r = r->next_peer) + duplicate_eh_regions_1 (&data, r, outer_region); } - /* Remap all the labels in the new regions. */ - for (i = cfun_last_region_number + 1; - VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i) - if (cur && cur->tree_label) - cur->tree_label = map (cur->tree_label, data); - - /* Remap all of the internal catch and cleanup linkages. Since we - duplicate entire subtrees, all of the referenced regions will have - been copied too. And since we renumbered them as a block, a simple - bit of arithmetic finds us the index for the replacement region. */ - for (i = cfun_last_region_number + 1; - VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i) - { - /* All removed EH that is toplevel in input function is now - in outer EH of output function. */ - if (cur == NULL) - { - gcc_assert (VEC_index - (eh_region, ifun->eh->region_array, - i - eh_offset) == NULL); - if (outer) - { - VEC_replace (eh_region, cfun->eh->region_array, i, outer); - if (outer->aka == NULL) - outer->aka = BITMAP_GGC_ALLOC (); - bitmap_set_bit (outer->aka, i); - } - continue; - } - if (i != cur->region_number) - continue; - -#define REMAP(REG) \ - (REG) = VEC_index (eh_region, cfun->eh->region_array, \ - (REG)->region_number + eh_offset) - - switch (cur->type) - { - case ERT_TRY: - if (cur->u.eh_try.eh_catch) - REMAP (cur->u.eh_try.eh_catch); - if (cur->u.eh_try.last_catch) - REMAP (cur->u.eh_try.last_catch); - break; - - case ERT_CATCH: - if (cur->u.eh_catch.next_catch) - REMAP (cur->u.eh_catch.next_catch); - if (cur->u.eh_catch.prev_catch) - REMAP (cur->u.eh_catch.prev_catch); - break; - - default: - break; - } - -#undef REMAP - } #ifdef ENABLE_CHECKING verify_eh_tree (cfun); #endif - return eh_offset; -} - -/* Return new copy of eh region OLD inside region NEW_OUTER. - Do not care about updating the tree otherwise. */ - -static struct eh_region_d * -copy_eh_region_1 (struct eh_region_d *old, struct eh_region_d *new_outer) -{ - struct eh_region_d *new_eh = gen_eh_region (old->type, new_outer); - new_eh->u = old->u; - new_eh->tree_label = old->tree_label; - new_eh->may_contain_throw = old->may_contain_throw; - VEC_safe_grow (eh_region, gc, cfun->eh->region_array, - cfun->eh->last_region_number + 1); - VEC_replace (eh_region, cfun->eh->region_array, new_eh->region_number, new_eh); - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Copying region %i to %i\n", old->region_number, new_eh->region_number); - return new_eh; -} - -/* Return new copy of eh region OLD inside region NEW_OUTER. - - Copy whole catch-try chain if neccesary. */ - -static struct eh_region_d * -copy_eh_region (struct eh_region_d *old, struct eh_region_d *new_outer) -{ - struct eh_region_d *r, *n, *old_try, *new_try, *ret = NULL; - VEC(eh_region,heap) *catch_list = NULL; - - if (old->type != ERT_CATCH) - { - gcc_assert (old->type != ERT_TRY); - r = copy_eh_region_1 (old, new_outer); - return r; - } - - /* Locate and copy corresponding TRY. */ - for (old_try = old->next_peer; old_try->type == ERT_CATCH; old_try = old_try->next_peer) - continue; - gcc_assert (old_try->type == ERT_TRY); - new_try = gen_eh_region_try (new_outer); - new_try->tree_label = old_try->tree_label; - new_try->may_contain_throw = old_try->may_contain_throw; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Copying try-catch regions. Try: %i to %i\n", - old_try->region_number, new_try->region_number); - VEC_safe_grow (eh_region, gc, cfun->eh->region_array, - cfun->eh->last_region_number + 1); - VEC_replace (eh_region, cfun->eh->region_array, new_try->region_number, new_try); - - /* In order to keep CATCH list in order, we need to copy in reverse order. */ - for (r = old_try->u.eh_try.last_catch; r->type == ERT_CATCH; r = r->next_peer) - VEC_safe_push (eh_region, heap, catch_list, r); - - while (VEC_length (eh_region, catch_list)) - { - r = VEC_pop (eh_region, catch_list); - - /* Duplicate CATCH. */ - n = gen_eh_region_catch (new_try, r->u.eh_catch.type_list); - n->tree_label = r->tree_label; - n->may_contain_throw = r->may_contain_throw; - VEC_safe_grow (eh_region, gc, cfun->eh->region_array, - cfun->eh->last_region_number + 1); - VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n); - n->tree_label = r->tree_label; - - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Copying try-catch regions. Catch: %i to %i\n", - r->region_number, n->region_number); - if (r == old) - ret = n; - } - VEC_free (eh_region, heap, catch_list); - gcc_assert (ret); - return ret; + return data.eh_map; } -/* Callback for forach_reachable_handler that push REGION into single VECtor DATA. */ +/* Return the region that is outer to both REGION_A and REGION_B in IFUN. */ -static void -push_reachable_handler (struct eh_region_d *region, void *data) +eh_region +eh_region_outermost (struct function *ifun, eh_region region_a, + eh_region region_b) { - VEC(eh_region,heap) **trace = (VEC(eh_region,heap) **) data; - VEC_safe_push (eh_region, heap, *trace, region); -} - -/* Redirect EH edge E that to NEW_DEST_LABEL. - IS_RESX, INLINABLE_CALL and REGION_NMUBER match the parameter of - foreach_reachable_handler. */ - -struct eh_region_d * -redirect_eh_edge_to_label (edge e, tree new_dest_label, bool is_resx, - bool inlinable_call, int region_number) -{ - struct eh_region_d *outer; - struct eh_region_d *region; - VEC (eh_region, heap) * trace = NULL; - int i; - int start_here = -1; - basic_block old_bb = e->dest; - struct eh_region_d *old, *r = NULL; - bool update_inplace = true; - edge_iterator ei; - edge e2; - - /* If there is only one EH edge, we don't need to duplicate; - just update labels in the tree. */ - FOR_EACH_EDGE (e2, ei, old_bb->preds) - if ((e2->flags & EDGE_EH) && e2 != e) - { - update_inplace = false; - break; - } - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - gcc_assert (region); - - foreach_reachable_handler (region_number, is_resx, inlinable_call, - push_reachable_handler, &trace); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - dump_eh_tree (dump_file, cfun); - fprintf (dump_file, "Trace: "); - for (i = 0; i < (int) VEC_length (eh_region, trace); i++) - fprintf (dump_file, " %i", VEC_index (eh_region, trace, i)->region_number); - fprintf (dump_file, " inplace: %i\n", update_inplace); - } - - if (update_inplace) - { - /* In easy route just walk trace and update all occurences of the label. */ - for (i = 0; i < (int) VEC_length (eh_region, trace); i++) - { - r = VEC_index (eh_region, trace, i); - if (r->tree_label && label_to_block (r->tree_label) == old_bb) - { - r->tree_label = new_dest_label; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Updating label for region %i\n", - r->region_number); - } - } - r = region; - } - else - { - /* Now look for outermost handler that reffers to the basic block in question. - We start our duplication there. */ - for (i = 0; i < (int) VEC_length (eh_region, trace); i++) - { - r = VEC_index (eh_region, trace, i); - if (r->tree_label && label_to_block (r->tree_label) == old_bb) - start_here = i; - } - outer = VEC_index (eh_region, trace, start_here)->outer; - gcc_assert (start_here >= 0); - - /* And now do the dirty job! */ - for (i = start_here; i >= 0; i--) - { - old = VEC_index (eh_region, trace, i); - gcc_assert (!outer || old->outer != outer->outer); - - /* Copy region and update label. */ - r = copy_eh_region (old, outer); - VEC_replace (eh_region, trace, i, r); - if (r->tree_label && label_to_block (r->tree_label) == old_bb) - { - r->tree_label = new_dest_label; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Updating label for region %i\n", - r->region_number); - } - - /* We got into copying CATCH. copy_eh_region already did job - of copying all catch blocks corresponding to the try. Now - we need to update labels in all of them and see trace. - - We continue nesting into TRY region corresponding to CATCH: - When duplicating EH tree contaiing subregions of CATCH, - the CATCH region itself is never inserted to trace so we - never get here anyway. */ - if (r->type == ERT_CATCH) - { - /* Walk other catch regions we copied and update labels as needed. */ - for (r = r->next_peer; r->type == ERT_CATCH; r = r->next_peer) - if (r->tree_label && label_to_block (r->tree_label) == old_bb) - { - r->tree_label = new_dest_label; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Updating label for region %i\n", - r->region_number); - } - gcc_assert (r->type == ERT_TRY); - - /* Skip sibling catch regions from the trace. - They are already updated. */ - while (i > 0 && VEC_index (eh_region, trace, i - 1)->outer == old->outer) - { - gcc_assert (VEC_index (eh_region, trace, i - 1)->type == ERT_CATCH); - i--; - } - } - - outer = r; - } - - if (is_resx || region->type == ERT_THROW) - r = copy_eh_region (region, outer); - } - - VEC_free (eh_region, heap, trace); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - dump_eh_tree (dump_file, cfun); - fprintf (dump_file, "New region: %i\n", r->region_number); - } - return r; -} - -/* Return region number of region that is outer to both if REGION_A and - REGION_B in IFUN. */ - -int -eh_region_outermost (struct function *ifun, int region_a, int region_b) -{ - struct eh_region_d *rp_a, *rp_b; sbitmap b_outer; - gcc_assert (ifun->eh->last_region_number > 0); + gcc_assert (ifun->eh->region_array); gcc_assert (ifun->eh->region_tree); - rp_a = VEC_index (eh_region, ifun->eh->region_array, region_a); - rp_b = VEC_index (eh_region, ifun->eh->region_array, region_b); - gcc_assert (rp_a != NULL); - gcc_assert (rp_b != NULL); - - b_outer = sbitmap_alloc (ifun->eh->last_region_number + 1); + b_outer = sbitmap_alloc (VEC_length (eh_region, ifun->eh->region_array)); sbitmap_zero (b_outer); do { - SET_BIT (b_outer, rp_b->region_number); - rp_b = rp_b->outer; + SET_BIT (b_outer, region_b->index); + region_b = region_b->outer; } - while (rp_b); + while (region_b); do { - if (TEST_BIT (b_outer, rp_a->region_number)) - { - sbitmap_free (b_outer); - return rp_a->region_number; - } - rp_a = rp_a->outer; + if (TEST_BIT (b_outer, region_a->index)) + break; + region_a = region_a->outer; } - while (rp_a); + while (region_a); sbitmap_free (b_outer); - return -1; + return region_a; } static int @@ -1770,7 +758,7 @@ ehspec_filter_hash (const void *pentry) return h; } -/* Add TYPE (which may be NULL) to crtl->eh.ttype_data, using TYPES_HASH +/* Add TYPE (which may be NULL) to cfun->eh->ttype_data, using TYPES_HASH to speed up the search. Return the filter value to be used. */ static int @@ -1787,16 +775,16 @@ add_ttypes_entry (htab_t ttypes_hash, tree type) n = XNEW (struct ttypes_filter); n->t = type; - n->filter = VEC_length (tree, crtl->eh.ttype_data) + 1; + n->filter = VEC_length (tree, cfun->eh->ttype_data) + 1; *slot = n; - VEC_safe_push (tree, gc, crtl->eh.ttype_data, type); + VEC_safe_push (tree, gc, cfun->eh->ttype_data, type); } return n->filter; } -/* Add LIST to crtl->eh.ehspec_data, using EHSPEC_HASH and TYPES_HASH +/* Add LIST to cfun->eh->ehspec_data, using EHSPEC_HASH and TYPES_HASH to speed up the search. Return the filter value to be used. */ static int @@ -1811,30 +799,38 @@ add_ehspec_entry (htab_t ehspec_hash, htab_t ttypes_hash, tree list) if ((n = *slot) == NULL) { + int len; + + if (targetm.arm_eabi_unwinder) + len = VEC_length (tree, cfun->eh->ehspec_data.arm_eabi); + else + len = VEC_length (uchar, cfun->eh->ehspec_data.other); + /* Filter value is a -1 based byte index into a uleb128 buffer. */ n = XNEW (struct ttypes_filter); n->t = list; - n->filter = -(VARRAY_ACTIVE_SIZE (crtl->eh.ehspec_data) + 1); + n->filter = -(len + 1); *slot = n; /* Generate a 0 terminated list of filter values. */ for (; list ; list = TREE_CHAIN (list)) { if (targetm.arm_eabi_unwinder) - VARRAY_PUSH_TREE (crtl->eh.ehspec_data, TREE_VALUE (list)); + VEC_safe_push (tree, gc, cfun->eh->ehspec_data.arm_eabi, + TREE_VALUE (list)); else { /* Look up each type in the list and encode its filter value as a uleb128. */ - push_uleb128 (&crtl->eh.ehspec_data, - add_ttypes_entry (ttypes_hash, TREE_VALUE (list))); + push_uleb128 (&cfun->eh->ehspec_data.other, + add_ttypes_entry (ttypes_hash, TREE_VALUE (list))); } } if (targetm.arm_eabi_unwinder) - VARRAY_PUSH_TREE (crtl->eh.ehspec_data, NULL_TREE); + VEC_safe_push (tree, gc, cfun->eh->ehspec_data.arm_eabi, NULL_TREE); else - VARRAY_PUSH_UCHAR (crtl->eh.ehspec_data, 0); + VEC_safe_push (uchar, gc, cfun->eh->ehspec_data.other, 0); } return n->filter; @@ -1845,64 +841,63 @@ add_ehspec_entry (htab_t ehspec_hash, htab_t ttypes_hash, tree list) we use lots of landing pads, and so every type or list can share the same filter value, which saves table space. */ -static void +void assign_filter_values (void) { int i; htab_t ttypes, ehspec; + eh_region r; + eh_catch c; - crtl->eh.ttype_data = VEC_alloc (tree, gc, 16); + cfun->eh->ttype_data = VEC_alloc (tree, gc, 16); if (targetm.arm_eabi_unwinder) - VARRAY_TREE_INIT (crtl->eh.ehspec_data, 64, "ehspec_data"); + cfun->eh->ehspec_data.arm_eabi = VEC_alloc (tree, gc, 64); else - VARRAY_UCHAR_INIT (crtl->eh.ehspec_data, 64, "ehspec_data"); + cfun->eh->ehspec_data.other = VEC_alloc (uchar, gc, 64); ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free); ehspec = htab_create (31, ehspec_filter_hash, ehspec_filter_eq, free); - for (i = cfun->eh->last_region_number; i > 0; --i) + for (i = 1; VEC_iterate (eh_region, cfun->eh->region_array, i, r); ++i) { - struct eh_region_d *r; - - r = VEC_index (eh_region, cfun->eh->region_array, i); - - /* Mind we don't process a region more than once. */ - if (!r || r->region_number != i) + if (r == NULL) continue; switch (r->type) { - case ERT_CATCH: - /* Whatever type_list is (NULL or true list), we build a list - of filters for the region. */ - r->u.eh_catch.filter_list = NULL_TREE; - - if (r->u.eh_catch.type_list != NULL) + case ERT_TRY: + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) { - /* Get a filter value for each of the types caught and store - them in the region's dedicated list. */ - tree tp_node = r->u.eh_catch.type_list; + /* Whatever type_list is (NULL or true list), we build a list + of filters for the region. */ + c->filter_list = NULL_TREE; - for (;tp_node; tp_node = TREE_CHAIN (tp_node)) + if (c->type_list != NULL) { - int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node)); - tree flt_node = build_int_cst (NULL_TREE, flt); + /* Get a filter value for each of the types caught and store + them in the region's dedicated list. */ + tree tp_node = c->type_list; + + for ( ; tp_node; tp_node = TREE_CHAIN (tp_node)) + { + int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node)); + tree flt_node = build_int_cst (NULL_TREE, flt); - r->u.eh_catch.filter_list - = tree_cons (NULL_TREE, flt_node, r->u.eh_catch.filter_list); + c->filter_list + = tree_cons (NULL_TREE, flt_node, c->filter_list); + } } - } - else - { - /* Get a filter value for the NULL list also since it will need - an action record anyway. */ - int flt = add_ttypes_entry (ttypes, NULL); - tree flt_node = build_int_cst (NULL_TREE, flt); + else + { + /* Get a filter value for the NULL list also since it + will need an action record anyway. */ + int flt = add_ttypes_entry (ttypes, NULL); + tree flt_node = build_int_cst (NULL_TREE, flt); - r->u.eh_catch.filter_list - = tree_cons (NULL_TREE, flt_node, r->u.eh_catch.filter_list); + c->filter_list + = tree_cons (NULL_TREE, flt_node, NULL); + } } - break; case ERT_ALLOWED_EXCEPTIONS: @@ -1946,256 +941,29 @@ emit_to_new_bb_before (rtx seq, rtx insn) bb->flags |= BB_SUPERBLOCK; return bb; } - -/* Generate the code to actually handle exceptions, which will follow the - landing pads. */ - -static void -build_post_landing_pads (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region_d *region; - rtx seq; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - /* Mind we don't process a region more than once. */ - if (!region || region->region_number != i) - continue; - - switch (region->type) - { - case ERT_TRY: - /* It is possible that TRY region is kept alive only because some of - contained catch region still have RESX instruction but they are - reached via their copies. In this case we need to do nothing. */ - if (!region->u.eh_try.eh_catch->label) - break; - - /* ??? Collect the set of all non-overlapping catch handlers - all the way up the chain until blocked by a cleanup. */ - /* ??? Outer try regions can share landing pads with inner - try regions if the types are completely non-overlapping, - and there are no intervening cleanups. */ - - region->post_landing_pad = gen_label_rtx (); - - start_sequence (); - - emit_label (region->post_landing_pad); - - /* ??? It is mighty inconvenient to call back into the - switch statement generation code in expand_end_case. - Rapid prototyping sez a sequence of ifs. */ - { - struct eh_region_d *c; - for (c = region->u.eh_try.eh_catch; c ; c = c->u.eh_catch.next_catch) - { - if (c->u.eh_catch.type_list == NULL) - emit_jump (c->label); - else - { - /* Need for one cmp/jump per type caught. Each type - list entry has a matching entry in the filter list - (see assign_filter_values). */ - tree tp_node = c->u.eh_catch.type_list; - tree flt_node = c->u.eh_catch.filter_list; - - for (; tp_node; ) - { - emit_cmp_and_jump_insns - (crtl->eh.filter, - GEN_INT (tree_low_cst (TREE_VALUE (flt_node), 0)), - EQ, NULL_RTX, - targetm.eh_return_filter_mode (), 0, c->label); - - tp_node = TREE_CHAIN (tp_node); - flt_node = TREE_CHAIN (flt_node); - } - } - } - } - - /* 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. */ - gcc_assert (!region->resume); - region->resume - = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number)); - emit_barrier (); - - seq = get_insns (); - end_sequence (); - - emit_to_new_bb_before (seq, region->u.eh_try.eh_catch->label); - - break; - - case ERT_ALLOWED_EXCEPTIONS: - if (!region->label) - break; - region->post_landing_pad = gen_label_rtx (); - - start_sequence (); - - emit_label (region->post_landing_pad); - - emit_cmp_and_jump_insns (crtl->eh.filter, - GEN_INT (region->u.allowed.filter), - EQ, NULL_RTX, - targetm.eh_return_filter_mode (), 0, region->label); - - /* 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. */ - gcc_assert (!region->resume); - region->resume - = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number)); - emit_barrier (); - - seq = get_insns (); - end_sequence (); - - emit_to_new_bb_before (seq, region->label); - break; - - case ERT_CLEANUP: - case ERT_MUST_NOT_THROW: - region->post_landing_pad = region->label; - break; - - case ERT_CATCH: - case ERT_THROW: - /* Nothing to do. */ - break; - - default: - gcc_unreachable (); - } - } -} - -/* Replace RESX patterns with jumps to the next handler if any, or calls to - _Unwind_Resume otherwise. */ - -static void -connect_post_landing_pads (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region_d *region; - struct eh_region_d *outer; - rtx seq; - rtx barrier; - rtx resume_list; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - /* Mind we don't process a region more than once. */ - if (!region || region->region_number != i) - continue; - - /* If there is no RESX, or it has been deleted by flow, there's - nothing to fix up. */ - if (! region->resume) - continue; - - /* Search for another landing pad in this function. */ - for (outer = region->outer; outer ; outer = outer->outer) - if (outer->post_landing_pad) - break; - - for (resume_list = region->resume; resume_list; - resume_list = (GET_CODE (resume_list) == INSN_LIST - ? XEXP (resume_list, 1) : NULL_RTX)) - { - rtx resume = (GET_CODE (resume_list) == INSN_LIST - ? XEXP (resume_list, 0) : resume_list); - if (INSN_DELETED_P (resume)) - continue; - start_sequence (); - - if (outer) - { - edge e; - basic_block src, dest; - - emit_jump (outer->post_landing_pad); - src = BLOCK_FOR_INSN (resume); - dest = BLOCK_FOR_INSN (outer->post_landing_pad); - while (EDGE_COUNT (src->succs) > 0) - remove_edge (EDGE_SUCC (src, 0)); - e = make_edge (src, dest, 0); - e->probability = REG_BR_PROB_BASE; - e->count = src->count; - } - else - { - emit_library_call (unwind_resume_libfunc, LCT_THROW, - VOIDmode, 1, crtl->eh.exc_ptr, ptr_mode); - - /* What we just emitted was a throwing libcall, so it got a - barrier automatically added after it. If the last insn in - the libcall sequence isn't the barrier, it's because the - target emits multiple insns for a call, and there are insns - after the actual call insn (which are redundant and would be - optimized away). The barrier is inserted exactly after the - call insn, so let's go get that and delete the insns after - it, because below we need the barrier to be the last insn in - the sequence. */ - delete_insns_since (NEXT_INSN (last_call_insn ())); - } - - seq = get_insns (); - end_sequence (); - barrier = emit_insn_before (seq, resume); - /* Avoid duplicate barrier. */ - gcc_assert (BARRIER_P (barrier)); - delete_insn (barrier); - delete_insn (resume); - } - - /* ??? From tree-ssa we can wind up with catch regions whose - label is not instantiated, but whose resx is present. Now - that we've dealt with the resx, kill the region. */ - if (region->label == NULL && region->type == ERT_CLEANUP) - remove_eh_handler (region); - } -} - +/* Expand the extra code needed at landing pads for dwarf2 unwinding. */ + static void dw2_build_landing_pads (void) { int i; + eh_landing_pad lp; - for (i = cfun->eh->last_region_number; i > 0; --i) + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) { - struct eh_region_d *region; - rtx seq; + eh_region region; basic_block bb; + rtx seq; edge e; - region = VEC_index (eh_region, cfun->eh->region_array, i); - /* Mind we don't process a region more than once. */ - if (!region || region->region_number != i) - continue; - - if (region->type != ERT_CLEANUP - && region->type != ERT_TRY - && region->type != ERT_ALLOWED_EXCEPTIONS) - continue; - - if (!region->post_landing_pad) + if (lp == NULL || lp->post_landing_pad == NULL) continue; start_sequence (); - region->landing_pad = gen_label_rtx (); - emit_label (region->landing_pad); + lp->landing_pad = gen_label_rtx (); + emit_label (lp->landing_pad); #ifdef HAVE_exception_receiver if (HAVE_exception_receiver) @@ -2209,16 +977,19 @@ dw2_build_landing_pads (void) #endif { /* Nothing */ } - emit_move_insn (crtl->eh.exc_ptr, - gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0))); - emit_move_insn (crtl->eh.filter, - gen_rtx_REG (targetm.eh_return_filter_mode (), - EH_RETURN_DATA_REGNO (1))); + region = lp->region; + if (region->exc_ptr_reg) + emit_move_insn (region->exc_ptr_reg, + gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0))); + if (region->filter_reg) + emit_move_insn (region->filter_reg, + gen_rtx_REG (targetm.eh_return_filter_mode (), + EH_RETURN_DATA_REGNO (1))); seq = get_insns (); end_sequence (); - bb = emit_to_new_bb_before (seq, region->post_landing_pad); + bb = emit_to_new_bb_before (seq, label_rtx (lp->post_landing_pad)); e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); e->count = bb->count; e->probability = REG_BR_PROB_BASE; @@ -2226,140 +997,71 @@ dw2_build_landing_pads (void) } -struct sjlj_lp_info -{ - int directly_reachable; - int action_index; - int dispatch_index; - int call_site_index; -}; - -static bool -sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info) -{ - rtx insn; - bool found_one = false; - - for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) - { - struct eh_region_d *region; - enum reachable_code rc; - tree type_thrown; - rtx note; - - if (! INSN_P (insn)) - continue; - - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note || INTVAL (XEXP (note, 0)) <= 0) - continue; - - region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - if (!region) - continue; - - type_thrown = NULL_TREE; - if (region->type == ERT_THROW) - { - type_thrown = region->u.eh_throw.type; - region = region->outer; - } - - /* Find the first containing region that might handle the exception. - That's the landing pad to which we will transfer control. */ - rc = RNL_NOT_CAUGHT; - for (; region; region = region->outer) - { - rc = reachable_next_level (region, type_thrown, NULL, false); - if (rc != RNL_NOT_CAUGHT) - break; - } - if (rc == RNL_MAYBE_CAUGHT || rc == RNL_CAUGHT) - { - lp_info[region->region_number].directly_reachable = 1; - found_one = true; - } - } +static VEC (int, heap) *sjlj_lp_call_site_index; - return found_one; -} +/* Process all active landing pads. Assign each one a compact dispatch + index, and a call-site index. */ -static void -sjlj_assign_call_site_values (rtx dispatch_label, struct sjlj_lp_info *lp_info) +static int +sjlj_assign_call_site_values (void) { htab_t ar_hash; - int i, index; - - /* First task: build the action table. */ + int i, disp_index; + eh_landing_pad lp; - VARRAY_UCHAR_INIT (crtl->eh.action_record_data, 64, "action_record_data"); + crtl->eh.action_record_data = VEC_alloc (uchar, gc, 64); ar_hash = htab_create (31, action_record_hash, action_record_eq, free); - for (i = cfun->eh->last_region_number; i > 0; --i) - if (lp_info[i].directly_reachable) + disp_index = 0; + call_site_base = 1; + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp && lp->post_landing_pad) { - struct eh_region_d *r = - VEC_index (eh_region, cfun->eh->region_array, i); + int action, call_site; - r->landing_pad = dispatch_label; - lp_info[i].action_index = collect_one_action_chain (ar_hash, r); - if (lp_info[i].action_index != -1) + /* First: build the action table. */ + action = collect_one_action_chain (ar_hash, lp->region); + if (action != -1) crtl->uses_eh_lsda = 1; - } - - htab_delete (ar_hash); - - /* Next: assign dispatch values. In dwarf2 terms, this would be the - landing pad label for the region. For sjlj though, there is one - common landing pad from which we dispatch to the post-landing pads. - - A region receives a dispatch index if it is directly reachable - and requires in-function processing. Regions that share post-landing - pads may share dispatch indices. */ - /* ??? Post-landing pad sharing doesn't actually happen at the moment - (see build_post_landing_pads) so we don't bother checking for it. */ - - index = 0; - for (i = cfun->eh->last_region_number; i > 0; --i) - if (lp_info[i].directly_reachable) - lp_info[i].dispatch_index = index++; - - /* Finally: assign call-site values. If dwarf2 terms, this would be - the region number assigned by convert_to_eh_region_ranges, but - handles no-action and must-not-throw differently. */ - - call_site_base = 1; - for (i = cfun->eh->last_region_number; i > 0; --i) - if (lp_info[i].directly_reachable) - { - int action = lp_info[i].action_index; + /* Next: assign call-site values. If dwarf2 terms, this would be + the region number assigned by convert_to_eh_region_ranges, but + handles no-action and must-not-throw differently. */ /* Map must-not-throw to otherwise unused call-site index 0. */ if (action == -2) - index = 0; + call_site = 0; /* Map no-action to otherwise unused call-site index -1. */ else if (action == -1) - index = -1; + call_site = -1; /* Otherwise, look it up in the table. */ else - index = add_call_site (GEN_INT (lp_info[i].dispatch_index), - action, 0); + call_site = add_call_site (GEN_INT (disp_index), action, 0); + VEC_replace (int, sjlj_lp_call_site_index, i, call_site); - lp_info[i].call_site_index = index; + disp_index++; } + + htab_delete (ar_hash); + + return disp_index; } +/* Emit code to record the current call-site index before every + insn that can throw. */ + static void -sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) +sjlj_mark_call_sites (void) { int last_call_site = -2; rtx insn, mem; for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) { - struct eh_region_d *region; + eh_landing_pad lp; + eh_region r; + bool nothrow; int this_call_site; - rtx note, before, p; + rtx before, p; /* Reset value tracking at extended basic block boundaries. */ if (LABEL_P (insn)) @@ -2368,31 +1070,23 @@ sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) if (! INSN_P (insn)) continue; - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - - /* Calls that are known to not throw need not be marked. */ - if (note && INTVAL (XEXP (note, 0)) <= 0) + nothrow = get_eh_region_and_lp_from_rtx (insn, &r, &lp); + if (nothrow) continue; - - if (note) - region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - else - region = NULL; - - if (!region) + if (lp) + this_call_site = VEC_index (int, sjlj_lp_call_site_index, lp->index); + else if (r == NULL) { /* Calls (and trapping insns) without notes are outside any exception handling region in this function. Mark them as no action. */ - if (CALL_P (insn) - || (flag_non_call_exceptions - && may_trap_p (PATTERN (insn)))) - this_call_site = -1; - else - continue; + this_call_site = -1; } else - this_call_site = lp_info[region->region_number].call_site_index; + { + gcc_assert (r->type == ERT_MUST_NOT_THROW); + this_call_site = 0; + } if (this_call_site == last_call_site) continue; @@ -2525,15 +1219,18 @@ sjlj_emit_function_exit (void) } static void -sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info) +sjlj_emit_dispatch_table (rtx dispatch_label, int num_dispatch) { enum machine_mode unwind_word_mode = targetm.unwind_word_mode (); enum machine_mode filter_mode = targetm.eh_return_filter_mode (); - int i, first_reachable; - rtx mem, dispatch, seq, fc; - rtx before; + eh_landing_pad lp; + rtx mem, seq, fc, before, exc_ptr_reg, filter_reg; + rtx first_reachable_label; basic_block bb; + eh_region r; edge e; + int i, disp_index; + gimple switch_stmt; fc = crtl->eh.sjlj_fc; @@ -2543,14 +1240,18 @@ sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info) #ifndef DONT_USE_BUILTIN_SETJMP expand_builtin_setjmp_receiver (dispatch_label); -#endif - /* Load up dispatch index, exc_ptr and filter values from the - function context. */ - mem = adjust_address (fc, TYPE_MODE (integer_type_node), - sjlj_fc_call_site_ofs); - dispatch = copy_to_reg (mem); + /* The caller of expand_builtin_setjmp_receiver is responsible for + making sure that the label doesn't vanish. The only other caller + is the expander for __builtin_setjmp_receiver, which places this + label on the nonlocal_goto_label list. Since we're modeling these + CFG edges more exactly, we can use the forced_labels list instead. */ + LABEL_PRESERVE_P (dispatch_label) = 1; + forced_labels + = gen_rtx_EXPR_LIST (VOIDmode, dispatch_label, forced_labels); +#endif + /* Load up exc_ptr and filter values from the function context. */ mem = adjust_address (fc, unwind_word_mode, sjlj_fc_data_ofs); if (unwind_word_mode != ptr_mode) { @@ -2560,58 +1261,110 @@ sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info) mem = convert_to_mode (ptr_mode, mem, 0); #endif } - emit_move_insn (crtl->eh.exc_ptr, mem); + exc_ptr_reg = force_reg (ptr_mode, mem); mem = adjust_address (fc, unwind_word_mode, sjlj_fc_data_ofs + GET_MODE_SIZE (unwind_word_mode)); if (unwind_word_mode != filter_mode) mem = convert_to_mode (filter_mode, mem, 0); - emit_move_insn (crtl->eh.filter, mem); + filter_reg = force_reg (filter_mode, mem); /* Jump to one of the directly reachable regions. */ - /* ??? This really ought to be using a switch statement. */ - first_reachable = 0; - for (i = cfun->eh->last_region_number; i > 0; --i) + disp_index = 0; + first_reachable_label = NULL; + + /* If there's exactly one call site in the function, don't bother + generating a switch statement. */ + switch_stmt = NULL; + if (num_dispatch > 1) { - if (! lp_info[i].directly_reachable) - continue; + tree disp; - if (! first_reachable) - { - first_reachable = i; - continue; - } + mem = adjust_address (fc, TYPE_MODE (integer_type_node), + sjlj_fc_call_site_ofs); + disp = make_tree (integer_type_node, mem); - emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index), - EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0, - (((struct eh_region_d *) - VEC_index (eh_region, - cfun->eh->region_array, i)) - ->post_landing_pad)); + switch_stmt = gimple_build_switch_nlabels (num_dispatch, disp, NULL); + } + + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp && lp->post_landing_pad) + { + rtx seq2, label; + + start_sequence (); + + lp->landing_pad = dispatch_label; + + if (num_dispatch > 1) + { + tree t_label, case_elt; + + t_label = create_artificial_label (UNKNOWN_LOCATION); + case_elt = build3 (CASE_LABEL_EXPR, void_type_node, + build_int_cst (NULL, disp_index), + NULL, t_label); + gimple_switch_set_label (switch_stmt, disp_index, case_elt); + + label = label_rtx (t_label); + } + else + label = gen_label_rtx (); + + if (disp_index == 0) + first_reachable_label = label; + emit_label (label); + + r = lp->region; + if (r->exc_ptr_reg) + emit_move_insn (r->exc_ptr_reg, exc_ptr_reg); + if (r->filter_reg) + emit_move_insn (r->filter_reg, filter_reg); + + seq2 = get_insns (); + end_sequence (); + + before = label_rtx (lp->post_landing_pad); + bb = emit_to_new_bb_before (seq2, before); + e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); + e->count = bb->count; + e->probability = REG_BR_PROB_BASE; + + disp_index++; + } + gcc_assert (disp_index == num_dispatch); + + if (num_dispatch > 1) + { + expand_case (switch_stmt); + expand_builtin_trap (); } seq = get_insns (); end_sequence (); - before = (((struct eh_region_d *) - VEC_index (eh_region, cfun->eh->region_array, first_reachable)) - ->post_landing_pad); - - bb = emit_to_new_bb_before (seq, before); - e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); - e->count = bb->count; - e->probability = REG_BR_PROB_BASE; + bb = emit_to_new_bb_before (seq, first_reachable_label); + if (num_dispatch == 1) + { + e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); + e->count = bb->count; + e->probability = REG_BR_PROB_BASE; + } } static void sjlj_build_landing_pads (void) { - struct sjlj_lp_info *lp_info; + int num_dispatch; - lp_info = XCNEWVEC (struct sjlj_lp_info, cfun->eh->last_region_number + 1); + num_dispatch = VEC_length (eh_landing_pad, cfun->eh->lp_array); + if (num_dispatch == 0) + return; + VEC_safe_grow (int, heap, sjlj_lp_call_site_index, num_dispatch); - if (sjlj_find_directly_reachable_regions (lp_info)) + num_dispatch = sjlj_assign_call_site_values (); + if (num_dispatch > 0) { rtx dispatch_label = gen_label_rtx (); int align = STACK_SLOT_ALIGNMENT (sjlj_fc_type_node, @@ -2622,15 +1375,13 @@ sjlj_build_landing_pads (void) int_size_in_bytes (sjlj_fc_type_node), align); - sjlj_assign_call_site_values (dispatch_label, lp_info); - sjlj_mark_call_sites (lp_info); - + sjlj_mark_call_sites (); sjlj_emit_function_enter (dispatch_label); - sjlj_emit_dispatch_table (dispatch_label, lp_info); + sjlj_emit_dispatch_table (dispatch_label, num_dispatch); sjlj_emit_function_exit (); } - free (lp_info); + VEC_free (int, heap, sjlj_lp_call_site_index); } /* After initial rtl generation, call back to finish generating @@ -2641,700 +1392,405 @@ finish_eh_generation (void) { basic_block bb; - /* Nothing to do if no regions created. */ - if (cfun->eh->region_tree == NULL) - return; - - /* The object here is to provide detailed information (via - reachable_handlers) on how exception control flows within the - function for the CFG construction. In this first pass, we can - include type information garnered from ERT_THROW and - ERT_ALLOWED_EXCEPTIONS regions, and hope that it will be useful - in deleting unreachable handlers. Subsequently, we will generate - landing pads which will connect many of the handlers, and then - type information will not be effective. Still, this is a win - over previous implementations. */ - - /* These registers are used by the landing pads. Make sure they - have been generated. */ - get_exception_pointer (); - get_exception_filter (); - /* Construct the landing pads. */ - - assign_filter_values (); - build_post_landing_pads (); - connect_post_landing_pads (); if (USING_SJLJ_EXCEPTIONS) sjlj_build_landing_pads (); else dw2_build_landing_pads (); - - crtl->eh.built_landing_pads = 1; - - /* We've totally changed the CFG. Start over. */ - find_exception_handler_labels (); break_superblocks (); + if (USING_SJLJ_EXCEPTIONS /* Kludge for Alpha/Tru64 (see alpha_gp_save_rtx). */ || single_succ_edge (ENTRY_BLOCK_PTR)->insns.r) commit_edge_insertions (); + + /* Redirect all EH edges from the post_landing_pad to the landing pad. */ FOR_EACH_BB (bb) { - edge e; + eh_landing_pad lp; edge_iterator ei; - bool eh = false; - for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) + edge e; + + lp = get_eh_landing_pad_from_rtx (BB_END (bb)); + + FOR_EACH_EDGE (e, ei, bb->succs) + if (e->flags & EDGE_EH) + break; + + /* We should not have generated any new throwing insns during this + pass, and we should not have lost any EH edges, so we only need + to handle two cases here: + (1) reachable handler and an existing edge to post-landing-pad, + (2) no reachable handler and no edge. */ + gcc_assert ((lp != NULL) == (e != NULL)); + if (lp != NULL) { - if (e->flags & EDGE_EH) - { - remove_edge (e); - eh = true; - } - else - ei_next (&ei); + gcc_assert (BB_HEAD (e->dest) == label_rtx (lp->post_landing_pad)); + + redirect_edge_succ (e, BLOCK_FOR_INSN (lp->landing_pad)); + e->flags |= (CALL_P (BB_END (bb)) + ? EDGE_ABNORMAL | EDGE_ABNORMAL_CALL + : EDGE_ABNORMAL); } - if (eh) - rtl_make_eh_edge (NULL, bb, BB_END (bb)); } } - -/* This section handles removing dead code for flow. */ -/* Splice REGION from the region tree and replace it by REPLACE etc. - When UPDATE_CATCH_TRY is true mind updating links from catch to try - region.*/ +static bool +gate_handle_eh (void) +{ + /* Nothing to do if no regions created. */ + return cfun->eh->region_tree != NULL; +} -static void -remove_eh_handler_and_replace (struct eh_region_d *region, - struct eh_region_d *replace, - bool update_catch_try) +/* Complete generation of exception handling code. */ +static unsigned int +rest_of_handle_eh (void) { - struct eh_region_d **pp, **pp_start, *p, *outer, *inner; - rtx lab; + finish_eh_generation (); + cleanup_cfg (CLEANUP_NO_INSN_DEL); + return 0; +} - outer = region->outer; +struct rtl_opt_pass pass_rtl_eh = +{ + { + RTL_PASS, + "eh", /* name */ + gate_handle_eh, /* gate */ + rest_of_handle_eh, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_JUMP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func /* todo_flags_finish */ + } +}; + +/* This section handles removing dead code for flow. */ - /* For the benefit of efficiently handling REG_EH_REGION notes, - replace this region in the region array with its containing - region. Note that previous region deletions may result in - multiple copies of this region in the array, so we have a - list of alternate numbers by which we are known. */ +void +remove_eh_landing_pad (eh_landing_pad lp) +{ + eh_landing_pad *pp; - VEC_replace (eh_region, cfun->eh->region_array, region->region_number, - replace); - if (region->aka) - { - unsigned i; - bitmap_iterator bi; + for (pp = &lp->region->landing_pads; *pp != lp; pp = &(*pp)->next_lp) + continue; + *pp = lp->next_lp; + + if (lp->post_landing_pad) + EH_LANDING_PAD_NR (lp->post_landing_pad) = 0; + VEC_replace (eh_landing_pad, cfun->eh->lp_array, lp->index, NULL); +} - EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i, bi) - { - VEC_replace (eh_region, cfun->eh->region_array, i, replace); - } - } +/* Splice REGION from the region tree. */ - if (replace) +void +remove_eh_handler (eh_region region) +{ + eh_region *pp, *pp_start, p, outer; + eh_landing_pad lp; + + for (lp = region->landing_pads; lp ; lp = lp->next_lp) { - if (!replace->aka) - replace->aka = BITMAP_GGC_ALLOC (); - if (region->aka) - bitmap_ior_into (replace->aka, region->aka); - bitmap_set_bit (replace->aka, region->region_number); + if (lp->post_landing_pad) + EH_LANDING_PAD_NR (lp->post_landing_pad) = 0; + VEC_replace (eh_landing_pad, cfun->eh->lp_array, lp->index, NULL); } - if (crtl->eh.built_landing_pads) - lab = region->landing_pad; - else - lab = region->label; + outer = region->outer; if (outer) pp_start = &outer->inner; else pp_start = &cfun->eh->region_tree; for (pp = pp_start, p = *pp; p != region; pp = &p->next_peer, p = *pp) continue; - *pp = region->next_peer; - - if (replace) - pp_start = &replace->inner; - else - pp_start = &cfun->eh->region_tree; - inner = region->inner; - if (inner) + if (region->inner) { - for (p = inner; p->next_peer ; p = p->next_peer) - p->outer = replace; - p->outer = replace; - - p->next_peer = *pp_start; - *pp_start = inner; + *pp = p = region->inner; + do + { + p->outer = outer; + pp = &p->next_peer; + p = *pp; + } + while (p); } + *pp = region->next_peer; - if (region->type == ERT_CATCH - && update_catch_try) - { - struct eh_region_d *eh_try, *next, *prev; + VEC_replace (eh_region, cfun->eh->region_array, region->index, NULL); +} - for (eh_try = region->next_peer; - eh_try->type == ERT_CATCH; - eh_try = eh_try->next_peer) - continue; - gcc_assert (eh_try->type == ERT_TRY); +/* Invokes CALLBACK for every exception handler landing pad label. + Only used by reload hackery; should not be used by new code. */ - next = region->u.eh_catch.next_catch; - prev = region->u.eh_catch.prev_catch; +void +for_each_eh_label (void (*callback) (rtx)) +{ + eh_landing_pad lp; + int i; - if (next) - next->u.eh_catch.prev_catch = prev; - else - eh_try->u.eh_try.last_catch = prev; - if (prev) - prev->u.eh_catch.next_catch = next; - else + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + { + if (lp) { - eh_try->u.eh_try.eh_catch = next; - if (! next) - remove_eh_handler (eh_try); + rtx lab = lp->landing_pad; + if (lab && LABEL_P (lab)) + (*callback) (lab); } } } - -/* Splice REGION from the region tree and replace it by the outer region - etc. */ - -static void -remove_eh_handler (struct eh_region_d *region) -{ - remove_eh_handler_and_replace (region, region->outer, true); -} - -/* Remove Eh region R that has turned out to have no code in its handler. */ + +/* Create the REG_EH_REGION note for INSN, given its ECF_FLAGS for a + call insn. + + At the gimple level, we use LP_NR + > 0 : The statement transfers to landing pad LP_NR + = 0 : The statement is outside any EH region + < 0 : The statement is within MUST_NOT_THROW region -LP_NR. + + At the rtl level, we use LP_NR + > 0 : The insn transfers to landing pad LP_NR + = 0 : The insn cannot throw + < 0 : The insn is within MUST_NOT_THROW region -LP_NR + = INT_MIN : The insn cannot throw or execute a nonlocal-goto. + missing note: The insn is outside any EH region. + + ??? This difference probably ought to be avoided. We could stand + to record nothrow for arbitrary gimple statements, and so avoid + some moderately complex lookups in stmt_could_throw_p. Perhaps + NOTHROW should be mapped on both sides to INT_MIN. Perhaps the + no-nonlocal-goto property should be recorded elsewhere as a bit + on the call_insn directly. Perhaps we should make more use of + attaching the trees to call_insns (reachable via symbol_ref in + direct call cases) and just pull the data out of the trees. */ void -remove_eh_region (int r) +make_reg_eh_region_note (rtx insn, int ecf_flags, int lp_nr) { - struct eh_region_d *region; - - region = VEC_index (eh_region, cfun->eh->region_array, r); - remove_eh_handler (region); + rtx value; + if (ecf_flags & ECF_NOTHROW) + value = const0_rtx; + else if (lp_nr != 0) + value = GEN_INT (lp_nr); + else + return; + add_reg_note (insn, REG_EH_REGION, value); } -/* Remove Eh region R that has turned out to have no code in its handler - and replace in by R2. */ +/* Create a REG_EH_REGION note for a CALL_INSN that cannot throw + nor perform a non-local goto. Replace the region note if it + already exists. */ void -remove_eh_region_and_replace_by_outer_of (int r, int r2) +make_reg_eh_region_note_nothrow_nononlocal (rtx insn) { - struct eh_region_d *region, *region2; + rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + rtx intmin = GEN_INT (INT_MIN); - region = VEC_index (eh_region, cfun->eh->region_array, r); - region2 = VEC_index (eh_region, cfun->eh->region_array, r2); - remove_eh_handler_and_replace (region, region2->outer, true); + if (note != 0) + XEXP (note, 0) = intmin; + else + add_reg_note (insn, REG_EH_REGION, intmin); } -/* Invokes CALLBACK for every exception handler label. Only used by old - loop hackery; should not be used by new code. */ +/* Return true if INSN could throw, assuming no REG_EH_REGION note + to the contrary. */ -void -for_each_eh_label (void (*callback) (rtx)) +bool +insn_could_throw_p (const_rtx insn) { - int i; - for (i = 0; i < cfun->eh->last_region_number; i++) - { - struct eh_region_d *r = VEC_index (eh_region, cfun->eh->region_array, i); - if (r && r->region_number == i && r->label - && LABEL_P (r->label)) - (*callback) (r->label); - } + if (CALL_P (insn)) + return true; + if (INSN_P (insn) && flag_non_call_exceptions) + return may_trap_p (PATTERN (insn)); + return false; } -/* Invoke CALLBACK for every exception region in the current function. */ +/* Copy an REG_EH_REGION note to each insn that might throw beginning + at FIRST and ending at LAST. NOTE_OR_INSN is either the source insn + to look for a note, or the note itself. */ void -for_each_eh_region (void (*callback) (struct eh_region_d *)) +copy_reg_eh_region_note_forward (rtx note_or_insn, rtx first, rtx last) { - int i, n = cfun->eh->last_region_number; - for (i = 1; i <= n; ++i) - { - struct eh_region_d *region; + rtx insn, note = note_or_insn; - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (region) - (*callback) (region); + if (INSN_P (note_or_insn)) + { + note = find_reg_note (note_or_insn, REG_EH_REGION, NULL_RTX); + if (note == NULL) + return; } -} - -/* This section describes CFG exception edges for flow. */ + note = XEXP (note, 0); -/* For communicating between calls to reachable_next_level. */ -struct reachable_info -{ - tree types_caught; - tree types_allowed; - void (*callback) (struct eh_region_d *, void *); - void *callback_data; -}; + for (insn = first; insn != last ; insn = NEXT_INSN (insn)) + if (!find_reg_note (insn, REG_EH_REGION, NULL_RTX) + && insn_could_throw_p (insn)) + add_reg_note (insn, REG_EH_REGION, note); +} -/* A subroutine of reachable_next_level. Return true if TYPE, or a - base class of TYPE, is in HANDLED. */ +/* Likewise, but iterate backward. */ -static int -check_handled (tree handled, tree type) +void +copy_reg_eh_region_note_backward (rtx note_or_insn, rtx last, rtx first) { - tree t; + rtx insn, note = note_or_insn; - /* We can check for exact matches without front-end help. */ - if (! lang_eh_type_covers) + if (INSN_P (note_or_insn)) { - for (t = handled; t ; t = TREE_CHAIN (t)) - { - tree t1 = TREE_VALUE (t); - tree t2 = type; - - /* If the types have been converted to runtime types (i.e., - when the IL is being read from disk in an LTO - compilation), then T1 and T2 will be pointers to the - runtime type of the form '(void *) &<runtime_type>' (See - cp/except.c:build_eh_type_type). Strip the conversion - and the address. */ - if (CONVERT_EXPR_P (t1)) - { - STRIP_NOPS (t1); - gcc_assert (TREE_CODE (t1) == ADDR_EXPR); - t1 = TREE_OPERAND (t1, 0); - } - - if (CONVERT_EXPR_P (t2)) - { - STRIP_NOPS (t2); - gcc_assert (TREE_CODE (t2) == ADDR_EXPR); - t2 = TREE_OPERAND (t2, 0); - } - - if (t1 == t2) - return 1; - } - } - else - { - for (t = handled; t ; t = TREE_CHAIN (t)) - if ((*lang_eh_type_covers) (TREE_VALUE (t), type)) - return 1; + note = find_reg_note (note_or_insn, REG_EH_REGION, NULL_RTX); + if (note == NULL) + return; } + note = XEXP (note, 0); - return 0; + for (insn = last; insn != first; insn = PREV_INSN (insn)) + if (insn_could_throw_p (insn)) + add_reg_note (insn, REG_EH_REGION, note); } -/* A subroutine of reachable_next_level. If we are collecting a list - of handlers, add one. After landing pad generation, reference - it instead of the handlers themselves. Further, the handlers are - all wired together, so by referencing one, we've got them all. - Before landing pad generation we reference each handler individually. - LP_REGION contains the landing pad; REGION is the handler. */ +/* Extract all EH information from INSN. Return true if the insn + was marked NOTHROW. */ -static void -add_reachable_handler (struct reachable_info *info, - struct eh_region_d *lp_region, - struct eh_region_d *region) +static bool +get_eh_region_and_lp_from_rtx (const_rtx insn, eh_region *pr, + eh_landing_pad *plp) { - if (! info) - return; + eh_landing_pad lp = NULL; + eh_region r = NULL; + bool ret = false; + rtx note; + int lp_nr; - if (crtl->eh.built_landing_pads) - info->callback (lp_region, info->callback_data); - else - info->callback (region, info->callback_data); -} + if (! INSN_P (insn)) + goto egress; -/* Process one level of exception regions for reachability. - If TYPE_THROWN is non-null, then it is the *exact* type being - propagated. If INFO is non-null, then collect handler labels - and caught/allowed type information between invocations. */ + if (NONJUMP_INSN_P (insn) + && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); -static enum reachable_code -reachable_next_level (struct eh_region_d *region, tree type_thrown, - struct reachable_info *info, - bool maybe_resx) -{ - switch (region->type) + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note) { - case ERT_CLEANUP: - /* Before landing-pad generation, we model control flow - directly to the individual handlers. In this way we can - see that catch handler types may shadow one another. */ - add_reachable_handler (info, region, region); - return RNL_MAYBE_CAUGHT; - - case ERT_TRY: - { - struct eh_region_d *c; - enum reachable_code ret = RNL_NOT_CAUGHT; - - for (c = region->u.eh_try.eh_catch; c ; c = c->u.eh_catch.next_catch) - { - /* A catch-all handler ends the search. */ - if (c->u.eh_catch.type_list == NULL) - { - add_reachable_handler (info, region, c); - return RNL_CAUGHT; - } - - if (type_thrown) - { - /* If we have at least one type match, end the search. */ - tree tp_node = c->u.eh_catch.type_list; - - for (; tp_node; tp_node = TREE_CHAIN (tp_node)) - { - tree type = TREE_VALUE (tp_node); - - if (type == type_thrown - || (lang_eh_type_covers - && (*lang_eh_type_covers) (type, type_thrown))) - { - add_reachable_handler (info, region, c); - return RNL_CAUGHT; - } - } - - /* If we have definitive information of a match failure, - the catch won't trigger. */ - if (lang_eh_type_covers) - return RNL_NOT_CAUGHT; - } - - /* At this point, we either don't know what type is thrown or - don't have front-end assistance to help deciding if it is - covered by one of the types in the list for this region. - - We'd then like to add this region to the list of reachable - handlers since it is indeed potentially reachable based on the - information we have. - - Actually, this handler is for sure not reachable if all the - types it matches have already been caught. That is, it is only - potentially reachable if at least one of the types it catches - has not been previously caught. */ - - if (! info) - ret = RNL_MAYBE_CAUGHT; - else - { - tree tp_node = c->u.eh_catch.type_list; - bool maybe_reachable = false; - - /* Compute the potential reachability of this handler and - update the list of types caught at the same time. */ - for (; tp_node; tp_node = TREE_CHAIN (tp_node)) - { - tree type = TREE_VALUE (tp_node); - - if (! check_handled (info->types_caught, type)) - { - info->types_caught - = tree_cons (NULL, type, info->types_caught); - - maybe_reachable = true; - } - } - - if (maybe_reachable) - { - add_reachable_handler (info, region, c); - - /* ??? If the catch type is a base class of every allowed - type, then we know we can stop the search. */ - ret = RNL_MAYBE_CAUGHT; - } - } - } - - return ret; - } - - case ERT_ALLOWED_EXCEPTIONS: - /* An empty list of types definitely ends the search. */ - if (region->u.allowed.type_list == NULL_TREE) - { - add_reachable_handler (info, region, region); - return RNL_CAUGHT; - } - - /* Collect a list of lists of allowed types for use in detecting - when a catch may be transformed into a catch-all. */ - if (info) - info->types_allowed = tree_cons (NULL_TREE, - region->u.allowed.type_list, - info->types_allowed); - - /* If we have definitive information about the type hierarchy, - then we can tell if the thrown type will pass through the - filter. */ - if (type_thrown && lang_eh_type_covers) - { - if (check_handled (region->u.allowed.type_list, type_thrown)) - return RNL_NOT_CAUGHT; - else - { - add_reachable_handler (info, region, region); - return RNL_CAUGHT; - } - } - - add_reachable_handler (info, region, region); - return RNL_MAYBE_CAUGHT; - - case ERT_CATCH: - /* Catch regions are handled by their controlling try region. */ - return RNL_NOT_CAUGHT; - - case ERT_MUST_NOT_THROW: - /* Here we end our search, since no exceptions may propagate. - - Local landing pads of ERT_MUST_NOT_THROW instructions are reachable - only via locally handled RESX instructions. - - When we inline a function call, we can bring in new handlers. In order - to avoid ERT_MUST_NOT_THROW landing pads from being deleted as unreachable - assume that such handlers exists prior for any inlinable call prior - inlining decisions are fixed. */ - - if (maybe_resx) - { - add_reachable_handler (info, region, region); - return RNL_CAUGHT; - } - else - return RNL_BLOCKED; - - case ERT_THROW: - case ERT_UNKNOWN: - /* Shouldn't see these here. */ - gcc_unreachable (); - break; - default: - gcc_unreachable (); + ret = !insn_could_throw_p (insn); + goto egress; } -} - -/* Invoke CALLBACK on each region reachable from REGION_NUMBER. */ -void -foreach_reachable_handler (int region_number, bool is_resx, bool inlinable_call, - void (*callback) (struct eh_region_d *, void *), - void *callback_data) -{ - struct reachable_info info; - struct eh_region_d *region; - tree type_thrown; - - memset (&info, 0, sizeof (info)); - info.callback = callback; - info.callback_data = callback_data; - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - if (!region) - return; - - type_thrown = NULL_TREE; - if (is_resx) - { - /* A RESX leaves a region instead of entering it. Thus the - region itself may have been deleted out from under us. */ - if (region == NULL) - return; - region = region->outer; - } - else if (region->type == ERT_THROW) + lp_nr = INTVAL (XEXP (note, 0)); + if (lp_nr == 0 || lp_nr == INT_MIN) { - type_thrown = region->u.eh_throw.type; - region = region->outer; + ret = true; + goto egress; } - while (region) + if (lp_nr < 0) + r = VEC_index (eh_region, cfun->eh->region_array, -lp_nr); + else { - if (reachable_next_level (region, type_thrown, &info, - inlinable_call || is_resx) >= RNL_CAUGHT) - break; - /* If we have processed one cleanup, there is no point in - processing any more of them. Each cleanup will have an edge - to the next outer cleanup region, so the flow graph will be - accurate. */ - if (region->type == ERT_CLEANUP) - { - enum reachable_code code = RNL_NOT_CAUGHT; - region = find_prev_try (region->outer); - /* Continue looking for outer TRY region until we find one - that might cath something. */ - while (region - && (code = reachable_next_level (region, type_thrown, &info, - inlinable_call || is_resx)) - == RNL_NOT_CAUGHT) - region = find_prev_try (region->outer); - if (code >= RNL_CAUGHT) - break; - } - if (region) - region = region->outer; + lp = VEC_index (eh_landing_pad, cfun->eh->lp_array, lp_nr); + r = lp->region; } + + egress: + *plp = lp; + *pr = r; + return ret; } -/* Retrieve a list of labels of exception handlers which can be - reached by a given insn. */ +/* Return the landing pad to which INSN may go, or NULL if it does not + have a reachable landing pad within this function. */ -static void -arh_to_landing_pad (struct eh_region_d *region, void *data) +eh_landing_pad +get_eh_landing_pad_from_rtx (const_rtx insn) { - rtx *p_handlers = (rtx *) data; - if (! *p_handlers) - *p_handlers = alloc_INSN_LIST (region->landing_pad, NULL_RTX); -} + eh_landing_pad lp; + eh_region r; -static void -arh_to_label (struct eh_region_d *region, void *data) -{ - rtx *p_handlers = (rtx *) data; - *p_handlers = alloc_INSN_LIST (region->label, *p_handlers); + get_eh_region_and_lp_from_rtx (insn, &r, &lp); + return lp; } -rtx -reachable_handlers (rtx insn) -{ - bool is_resx = false; - rtx handlers = NULL; - int region_number; +/* Return the region to which INSN may go, or NULL if it does not + have a reachable region within this function. */ - if (JUMP_P (insn) - && GET_CODE (PATTERN (insn)) == RESX) - { - region_number = XINT (PATTERN (insn), 0); - is_resx = true; - } - else - { - rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note || INTVAL (XEXP (note, 0)) <= 0) - return NULL; - region_number = INTVAL (XEXP (note, 0)); - } - - foreach_reachable_handler (region_number, is_resx, false, - (crtl->eh.built_landing_pads - ? arh_to_landing_pad - : arh_to_label), - &handlers); +eh_region +get_eh_region_from_rtx (const_rtx insn) +{ + eh_landing_pad lp; + eh_region r; - return handlers; + get_eh_region_and_lp_from_rtx (insn, &r, &lp); + return r; } -/* Determine if the given INSN can throw an exception that is caught - within the function. */ +/* Return true if INSN throws and is caught by something in this function. */ bool -can_throw_internal_1 (int region_number, bool is_resx, bool inlinable_call) +can_throw_internal (const_rtx insn) { - struct eh_region_d *region; - tree type_thrown; - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - if (!region) - return false; - - type_thrown = NULL_TREE; - if (is_resx) - region = region->outer; - else if (region->type == ERT_THROW) - { - type_thrown = region->u.eh_throw.type; - region = region->outer; - } - - /* If this exception is ignored by each and every containing region, - then control passes straight out. The runtime may handle some - regions, which also do not require processing internally. */ - for (; region; region = region->outer) - { - enum reachable_code how = reachable_next_level (region, type_thrown, 0, - inlinable_call || is_resx); - if (how == RNL_BLOCKED) - return false; - if (how != RNL_NOT_CAUGHT) - return true; - } - - return false; + return get_eh_landing_pad_from_rtx (insn) != NULL; } +/* Return true if INSN throws and escapes from the current function. */ + bool -can_throw_internal (const_rtx insn) +can_throw_external (const_rtx insn) { - rtx note; + eh_landing_pad lp; + eh_region r; + bool nothrow; if (! INSN_P (insn)) return false; - if (JUMP_P (insn) - && GET_CODE (PATTERN (insn)) == RESX - && XINT (PATTERN (insn), 0) > 0) - return can_throw_internal_1 (XINT (PATTERN (insn), 0), true, false); - if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, 0); - - /* Every insn that might throw has an EH_REGION note. */ - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note || INTVAL (XEXP (note, 0)) <= 0) - return false; + { + rtx seq = PATTERN (insn); + int i, n = XVECLEN (seq, 0); - return can_throw_internal_1 (INTVAL (XEXP (note, 0)), false, false); -} + for (i = 0; i < n; i++) + if (can_throw_external (XVECEXP (seq, 0, i))) + return true; -/* Determine if the given INSN can throw an exception that is - visible outside the function. */ + return false; + } -bool -can_throw_external_1 (int region_number, bool is_resx, bool inlinable_call) -{ - struct eh_region_d *region; - tree type_thrown; + nothrow = get_eh_region_and_lp_from_rtx (insn, &r, &lp); - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - if (!region) - return true; + /* If we can't throw, we obviously can't throw external. */ + if (nothrow) + return false; - type_thrown = NULL_TREE; - if (is_resx) - region = region->outer; - else if (region->type == ERT_THROW) - { - type_thrown = region->u.eh_throw.type; - region = region->outer; - } + /* If we have an internal landing pad, then we're not external. */ + if (lp != NULL) + return false; - /* If the exception is caught or blocked by any containing region, - then it is not seen by any calling function. */ - for (; region ; region = region->outer) - if (reachable_next_level (region, type_thrown, NULL, - inlinable_call || is_resx) >= RNL_CAUGHT) - return false; + /* If we're not within an EH region, then we are external. */ + if (r == NULL) + return true; - return true; + /* The only thing that ought to be left is MUST_NOT_THROW regions, + which don't always have landing pads. */ + gcc_assert (r->type == ERT_MUST_NOT_THROW); + return false; } +/* Return true if INSN cannot throw at all. */ + bool -can_throw_external (const_rtx insn) +insn_nothrow_p (const_rtx insn) { - rtx note; + eh_landing_pad lp; + eh_region r; if (! INSN_P (insn)) - return false; - - if (JUMP_P (insn) - && GET_CODE (PATTERN (insn)) == RESX - && XINT (PATTERN (insn), 0) > 0) - return can_throw_external_1 (XINT (PATTERN (insn), 0), true, false); + return true; if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) @@ -3343,30 +1799,30 @@ can_throw_external (const_rtx insn) int i, n = XVECLEN (seq, 0); for (i = 0; i < n; i++) - if (can_throw_external (XVECEXP (seq, 0, i))) - return true; + if (!insn_nothrow_p (XVECEXP (seq, 0, i))) + return false; - return false; + return true; } - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note) + return get_eh_region_and_lp_from_rtx (insn, &r, &lp); +} + +/* Return true if INSN can perform a non-local goto. */ +/* ??? This test is here in this file because it (ab)uses REG_EH_REGION. */ + +bool +can_nonlocal_goto (const_rtx insn) +{ + if (nonlocal_goto_handler_labels && CALL_P (insn)) { - /* Calls (and trapping insns) without notes are outside any - exception handling region in this function. We have to - assume it might throw. Given that the front end and middle - ends mark known NOTHROW functions, this isn't so wildly - inaccurate. */ - return (CALL_P (insn) - || (flag_non_call_exceptions - && may_trap_p (PATTERN (insn)))); + rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || INTVAL (XEXP (note, 0)) != INT_MIN) + return true; } - if (INTVAL (XEXP (note, 0)) <= 0) - return false; - - return can_throw_external_1 (INTVAL (XEXP (note, 0)), false, false); + return false; } - + /* Set TREE_NOTHROW and crtl->all_throwers_are_sibcalls. */ unsigned int @@ -3457,6 +1913,79 @@ struct rtl_opt_pass pass_set_nothrow_function_flags = /* Various hooks for unwind library. */ +/* Expand the EH support builtin functions: + __builtin_eh_pointer and __builtin_eh_filter. */ + +static eh_region +expand_builtin_eh_common (tree region_nr_t) +{ + HOST_WIDE_INT region_nr; + eh_region region; + + gcc_assert (host_integerp (region_nr_t, 0)); + region_nr = tree_low_cst (region_nr_t, 0); + + region = VEC_index (eh_region, cfun->eh->region_array, region_nr); + + /* ??? We shouldn't have been able to delete a eh region without + deleting all the code that depended on it. */ + gcc_assert (region != NULL); + + return region; +} + +/* Expand to the exc_ptr value from the given eh region. */ + +rtx +expand_builtin_eh_pointer (tree exp) +{ + eh_region region + = expand_builtin_eh_common (CALL_EXPR_ARG (exp, 0)); + if (region->exc_ptr_reg == NULL) + region->exc_ptr_reg = gen_reg_rtx (ptr_mode); + return region->exc_ptr_reg; +} + +/* Expand to the filter value from the given eh region. */ + +rtx +expand_builtin_eh_filter (tree exp) +{ + eh_region region + = expand_builtin_eh_common (CALL_EXPR_ARG (exp, 0)); + if (region->filter_reg == NULL) + region->filter_reg = gen_reg_rtx (targetm.eh_return_filter_mode ()); + return region->filter_reg; +} + +/* Copy the exc_ptr and filter values from one landing pad's registers + to another. This is used to inline the resx statement. */ + +rtx +expand_builtin_eh_copy_values (tree exp) +{ + eh_region dst + = expand_builtin_eh_common (CALL_EXPR_ARG (exp, 0)); + eh_region src + = expand_builtin_eh_common (CALL_EXPR_ARG (exp, 1)); + enum machine_mode fmode = targetm.eh_return_filter_mode (); + + if (dst->exc_ptr_reg == NULL) + dst->exc_ptr_reg = gen_reg_rtx (ptr_mode); + if (src->exc_ptr_reg == NULL) + src->exc_ptr_reg = gen_reg_rtx (ptr_mode); + + if (dst->filter_reg == NULL) + dst->filter_reg = gen_reg_rtx (fmode); + if (src->filter_reg == NULL) + src->filter_reg = gen_reg_rtx (fmode); + + emit_move_insn (dst->exc_ptr_reg, src->exc_ptr_reg); + emit_move_insn (dst->filter_reg, src->filter_reg); + + return const0_rtx; +} + /* Do any necessary initialization to access arbitrary stack frames. On the SPARC, this means flushing the register windows. */ @@ -3472,6 +2001,10 @@ expand_builtin_unwind_init (void) #endif } +/* Map a non-negative number to an eh return data register number; expands + to -1 if no return data register is associated with the input number. + At least the inputs 0 and 1 must be mapped; the target may provide more. */ + rtx expand_builtin_eh_return_data_regno (tree exp) { @@ -3580,6 +2113,10 @@ expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED, emit_jump (crtl->eh.ehr_label); } +/* Expand __builtin_eh_return. This exit path from the function loads up + the eh return data registers, adjusts the stack, and branches to a + given PC other than the normal return address. */ + void expand_eh_return (void) { @@ -3685,7 +2222,7 @@ add_action_record (htab_t ar_hash, int filter, int next) if ((new_ar = *slot) == NULL) { new_ar = XNEW (struct action_record); - new_ar->offset = VARRAY_ACTIVE_SIZE (crtl->eh.action_record_data) + 1; + new_ar->offset = VEC_length (uchar, crtl->eh.action_record_data) + 1; new_ar->filter = filter; new_ar->next = next; *slot = new_ar; @@ -3697,7 +2234,7 @@ add_action_record (htab_t ar_hash, int filter, int next) push_sleb128 (&crtl->eh.action_record_data, filter); if (next) - next -= VARRAY_ACTIVE_SIZE (crtl->eh.action_record_data) + 1; + next -= VEC_length (uchar, crtl->eh.action_record_data) + 1; push_sleb128 (&crtl->eh.action_record_data, next); } @@ -3705,9 +2242,8 @@ add_action_record (htab_t ar_hash, int filter, int next) } static int -collect_one_action_chain (htab_t ar_hash, struct eh_region_d *region) +collect_one_action_chain (htab_t ar_hash, eh_region region) { - struct eh_region_d *c; int next; /* If we've reached the top of the region chain, then we have @@ -3718,67 +2254,72 @@ collect_one_action_chain (htab_t ar_hash, struct eh_region_d *region) switch (region->type) { case ERT_CLEANUP: - /* A cleanup adds a zero filter to the beginning of the chain, but - there are special cases to look out for. If there are *only* - cleanups along a path, then it compresses to a zero action. - Further, if there are multiple cleanups along a path, we only - need to represent one of them, as that is enough to trigger - entry to the landing pad at runtime. */ - next = collect_one_action_chain (ar_hash, region->outer); - if (next <= 0) - return 0; - for (c = region->outer; c ; c = c->outer) - if (c->type == ERT_CLEANUP) - return next; - return add_action_record (ar_hash, 0, next); + { + eh_region r; + /* A cleanup adds a zero filter to the beginning of the chain, but + there are special cases to look out for. If there are *only* + cleanups along a path, then it compresses to a zero action. + Further, if there are multiple cleanups along a path, we only + need to represent one of them, as that is enough to trigger + entry to the landing pad at runtime. */ + next = collect_one_action_chain (ar_hash, region->outer); + if (next <= 0) + return 0; + for (r = region->outer; r ; r = r->outer) + if (r->type == ERT_CLEANUP) + return next; + return add_action_record (ar_hash, 0, next); + } case ERT_TRY: - /* Process the associated catch regions in reverse order. - If there's a catch-all handler, then we don't need to - search outer regions. Use a magic -3 value to record - that we haven't done the outer search. */ - next = -3; - for (c = region->u.eh_try.last_catch; c ; c = c->u.eh_catch.prev_catch) - { - if (c->u.eh_catch.type_list == NULL) - { - /* Retrieve the filter from the head of the filter list - where we have stored it (see assign_filter_values). */ - int filter - = TREE_INT_CST_LOW (TREE_VALUE (c->u.eh_catch.filter_list)); - - next = add_action_record (ar_hash, filter, 0); - } - else - { - /* Once the outer search is done, trigger an action record for - each filter we have. */ - tree flt_node; + { + eh_catch c; + + /* Process the associated catch regions in reverse order. + If there's a catch-all handler, then we don't need to + search outer regions. Use a magic -3 value to record + that we haven't done the outer search. */ + next = -3; + for (c = region->u.eh_try.last_catch; c ; c = c->prev_catch) + { + if (c->type_list == NULL) + { + /* Retrieve the filter from the head of the filter list + where we have stored it (see assign_filter_values). */ + int filter = TREE_INT_CST_LOW (TREE_VALUE (c->filter_list)); + next = add_action_record (ar_hash, filter, 0); + } + else + { + /* Once the outer search is done, trigger an action record for + each filter we have. */ + tree flt_node; - if (next == -3) - { - next = collect_one_action_chain (ar_hash, region->outer); - - /* If there is no next action, terminate the chain. */ - if (next == -1) - next = 0; - /* If all outer actions are cleanups or must_not_throw, - we'll have no action record for it, since we had wanted - to encode these states in the call-site record directly. - Add a cleanup action to the chain to catch these. */ - else if (next <= 0) - next = add_action_record (ar_hash, 0, 0); - } + if (next == -3) + { + next = collect_one_action_chain (ar_hash, region->outer); + + /* If there is no next action, terminate the chain. */ + if (next == -1) + next = 0; + /* If all outer actions are cleanups or must_not_throw, + we'll have no action record for it, since we had wanted + to encode these states in the call-site record directly. + Add a cleanup action to the chain to catch these. */ + else if (next <= 0) + next = add_action_record (ar_hash, 0, 0); + } - flt_node = c->u.eh_catch.filter_list; - for (; flt_node; flt_node = TREE_CHAIN (flt_node)) - { - int filter = TREE_INT_CST_LOW (TREE_VALUE (flt_node)); - next = add_action_record (ar_hash, filter, next); - } - } - } - return next; + flt_node = c->filter_list; + for (; flt_node; flt_node = TREE_CHAIN (flt_node)) + { + int filter = TREE_INT_CST_LOW (TREE_VALUE (flt_node)); + next = add_action_record (ar_hash, filter, next); + } + } + } + return next; + } case ERT_ALLOWED_EXCEPTIONS: /* An exception specification adds its filter to the @@ -3803,23 +2344,16 @@ collect_one_action_chain (htab_t ar_hash, struct eh_region_d *region) the no handler or cleanup case in that we do require an lsda to be generated. Return a magic -2 value to record this. */ return -2; - - case ERT_CATCH: - case ERT_THROW: - /* CATCH regions are handled in TRY above. THROW regions are - for optimization information only and produce no output. */ - return collect_one_action_chain (ar_hash, region->outer); - - default: - gcc_unreachable (); } + + gcc_unreachable (); } static int add_call_site (rtx landing_pad, int action, int section) { call_site_record record; - + record = GGC_NEW (struct call_site_record_d); record->landing_pad = landing_pad; record->action = action; @@ -3835,7 +2369,7 @@ add_call_site (rtx landing_pad, int action, int section) The new note numbers will not refer to region numbers, but instead to call site entries. */ -unsigned int +static unsigned int convert_to_eh_region_ranges (void) { rtx insn, iter, note; @@ -3854,17 +2388,16 @@ convert_to_eh_region_ranges (void) int min_labelno = 0, max_labelno = 0; int saved_call_site_base = call_site_base; - if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL) - return 0; - - VARRAY_UCHAR_INIT (crtl->eh.action_record_data, 64, "action_record_data"); + crtl->eh.action_record_data = VEC_alloc (uchar, gc, 64); ar_hash = htab_create (31, action_record_hash, action_record_eq, free); for (iter = get_insns (); iter ; iter = NEXT_INSN (iter)) if (INSN_P (iter)) { - struct eh_region_d *region; + eh_landing_pad lp; + eh_region region; + bool nothrow; int this_action; rtx this_landing_pad; @@ -3873,23 +2406,13 @@ convert_to_eh_region_ranges (void) && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note) - { - if (! (CALL_P (insn) - || (flag_non_call_exceptions - && may_trap_p (PATTERN (insn))))) - continue; - this_action = -1; - region = NULL; - } + nothrow = get_eh_region_and_lp_from_rtx (insn, ®ion, &lp); + if (nothrow) + continue; + if (region) + this_action = collect_one_action_chain (ar_hash, region); else - { - if (INTVAL (XEXP (note, 0)) <= 0) - continue; - region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - this_action = collect_one_action_chain (ar_hash, region); - } + this_action = -1; /* Existence of catch handlers, or must-not-throw regions implies that an lsda is needed (even if empty). */ @@ -3904,15 +2427,8 @@ convert_to_eh_region_ranges (void) last_action = -1; } - /* Cleanups and handlers may share action chains but not - landing pads. Collect the landing pad for this region. */ if (this_action >= 0) - { - struct eh_region_d *o; - for (o = region; ! o->landing_pad ; o = o->outer) - continue; - this_landing_pad = o->landing_pad; - } + this_landing_pad = lp->landing_pad; else this_landing_pad = NULL_RTX; @@ -4116,12 +2632,19 @@ convert_to_eh_region_ranges (void) return 0; } +static bool +gate_convert_to_eh_region_ranges (void) +{ + /* Nothing to do for SJLJ exceptions or if no regions created. */ + return !(USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL); +} + struct rtl_opt_pass pass_convert_to_eh_region_ranges = { { RTL_PASS, "eh_ranges", /* name */ - NULL, /* gate */ + gate_convert_to_eh_region_ranges, /* gate */ convert_to_eh_region_ranges, /* execute */ NULL, /* sub */ NULL, /* next */ @@ -4134,10 +2657,9 @@ struct rtl_opt_pass pass_convert_to_eh_region_ranges = TODO_dump_func, /* todo_flags_finish */ } }; - static void -push_uleb128 (varray_type *data_area, unsigned int value) +push_uleb128 (VEC (uchar, gc) **data_area, unsigned int value) { do { @@ -4145,13 +2667,13 @@ push_uleb128 (varray_type *data_area, unsigned int value) value >>= 7; if (value) byte |= 0x80; - VARRAY_PUSH_UCHAR (*data_area, byte); + VEC_safe_push (uchar, gc, *data_area, byte); } while (value); } static void -push_sleb128 (varray_type *data_area, int value) +push_sleb128 (VEC (uchar, gc) **data_area, int value) { unsigned char byte; int more; @@ -4164,7 +2686,7 @@ push_sleb128 (varray_type *data_area, int value) || (value == -1 && (byte & 0x40) != 0)); if (more) byte |= 0x80; - VARRAY_PUSH_UCHAR (*data_area, byte); + VEC_safe_push (uchar, gc, *data_area, byte); } while (more); } @@ -4394,7 +2916,7 @@ static void output_one_function_exception_table (const char * ARG_UNUSED (fnname), int section, rtx ARG_UNUSED (personality)) { - int tt_format, cs_format, lp_format, i, n; + int tt_format, cs_format, lp_format, i; #ifdef HAVE_AS_LEB128 char ttype_label[32]; char cs_after_size_label[32]; @@ -4419,8 +2941,10 @@ output_one_function_exception_table (const char * ARG_UNUSED (fnname), /* If the target wants a label to begin the table, emit it here. */ targetm.asm_out.except_table_label (asm_out_file); - have_tt_data = (VEC_length (tree, crtl->eh.ttype_data) > 0 - || VARRAY_ACTIVE_SIZE (crtl->eh.ehspec_data) > 0); + have_tt_data = (VEC_length (tree, cfun->eh->ttype_data) + || (targetm.arm_eabi_unwinder + ? VEC_length (tree, cfun->eh->ehspec_data.arm_eabi) + : VEC_length (uchar, cfun->eh->ehspec_data.other))); /* Indicate the format of the @TType entries. */ if (! have_tt_data) @@ -4483,8 +3007,8 @@ output_one_function_exception_table (const char * ARG_UNUSED (fnname), before_disp = 1 + 1; after_disp = (1 + size_of_uleb128 (call_site_len) + call_site_len - + VARRAY_ACTIVE_SIZE (crtl->eh.action_record_data) - + (VEC_length (tree, crtl->eh.ttype_data) + + VEC_length (uchar, crtl->eh.action_record_data) + + (VEC_length (tree, cfun->eh->ttype_data) * tt_format_size)); disp = after_disp; @@ -4540,18 +3064,19 @@ output_one_function_exception_table (const char * ARG_UNUSED (fnname), #endif /* ??? Decode and interpret the data for flag_debug_asm. */ - n = VARRAY_ACTIVE_SIZE (crtl->eh.action_record_data); - for (i = 0; i < n; ++i) - dw2_asm_output_data (1, VARRAY_UCHAR (crtl->eh.action_record_data, i), - (i ? NULL : "Action record table")); + { + uchar uc; + for (i = 0; VEC_iterate (uchar, crtl->eh.action_record_data, i, uc); ++i) + dw2_asm_output_data (1, uc, i ? NULL : "Action record table"); + } if (have_tt_data) assemble_align (tt_format_size * BITS_PER_UNIT); - i = VEC_length (tree, crtl->eh.ttype_data); + i = VEC_length (tree, cfun->eh->ttype_data); while (i-- > 0) { - tree type = VEC_index (tree, crtl->eh.ttype_data, i); + tree type = VEC_index (tree, cfun->eh->ttype_data, i); output_ttype (type, tt_format, tt_format_size); } @@ -4561,17 +3086,20 @@ output_one_function_exception_table (const char * ARG_UNUSED (fnname), #endif /* ??? Decode and interpret the data for flag_debug_asm. */ - n = VARRAY_ACTIVE_SIZE (crtl->eh.ehspec_data); - for (i = 0; i < n; ++i) + if (targetm.arm_eabi_unwinder) { - if (targetm.arm_eabi_unwinder) - { - tree type = VARRAY_TREE (crtl->eh.ehspec_data, i); - output_ttype (type, tt_format, tt_format_size); - } - else - dw2_asm_output_data (1, VARRAY_UCHAR (crtl->eh.ehspec_data, i), - (i ? NULL : "Exception specification table")); + tree type; + for (i = 0; + VEC_iterate (tree, cfun->eh->ehspec_data.arm_eabi, i, type); ++i) + output_ttype (type, tt_format, tt_format_size); + } + else + { + uchar uc; + for (i = 0; + VEC_iterate (uchar, cfun->eh->ehspec_data.other, i, uc); ++i) + dw2_asm_output_data (1, uc, + i ? NULL : "Exception specification table"); } } @@ -4605,82 +3133,50 @@ get_eh_throw_stmt_table (struct function *fun) { return fun->eh->throw_stmt_table; } - -/* Return true if the function deeds a EH personality function. */ + +/* Determine if the function needs an EH personality function. */ enum eh_personality_kind function_needs_eh_personality (struct function *fn) { - struct eh_region_d *i; - int depth = 0; enum eh_personality_kind kind = eh_personality_none; + eh_region i; - i = fn->eh->region_tree; - if (!i) - return eh_personality_none; - - while (1) + FOR_ALL_EH_REGION_FN (i, fn) { switch (i->type) { - case ERT_TRY: - case ERT_THROW: - /* Do not need a EH personality function. */ - break; - - case ERT_MUST_NOT_THROW: - /* Always needs a EH personality function. */ - return eh_personality_lang; - case ERT_CLEANUP: /* Can do with any personality including the generic C one. */ kind = eh_personality_any; break; - case ERT_CATCH: + case ERT_TRY: case ERT_ALLOWED_EXCEPTIONS: /* Always needs a EH personality function. The generic C personality doesn't handle these even for empty type lists. */ return eh_personality_lang; - case ERT_UNKNOWN: + case ERT_MUST_NOT_THROW: + /* Always needs a EH personality function. The language may specify + what abort routine that must be used, e.g. std::terminate. */ return eh_personality_lang; } - /* If there are sub-regions, process them. */ - if (i->inner) - i = i->inner, depth++; - /* If there are peers, process them. */ - else if (i->next_peer) - i = i->next_peer; - /* Otherwise, step back up the tree to the next peer. */ - else - { - do - { - i = i->outer; - depth--; - if (i == NULL) - return kind; - } - while (i->next_peer == NULL); - i = i->next_peer; - } } return kind; } - + /* Dump EH information to OUT. */ void dump_eh_tree (FILE * out, struct function *fun) { - struct eh_region_d *i; + eh_region i; int depth = 0; - static const char *const type_name[] = { "unknown", "cleanup", "try", "catch", - "allowed_exceptions", "must_not_throw", - "throw" - }; + static const char *const type_name[] = { + "cleanup", "try", "allowed_exceptions", "must_not_throw" + }; i = fun->eh->region_tree; if (!i) @@ -4690,91 +3186,82 @@ dump_eh_tree (FILE * out, struct function *fun) while (1) { fprintf (out, " %*s %i %s", depth * 2, "", - i->region_number, type_name[(int) i->type]); - if (i->tree_label) - { - fprintf (out, " tree_label:"); - print_generic_expr (out, i->tree_label, 0); - } - if (i->label) - fprintf (out, " label:%i", INSN_UID (i->label)); - if (i->landing_pad) - { - fprintf (out, " landing_pad:%i", INSN_UID (i->landing_pad)); - if (NOTE_P (i->landing_pad)) - fprintf (out, " (deleted)"); - } - if (i->post_landing_pad) - { - fprintf (out, " post_landing_pad:%i", INSN_UID (i->post_landing_pad)); - if (NOTE_P (i->post_landing_pad)) - fprintf (out, " (deleted)"); - } - if (i->resume) + i->index, type_name[(int) i->type]); + + if (i->landing_pads) { - rtx resume_list = i->resume; - fprintf (out, " resume:"); - while (GET_CODE (resume_list) == INSN_LIST) + eh_landing_pad lp; + + fprintf (out, " land:"); + if (current_ir_type () == IR_GIMPLE) + { + for (lp = i->landing_pads; lp ; lp = lp->next_lp) + { + fprintf (out, "{%i,", lp->index); + print_generic_expr (out, lp->post_landing_pad, 0); + fputc ('}', out); + if (lp->next_lp) + fputc (',', out); + } + } + else { - fprintf (out, "%i,", INSN_UID (XEXP (resume_list, 0))); - if (NOTE_P (XEXP (resume_list, 0))) - fprintf (out, " (deleted)"); - resume_list = XEXP (resume_list, 1); + for (lp = i->landing_pads; lp ; lp = lp->next_lp); + { + fprintf (out, "{%i,", lp->index); + if (lp->landing_pad) + fprintf (out, "%i%s,", INSN_UID (lp->landing_pad), + NOTE_P (lp->landing_pad) ? "(del)" : ""); + else + fprintf (out, "(nil),"); + if (lp->post_landing_pad) + { + rtx lab = label_rtx (lp->post_landing_pad); + fprintf (out, "%i%s}", INSN_UID (lab), + NOTE_P (lab) ? "(del)" : ""); + } + else + fprintf (out, "(nil)}"); + if (lp->next_lp) + fputc (',', out); + } } - fprintf (out, " resume:%i", INSN_UID (i->resume)); - if (NOTE_P (i->resume)) - fprintf (out, " (deleted)"); } - if (i->may_contain_throw) - fprintf (out, " may_contain_throw"); + switch (i->type) { case ERT_CLEANUP: + case ERT_MUST_NOT_THROW: break; case ERT_TRY: { - struct eh_region_d *c; - fprintf (out, " catch regions:"); - for (c = i->u.eh_try.eh_catch; c; c = c->u.eh_catch.next_catch) - fprintf (out, " %i", c->region_number); + eh_catch c; + fprintf (out, " catch:"); + for (c = i->u.eh_try.first_catch; c; c = c->next_catch) + { + fputc ('{', out); + if (c->label) + { + fprintf (out, "lab:"); + print_generic_expr (out, c->label, 0); + fputc (';', out); + } + print_generic_expr (out, c->type_list, 0); + fputc ('}', out); + if (c->next_catch) + fputc (',', out); + } } break; - case ERT_CATCH: - if (i->u.eh_catch.prev_catch) - fprintf (out, " prev: %i", - i->u.eh_catch.prev_catch->region_number); - if (i->u.eh_catch.next_catch) - fprintf (out, " next %i", - i->u.eh_catch.next_catch->region_number); - fprintf (out, " type:"); - print_generic_expr (out, i->u.eh_catch.type_list, 0); - break; - case ERT_ALLOWED_EXCEPTIONS: fprintf (out, " filter :%i types:", i->u.allowed.filter); print_generic_expr (out, i->u.allowed.type_list, 0); break; - - case ERT_THROW: - fprintf (out, " type:"); - print_generic_expr (out, i->u.eh_throw.type, 0); - break; - - case ERT_MUST_NOT_THROW: - break; - - case ERT_UNKNOWN: - break; } - if (i->aka) - { - fprintf (out, " also known as:"); - dump_bitmap (out, i->aka); - } - else - fprintf (out, "\n"); + fputc ('\n', out); + /* If there are sub-regions, process them. */ if (i->inner) i = i->inner, depth++; @@ -4805,217 +3292,123 @@ debug_eh_tree (struct function *fn) dump_eh_tree (stderr, fn); } - -/* Verify EH region invariants. */ - -static bool -verify_eh_region (struct eh_region_d *region) -{ - bool found = false; - if (!region) - return false; - switch (region->type) - { - case ERT_TRY: - { - struct eh_region_d *c, *prev = NULL; - if (region->u.eh_try.eh_catch->u.eh_catch.prev_catch) - { - error ("Try region %i has wrong rh_catch pointer to %i", - region->region_number, - region->u.eh_try.eh_catch->region_number); - found = true; - } - for (c = region->u.eh_try.eh_catch; c; c = c->u.eh_catch.next_catch) - { - if (c->outer != region->outer) - { - error - ("Catch region %i has different outer region than try region %i", - c->region_number, region->region_number); - found = true; - } - if (c->u.eh_catch.prev_catch != prev) - { - error ("Catch region %i has corrupted catchlist", - c->region_number); - found = true; - } - prev = c; - } - if (prev != region->u.eh_try.last_catch) - { - error - ("Try region %i has wrong last_catch pointer to %i instead of %i", - region->region_number, - region->u.eh_try.last_catch->region_number, - prev->region_number); - found = true; - } - } - break; - case ERT_CATCH: - if (!region->u.eh_catch.prev_catch - && (!region->next_peer || region->next_peer->type != ERT_TRY)) - { - error ("Catch region %i should be followed by try", region->region_number); - found = true; - } - break; - case ERT_CLEANUP: - case ERT_ALLOWED_EXCEPTIONS: - case ERT_MUST_NOT_THROW: - case ERT_THROW: - break; - case ERT_UNKNOWN: - gcc_unreachable (); - } - for (region = region->inner; region; region = region->next_peer) - found |= verify_eh_region (region); - return found; -} - /* Verify invariants on EH datastructures. */ void verify_eh_tree (struct function *fun) { - struct eh_region_d *i, *outer = NULL; + eh_region r, outer; + int nvisited_lp, nvisited_r; + int count_lp, count_r, depth, i; + eh_landing_pad lp; bool err = false; - int nvisited = 0; - int count = 0; - int j; - int depth = 0; if (!fun->eh->region_tree) return; - for (j = fun->eh->last_region_number; j > 0; --j) - if ((i = VEC_index (eh_region, fun->eh->region_array, j))) + + count_r = 0; + for (i = 1; VEC_iterate (eh_region, fun->eh->region_array, i, r); ++i) + if (r) { - if (i->region_number == j) - count++; - if (i->region_number != j && (!i->aka || !bitmap_bit_p (i->aka, j))) + if (r->index == i) + count_r++; + else { - error ("region_array is corrupted for region %i", - i->region_number); + error ("region_array is corrupted for region %i", r->index); err = true; } } - i = fun->eh->region_tree; + count_lp = 0; + for (i = 1; VEC_iterate (eh_landing_pad, fun->eh->lp_array, i, lp); ++i) + if (lp) + { + if (lp->index == i) + count_lp++; + else + { + error ("lp_array is corrupted for lp %i", lp->index); + err = true; + } + } + + depth = nvisited_lp = nvisited_r = 0; + outer = NULL; + r = fun->eh->region_tree; while (1) { - if (VEC_index (eh_region, fun->eh->region_array, i->region_number) != i) + if (VEC_index (eh_region, fun->eh->region_array, r->index) != r) { - error ("region_array is corrupted for region %i", i->region_number); + error ("region_array is corrupted for region %i", r->index); err = true; } - if (i->outer != outer) + if (r->outer != outer) { - error ("outer block of region %i is wrong", i->region_number); + error ("outer block of region %i is wrong", r->index); err = true; } - if (i->may_contain_throw && outer && !outer->may_contain_throw) + if (depth < 0) { - error - ("region %i may contain throw and is contained in region that may not", - i->region_number); + error ("negative nesting depth of region %i", r->index); err = true; } - if (depth < 0) + nvisited_r++; + + for (lp = r->landing_pads; lp ; lp = lp->next_lp) { - error ("negative nesting depth of region %i", i->region_number); - err = true; + if (VEC_index (eh_landing_pad, fun->eh->lp_array, lp->index) != lp) + { + error ("lp_array is corrupted for lp %i", lp->index); + err = true; + } + if (lp->region != r) + { + error ("region of lp %i is wrong", lp->index); + err = true; + } + nvisited_lp++; } - nvisited++; - /* If there are sub-regions, process them. */ - if (i->inner) - outer = i, i = i->inner, depth++; - /* If there are peers, process them. */ - else if (i->next_peer) - i = i->next_peer; - /* Otherwise, step back up the tree to the next peer. */ + + if (r->inner) + outer = r, r = r->inner, depth++; + else if (r->next_peer) + r = r->next_peer; else { do { - i = i->outer; + r = r->outer; + if (r == NULL) + goto region_done; depth--; - if (i == NULL) - { - if (depth != -1) - { - error ("tree list ends on depth %i", depth + 1); - err = true; - } - if (count != nvisited) - { - error ("array does not match the region tree"); - err = true; - } - if (!err) - for (i = fun->eh->region_tree; i; i = i->next_peer) - err |= verify_eh_region (i); - - if (err) - { - dump_eh_tree (stderr, fun); - internal_error ("verify_eh_tree failed"); - } - return; - } - outer = i->outer; + outer = r->outer; } - while (i->next_peer == NULL); - i = i->next_peer; + while (r->next_peer == NULL); + r = r->next_peer; } } -} - -/* Initialize unwind_resume_libfunc. */ + region_done: + if (depth != 0) + { + error ("tree list ends on depth %i", depth); + err = true; + } + if (count_r != nvisited_r) + { + error ("region_array does not match region_tree"); + err = true; + } + if (count_lp != nvisited_lp) + { + error ("lp_array does not match region_tree"); + err = true; + } -void -default_init_unwind_resume_libfunc (void) -{ - /* The default c++ routines aren't actually c++ specific, so use those. */ - unwind_resume_libfunc = - init_one_libfunc ( USING_SJLJ_EXCEPTIONS ? "_Unwind_SjLj_Resume" - : "_Unwind_Resume"); + if (err) + { + dump_eh_tree (stderr, fun); + internal_error ("verify_eh_tree failed"); + } } - -static bool -gate_handle_eh (void) -{ - return doing_eh (0); -} - -/* Complete generation of exception handling code. */ -static unsigned int -rest_of_handle_eh (void) -{ - finish_eh_generation (); - cleanup_cfg (CLEANUP_NO_INSN_DEL); - return 0; -} - -struct rtl_opt_pass pass_rtl_eh = -{ - { - RTL_PASS, - "eh", /* name */ - gate_handle_eh, /* gate */ - rest_of_handle_eh, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_JUMP, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ - } -}; - #include "gt-except.h" diff --git a/gcc/except.h b/gcc/except.h index af63e98..3e9a39c 100644 --- a/gcc/except.h +++ b/gcc/except.h @@ -23,20 +23,96 @@ along with GCC; see the file COPYING3. If not see #include "vecprim.h" struct function; +struct eh_region_d; +struct pointer_map_t; /* The type of an exception region. */ enum eh_region_type { - ERT_UNKNOWN = 0, + /* CLEANUP regions implement e.g. destructors run when exiting a block. + They can be generated from both GIMPLE_TRY_FINALLY and GIMPLE_TRY_CATCH + nodes. It is expected by the runtime that cleanup regions will *not* + resume normal program flow, but will continue propagation of the + exception. */ ERT_CLEANUP, + + /* TRY regions implement catching an exception. The list of types associated + with the attached catch handlers is examined in order by the runtime and + control is transfered to the appropriate handler. Note that a NULL type + list is a catch-all handler, and that it will catch *all* exceptions + including those originating from a different language. */ ERT_TRY, - ERT_CATCH, + + /* ALLOWED_EXCEPTIONS regions implement exception filtering, e.g. the + throw(type-list) specification that can be added to C++ functions. + The runtime examines the thrown exception vs the type list, and if + the exception does not match, transfers control to the handler. The + normal handler for C++ calls __cxa_call_unexpected. */ ERT_ALLOWED_EXCEPTIONS, - ERT_MUST_NOT_THROW, - ERT_THROW + + /* MUST_NOT_THROW regions prevent all exceptions from propagating. This + region type is used in C++ to surround destructors being run inside a + CLEANUP region. This differs from an ALLOWED_EXCEPTIONS region with + an empty type list in that the runtime is prepared to terminate the + program directly. We only generate code for MUST_NOT_THROW regions + along control paths that are already handling an exception within the + current function. */ + ERT_MUST_NOT_THROW +}; + + +/* A landing pad for a given exception region. Any transfer of control + from the EH runtime to the function happens at a landing pad. */ + +struct GTY(()) eh_landing_pad_d +{ + /* The linked list of all landing pads associated with the region. */ + struct eh_landing_pad_d *next_lp; + + /* The region with which this landing pad is associated. */ + struct eh_region_d *region; + + /* At the gimple level, the location to which control will be transfered + for this landing pad. There can be both EH and normal edges into the + block containing the post-landing-pad label. */ + tree post_landing_pad; + + /* At the rtl level, the location to which the runtime will transfer + control. This differs from the post-landing-pad in that the target's + EXCEPTION_RECEIVER pattern will be expanded here, as well as other + bookkeeping specific to exceptions. There must not be normal edges + into the block containing the landing-pad label. */ + rtx landing_pad; + + /* The index of this landing pad within fun->eh->lp_array. */ + int index; +}; + +/* A catch handler associated with an ERT_TRY region. */ + +struct GTY(()) eh_catch_d +{ + /* The double-linked list of all catch handlers for the region. */ + struct eh_catch_d *next_catch; + struct eh_catch_d *prev_catch; + + /* A TREE_LIST of runtime type objects that this catch handler + will catch, or NULL if all exceptions are caught. */ + tree type_list; + + /* A TREE_LIST of INTEGER_CSTs that correspond to the type_list entries, + having been mapped by assign_filter_values. These integers are to be + compared against the __builtin_eh_filter value. */ + tree filter_list; + + /* The code that should be executed if this catch handler matches the + thrown exception. This label is only maintained until + pass_lower_eh_dispatch, at which point it is cleared. */ + tree label; }; /* Describes one exception region. */ + struct GTY(()) eh_region_d { /* The immediately surrounding region. */ @@ -46,124 +122,123 @@ struct GTY(()) eh_region_d struct eh_region_d *inner; struct eh_region_d *next_peer; - /* List of regions sharing label. */ - struct eh_region_d *next_region_sharing_label; - - /* An identifier for this region. */ - int region_number; - - /* When a region is deleted, its parents inherit the REG_EH_REGION - numbers already assigned. */ - bitmap aka; + /* The index of this region within fun->eh->region_array. */ + int index; /* Each region does exactly one thing. */ enum eh_region_type type; /* Holds the action to perform based on the preceding type. */ union eh_region_u { - /* A list of catch blocks, a surrounding try block, - and the label for continuing after a catch. */ struct eh_region_u_try { - struct eh_region_d *eh_catch; - struct eh_region_d *last_catch; + /* The double-linked list of all catch handlers for this region. */ + struct eh_catch_d *first_catch; + struct eh_catch_d *last_catch; } GTY ((tag ("ERT_TRY"))) eh_try; - /* The list through the catch handlers, the list of type objects - matched, and the list of associated filters. */ - struct eh_region_u_catch { - struct eh_region_d *next_catch; - struct eh_region_d *prev_catch; - tree type_list; - tree filter_list; - } GTY ((tag ("ERT_CATCH"))) eh_catch; - - /* A tree_list of allowed types. */ struct eh_region_u_allowed { + /* A TREE_LIST of runtime type objects allowed to pass. */ tree type_list; + /* The code that should be executed if the thrown exception does + not match the type list. This label is only maintained until + pass_lower_eh_dispatch, at which point it is cleared. */ + tree label; + /* The integer that will be passed by the runtime to signal that + we should execute the code at LABEL. This integer is assigned + by assign_filter_values and is to be compared against the + __builtin_eh_filter value. */ int filter; } GTY ((tag ("ERT_ALLOWED_EXCEPTIONS"))) allowed; - /* The type given by a call to "throw foo();", or discovered - for a throw. */ - struct eh_region_u_throw { - tree type; - } GTY ((tag ("ERT_THROW"))) eh_throw; + struct eh_region_u_must_not_throw { + /* A function decl to be invoked if this region is actually reachable + from within the function, rather than implementable from the runtime. + The normal way for this to happen is for there to be a CLEANUP region + contained within this MUST_NOT_THROW region. Note that if the + runtime handles the MUST_NOT_THROW region, we have no control over + what termination function is called; it will be decided by the + personality function in effect for this CIE. */ + tree failure_decl; + /* The location assigned to the call of FAILURE_DECL, if expanded. */ + location_t failure_loc; + } GTY ((tag ("ERT_MUST_NOT_THROW"))) must_not_throw; } GTY ((desc ("%0.type"))) u; - /* Entry point for this region's handler before landing pads are built. */ - rtx label; - tree tree_label; - - /* Entry point for this region's handler from the runtime eh library. */ - rtx landing_pad; - - /* Entry point for this region's handler from an inner region. */ - rtx post_landing_pad; + /* The list of landing pads associated with this region. */ + struct eh_landing_pad_d *landing_pads; - /* The RESX insn for handing off control to the next outermost handler, - if appropriate. */ - rtx resume; - - /* True if something in this region may throw. */ - unsigned may_contain_throw : 1; + /* EXC_PTR and FILTER values copied from the runtime for this region. + Each region gets its own psuedos so that if there are nested exceptions + we do not overwrite the values of the first exception. */ + rtx exc_ptr_reg, filter_reg; }; +typedef struct eh_landing_pad_d *eh_landing_pad; +typedef struct eh_catch_d *eh_catch; typedef struct eh_region_d *eh_region; + DEF_VEC_P(eh_region); DEF_VEC_ALLOC_P(eh_region, gc); DEF_VEC_ALLOC_P(eh_region, heap); -/* Per-function EH data. Used to save exception status for each - function. */ +DEF_VEC_P(eh_landing_pad); +DEF_VEC_ALLOC_P(eh_landing_pad, gc); + + +/* The exception status for each function. */ + struct GTY(()) eh_status { /* The tree of all regions for this function. */ - struct eh_region_d *region_tree; + eh_region region_tree; /* The same information as an indexable array. */ VEC(eh_region,gc) *region_array; - int last_region_number; + /* The landing pads as an indexable array. */ + VEC(eh_landing_pad,gc) *lp_array; + + /* At the gimple level, a mapping from gimple statement to landing pad + or must-not-throw region. See record_stmt_eh_region. */ htab_t GTY((param_is (struct throw_stmt_node))) throw_stmt_table; + + /* All of the runtime type data used by the function. These objects + are emitted to the lang-specific-data-area for the function. */ + VEC(tree,gc) *ttype_data; + + /* The table of all action chains. These encode the eh_region tree in + a compact form for use by the runtime, and is also emitted to the + lang-specific-data-area. Note that the ARM EABI uses a different + format for the encoding than all other ports. */ + union eh_status_u { + VEC(tree,gc) * GTY((tag ("1"))) arm_eabi; + VEC(uchar,gc) * GTY((tag ("0"))) other; + } GTY ((desc ("targetm.arm_eabi_unwinder"))) ehspec_data; }; /* Test: is exception handling turned on? */ extern int doing_eh (int); -/* 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_d *); - /* Invokes CALLBACK for every exception handler label. Only used by old loop hackery; should not be used by new code. */ extern void for_each_eh_label (void (*) (rtx)); -/* Invokes CALLBACK for every exception region in the current function. */ -extern void for_each_eh_region (void (*) (struct eh_region_d *)); - -/* Determine if the given INSN can throw an exception. */ -extern bool can_throw_internal_1 (int, bool, bool); -extern bool can_throw_internal (const_rtx); -extern bool can_throw_external_1 (int, bool, bool); -extern bool can_throw_external (const_rtx); - /* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls. */ extern unsigned int set_nothrow_function_flags (void); extern void init_eh (void); extern void init_eh_for_function (void); -extern rtx reachable_handlers (rtx); -extern void remove_eh_region (int); -extern void remove_eh_region_and_replace_by_outer_of (int, int); +extern void remove_eh_landing_pad (eh_landing_pad); +extern void remove_eh_handler (eh_region); -extern void convert_from_eh_region_ranges (void); -extern unsigned int convert_to_eh_region_ranges (void); -extern void find_exception_handler_labels (void); extern bool current_function_has_exception_handlers (void); extern void output_function_exception_table (const char *); +extern rtx expand_builtin_eh_pointer (tree); +extern rtx expand_builtin_eh_filter (tree); +extern rtx expand_builtin_eh_copy_values (tree); extern void expand_builtin_unwind_init (void); extern rtx expand_builtin_eh_return_data_regno (tree); extern rtx expand_builtin_extract_return_addr (tree); @@ -173,46 +248,50 @@ extern rtx expand_builtin_dwarf_sp_column (void); extern void expand_builtin_eh_return (tree, tree); extern void expand_eh_return (void); extern rtx expand_builtin_extend_pointer (tree); -extern rtx get_exception_pointer (void); -extern rtx get_exception_filter (void); + typedef tree (*duplicate_eh_regions_map) (tree, void *); -extern int duplicate_eh_regions (struct function *, duplicate_eh_regions_map, - void *, int, int); +extern struct pointer_map_t *duplicate_eh_regions + (struct function *, eh_region, int, duplicate_eh_regions_map, void *); extern void sjlj_emit_function_exit_after (rtx); -extern void default_init_unwind_resume_libfunc (void); - -extern struct eh_region_d *gen_eh_region_cleanup (struct eh_region_d *); -extern struct eh_region_d *gen_eh_region_try (struct eh_region_d *); -extern struct eh_region_d *gen_eh_region_catch (struct eh_region_d *, tree); -extern struct eh_region_d *gen_eh_region_allowed (struct eh_region_d *, tree); -extern struct eh_region_d *gen_eh_region_must_not_throw (struct eh_region_d *); -extern int get_eh_region_number (struct eh_region_d *); -extern bool get_eh_region_may_contain_throw (struct eh_region_d *); -extern tree get_eh_region_no_tree_label (int); -extern tree get_eh_region_tree_label (struct eh_region_d *); -extern void set_eh_region_tree_label (struct eh_region_d *, tree); - -extern void foreach_reachable_handler (int, bool, bool, - void (*) (struct eh_region_d *, void *), - void *); - -extern void collect_eh_region_array (void); -extern void expand_resx_stmt (gimple); + +extern eh_region gen_eh_region_cleanup (eh_region); +extern eh_region gen_eh_region_try (eh_region); +extern eh_region gen_eh_region_allowed (eh_region, tree); +extern eh_region gen_eh_region_must_not_throw (eh_region); + +extern eh_catch gen_eh_region_catch (eh_region, tree); +extern eh_landing_pad gen_eh_landing_pad (eh_region); + +extern eh_region get_eh_region_from_number_fn (struct function *, int); +extern eh_region get_eh_region_from_number (int); +extern eh_landing_pad get_eh_landing_pad_from_number_fn (struct function*,int); +extern eh_landing_pad get_eh_landing_pad_from_number (int); +extern eh_region get_eh_region_from_lp_number_fn (struct function *, int); +extern eh_region get_eh_region_from_lp_number (int); + +extern eh_region eh_region_outermost (struct function *, eh_region, eh_region); + +extern void make_reg_eh_region_note (rtx insn, int ecf_flags, int lp_nr); +extern void make_reg_eh_region_note_nothrow_nononlocal (rtx); + extern void verify_eh_tree (struct function *); extern void dump_eh_tree (FILE *, struct function *); void debug_eh_tree (struct function *); -extern int eh_region_outermost (struct function *, int, int); extern void add_type_for_runtime (tree); extern tree lookup_type_for_runtime (tree); +extern void assign_filter_values (void); + +extern eh_region get_eh_region_from_rtx (const_rtx); +extern eh_landing_pad get_eh_landing_pad_from_rtx (const_rtx); -/* If non-NULL, this is a function that returns an expression to be +/* If non-NULL, this is a function that returns a function decl to be executed if an unhandled exception is propagated out of a cleanup region. For example, in C++, an exception thrown by a destructor during stack unwinding is required to result in a call to `std::terminate', so the C++ version of this function returns a - CALL_EXPR for `std::terminate'. */ -extern gimple (*lang_protect_cleanup_actions) (void); + FUNCTION_DECL for `std::terminate'. */ +extern tree (*lang_protect_cleanup_actions) (void); /* Return true if type A catches type B. */ extern int (*lang_eh_type_covers) (tree a, tree b); @@ -263,17 +342,11 @@ extern int (*lang_eh_type_covers) (tree a, tree b); struct GTY(()) throw_stmt_node { gimple stmt; - int region_nr; + int lp_nr; }; extern struct htab *get_eh_throw_stmt_table (struct function *); extern void set_eh_throw_stmt_table (struct function *, struct htab *); -extern void remove_unreachable_regions (sbitmap, sbitmap); -extern VEC(int,heap) * label_to_region_map (void); -extern int num_eh_regions (void); -extern bitmap must_not_throw_labels (void); -extern struct eh_region_d *redirect_eh_edge_to_label (struct edge_def *, tree, bool, bool, int); -extern int get_next_region_sharing_label (int); enum eh_personality_kind { eh_personality_none, @@ -283,3 +356,34 @@ enum eh_personality_kind { extern enum eh_personality_kind function_needs_eh_personality (struct function *); + +/* Pre-order iteration within the eh_region tree. */ + +static inline eh_region +ehr_next (eh_region r, eh_region start) +{ + if (r->inner) + r = r->inner; + else if (r->next_peer && r != start) + r = r->next_peer; + else + { + do + { + r = r->outer; + if (r == start) + return NULL; + } + while (r->next_peer == NULL); + r = r->next_peer; + } + return r; +} + +#define FOR_ALL_EH_REGION_AT(R, START) \ + for ((R) = (START); (R) != NULL; (R) = ehr_next (R, START)) + +#define FOR_ALL_EH_REGION_FN(R, FN) \ + for ((R) = (FN)->eh->region_tree; (R) != NULL; (R) = ehr_next (R, NULL)) + +#define FOR_ALL_EH_REGION(R) FOR_ALL_EH_REGION_FN (R, cfun) @@ -7110,7 +7110,7 @@ rtx expand_expr_real (tree exp, rtx target, enum machine_mode tmode, enum expand_modifier modifier, rtx *alt_rtl) { - int rn = -1; + int lp_nr = 0; rtx ret, last = NULL; /* Handle ERROR_MARK before anybody tries to access its type. */ @@ -7123,10 +7123,8 @@ expand_expr_real (tree exp, rtx target, enum machine_mode tmode, if (flag_non_call_exceptions) { - rn = lookup_expr_eh_region (exp); - - /* If rn < 0, then either (1) tree-ssa not used or (2) doesn't throw. */ - if (rn >= 0) + lp_nr = lookup_expr_eh_lp (exp); + if (lp_nr) last = get_last_insn (); } @@ -7159,7 +7157,7 @@ expand_expr_real (tree exp, rtx target, enum machine_mode tmode, /* If using non-call exceptions, mark all insns that may trap. expand_call() will mark CALL_INSNs before we get to this code, but it doesn't handle libcalls, and these may trap. */ - if (rn >= 0) + if (lp_nr) { rtx insn; for (insn = next_real_insn (last); insn; @@ -7170,8 +7168,8 @@ expand_expr_real (tree exp, rtx target, enum machine_mode tmode, may_trap_p instruction may throw. */ && GET_CODE (PATTERN (insn)) != CLOBBER && GET_CODE (PATTERN (insn)) != USE - && (CALL_P (insn) || may_trap_p (PATTERN (insn)))) - add_reg_note (insn, REG_EH_REGION, GEN_INT (rn)); + && insn_could_throw_p (insn)) + make_reg_eh_region_note (insn, 0, lp_nr); } } @@ -7239,6 +7237,7 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode, switch (code) { + case NON_LVALUE_EXPR: case PAREN_EXPR: CASE_CONVERT: if (treeop0 == error_mark_node) @@ -9490,7 +9489,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, case GOTO_EXPR: case SWITCH_EXPR: case ASM_EXPR: - case RESX_EXPR: /* Expanded in cfgexpand.c. */ gcc_unreachable (); @@ -9519,12 +9517,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, /* Lowered by gimplify.c. */ gcc_unreachable (); - case EXC_PTR_EXPR: - return get_exception_pointer (); - - case FILTER_EXPR: - return get_exception_filter (); - case FDESC_EXPR: /* Function descriptors are not valid except for as initialization constants, and should not be expanded. */ diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 342e376..1ce0013 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -15224,9 +15224,7 @@ tree_expr_nonnegative_warnv_p (tree t, bool *strict_overflow_p) case ASSERT_EXPR: case ADDR_EXPR: case WITH_SIZE_EXPR: - case EXC_PTR_EXPR: case SSA_NAME: - case FILTER_EXPR: return tree_single_nonnegative_warnv_p (t, strict_overflow_p); default: @@ -15518,9 +15516,7 @@ tree_expr_nonzero_warnv_p (tree t, bool *strict_overflow_p) case ASSERT_EXPR: case ADDR_EXPR: case WITH_SIZE_EXPR: - case EXC_PTR_EXPR: case SSA_NAME: - case FILTER_EXPR: return tree_single_nonzero_warnv_p (t, strict_overflow_p); case COMPOUND_EXPR: diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 7cf6e86..b8586c2 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,10 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * f95-lang.c (gfc_init_builtin_functions): Update call to + build_common_builtin_nodes. + (gfc_maybe_initialize_eh): Don't call + default_init_unwind_resume_libfunc. + 2009-09-13 Richard Guenther <rguenther@suse.de> Rafael Avila de Espindola <espindola@google.com> diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c index 3d94fd6..a21044c 100644 --- a/gcc/fortran/f95-lang.c +++ b/gcc/fortran/f95-lang.c @@ -1131,7 +1131,7 @@ gfc_init_builtin_functions (void) BUILT_IN_EMUTLS_REGISTER_COMMON, "__emutls_register_common", false); - build_common_builtin_nodes (); + build_common_builtin_nodes (false); targetm.init_builtins (); } @@ -1155,7 +1155,6 @@ gfc_maybe_initialize_eh (void) return; gfc_eh_initialized_p = true; - default_init_unwind_resume_libfunc (); using_eh_for_cleanups (); } diff --git a/gcc/function.h b/gcc/function.h index 446bc9d..72aad00 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -24,7 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "hashtab.h" -#include "varray.h" +#include "vecprim.h" /* Stack of pending (incomplete) sequences saved by `start_sequence'. Each element describes one pending sequence. @@ -144,11 +144,6 @@ DEF_VEC_ALLOC_P(call_site_record, gc); /* RTL representation of exception handling. */ struct GTY(()) rtl_eh { - rtx filter; - rtx exc_ptr; - - int built_landing_pads; - rtx ehr_stackadj; rtx ehr_handler; rtx ehr_label; @@ -156,9 +151,7 @@ struct GTY(()) rtl_eh { rtx sjlj_fc; rtx sjlj_exit_after; - VEC(tree,gc) *ttype_data; - varray_type ehspec_data; - varray_type action_record_data; + VEC(uchar,gc) *action_record_data; VEC(call_site_record,gc) *call_site_record[2]; }; @@ -1353,9 +1353,11 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table_d *table) /* Don't GCSE something if we can't do a reg/reg copy. */ && can_copy_p (GET_MODE (dest)) /* GCSE commonly inserts instruction after the insn. We can't - do that easily for EH_REGION notes so disable GCSE on these - for now. */ - && !find_reg_note (insn, REG_EH_REGION, NULL_RTX) + do that easily for EH edges so disable GCSE on these for now. */ + /* ??? We can now easily create new EH landing pads at the + gimple level, for splitting edges; there's no reason we + can't do the same thing at the rtl level. */ + && !can_throw_internal (insn) /* Is SET_SRC something we want to gcse? */ && want_to_gcse_p (src) /* Don't CSE a nop. */ @@ -1415,9 +1417,8 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table_d *table) /* Don't GCSE something if we can't do a reg/reg copy. */ && can_copy_p (GET_MODE (src)) /* GCSE commonly inserts instruction after the insn. We can't - do that easily for EH_REGION notes so disable GCSE on these - for now. */ - && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX) + do that easily for EH edges so disable GCSE on these for now. */ + && !can_throw_internal (insn) /* Is SET_DEST something we want to gcse? */ && want_to_gcse_p (dest) /* Don't CSE a nop. */ diff --git a/gcc/gengtype.c b/gcc/gengtype.c index 1a2f38b..69fa68d 100644 --- a/gcc/gengtype.c +++ b/gcc/gengtype.c @@ -1567,7 +1567,8 @@ open_base_files (void) "hard-reg-set.h", "basic-block.h", "cselib.h", "insn-addr.h", "optabs.h", "libfuncs.h", "debug.h", "ggc.h", "cgraph.h", "tree-flow.h", "reload.h", "cpp-id-data.h", "tree-chrec.h", - "cfglayout.h", "except.h", "output.h", "gimple.h", "cfgloop.h", NULL + "cfglayout.h", "except.h", "output.h", "gimple.h", "cfgloop.h", + "target.h", NULL }; const char *const *ifp; outf_p gtype_desc_c; diff --git a/gcc/gimple-iterator.c b/gcc/gimple-iterator.c index 876225b..66927d6 100644 --- a/gcc/gimple-iterator.c +++ b/gcc/gimple-iterator.c @@ -363,7 +363,6 @@ gsi_split_seq_before (gimple_stmt_iterator *i) void gsi_replace (gimple_stmt_iterator *gsi, gimple stmt, bool update_eh_info) { - int eh_region; gimple orig_stmt = gsi_stmt (*gsi); if (stmt == orig_stmt) @@ -375,14 +374,7 @@ gsi_replace (gimple_stmt_iterator *gsi, gimple stmt, bool update_eh_info) /* Preserve EH region information from the original statement, if requested by the caller. */ if (update_eh_info) - { - eh_region = lookup_stmt_eh_region (orig_stmt); - if (eh_region >= 0) - { - remove_stmt_from_eh_region (orig_stmt); - add_stmt_to_eh_region (stmt, eh_region); - } - } + maybe_clean_or_replace_eh_stmt (orig_stmt, stmt); gimple_duplicate_stmt_histograms (cfun, stmt, cfun, orig_stmt); gimple_remove_stmt_histograms (cfun, orig_stmt); @@ -485,7 +477,7 @@ gsi_remove (gimple_stmt_iterator *i, bool remove_permanently) if (remove_permanently) { - remove_stmt_from_eh_region (stmt); + remove_stmt_from_eh_lp (stmt); gimple_remove_stmt_histograms (cfun, stmt); } diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index eba8672..b58fd7b 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -360,6 +360,7 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data) case GIMPLE_PREDICT: case GIMPLE_LABEL: case GIMPLE_SWITCH: + case GIMPLE_EH_MUST_NOT_THROW: case GIMPLE_OMP_FOR: case GIMPLE_OMP_SECTIONS: case GIMPLE_OMP_SECTIONS_SWITCH: @@ -497,8 +498,8 @@ try_catch_may_fallthru (const_tree stmt) default: /* This case represents statements to be executed when an exception occurs. Those statements are implicitly followed - by a RESX_EXPR to resume execution after the exception. So - in this case the TRY_CATCH never falls through. */ + by a RESX statement to resume execution after the exception. + So in this case the TRY_CATCH never falls through. */ return false; } } @@ -571,7 +572,6 @@ block_may_fallthru (const_tree block) { case GOTO_EXPR: case RETURN_EXPR: - case RESX_EXPR: /* Easy cases. If the last statement of the block implies control transfer, then we can't fall through. */ return false; diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c index 5018020..8903c66 100644 --- a/gcc/gimple-pretty-print.c +++ b/gcc/gimple-pretty-print.c @@ -626,6 +626,8 @@ dump_gimple_label (pretty_printer *buffer, gimple gs, int spc, int flags) } if (DECL_NONLOCAL (label)) pp_string (buffer, " [non-local]"); + if ((flags & TDF_EH) && EH_LANDING_PAD_NR (label)) + pp_printf (buffer, " [LP %d]", EH_LANDING_PAD_NR (label)); } /* Dump a GIMPLE_GOTO tuple on the pretty_printer BUFFER, SPC @@ -766,6 +768,21 @@ dump_gimple_eh_filter (pretty_printer *buffer, gimple gs, int spc, int flags) } +/* Dump a GIMPLE_EH_MUST_NOT_THROW tuple. */ + +static void +dump_gimple_eh_must_not_throw (pretty_printer *buffer, gimple gs, + int spc, int flags) +{ + if (flags & TDF_RAW) + dump_gimple_fmt (buffer, spc, flags, "%G <%T>", gs, + gimple_eh_must_not_throw_fndecl (gs)); + else + dump_gimple_fmt (buffer, spc, flags, "<<<eh_must_not_throw (%T)>>>", + gimple_eh_must_not_throw_fndecl (gs)); +} + + /* Dump a GIMPLE_RESX tuple on the pretty_printer BUFFER, SPC spaces of indent. FLAGS specifies details to show in the dump (see TDF_* in tree-pass.h). */ @@ -775,11 +792,24 @@ dump_gimple_resx (pretty_printer *buffer, gimple gs, int spc, int flags) { if (flags & TDF_RAW) dump_gimple_fmt (buffer, spc, flags, "%G <%d>", gs, - gimple_resx_region (gs)); + gimple_resx_region (gs)); else dump_gimple_fmt (buffer, spc, flags, "resx %d", gimple_resx_region (gs)); } +/* Dump a GIMPLE_EH_DISPATCH tuple on the pretty_printer BUFFER. */ + +static void +dump_gimple_eh_dispatch (pretty_printer *buffer, gimple gs, int spc, int flags) +{ + if (flags & TDF_RAW) + dump_gimple_fmt (buffer, spc, flags, "%G <%d>", gs, + gimple_eh_dispatch_region (gs)); + else + dump_gimple_fmt (buffer, spc, flags, "eh_dispatch %d", + gimple_eh_dispatch_region (gs)); +} + /* Dump a GIMPLE_DEBUG tuple on the pretty_printer BUFFER, SPC spaces of indent. FLAGS specifies details to show in the dump (see TDF_* in tree-pass.h). */ @@ -1427,9 +1457,11 @@ dump_gimple_stmt (pretty_printer *buffer, gimple gs, int spc, int flags) if (flags & TDF_EH) { - int eh_region = lookup_stmt_eh_region_fn (cfun, gs); - if (eh_region >= 0) - pp_printf (buffer, "[EH #%d] ", eh_region); + int lp_nr = lookup_stmt_eh_lp (gs); + if (lp_nr > 0) + pp_printf (buffer, "[LP %d] ", lp_nr); + else if (lp_nr < 0) + pp_printf (buffer, "[MNT %d] ", -lp_nr); } if ((flags & (TDF_VOPS|TDF_MEMSYMS)) @@ -1545,10 +1577,18 @@ dump_gimple_stmt (pretty_printer *buffer, gimple gs, int spc, int flags) dump_gimple_eh_filter (buffer, gs, spc, flags); break; + case GIMPLE_EH_MUST_NOT_THROW: + dump_gimple_eh_must_not_throw (buffer, gs, spc, flags); + break; + case GIMPLE_RESX: dump_gimple_resx (buffer, gs, spc, flags); break; + case GIMPLE_EH_DISPATCH: + dump_gimple_eh_dispatch (buffer, gs, spc, flags); + break; + case GIMPLE_DEBUG: dump_gimple_debug (buffer, gs, spc, flags); break; diff --git a/gcc/gimple.c b/gcc/gimple.c index 3be6d84..33daafc 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -627,6 +627,20 @@ gimple_build_eh_filter (tree types, gimple_seq failure) return p; } +/* Build a GIMPLE_EH_MUST_NOT_THROW statement. */ + +gimple +gimple_build_eh_must_not_throw (tree decl) +{ + gimple p = gimple_alloc (GIMPLE_EH_MUST_NOT_THROW, 1); + + gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + gcc_assert (flags_from_decl_or_type (decl) & ECF_NORETURN); + p->gimple_eh_mnt.fndecl = decl; + + return p; +} + /* Build a GIMPLE_TRY statement. EVAL is the expression to evaluate. @@ -666,16 +680,13 @@ gimple_build_wce (gimple_seq cleanup) } -/* Build a GIMPLE_RESX statement. - - REGION is the region number from which this resx causes control flow to - leave. */ +/* Build a GIMPLE_RESX statement. */ gimple gimple_build_resx (int region) { - gimple p = gimple_alloc (GIMPLE_RESX, 0); - gimple_resx_set_region (p, region); + gimple p = gimple_build_with_ops (GIMPLE_RESX, ERROR_MARK, 0); + p->gimple_eh_ctrl.region = region; return p; } @@ -685,14 +696,15 @@ gimple_build_resx (int region) NLABELS is the number of labels in the switch excluding the default. DEFAULT_LABEL is the default label for the switch statement. */ -static inline gimple -gimple_build_switch_1 (unsigned nlabels, tree index, tree default_label) +gimple +gimple_build_switch_nlabels (unsigned nlabels, tree index, tree default_label) { /* nlabels + 1 default label + 1 index. */ gimple p = gimple_build_with_ops (GIMPLE_SWITCH, ERROR_MARK, - nlabels + 1 + 1); + 1 + (default_label != NULL) + nlabels); gimple_switch_set_index (p, index); - gimple_switch_set_default_label (p, default_label); + if (default_label) + gimple_switch_set_default_label (p, default_label); return p; } @@ -707,15 +719,14 @@ gimple gimple_build_switch (unsigned nlabels, tree index, tree default_label, ...) { va_list al; - unsigned i; - gimple p; - - p = gimple_build_switch_1 (nlabels, index, default_label); + unsigned i, offset; + gimple p = gimple_build_switch_nlabels (nlabels, index, default_label); /* Store the rest of the labels. */ va_start (al, default_label); - for (i = 1; i <= nlabels; i++) - gimple_switch_set_label (p, i, va_arg (al, tree)); + offset = (default_label != NULL); + for (i = 0; i < nlabels; i++) + gimple_switch_set_label (p, i + offset, va_arg (al, tree)); va_end (al); return p; @@ -731,18 +742,26 @@ gimple_build_switch (unsigned nlabels, tree index, tree default_label, ...) gimple gimple_build_switch_vec (tree index, tree default_label, VEC(tree, heap) *args) { - unsigned i; - unsigned nlabels = VEC_length (tree, args); - gimple p = gimple_build_switch_1 (nlabels, index, default_label); + unsigned i, offset, nlabels = VEC_length (tree, args); + gimple p = gimple_build_switch_nlabels (nlabels, index, default_label); - /* Put labels in labels[1 - (nlabels + 1)]. - Default label is in labels[0]. */ - for (i = 1; i <= nlabels; i++) - gimple_switch_set_label (p, i, VEC_index (tree, args, i - 1)); + /* Copy the labels from the vector to the switch statement. */ + offset = (default_label != NULL); + for (i = 0; i < nlabels; i++) + gimple_switch_set_label (p, i + offset, VEC_index (tree, args, i)); return p; } +/* Build a GIMPLE_EH_DISPATCH statement. */ + +gimple +gimple_build_eh_dispatch (int region) +{ + gimple p = gimple_build_with_ops (GIMPLE_EH_DISPATCH, ERROR_MARK, 0); + p->gimple_eh_ctrl.region = region; + return p; +} /* Build a new GIMPLE_DEBUG_BIND statement. @@ -2394,9 +2413,7 @@ get_gimple_rhs_num_ops (enum tree_code code) || (SYM) == ASSERT_EXPR \ || (SYM) == ADDR_EXPR \ || (SYM) == WITH_SIZE_EXPR \ - || (SYM) == EXC_PTR_EXPR \ || (SYM) == SSA_NAME \ - || (SYM) == FILTER_EXPR \ || (SYM) == POLYNOMIAL_CHREC \ || (SYM) == DOT_PROD_EXPR \ || (SYM) == VEC_COND_EXPR \ @@ -2658,7 +2675,6 @@ is_gimple_stmt (tree t) case EH_FILTER_EXPR: case CATCH_EXPR: case ASM_EXPR: - case RESX_EXPR: case STATEMENT_LIST: case OMP_PARALLEL: case OMP_FOR: @@ -2784,11 +2800,6 @@ is_gimple_val (tree t) && !is_gimple_reg (t)) return false; - /* FIXME make these decls. That can happen only when we expose the - entire landing-pad construct at the tree level. */ - if (TREE_CODE (t) == EXC_PTR_EXPR || TREE_CODE (t) == FILTER_EXPR) - return true; - return (is_gimple_variable (t) || is_gimple_min_invariant (t)); } diff --git a/gcc/gimple.def b/gcc/gimple.def index 1a3f345..603d97e 100644 --- a/gcc/gimple.def +++ b/gcc/gimple.def @@ -1,6 +1,6 @@ /* This file contains the definitions of the GIMPLE IR tuples used in GCC. - Copyright (C) 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. Contributed by Aldy Hernandez <aldyh@redhat.com> This file is part of GCC. @@ -145,6 +145,18 @@ DEFGSCODE(GIMPLE_CATCH, "gimple_catch", GSS_CATCH) sequence of statements to execute on failure. */ DEFGSCODE(GIMPLE_EH_FILTER, "gimple_eh_filter", GSS_EH_FILTER) +/* GIMPLE_EH_MUST_NOT_THROW <DECL> represents an exception barrier. + DECL is a noreturn function decl taking no arguments that will + be invoked if an exception propagates to this point. */ +DEFGSCODE(GIMPLE_EH_MUST_NOT_THROW, "gimple_eh_must_not_throw", GSS_EH_MNT) + +/* GIMPLE_RESX resumes execution after an exception. */ +DEFGSCODE(GIMPLE_RESX, "gimple_resx", GSS_EH_CTRL) + +/* GIMPLE_EH_DISPATCH demultiplexes an exception edge based on + the FILTER argument. */ +DEFGSCODE(GIMPLE_EH_DISPATCH, "gimple_eh_dispatch", GSS_EH_CTRL) + /* GIMPLE_PHI <RESULT, ARG1, ..., ARGN> represents the PHI node RESULT = PHI <ARG1, ..., ARGN> @@ -157,10 +169,6 @@ DEFGSCODE(GIMPLE_EH_FILTER, "gimple_eh_filter", GSS_EH_FILTER) tree node of class tcc_constant. */ DEFGSCODE(GIMPLE_PHI, "gimple_phi", GSS_PHI) -/* GIMPLE_RESX <REGION> resumes execution after an exception. - REGION is the region number being left. */ -DEFGSCODE(GIMPLE_RESX, "gimple_resx", GSS_RESX) - /* GIMPLE_TRY <TRY_KIND, EVAL, CLEANUP> represents a try/catch or a try/finally statement. diff --git a/gcc/gimple.h b/gcc/gimple.h index 8ca1f28..b539623 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -444,9 +444,6 @@ struct GTY(()) gimple_statement_eh_filter { /* [ WORD 1-4 ] */ struct gimple_statement_base gsbase; - /* Subcode: EH_FILTER_MUST_NOT_THROW. A boolean flag analogous to - the tree counterpart. */ - /* [ WORD 5 ] Filter types. */ tree types; @@ -457,6 +454,16 @@ struct GTY(()) gimple_statement_eh_filter { }; +/* GIMPLE_EH_MUST_NOT_THROW */ + +struct GTY(()) gimple_statement_eh_mnt { + /* [ WORD 1-4 ] */ + struct gimple_statement_base gsbase; + + /* [ WORD 5 ] Abort function decl. */ + tree fndecl; +}; + /* GIMPLE_PHI */ struct GTY(()) gimple_statement_phi { @@ -475,9 +482,10 @@ struct GTY(()) gimple_statement_phi { }; -/* GIMPLE_RESX */ +/* GIMPLE_RESX, GIMPLE_EH_DISPATCH */ -struct GTY(()) gimple_statement_resx { +struct GTY(()) gimple_statement_eh_ctrl +{ /* [ WORD 1-4 ] */ struct gimple_statement_base gsbase; @@ -733,8 +741,9 @@ union GTY ((desc ("gimple_statement_structure (&%h)"))) gimple_statement_d { struct gimple_statement_bind GTY ((tag ("GSS_BIND"))) gimple_bind; struct gimple_statement_catch GTY ((tag ("GSS_CATCH"))) gimple_catch; struct gimple_statement_eh_filter GTY ((tag ("GSS_EH_FILTER"))) gimple_eh_filter; + struct gimple_statement_eh_mnt GTY ((tag ("GSS_EH_MNT"))) gimple_eh_mnt; struct gimple_statement_phi GTY ((tag ("GSS_PHI"))) gimple_phi; - struct gimple_statement_resx GTY ((tag ("GSS_RESX"))) gimple_resx; + struct gimple_statement_eh_ctrl GTY ((tag ("GSS_EH_CTRL"))) gimple_eh_ctrl; struct gimple_statement_try GTY ((tag ("GSS_TRY"))) gimple_try; struct gimple_statement_wce GTY ((tag ("GSS_WCE"))) gimple_wce; struct gimple_statement_asm GTY ((tag ("GSS_ASM"))) gimple_asm; @@ -788,9 +797,12 @@ gimple gimple_build_asm_vec (const char *, VEC(tree,gc) *, VEC(tree,gc) *, VEC(tree,gc) *); gimple gimple_build_catch (tree, gimple_seq); gimple gimple_build_eh_filter (tree, gimple_seq); +gimple gimple_build_eh_must_not_throw (tree); gimple gimple_build_try (gimple_seq, gimple_seq, enum gimple_try_flags); gimple gimple_build_wce (gimple_seq); gimple gimple_build_resx (int); +gimple gimple_build_eh_dispatch (int); +gimple gimple_build_switch_nlabels (unsigned, tree, tree); gimple gimple_build_switch (unsigned, tree, tree, ...); gimple gimple_build_switch_vec (tree, tree, VEC(tree,heap) *); gimple gimple_build_omp_parallel (gimple_seq, tree, tree, tree); @@ -2863,26 +2875,15 @@ gimple_eh_filter_set_failure (gimple gs, gimple_seq failure) gs->gimple_eh_filter.failure = failure; } -/* Return the EH_FILTER_MUST_NOT_THROW flag. */ +/* Get the function decl to be called by the MUST_NOT_THROW region. */ -static inline bool - -gimple_eh_filter_must_not_throw (gimple gs) -{ - GIMPLE_CHECK (gs, GIMPLE_EH_FILTER); - return gs->gsbase.subcode != 0; -} - -/* Set the EH_FILTER_MUST_NOT_THROW flag to the value MNTP. */ - -static inline void -gimple_eh_filter_set_must_not_throw (gimple gs, bool mntp) +static inline tree +gimple_eh_must_not_throw_fndecl (gimple gs) { - GIMPLE_CHECK (gs, GIMPLE_EH_FILTER); - gs->gsbase.subcode = (unsigned int) mntp; + GIMPLE_CHECK (gs, GIMPLE_EH_MUST_NOT_THROW); + return gs->gimple_eh_mnt.fndecl; } - /* GIMPLE_TRY accessors. */ /* Return the kind of try block represented by GIMPLE_TRY GS. This is @@ -3092,7 +3093,7 @@ static inline int gimple_resx_region (const_gimple gs) { GIMPLE_CHECK (gs, GIMPLE_RESX); - return gs->gimple_resx.region; + return gs->gimple_eh_ctrl.region; } /* Set REGION to be the region number for GIMPLE_RESX GS. */ @@ -3101,9 +3102,26 @@ static inline void gimple_resx_set_region (gimple gs, int region) { GIMPLE_CHECK (gs, GIMPLE_RESX); - gs->gimple_resx.region = region; + gs->gimple_eh_ctrl.region = region; } +/* Return the region number for GIMPLE_EH_DISPATCH GS. */ + +static inline int +gimple_eh_dispatch_region (const_gimple gs) +{ + GIMPLE_CHECK (gs, GIMPLE_EH_DISPATCH); + return gs->gimple_eh_ctrl.region; +} + +/* Set REGION to be the region number for GIMPLE_EH_DISPATCH GS. */ + +static inline void +gimple_eh_dispatch_set_region (gimple gs, int region) +{ + GIMPLE_CHECK (gs, GIMPLE_EH_DISPATCH); + gs->gimple_eh_ctrl.region = region; +} /* Return the number of labels associated with the switch statement GS. */ @@ -4253,6 +4271,14 @@ gimple_nop_p (const_gimple g) } +/* Return true if GS is a GIMPLE_RESX. */ + +static inline bool +is_gimple_resx (const_gimple gs) +{ + return gimple_code (gs) == GIMPLE_RESX; +} + /* Return the predictor of GIMPLE_PREDICT statement GS. */ static inline enum br_predictor diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 7f1dc4a..381e611 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -6645,11 +6645,6 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ret = gimplify_decl_expr (expr_p, pre_p); break; - case EXC_PTR_EXPR: - /* FIXME make this a decl. */ - ret = GS_ALL_DONE; - break; - case BIND_EXPR: ret = gimplify_bind_expr (expr_p, pre_p); break; @@ -6841,8 +6836,6 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, gimplify_and_add (EH_FILTER_FAILURE (*expr_p), &failure); ehf = gimple_build_eh_filter (EH_FILTER_TYPES (*expr_p), failure); gimple_set_no_warning (ehf, TREE_NO_WARNING (*expr_p)); - gimple_eh_filter_set_must_not_throw - (ehf, EH_FILTER_MUST_NOT_THROW (*expr_p)); gimplify_seq_add_stmt (pre_p, ehf); ret = GS_ALL_DONE; break; @@ -7178,7 +7171,6 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, && code != GOTO_EXPR && code != LABEL_EXPR && code != LOOP_EXPR - && code != RESX_EXPR && code != SWITCH_EXPR && code != TRY_FINALLY_EXPR && code != OMP_CRITICAL diff --git a/gcc/gsstruct.def b/gcc/gsstruct.def index 97875c9..29cb90d 100644 --- a/gcc/gsstruct.def +++ b/gcc/gsstruct.def @@ -34,7 +34,8 @@ DEFGSSTRUCT(GSS_PHI, gimple_statement_phi, false) DEFGSSTRUCT(GSS_TRY, gimple_statement_try, false) DEFGSSTRUCT(GSS_CATCH, gimple_statement_catch, false) DEFGSSTRUCT(GSS_EH_FILTER, gimple_statement_eh_filter, false) -DEFGSSTRUCT(GSS_RESX, gimple_statement_resx, false) +DEFGSSTRUCT(GSS_EH_MNT, gimple_statement_eh_mnt, false) +DEFGSSTRUCT(GSS_EH_CTRL, gimple_statement_eh_ctrl, false) DEFGSSTRUCT(GSS_WCE, gimple_statement_wce, false) DEFGSSTRUCT(GSS_OMP, gimple_statement_omp, false) DEFGSSTRUCT(GSS_OMP_CRITICAL, gimple_statement_omp_critical, false) diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 79de363..c6bbece 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -1735,7 +1735,6 @@ estimate_function_body_sizes (struct cgraph_node *node) tree arg; int freq; tree funtype = TREE_TYPE (node->decl); - bitmap must_not_throw = must_not_throw_labels (); if (dump_file) { @@ -1748,35 +1747,20 @@ estimate_function_body_sizes (struct cgraph_node *node) freq = compute_call_stmt_bb_frequency (node->decl, bb); for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi)) { - int this_size = estimate_num_insns (gsi_stmt (bsi), &eni_size_weights); - int this_time = estimate_num_insns (gsi_stmt (bsi), &eni_time_weights); - - /* MUST_NOT_THROW is usually handled by runtime calling terminate and stopping - stacking unwinding. However when there is local cleanup that can resume - to MUST_NOT_THROW then we generate explicit handler containing - std::terminate () call. - - Because inlining of function can introduce new cleanup region, prior - inlining we keep std::terinate () calls for every MUST_NOT_THROW containing - function call. Wast majority of these will be eliminated after inlining - and crossjumping will inify possible duplicated calls. So ignore - the handlers for function body estimates. */ - if (gimple_code (gsi_stmt (bsi)) == GIMPLE_LABEL - && bitmap_bit_p (must_not_throw, - LABEL_DECL_UID (gimple_label_label (gsi_stmt (bsi))))) - { - if (dump_file) - fprintf (dump_file, " MUST_NOT_THROW landing pad. Ignoring whole BB.\n"); - } + gimple stmt = gsi_stmt (bsi); + int this_size = estimate_num_insns (stmt, &eni_size_weights); + int this_time = estimate_num_insns (stmt, &eni_time_weights); + if (dump_file) { - fprintf (dump_file, " freq:%6i size:%3i time:%3i ", freq, this_size, this_time); - print_gimple_stmt (dump_file, gsi_stmt (bsi), 0, 0); + fprintf (dump_file, " freq:%6i size:%3i time:%3i ", + freq, this_size, this_time); + print_gimple_stmt (dump_file, stmt, 0, 0); } this_time *= freq; time += this_time; size += this_size; - if (likely_eliminated_by_inlining_p (gsi_stmt (bsi))) + if (likely_eliminated_by_inlining_p (stmt)) { size_inlining_benefit += this_size; time_inlining_benefit += this_time; @@ -1825,7 +1809,6 @@ estimate_function_body_sizes (struct cgraph_node *node) } inline_summary (node)->time_inlining_benefit = time_inlining_benefit; inline_summary (node)->size_inlining_benefit = size_inlining_benefit; - BITMAP_FREE (must_not_throw); } /* Compute parameters of functions used by inliner. */ diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 201dc59..e5ff3a7 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -346,8 +346,8 @@ check_call (funct_state local, gimple call, bool ipa) { if (dump_file) { - fprintf (dump_file, " can throw externally in region %i\n", - lookup_stmt_eh_region (call)); + fprintf (dump_file, " can throw externally to lp %i\n", + lookup_stmt_eh_lp (call)); if (callee_t) fprintf (dump_file, " callee:%s\n", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t))); diff --git a/gcc/ipa-type-escape.c b/gcc/ipa-type-escape.c index 62516d0..edfaab0 100644 --- a/gcc/ipa-type-escape.c +++ b/gcc/ipa-type-escape.c @@ -1128,9 +1128,6 @@ check_operand (tree t) static void check_tree (tree t) { - if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR)) - return; - /* We want to catch here also REALPART_EXPR and IMAGEPART_EXPR, but they already included in handled_component_p. */ while (handled_component_p (t)) diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c index a001916..0b7ec66 100644 --- a/gcc/ipa-utils.c +++ b/gcc/ipa-utils.c @@ -215,10 +215,6 @@ ipa_utils_reduced_inorder (struct cgraph_node **order, tree get_base_var (tree t) { - if (TREE_CODE (t) == EXC_PTR_EXPR - || TREE_CODE (t) == FILTER_EXPR) - return t; - while (!SSA_VAR_P (t) && (!CONSTANT_CLASS_P (t)) && TREE_CODE (t) != LABEL_DECL diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index 6b7d930..6c58a99 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,3 +1,16 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * builtins.c (initialize_builtins): Update call to + build_common_builtin_nodes. + * decl.c (java_init_decl_processing): Don't call + default_init_unwind_resume_libfunc. + * except.c: Include tree-iterator.h. + (build_exception_object_var): New. + (build_exception_object_ref): Use it. + (expand_end_java_handler): Initialize it from __builtin_eh_pointer. + Attach all CATCH_EXPRs to a single TRY_CATCH_EXPR. + * java-tree.h (DECL_FUNCTION_EXC_OBJ): New. + 2009-09-13 Richard Guenther <rguenther@suse.de> Rafael Avila de Espindola <espindola@google.com> diff --git a/gcc/java/builtins.c b/gcc/java/builtins.c index 6e4815b..a05ff53 100644 --- a/gcc/java/builtins.c +++ b/gcc/java/builtins.c @@ -584,7 +584,7 @@ initialize_builtins (void) build_function_type_list (ptr_type_node, int_type_node, NULL_TREE), "__builtin_return_address", BUILTIN_NOTHROW); - build_common_builtin_nodes (); + build_common_builtin_nodes (true); } /* If the call matches a builtin, return the diff --git a/gcc/java/decl.c b/gcc/java/decl.c index c9ccc9d..c593b53 100644 --- a/gcc/java/decl.c +++ b/gcc/java/decl.c @@ -1,7 +1,7 @@ /* Process declarations and variables for the GNU compiler for the Java(TM) language. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007, - 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GCC. @@ -1188,14 +1188,8 @@ java_init_decl_processing (void) build_function_type (long_type_node, t), 0, NOT_BUILT_IN, NULL, NULL_TREE); - /* Initialize variables for except.c. */ - - if (targetm.arm_eabi_unwinder) - unwind_resume_libfunc = init_one_libfunc ("__cxa_end_cleanup"); - else - default_init_unwind_resume_libfunc (); - initialize_builtins (); + soft_fmod_node = built_in_decls[BUILT_IN_FMOD]; parse_version (); diff --git a/gcc/java/except.c b/gcc/java/except.c index e97ed77..4e46514 100644 --- a/gcc/java/except.c +++ b/gcc/java/except.c @@ -1,6 +1,6 @@ /* Handle exceptions for GNU compiler for the Java(TM) language. Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, - 2007, 2008 Free Software Foundation, Inc. + 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GCC. @@ -37,6 +37,8 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "except.h" #include "java-except.h" #include "toplev.h" +#include "tree-iterator.h" + static void expand_start_java_handler (struct eh_range *); static struct eh_range *find_handler_in_range (int, struct eh_range *, @@ -457,6 +459,26 @@ java_expand_catch_classes (tree this_class) expand_catch_class, NULL); } +/* Build and push the variable that will hold the exception object + within this function. */ + +static tree +build_exception_object_var (void) +{ + tree decl = DECL_FUNCTION_EXC_OBJ (current_function_decl); + if (decl == NULL) + { + decl = build_decl (DECL_SOURCE_LOCATION (current_function_decl), + VAR_DECL, get_identifier ("#exc_obj"), ptr_type_node); + DECL_IGNORED_P (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + + DECL_FUNCTION_EXC_OBJ (current_function_decl) = decl; + pushdecl_function_level (decl); + } + return decl; +} + /* Build a reference to the jthrowable object being carried in the exception header. */ @@ -467,7 +489,8 @@ build_exception_object_ref (tree type) /* Java only passes object via pointer and doesn't require adjusting. The java object is immediately before the generic exception header. */ - obj = build0 (EXC_PTR_EXPR, build_pointer_type (type)); + obj = build_exception_object_var (); + obj = fold_convert (build_pointer_type (type), obj); obj = build2 (POINTER_PLUS_EXPR, TREE_TYPE (obj), obj, fold_build1 (NEGATE_EXPR, sizetype, TYPE_SIZE_UNIT (TREE_TYPE (obj)))); @@ -482,29 +505,48 @@ void expand_end_java_handler (struct eh_range *range) { tree handler = range->handlers; - - for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) + if (handler) { - /* For bytecode we treat exceptions a little unusually. A - `finally' clause looks like an ordinary exception handler for - Throwable. The reason for this is that the bytecode has - already expanded the finally logic, and we would have to do - extra (and difficult) work to get this to look like a - gcc-style finally clause. */ - tree type = TREE_PURPOSE (handler); - if (type == NULL) - type = throwable_type_node; - type = prepare_eh_table_type (type); + tree exc_obj = build_exception_object_var (); + tree catches = make_node (STATEMENT_LIST); + tree_stmt_iterator catches_i = tsi_last (catches); + tree *body; - { - tree catch_expr = build2 (CATCH_EXPR, void_type_node, type, - build1 (GOTO_EXPR, void_type_node, - TREE_VALUE (handler))); - tree try_catch_expr = build2 (TRY_CATCH_EXPR, void_type_node, - *get_stmts (), catch_expr); - *get_stmts () = try_catch_expr; - } + for (; handler; handler = TREE_CHAIN (handler)) + { + tree type, eh_type, x; + tree stmts = make_node (STATEMENT_LIST); + tree_stmt_iterator stmts_i = tsi_last (stmts); + + type = TREE_PURPOSE (handler); + if (type == NULL) + type = throwable_type_node; + eh_type = prepare_eh_table_type (type); + + x = build_call_expr (built_in_decls[BUILT_IN_EH_POINTER], + 1, integer_zero_node); + x = build2 (MODIFY_EXPR, void_type_node, exc_obj, x); + tsi_link_after (&stmts_i, x, TSI_CONTINUE_LINKING); + + x = build1 (GOTO_EXPR, void_type_node, TREE_VALUE (handler)); + tsi_link_after (&stmts_i, x, TSI_CONTINUE_LINKING); + + x = build2 (CATCH_EXPR, void_type_node, eh_type, stmts); + tsi_link_after (&catches_i, x, TSI_CONTINUE_LINKING); + + /* Throwable can match anything in Java, and therefore + any subsequent handlers are unreachable. */ + /* ??? If we're assured of no foreign language exceptions, + we'd be better off using NULL as the exception type + for the catch. */ + if (type == throwable_type_node) + break; + } + + body = get_stmts (); + *body = build2 (TRY_CATCH_EXPR, void_type_node, *body, catches); } + #if defined(DEBUG_JAVA_BINDING_LEVELS) indent (); fprintf (stderr, "expand end handler pc %d <-- %d\n", diff --git a/gcc/java/java-tree.h b/gcc/java/java-tree.h index 29027eb..8ffe242 100644 --- a/gcc/java/java-tree.h +++ b/gcc/java/java-tree.h @@ -714,6 +714,8 @@ union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), /* List of checked thrown exceptions, as specified with the `throws' keyword */ #define DECL_FUNCTION_THROWS(DECL) (DECL_LANG_SPECIFIC(DECL)->u.f.throws_list) +/* VAR_DECL containing the caught exception object. */ +#define DECL_FUNCTION_EXC_OBJ(DECL) (DECL_LANG_SPECIFIC(DECL)->u.f.exc_obj) /* For each function decl, init_test_table contains a hash table whose entries are keyed on class names, and whose values are local boolean decls. The variables are intended to be TRUE when the @@ -785,6 +787,7 @@ struct GTY(()) lang_decl_func { int arg_slot_count; source_location last_line; /* End line number for a function decl */ tree throws_list; /* Exception specified by `throws' */ + tree exc_obj; /* Decl holding the exception object. */ /* Class initialization test variables */ htab_t GTY ((param_is (struct treetreehash_entry))) init_test_table; diff --git a/gcc/libfuncs.h b/gcc/libfuncs.h index 997ecb0..d3e69e3 100644 --- a/gcc/libfuncs.h +++ b/gcc/libfuncs.h @@ -30,7 +30,6 @@ enum libfunc_index LTI_memset, LTI_setbits, - LTI_unwind_resume, LTI_setjmp, LTI_longjmp, LTI_unwind_sjlj_register, @@ -59,7 +58,6 @@ extern GTY(()) rtx libfunc_table[LTI_MAX]; #define memset_libfunc (libfunc_table[LTI_memset]) #define setbits_libfunc (libfunc_table[LTI_setbits]) -#define unwind_resume_libfunc (libfunc_table[LTI_unwind_resume]) #define setjmp_libfunc (libfunc_table[LTI_setjmp]) #define longjmp_libfunc (libfunc_table[LTI_longjmp]) #define unwind_sjlj_register_libfunc (libfunc_table[LTI_unwind_sjlj_register]) diff --git a/gcc/lower-subreg.c b/gcc/lower-subreg.c index 3ff20eb..3ce714b 100644 --- a/gcc/lower-subreg.c +++ b/gcc/lower-subreg.c @@ -559,30 +559,6 @@ adjust_decomposed_uses (rtx *px, void *data ATTRIBUTE_UNUSED) return 0; } -/* We are deleting INSN. Move any EH_REGION notes to INSNS. */ - -static void -move_eh_region_note (rtx insn, rtx insns) -{ - rtx note, p; - - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (note == NULL_RTX) - return; - - gcc_assert (CALL_P (insn) - || (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))); - - for (p = insns; p != NULL_RTX; p = NEXT_INSN (p)) - { - if (CALL_P (p) - || (flag_non_call_exceptions - && INSN_P (p) - && may_trap_p (PATTERN (p)))) - add_reg_note (p, REG_EH_REGION, XEXP (note, 0)); - } -} - /* Resolve any decomposed registers which appear in register notes on INSN. */ @@ -847,7 +823,7 @@ resolve_simple_move (rtx set, rtx insn) insns = get_insns (); end_sequence (); - move_eh_region_note (insn, insns); + copy_reg_eh_region_note_forward (insn, insns, NULL_RTX); emit_insn_before (insns, insn); diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index 59317eb..70f841b 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,9 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * objc-act.c (objc_init_exceptions): Don't call + default_init_unwind_resume_libfunc. + (objc_build_exc_ptr): Use __builtin_eh_pointer. + 2009-09-13 Richard Guenther <rguenther@suse.de> Rafael Avila de Espindola <espindola@google.com> diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index f695431..eac7ff0 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -3475,7 +3475,7 @@ struct objc_try_context /* The CATCH_EXPR of an open @catch clause. */ tree current_catch; - /* The VAR_DECL holding the Darwin equivalent of EXC_PTR_EXPR. */ + /* The VAR_DECL holding the Darwin equivalent of __builtin_eh_pointer. */ tree caught_decl; tree stack_decl; tree rethrow_decl; @@ -3510,9 +3510,9 @@ objc_eh_personality (void) } #endif -/* Build an EXC_PTR_EXPR, or the moral equivalent. In the case of Darwin, - we'll arrange for it to be initialized (and associated with a binding) - later. */ +/* Build __builtin_eh_pointer, or the moral equivalent. In the case + of Darwin, we'll arrange for it to be initialized (and associated + with a binding) later. */ static tree objc_build_exc_ptr (void) @@ -3528,7 +3528,12 @@ objc_build_exc_ptr (void) return var; } else - return build0 (EXC_PTR_EXPR, objc_object_type); + { + tree t; + t = built_in_decls[BUILT_IN_EH_POINTER]; + t = build_call_expr (t, 1, integer_zero_node); + return fold_convert (objc_object_type, t); + } } /* Build "objc_exception_try_exit(&_stack)". */ diff --git a/gcc/omp-low.c b/gcc/omp-low.c index a7de367..5cd9463 100644 --- a/gcc/omp-low.c +++ b/gcc/omp-low.c @@ -1214,7 +1214,7 @@ new_omp_context (gimple stmt, omp_context *outer_ctx) ctx->cb.dst_node = ctx->cb.src_node; ctx->cb.src_cfun = cfun; ctx->cb.copy_decl = omp_copy_decl; - ctx->cb.eh_region = -1; + ctx->cb.eh_lp_nr = 0; ctx->cb.transform_call_graph_edges = CB_CGE_MOVE; ctx->depth = 1; } @@ -3114,23 +3114,22 @@ expand_task_call (basic_block bb, gimple entry_stmt) static gimple_seq maybe_catch_exception (gimple_seq body) { - gimple f, t; + gimple g; + tree decl; if (!flag_exceptions) return body; if (lang_protect_cleanup_actions) - t = lang_protect_cleanup_actions (); + decl = lang_protect_cleanup_actions (); else - t = gimple_build_call (built_in_decls[BUILT_IN_TRAP], 0); + decl = built_in_decls[BUILT_IN_TRAP]; - f = gimple_build_eh_filter (NULL, gimple_seq_alloc_with_stmt (t)); - gimple_eh_filter_set_must_not_throw (f, true); - - t = gimple_build_try (body, gimple_seq_alloc_with_stmt (f), + g = gimple_build_eh_must_not_throw (decl); + g = gimple_build_try (body, gimple_seq_alloc_with_stmt (g), GIMPLE_TRY_CATCH); - return gimple_seq_alloc_with_stmt (t); + return gimple_seq_alloc_with_stmt (g); } /* Chain all the DECLs in LIST by their TREE_CHAIN fields. */ @@ -6244,7 +6243,7 @@ create_task_copyfn (gimple task_stmt, omp_context *ctx) tcctx.cb.dst_node = tcctx.cb.src_node; tcctx.cb.src_cfun = ctx->cb.src_cfun; tcctx.cb.copy_decl = task_copyfn_copy_decl; - tcctx.cb.eh_region = -1; + tcctx.cb.eh_lp_nr = 0; tcctx.cb.transform_call_graph_edges = CB_CGE_MOVE; tcctx.cb.decl_map = pointer_map_create (); tcctx.ctx = ctx; diff --git a/gcc/optabs.c b/gcc/optabs.c index 35f95f2..a1adc58 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -3858,32 +3858,31 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) /* If we're using non-call exceptions, a libcall corresponding to an operation that may trap may also trap. */ + /* ??? See the comment in front of make_reg_eh_region_note. */ if (flag_non_call_exceptions && may_trap_p (equiv)) { for (insn = insns; insn; insn = NEXT_INSN (insn)) if (CALL_P (insn)) { rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - - if (note != 0 && INTVAL (XEXP (note, 0)) <= 0) - remove_note (insn, note); + if (note) + { + int lp_nr = INTVAL (XEXP (note, 0)); + if (lp_nr == 0 || lp_nr == INT_MIN) + remove_note (insn, note); + } } } else - /* look for any CALL_INSNs in this sequence, and attach a REG_EH_REGION - reg note to indicate that this call cannot throw or execute a nonlocal - goto (unless there is already a REG_EH_REGION note, in which case - we update it). */ - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (CALL_P (insn)) - { - rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - - if (note != 0) - XEXP (note, 0) = constm1_rtx; - else - add_reg_note (insn, REG_EH_REGION, constm1_rtx); - } + { + /* Look for any CALL_INSNs in this sequence, and attach a REG_EH_REGION + reg note to indicate that this call cannot throw or execute a nonlocal + goto (unless there is already a REG_EH_REGION note, in which case + we update it). */ + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (CALL_P (insn)) + make_reg_eh_region_note_nothrow_nononlocal (insn); + } /* First emit all insns that set pseudos. Remove them from the list as we go. Avoid insns that set pseudos which were referenced in previous diff --git a/gcc/passes.c b/gcc/passes.c index 531dc60..fa2de03 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -590,6 +590,7 @@ init_optimization_passes (void) /* These passes are run after IPA passes on every function that is being output to the assembler file. */ p = &all_passes; + NEXT_PASS (pass_lower_eh_dispatch); NEXT_PASS (pass_all_optimizations); { struct opt_pass **p = &pass_all_optimizations.pass.sub; @@ -713,6 +714,7 @@ init_optimization_passes (void) NEXT_PASS (pass_local_pure_const); } NEXT_PASS (pass_cleanup_eh); + NEXT_PASS (pass_lower_resx); NEXT_PASS (pass_nrv); NEXT_PASS (pass_mudflap_2); NEXT_PASS (pass_cleanup_cfg_post_optimizing); diff --git a/gcc/print-tree.c b/gcc/print-tree.c index 26f467c..1d66769 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -393,6 +393,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent) if (code == LABEL_DECL && DECL_ERROR_ISSUED (node)) fputs (" error-issued", file); + if (code == LABEL_DECL && EH_LANDING_PAD_NR (node)) + fprintf (file, " landing-pad:%d", EH_LANDING_PAD_NR (node)); if (code == VAR_DECL && DECL_IN_TEXT_SECTION (node)) fputs (" in-text-section", file); diff --git a/gcc/recog.c b/gcc/recog.c index c1e25d7..bfca43b 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -3234,37 +3234,35 @@ peephole2_optimize (void) if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) break; - for (x = attempt ; x != before_try ; x = PREV_INSN (x)) - if (CALL_P (x) - || (flag_non_call_exceptions - && may_trap_p (PATTERN (x)) - && !find_reg_note (x, REG_EH_REGION, NULL))) - { - if (note) - add_reg_note (x, REG_EH_REGION, XEXP (note, 0)); - - if (x != BB_END (bb) && eh_edge) - { - edge nfte, nehe; - int flags; - - nfte = split_block (bb, x); - flags = (eh_edge->flags - & (EDGE_EH | EDGE_ABNORMAL)); - if (CALL_P (x)) - flags |= EDGE_ABNORMAL_CALL; - nehe = make_edge (nfte->src, eh_edge->dest, - flags); - - nehe->probability = eh_edge->probability; - nfte->probability - = REG_BR_PROB_BASE - nehe->probability; - - do_cleanup_cfg |= purge_dead_edges (nfte->dest); - bb = nfte->src; - eh_edge = nehe; - } - } + if (note) + copy_reg_eh_region_note_backward (note, attempt, + before_try); + + if (eh_edge) + for (x = attempt ; x != before_try ; x = PREV_INSN (x)) + if (x != BB_END (bb) + && (can_throw_internal (x) + || can_nonlocal_goto (x))) + { + edge nfte, nehe; + int flags; + + nfte = split_block (bb, x); + flags = (eh_edge->flags + & (EDGE_EH | EDGE_ABNORMAL)); + if (CALL_P (x)) + flags |= EDGE_ABNORMAL_CALL; + nehe = make_edge (nfte->src, eh_edge->dest, + flags); + + nehe->probability = eh_edge->probability; + nfte->probability + = REG_BR_PROB_BASE - nehe->probability; + + do_cleanup_cfg |= purge_dead_edges (nfte->dest); + bb = nfte->src; + eh_edge = nehe; + } /* Converting possibly trapping insn to non-trapping is possible. Zap dummy outgoing edges. */ diff --git a/gcc/reload1.c b/gcc/reload1.c index d5cd37c..984913a 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -447,7 +447,6 @@ static rtx inc_for_reload (rtx, rtx, rtx, int); #ifdef AUTO_INC_DEC static void add_auto_inc_notes (rtx, rtx); #endif -static void copy_eh_notes (rtx, rtx); static void substitute (rtx *, const_rtx, rtx); static bool gen_reload_chain_without_interm_reg_p (int, int); static int reloads_conflict (int, int); @@ -4132,17 +4131,11 @@ static void fixup_eh_region_note (rtx insn, rtx prev, rtx next) { rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - rtx i; - if (note == NULL) return; - - if (! may_trap_p (PATTERN (insn))) + if (!insn_could_throw_p (insn)) remove_note (insn, note); - - for (i = NEXT_INSN (prev); i != next; i = NEXT_INSN (i)) - if (INSN_P (i) && i != insn && may_trap_p (PATTERN (i))) - add_reg_note (i, REG_EH_REGION, XEXP (note, 0)); + copy_reg_eh_region_note_forward (note, NEXT_INSN (prev), next); } /* Reload pseudo-registers into hard regs around each insn as needed. @@ -7294,7 +7287,7 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, } if (flag_non_call_exceptions) - copy_eh_notes (insn, get_insns ()); + copy_reg_eh_region_note_forward (insn, get_insns (), NULL); /* End this sequence. */ *where = get_insns (); @@ -7514,7 +7507,7 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl, output_reload_insns[rl->opnum] = get_insns (); if (flag_non_call_exceptions) - copy_eh_notes (insn, get_insns ()); + copy_reg_eh_region_note_forward (insn, get_insns (), NULL); end_sequence (); } @@ -8890,21 +8883,6 @@ add_auto_inc_notes (rtx insn, rtx x) } #endif -/* Copy EH notes from an insn to its reloads. */ -static void -copy_eh_notes (rtx insn, rtx x) -{ - rtx eh_note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (eh_note) - { - for (; x != 0; x = NEXT_INSN (x)) - { - if (may_trap_p (PATTERN (x))) - add_reg_note (x, REG_EH_REGION, XEXP (eh_note, 0)); - } - } -} - /* This is used by reload pass, that does emit some instructions after abnormal calls moving basic block end, but in fact it wants to emit them on the edge. Looks for abnormal call edges, find backward the diff --git a/gcc/rtl.def b/gcc/rtl.def index bcb5cbc..d1c0793 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -301,11 +301,6 @@ DEF_RTL_EXPR(EH_RETURN, "eh_return", "", RTX_EXTRA) For an unconditional trap, make the condition (const_int 1). */ DEF_RTL_EXPR(TRAP_IF, "trap_if", "ee", RTX_EXTRA) -/* Placeholder for _Unwind_Resume before we know if a function call - or a branch is needed. Operand 1 is the exception region from - which control is flowing. */ -DEF_RTL_EXPR(RESX, "resx", "i", RTX_EXTRA) - /* ---------------------------------------------------------------------- Primitive values for use in expressions. ---------------------------------------------------------------------- */ @@ -1841,6 +1841,13 @@ extern int volatile_insn_p (const_rtx); extern int may_trap_p_1 (const_rtx, unsigned); extern int may_trap_p (const_rtx); extern int may_trap_or_fault_p (const_rtx); +extern bool can_throw_internal (const_rtx); +extern bool can_throw_external (const_rtx); +extern bool insn_could_throw_p (const_rtx); +extern bool insn_nothrow_p (const_rtx); +extern bool can_nonlocal_goto (const_rtx); +extern void copy_reg_eh_region_note_forward (rtx, rtx, rtx); +extern void copy_reg_eh_region_note_backward(rtx, rtx, rtx); extern int inequality_comparisons_p (const_rtx); extern rtx replace_rtx (rtx, rtx, rtx); extern int replace_label (rtx *, void *); @@ -1186,7 +1186,6 @@ graphite_copy_stmts_from_block (basic_block bb, basic_block new_bb, htab_t map) { def_operand_p def_p; ssa_op_iter op_iter; - int region; gimple stmt = gsi_stmt (gsi); gimple copy; @@ -1199,9 +1198,7 @@ graphite_copy_stmts_from_block (basic_block bb, basic_block new_bb, htab_t map) gsi_insert_after (&gsi_tgt, copy, GSI_NEW_STMT); mark_sym_for_renaming (gimple_vop (cfun)); - region = lookup_stmt_eh_region (stmt); - if (region >= 0) - add_stmt_to_eh_region (copy, region); + maybe_duplicate_eh_stmt (copy, stmt); gimple_duplicate_stmt_histograms (cfun, copy, cfun, stmt); /* Create new names for all the definitions created by COPY and diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 23e9dbf..db86cf6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2009-09-14 Richard Henderson <rth@redhat.com> + + * g++.dg/eh/builtin1.C: Update resx pattern match. + * g++.dg/eh/builtin2.C, g++.dg/eh/builtin3.C: Likewise. + 2009-09-14 Richard Sandiford <rdsandiford@googlemail.com> * gcc.target/mips/branch-helper.h: New file. diff --git a/gcc/testsuite/g++.dg/eh/builtin1.C b/gcc/testsuite/g++.dg/eh/builtin1.C index 1f56d1a..ed49e9a 100644 --- a/gcc/testsuite/g++.dg/eh/builtin1.C +++ b/gcc/testsuite/g++.dg/eh/builtin1.C @@ -22,5 +22,5 @@ bar () __builtin_printf ("foo %d\n", a.i); } -/* { dg-final { scan-tree-dump-times "resx 1" 2 "eh" } } */ +/* { dg-final { scan-tree-dump-times "resx" 2 "eh" } } */ /* { dg-final { cleanup-tree-dump "eh" } } */ diff --git a/gcc/testsuite/g++.dg/eh/builtin2.C b/gcc/testsuite/g++.dg/eh/builtin2.C index b106516..fe0c4de 100644 --- a/gcc/testsuite/g++.dg/eh/builtin2.C +++ b/gcc/testsuite/g++.dg/eh/builtin2.C @@ -21,5 +21,5 @@ bar () __builtin_printf ("foo %d\n", a.i); } -/* { dg-final { scan-tree-dump-times "resx 1" 0 "eh" } } */ +/* { dg-final { scan-tree-dump-times "resx" 0 "eh" } } */ /* { dg-final { cleanup-tree-dump "eh" } } */ diff --git a/gcc/testsuite/g++.dg/eh/builtin3.C b/gcc/testsuite/g++.dg/eh/builtin3.C index be1629e..45809b8 100644 --- a/gcc/testsuite/g++.dg/eh/builtin3.C +++ b/gcc/testsuite/g++.dg/eh/builtin3.C @@ -12,5 +12,5 @@ bar () __builtin_printf ("foo %d\n", a.i); } -/* { dg-final { scan-tree-dump-times "resx 1" 1 "eh" } } */ +/* { dg-final { scan-tree-dump-times "resx" 1 "eh" } } */ /* { dg-final { cleanup-tree-dump "eh" } } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C index 3de72aa..8dfaa52 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C +++ b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C @@ -19,6 +19,6 @@ t (void) // { dg-final { scan-tree-dump-times "Empty EH handler" 1 "ehcleanup1" } } // // And as a result also contained control flow. -// { dg-final { scan-tree-dump-times "Removing unreachable" 1 "ehcleanup1" } } +// { dg-final { scan-tree-dump-times "Removing unreachable" 2 "ehcleanup1" } } // // { dg-final { cleanup-tree-dump "ehcleanup1" } } diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 02daee0..fb24cc4 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -545,6 +545,9 @@ make_edges (void) make_eh_edges (last); fallthru = false; break; + case GIMPLE_EH_DISPATCH: + fallthru = make_eh_dispatch_edges (last); + break; case GIMPLE_CALL: /* If this function receives a nonlocal goto, then we need to @@ -565,9 +568,7 @@ make_edges (void) /* A GIMPLE_ASSIGN may throw internally and thus be considered control-altering. */ if (is_ctrl_altering_stmt (last)) - { - make_eh_edges (last); - } + make_eh_edges (last); fallthru = true; break; @@ -1033,29 +1034,6 @@ static struct label_record bool used; } *label_for_bb; -/* Callback for for_each_eh_region. Helper for cleanup_dead_labels. */ -static void -update_eh_label (struct eh_region_d *region) -{ - tree old_label = get_eh_region_tree_label (region); - if (old_label) - { - tree new_label; - basic_block bb = label_to_block (old_label); - - /* ??? After optimizing, there may be EH regions with labels - that have already been removed from the function body, so - there is no basic block for them. */ - if (! bb) - return; - - new_label = label_for_bb[bb->index].label; - label_for_bb[bb->index].used = true; - set_eh_region_tree_label (region, new_label); - } -} - - /* Given LABEL return the first label in the same basic block. */ static tree @@ -1075,6 +1053,58 @@ main_block_label (tree label) return main_label; } +/* Clean up redundant labels within the exception tree. */ + +static void +cleanup_dead_labels_eh (void) +{ + eh_landing_pad lp; + eh_region r; + tree lab; + int i; + + if (cfun->eh == NULL) + return; + + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp && lp->post_landing_pad) + { + lab = main_block_label (lp->post_landing_pad); + if (lab != lp->post_landing_pad) + { + EH_LANDING_PAD_NR (lp->post_landing_pad) = 0; + EH_LANDING_PAD_NR (lab) = lp->index; + } + } + + FOR_ALL_EH_REGION (r) + switch (r->type) + { + case ERT_CLEANUP: + case ERT_MUST_NOT_THROW: + break; + + case ERT_TRY: + { + eh_catch c; + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + lab = c->label; + if (lab) + c->label = main_block_label (lab); + } + } + break; + + case ERT_ALLOWED_EXCEPTIONS: + lab = r->u.allowed.label; + if (lab) + r->u.allowed.label = main_block_label (lab); + break; + } +} + + /* Cleanup redundant labels. This is a three-step process: 1) Find the leading label for each block. 2) Redirect all references to labels to the leading labels. @@ -1173,7 +1203,8 @@ cleanup_dead_labels (void) } } - for_each_eh_region (update_eh_label); + /* Do the same for the exception region tree labels. */ + cleanup_dead_labels_eh (); /* Finally, purge dead labels. All user-defined labels and labels that can be the target of non-local gotos and labels which have their @@ -1584,9 +1615,11 @@ gimple_merge_blocks (basic_block a, basic_block b) /* Remove labels from B and set gimple_bb to A for other statements. */ for (gsi = gsi_start_bb (b); !gsi_end_p (gsi);) { - if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL) + gimple stmt = gsi_stmt (gsi); + if (gimple_code (stmt) == GIMPLE_LABEL) { - gimple label = gsi_stmt (gsi); + tree label = gimple_label_label (stmt); + int lp_nr; gsi_remove (&gsi, false); @@ -1596,15 +1629,22 @@ gimple_merge_blocks (basic_block a, basic_block b) used in other ways (think about the runtime checking for Fortran assigned gotos). So we can not just delete the label. Instead we move the label to the start of block A. */ - if (FORCED_LABEL (gimple_label_label (label))) + if (FORCED_LABEL (label)) { gimple_stmt_iterator dest_gsi = gsi_start_bb (a); - gsi_insert_before (&dest_gsi, label, GSI_NEW_STMT); + gsi_insert_before (&dest_gsi, stmt, GSI_NEW_STMT); + } + + lp_nr = EH_LANDING_PAD_NR (label); + if (lp_nr) + { + eh_landing_pad lp = get_eh_landing_pad_from_number (lp_nr); + lp->post_landing_pad = NULL; } } else { - gimple_set_bb (gsi_stmt (gsi), a); + gimple_set_bb (stmt, a); gsi_next (&gsi); } } @@ -1917,10 +1957,7 @@ remove_useless_stmts_tc (gimple_stmt_iterator *gsi, struct rus_data *data) break; case GIMPLE_EH_FILTER: - /* If the first element is an eh_filter, it should stand alone. */ - if (gimple_eh_filter_must_not_throw (stmt)) - this_may_throw = false; - else if (gimple_eh_filter_types (stmt) == NULL) + if (gimple_eh_filter_types (stmt) == NULL) this_may_throw = false; failure_seq = gimple_eh_filter_failure (stmt); failure_gsi = gsi_start (failure_seq); @@ -1928,6 +1965,10 @@ remove_useless_stmts_tc (gimple_stmt_iterator *gsi, struct rus_data *data) gsi_next (gsi); break; + case GIMPLE_EH_MUST_NOT_THROW: + this_may_throw = false; + break; + default: /* Otherwise this is a list of cleanup statements. */ remove_useless_stmts_1 (&cleanup_gsi, data); @@ -2774,6 +2815,12 @@ is_ctrl_altering_stmt (gimple t) } break; + case GIMPLE_EH_DISPATCH: + /* EH_DISPATCH branches to the individual catch handlers at + this level of a try or allowed-exceptions region. It can + fallthru to the next statement as well. */ + return true; + CASE_GIMPLE_OMP: /* OpenMP directives alter control flow. */ return true; @@ -4039,8 +4086,6 @@ verify_gimple_assign_single (gimple stmt) case OBJ_TYPE_REF: case ASSERT_EXPR: case WITH_SIZE_EXPR: - case EXC_PTR_EXPR: - case FILTER_EXPR: case POLYNOMIAL_CHREC: case DOT_PROD_EXPR: case VEC_COND_EXPR: @@ -4248,8 +4293,9 @@ verify_types_in_gimple_stmt (gimple stmt) /* Tuples that do not have tree operands. */ case GIMPLE_NOP: - case GIMPLE_RESX: case GIMPLE_PREDICT: + case GIMPLE_RESX: + case GIMPLE_EH_DISPATCH: return false; CASE_GIMPLE_OMP: @@ -4334,6 +4380,7 @@ verify_stmt (gimple_stmt_iterator *gsi) struct walk_stmt_info wi; bool last_in_block = gsi_one_before_end_p (*gsi); gimple stmt = gsi_stmt (*gsi); + int lp_nr; if (is_gimple_omp (stmt)) { @@ -4388,17 +4435,21 @@ verify_stmt (gimple_stmt_iterator *gsi) have optimizations that simplify statements such that we prove that they cannot throw, that we update other data structures to match. */ - if (lookup_stmt_eh_region (stmt) >= 0) + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr != 0) { - /* During IPA passes, ipa-pure-const sets nothrow flags on calls - and they are updated on statements only after fixup_cfg - is executed at beggining of expansion stage. */ - if (!stmt_could_throw_p (stmt) && cgraph_state != CGRAPH_STATE_IPA_SSA) + if (!stmt_could_throw_p (stmt)) { - error ("statement marked for throw, but doesn%'t"); - goto fail; + /* During IPA passes, ipa-pure-const sets nothrow flags on calls + and they are updated on statements only after fixup_cfg + is executed at beggining of expansion stage. */ + if (cgraph_state != CGRAPH_STATE_IPA_SSA) + { + error ("statement marked for throw, but doesn%'t"); + goto fail; + } } - if (!last_in_block && stmt_can_throw_internal (stmt)) + else if (lp_nr > 0 && !last_in_block && stmt_can_throw_internal (stmt)) { error ("statement marked for throw in middle of block"); goto fail; @@ -4586,9 +4637,20 @@ verify_stmts (void) if (uid == -1 || VEC_index (basic_block, label_to_block_map, uid) != bb) { - error ("incorrect entry in label_to_block_map.\n"); + error ("incorrect entry in label_to_block_map"); err |= true; } + + uid = EH_LANDING_PAD_NR (decl); + if (uid) + { + eh_landing_pad lp = get_eh_landing_pad_from_number (uid); + if (decl != lp->post_landing_pad) + { + error ("incorrect setting of landing pad number"); + err |= true; + } + } } err |= verify_stmt (&gsi); @@ -4735,6 +4797,9 @@ gimple_verify_flow_info (void) stmt = gsi_stmt (gsi); + if (gimple_code (stmt) == GIMPLE_LABEL) + continue; + err |= verify_eh_edges (stmt); if (is_ctrl_stmt (stmt)) @@ -4904,8 +4969,14 @@ gimple_verify_flow_info (void) FOR_EACH_EDGE (e, ei, bb->succs) e->dest->aux = (void *)0; } + break; + + case GIMPLE_EH_DISPATCH: + err |= verify_eh_dispatch_edge (stmt); + break; - default: ; + default: + break; } } @@ -5129,6 +5200,11 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) /* The edges from OMP constructs can be simply redirected. */ break; + case GIMPLE_EH_DISPATCH: + if (!(e->flags & EDGE_FALLTHRU)) + redirect_eh_dispatch_edge (stmt, e, dest); + break; + default: /* Otherwise it must be a fallthru edge, and we don't need to do anything besides redirecting it. */ @@ -5278,7 +5354,6 @@ gimple_duplicate_bb (basic_block bb) { def_operand_p def_p; ssa_op_iter op_iter; - int region; stmt = gsi_stmt (gsi); if (gimple_code (stmt) == GIMPLE_LABEL) @@ -5288,9 +5363,8 @@ gimple_duplicate_bb (basic_block bb) operands. */ copy = gimple_copy (stmt); gsi_insert_after (&gsi_tgt, copy, GSI_NEW_STMT); - region = lookup_stmt_eh_region (stmt); - if (region >= 0) - add_stmt_to_eh_region (copy, region); + + maybe_duplicate_eh_stmt (copy, stmt); gimple_duplicate_stmt_histograms (cfun, copy, cfun, stmt); /* Create new names for all the definitions created by COPY and @@ -5818,6 +5892,7 @@ struct move_stmt_d tree to_context; struct pointer_map_t *vars_map; htab_t new_label_map; + struct pointer_map_t *eh_map; bool remap_decls_p; }; @@ -5883,6 +5958,35 @@ move_stmt_op (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +/* Helper for move_stmt_r. Given an EH region number for the source + function, map that to the duplicate EH regio number in the dest. */ + +static int +move_stmt_eh_region_nr (int old_nr, struct move_stmt_d *p) +{ + eh_region old_r, new_r; + void **slot; + + old_r = get_eh_region_from_number (old_nr); + slot = pointer_map_contains (p->eh_map, old_r); + new_r = (eh_region) *slot; + + return new_r->index; +} + +/* Similar, but operate on INTEGER_CSTs. */ + +static tree +move_stmt_eh_region_tree_nr (tree old_t_nr, struct move_stmt_d *p) +{ + int old_nr, new_nr; + + old_nr = tree_low_cst (old_t_nr, 0); + new_nr = move_stmt_eh_region_nr (old_nr, p); + + return build_int_cst (NULL, new_nr); +} + /* Like move_stmt_op, but for gimple statements. Helper for move_block_to_fn. Set GIMPLE_BLOCK in every expression @@ -5911,21 +6015,70 @@ move_stmt_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, } #endif - if (is_gimple_omp (stmt) - && gimple_code (stmt) != GIMPLE_OMP_RETURN - && gimple_code (stmt) != GIMPLE_OMP_CONTINUE) + switch (gimple_code (stmt)) { - /* Do not remap variables inside OMP directives. Variables - referenced in clauses and directive header belong to the - parent function and should not be moved into the child - function. */ - bool save_remap_decls_p = p->remap_decls_p; - p->remap_decls_p = false; - *handled_ops_p = true; + case GIMPLE_CALL: + /* Remap the region numbers for __builtin_eh_{pointer,filter}. */ + { + tree r, fndecl = gimple_call_fndecl (stmt); + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_COPY_VALUES: + r = gimple_call_arg (stmt, 1); + r = move_stmt_eh_region_tree_nr (r, p); + gimple_call_set_arg (stmt, 1, r); + /* FALLTHRU */ + + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_FILTER: + r = gimple_call_arg (stmt, 0); + r = move_stmt_eh_region_tree_nr (r, p); + gimple_call_set_arg (stmt, 0, r); + break; - walk_gimple_seq (gimple_omp_body (stmt), move_stmt_r, move_stmt_op, wi); + default: + break; + } + } + break; + + case GIMPLE_RESX: + { + int r = gimple_resx_region (stmt); + r = move_stmt_eh_region_nr (r, p); + gimple_resx_set_region (stmt, r); + } + break; - p->remap_decls_p = save_remap_decls_p; + case GIMPLE_EH_DISPATCH: + { + int r = gimple_eh_dispatch_region (stmt); + r = move_stmt_eh_region_nr (r, p); + gimple_eh_dispatch_set_region (stmt, r); + } + break; + + case GIMPLE_OMP_RETURN: + case GIMPLE_OMP_CONTINUE: + break; + default: + if (is_gimple_omp (stmt)) + { + /* Do not remap variables inside OMP directives. Variables + referenced in clauses and directive header belong to the + parent function and should not be moved into the child + function. */ + bool save_remap_decls_p = p->remap_decls_p; + p->remap_decls_p = false; + *handled_ops_p = true; + + walk_gimple_seq (gimple_omp_body (stmt), move_stmt_r, + move_stmt_op, wi); + + p->remap_decls_p = save_remap_decls_p; + } + break; } return NULL_TREE; @@ -5959,7 +6112,7 @@ mark_virtual_ops_in_bb (basic_block bb) static void move_block_to_fn (struct function *dest_cfun, basic_block bb, basic_block after, bool update_edge_count_p, - struct move_stmt_d *d, int eh_offset) + struct move_stmt_d *d) { struct control_flow_graph *cfg; edge_iterator ei; @@ -6035,7 +6188,6 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); - int region; struct walk_stmt_info wi; memset (&wi, 0, sizeof (wi)); @@ -6065,17 +6217,12 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, if (uid >= dest_cfun->cfg->last_label_uid) dest_cfun->cfg->last_label_uid = uid + 1; } - else if (gimple_code (stmt) == GIMPLE_RESX && eh_offset != 0) - gimple_resx_set_region (stmt, gimple_resx_region (stmt) + eh_offset); - region = lookup_stmt_eh_region (stmt); - if (region >= 0) - { - add_stmt_to_eh_region_fn (dest_cfun, stmt, region + eh_offset); - remove_stmt_from_eh_region (stmt); - gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt); - gimple_remove_stmt_histograms (cfun, stmt); - } + maybe_duplicate_eh_stmt_fn (dest_cfun, stmt, cfun, stmt, d->eh_map, 0); + remove_stmt_from_eh_lp_fn (cfun, stmt); + + gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt); + gimple_remove_stmt_histograms (cfun, stmt); /* We cannot leave any operands allocated from the operand caches of the current function. */ @@ -6106,29 +6253,28 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, /* Examine the statements in BB (which is in SRC_CFUN); find and return the outermost EH region. Use REGION as the incoming base EH region. */ -static int +static eh_region find_outermost_region_in_block (struct function *src_cfun, - basic_block bb, int region) + basic_block bb, eh_region region) { gimple_stmt_iterator si; for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); - int stmt_region; + eh_region stmt_region; + int lp_nr; - if (gimple_code (stmt) == GIMPLE_RESX) - stmt_region = gimple_resx_region (stmt); - else - stmt_region = lookup_stmt_eh_region_fn (src_cfun, stmt); - if (stmt_region > 0) + lp_nr = lookup_stmt_eh_lp_fn (src_cfun, stmt); + stmt_region = get_eh_region_from_lp_number_fn (src_cfun, lp_nr); + if (stmt_region) { - if (region < 0) + if (region == NULL) region = stmt_region; else if (stmt_region != region) { region = eh_region_outermost (src_cfun, stmt_region, region); - gcc_assert (region != -1); + gcc_assert (region != NULL); } } } @@ -6218,13 +6364,13 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, basic_block dom_entry = get_immediate_dominator (CDI_DOMINATORS, entry_bb); basic_block after, bb, *entry_pred, *exit_succ, abb; struct function *saved_cfun = cfun; - int *entry_flag, *exit_flag, eh_offset; + int *entry_flag, *exit_flag; unsigned *entry_prob, *exit_prob; unsigned i, num_entry_edges, num_exit_edges; edge e; edge_iterator ei; htab_t new_label_map; - struct pointer_map_t *vars_map; + struct pointer_map_t *vars_map, *eh_map; struct loop *loop = entry_bb->loop_father; struct move_stmt_d d; @@ -6294,21 +6440,21 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, init_empty_tree_cfg (); /* Initialize EH information for the new function. */ - eh_offset = 0; + eh_map = NULL; new_label_map = NULL; if (saved_cfun->eh) { - int region = -1; + eh_region region = NULL; for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++) region = find_outermost_region_in_block (saved_cfun, bb, region); init_eh_for_function (); - if (region != -1) + if (region != NULL) { new_label_map = htab_create (17, tree_map_hash, tree_map_eq, free); - eh_offset = duplicate_eh_regions (saved_cfun, new_label_mapper, - new_label_map, region, 0); + eh_map = duplicate_eh_regions (saved_cfun, region, 0, + new_label_mapper, new_label_map); } } @@ -6320,20 +6466,21 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, vars_map = pointer_map_create (); memset (&d, 0, sizeof (d)); - d.vars_map = vars_map; + d.orig_block = orig_block; + d.new_block = DECL_INITIAL (dest_cfun->decl); d.from_context = cfun->decl; d.to_context = dest_cfun->decl; + d.vars_map = vars_map; d.new_label_map = new_label_map; + d.eh_map = eh_map; d.remap_decls_p = true; - d.orig_block = orig_block; - d.new_block = DECL_INITIAL (dest_cfun->decl); for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++) { /* No need to update edge counts on the last block. It has already been updated earlier when we detached the region from the original CFG. */ - move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, &d, eh_offset); + move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, &d); after = bb; } @@ -6356,6 +6503,8 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, if (new_label_map) htab_delete (new_label_map); + if (eh_map) + pointer_map_destroy (eh_map); pointer_map_destroy (vars_map); /* Rewire the entry and exit blocks. The successor to the entry diff --git a/gcc/tree-cfgcleanup.c b/gcc/tree-cfgcleanup.c index 5cce1b6..d3a8ca9 100644 --- a/gcc/tree-cfgcleanup.c +++ b/gcc/tree-cfgcleanup.c @@ -239,6 +239,16 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted) gcc_assert (bb != ENTRY_BLOCK_PTR); #endif + /* There should not be an edge coming from entry, or an EH edge. */ + { + edge_iterator ei; + edge e; + + FOR_EACH_EDGE (e, ei, bb->preds) + if (e->src == ENTRY_BLOCK_PTR || (e->flags & EDGE_EH)) + return false; + } + /* Now walk through the statements backward. We can ignore labels, anything else means this is not a forwarder block. */ for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi)) @@ -262,9 +272,6 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted) } } - if (find_edge (ENTRY_BLOCK_PTR, bb)) - return false; - if (current_loops) { basic_block dest; diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index b6eff5e..e9a645e 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -201,7 +201,6 @@ create_tree_common_ann (tree t) ann = GGC_CNEW (struct tree_ann_common_d); ann->type = TREE_ANN_COMMON; - ann->rn = -1; t->base.ann = (tree_ann_t) ann; return ann; diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index ac87f42..a1aa4b2 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see #include "ggc.h" #include "toplev.h" #include "gimple.h" +#include "target.h" /* In some instances a tree and a gimple need to be stored in a same table, i.e. in hash tables. This is a structure to do this. */ @@ -74,7 +75,7 @@ struct_ptr_hash (const void *a) } -/* Remember and lookup EH region data for arbitrary statements. +/* Remember and lookup EH landing pad data for arbitrary statements. Really this means any statement that could_throw_p. We could stuff this information into the stmt_ann data structure, but: @@ -86,30 +87,19 @@ struct_ptr_hash (const void *a) compared to those that can. We should be saving some amount of space by only allocating memory for those that can throw. */ -static void -record_stmt_eh_region (struct eh_region_d *region, gimple t) -{ - if (!region) - return; - - add_stmt_to_eh_region (t, get_eh_region_number (region)); -} - - -/* Add statement T in function IFUN to EH region NUM. */ +/* Add statement T in function IFUN to landing pad NUM. */ void -add_stmt_to_eh_region_fn (struct function *ifun, gimple t, int num) +add_stmt_to_eh_lp_fn (struct function *ifun, gimple t, int num) { struct throw_stmt_node *n; void **slot; - gcc_assert (num >= 0); - gcc_assert (gimple_code (t) != GIMPLE_RESX); + gcc_assert (num != 0); n = GGC_NEW (struct throw_stmt_node); n->stmt = t; - n->region_nr = num; + n->lp_nr = num; if (!get_eh_throw_stmt_table (ifun)) set_eh_throw_stmt_table (ifun, htab_create_ggc (31, struct_ptr_hash, @@ -121,21 +111,39 @@ add_stmt_to_eh_region_fn (struct function *ifun, gimple t, int num) *slot = n; } - -/* Add statement T in the current function (cfun) to EH region number - NUM. */ +/* Add statement T in the current function (cfun) to EH landing pad NUM. */ void -add_stmt_to_eh_region (gimple t, int num) +add_stmt_to_eh_lp (gimple t, int num) { - add_stmt_to_eh_region_fn (cfun, t, num); + add_stmt_to_eh_lp_fn (cfun, t, num); } +/* Add statement T to the single EH landing pad in REGION. */ -/* Remove statement T in function IFUN from the EH region holding it. */ +static void +record_stmt_eh_region (eh_region region, gimple t) +{ + if (region == NULL) + return; + if (region->type == ERT_MUST_NOT_THROW) + add_stmt_to_eh_lp_fn (cfun, t, -region->index); + else + { + eh_landing_pad lp = region->landing_pads; + if (lp == NULL) + lp = gen_eh_landing_pad (region); + else + gcc_assert (lp->next_lp == NULL); + add_stmt_to_eh_lp_fn (cfun, t, lp->index); + } +} + + +/* Remove statement T in function IFUN from its EH landing pad. */ bool -remove_stmt_from_eh_region_fn (struct function *ifun, gimple t) +remove_stmt_from_eh_lp_fn (struct function *ifun, gimple t) { struct throw_stmt_node dummy; void **slot; @@ -156,75 +164,57 @@ remove_stmt_from_eh_region_fn (struct function *ifun, gimple t) } -/* Remove statement T in the current function (cfun) from the EH - region holding it. */ +/* Remove statement T in the current function (cfun) from its + EH landing pad. */ bool -remove_stmt_from_eh_region (gimple t) +remove_stmt_from_eh_lp (gimple t) { - return remove_stmt_from_eh_region_fn (cfun, t); + return remove_stmt_from_eh_lp_fn (cfun, t); } /* Determine if statement T is inside an EH region in function IFUN. - Return the EH region number if found, return -2 if IFUN does not - have an EH table and -1 if T could not be found in IFUN's EH region - table. */ + Positive numbers indicate a landing pad index; negative numbers + indicate a MUST_NOT_THROW region index; zero indicates that the + statement is not recorded in the region table. */ int -lookup_stmt_eh_region_fn (struct function *ifun, gimple t) +lookup_stmt_eh_lp_fn (struct function *ifun, gimple t) { struct throw_stmt_node *p, n; - if (!get_eh_throw_stmt_table (ifun)) - return -2; + if (ifun->eh->throw_stmt_table == NULL) + return 0; n.stmt = t; - p = (struct throw_stmt_node *) htab_find (get_eh_throw_stmt_table (ifun), &n); - return (p ? p->region_nr : -1); + p = (struct throw_stmt_node *) htab_find (ifun->eh->throw_stmt_table, &n); + return p ? p->lp_nr : 0; } - -/* Determine if statement T is inside an EH region in the current - function (cfun). Return the EH region number if found, return -2 - if cfun does not have an EH table and -1 if T could not be found in - cfun's EH region table. */ +/* Likewise, but always use the current function. */ int -lookup_stmt_eh_region (gimple t) +lookup_stmt_eh_lp (gimple t) { /* We can get called from initialized data when -fnon-call-exceptions is on; prevent crash. */ if (!cfun) - return -1; - - return lookup_stmt_eh_region_fn (cfun, t); + return 0; + return lookup_stmt_eh_lp_fn (cfun, t); } - -/* Determine if expression T is inside an EH region in the current - function (cfun). Return the EH region number if found, return -2 - if IFUN does not have an EH table and -1 if T could not be found in - IFUN's EH region table. */ +/* Likewise, but reference a tree expression instead. */ int -lookup_expr_eh_region (tree t) +lookup_expr_eh_lp (tree t) { - /* We can get called from initialized data when -fnon-call-exceptions - is on; prevent crash. */ - if (!cfun) - return -1; - - if (!get_eh_throw_stmt_table (cfun)) - return -2; - - if (t && EXPR_P (t)) + if (cfun && cfun->eh->throw_stmt_table && t && EXPR_P (t)) { tree_ann_common_t ann = tree_common_ann (t); if (ann) - return (int) ann->rn; + return ann->lp_nr; } - - return -1; + return 0; } @@ -238,7 +228,7 @@ struct finally_tree_node when deciding whether a GOTO to a certain LABEL_DECL (which is a tree) leaves the TRY block, its necessary to record a tree in this field. Thus a treemple is used. */ - treemple child; + treemple child; gimple parent; }; @@ -263,7 +253,7 @@ record_in_finally_tree (treemple child, gimple parent) static void collect_finally_tree (gimple stmt, gimple region); -/* Go through the gimple sequence. Works with collect_finally_tree to +/* Go through the gimple sequence. Works with collect_finally_tree to record all GIMPLE_LABEL and GIMPLE_TRY statements. */ static void @@ -344,6 +334,14 @@ outside_finally_tree (treemple start, gimple target) The eh region creation is straight-forward, but frobbing all the gotos and such into shape isn't. */ +/* The sequence into which we record all EH stuff. This will be + placed at the end of the function when we're all done. */ +static gimple_seq eh_seq; + +/* Record whether an EH region contains something that can throw, + indexed by EH region number. */ +static bitmap eh_region_may_contain_throw; + /* The GOTO_QUEUE is is an array of GIMPLE_GOTO and GIMPLE_RETURN statements that are seen to escape this GIMPLE_TRY_FINALLY node. The idea is to record a gimple statement for everything except for @@ -371,7 +369,12 @@ struct leh_state /* What's "current" while constructing the eh region tree. These correspond to variables of the same name in cfun->eh, which we don't have easy access to. */ - struct eh_region_d *cur_region; + eh_region cur_region; + + /* What's "current" for the purposes of __builtin_eh_pointer. For + a CATCH, this is the associated TRY. For an EH_FILTER, this is + the associated ALLOWED_EXCEPTIONS, etc. */ + eh_region ehp_region; /* Processing of TRY_FINALLY requires a bit more state. This is split out into a separate structure so that we don't have to @@ -387,6 +390,7 @@ struct leh_tf_state in the collect_finally_tree data structures. */ gimple try_finally_expr; gimple top_p; + /* While lowering a top_p usually it is expanded into multiple statements, thus we need the following field to store them. */ gimple_seq top_p_seq; @@ -395,7 +399,7 @@ struct leh_tf_state struct leh_state *outer; /* The exception region created for it. */ - struct eh_region_d *region; + eh_region region; /* The goto queue. */ struct goto_queue_node *goto_queue; @@ -413,10 +417,6 @@ struct leh_tf_state though subsequent transformations may have cleared that flag. */ tree fallthru_label; - /* A label that has been registered with except.c to be the - landing pad for this try block. */ - tree eh_label; - /* True if it is possible to fall out the bottom of the try block. Cleared if the fallthru is converted to a goto. */ bool may_fallthru; @@ -429,7 +429,7 @@ struct leh_tf_state bool may_throw; }; -static gimple_seq lower_eh_filter (struct leh_state *, gimple); +static gimple_seq lower_eh_must_not_throw (struct leh_state *, gimple); /* Search for STMT in the goto queue. Return the replacement, or null if the statement isn't in the queue. */ @@ -810,7 +810,7 @@ do_goto_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod, if (!q->repl_stmt) q->repl_stmt = gimple_seq_alloc (); - q->cont_stmt = gimple_build_goto (VEC_index (tree, tf->dest_array,q->index)); + q->cont_stmt = gimple_build_goto (VEC_index (tree, tf->dest_array, q->index)); if (mod) gimple_seq_add_seq (&q->repl_stmt, mod); @@ -819,17 +819,76 @@ do_goto_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod, gimple_seq_add_stmt (&q->repl_stmt, x); } +/* Emit a standard landing pad sequence into SEQ for REGION. */ + +static void +emit_post_landing_pad (gimple_seq *seq, eh_region region) +{ + eh_landing_pad lp = region->landing_pads; + gimple x; + + if (lp == NULL) + lp = gen_eh_landing_pad (region); + + lp->post_landing_pad = create_artificial_label (UNKNOWN_LOCATION); + EH_LANDING_PAD_NR (lp->post_landing_pad) = lp->index; + + x = gimple_build_label (lp->post_landing_pad); + gimple_seq_add_stmt (seq, x); +} + +/* Emit a RESX statement into SEQ for REGION. */ + +static void +emit_resx (gimple_seq *seq, eh_region region) +{ + gimple x = gimple_build_resx (region->index); + gimple_seq_add_stmt (seq, x); + if (region->outer) + record_stmt_eh_region (region->outer, x); +} + +/* Emit an EH_DISPATCH statement into SEQ for REGION. */ + +static void +emit_eh_dispatch (gimple_seq *seq, eh_region region) +{ + gimple x = gimple_build_eh_dispatch (region->index); + gimple_seq_add_stmt (seq, x); +} + +/* Note that the current EH region may contain a throw, or a + call to a function which itself may contain a throw. */ + +static void +note_eh_region_may_contain_throw (eh_region region) +{ + while (!bitmap_bit_p (eh_region_may_contain_throw, region->index)) + { + bitmap_set_bit (eh_region_may_contain_throw, region->index); + region = region->outer; + if (region == NULL) + break; + } +} + /* We want to transform try { body; } catch { stuff; } to - body; goto over; lab: stuff; over: - - TP is a GIMPLE_TRY node. LAB is the label that + normal_seqence: + body; + over: + eh_seqence: + landing_pad: + stuff; + goto over; + + TP is a GIMPLE_TRY node. REGION is the region whose post_landing_pad should be placed before the second operand, or NULL. OVER is an existing label that should be put at the exit, or NULL. */ static gimple_seq -frob_into_branch_around (gimple tp, tree lab, tree over) +frob_into_branch_around (gimple tp, eh_region region, tree over) { gimple x; gimple_seq cleanup, result; @@ -838,21 +897,17 @@ frob_into_branch_around (gimple tp, tree lab, tree over) cleanup = gimple_try_cleanup (tp); result = gimple_try_eval (tp); - if (gimple_seq_may_fallthru (result)) + if (region) + emit_post_landing_pad (&eh_seq, region); + + if (gimple_seq_may_fallthru (cleanup)) { if (!over) over = create_artificial_label (loc); x = gimple_build_goto (over); - gimple_seq_add_stmt (&result, x); + gimple_seq_add_stmt (&cleanup, x); } - - if (lab) - { - x = gimple_build_label (lab); - gimple_seq_add_stmt (&result, x); - } - - gimple_seq_add_seq (&result, cleanup); + gimple_seq_add_seq (&eh_seq, cleanup); if (over) { @@ -928,44 +983,21 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, struct leh_state *this_state, struct leh_tf_state *tf) { - gimple protect_cleanup_actions; + tree protect_cleanup_actions; gimple_stmt_iterator gsi; bool finally_may_fallthru; gimple_seq finally; gimple x; /* First check for nothing to do. */ - if (lang_protect_cleanup_actions) - protect_cleanup_actions = lang_protect_cleanup_actions (); - else - protect_cleanup_actions = NULL; + if (lang_protect_cleanup_actions == NULL) + return; + protect_cleanup_actions = lang_protect_cleanup_actions (); + if (protect_cleanup_actions == NULL) + return; finally = gimple_try_cleanup (tf->top_p); - - /* If the EH case of the finally block can fall through, this may be a - structure of the form - try { - try { - throw ...; - } cleanup { - try { - throw ...; - } catch (...) { - } - } - } catch (...) { - yyy; - } - E.g. with an inline destructor with an embedded try block. In this - case we must save the runtime EH data around the nested exception. - - This complication means that any time the previous runtime data might - be used (via fallthru from the finally) we handle the eh case here, - whether or not protect_cleanup_actions is active. */ - finally_may_fallthru = gimple_seq_may_fallthru (finally); - if (!finally_may_fallthru && !protect_cleanup_actions) - return; /* Duplicate the FINALLY block. Only need to do this for try-finally, and not for cleanups. */ @@ -981,8 +1013,7 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, MUST_NOT_THROW filter. */ gsi = gsi_start (finally); x = gsi_stmt (gsi); - if (protect_cleanup_actions - && gimple_code (x) == GIMPLE_TRY + if (gimple_code (x) == GIMPLE_TRY && gimple_try_kind (x) == GIMPLE_TRY_CATCH && gimple_try_catch_is_cleanup (x)) { @@ -990,77 +1021,17 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, gsi_remove (&gsi, false); } - /* Resume execution after the exception. Adding this now lets - lower_eh_filter not add unnecessary gotos, as it is clear that - we never fallthru from this copy of the finally block. */ - if (finally_may_fallthru) - { - tree save_eptr, save_filt; - tree tmp; - - save_eptr = create_tmp_var (ptr_type_node, "save_eptr"); - save_filt = create_tmp_var (integer_type_node, "save_filt"); - - gsi = gsi_start (finally); - tmp = build0 (EXC_PTR_EXPR, ptr_type_node); - x = gimple_build_assign (save_eptr, tmp); - gsi_insert_before (&gsi, x, GSI_CONTINUE_LINKING); - - tmp = build0 (FILTER_EXPR, integer_type_node); - x = gimple_build_assign (save_filt, tmp); - gsi_insert_before (&gsi, x, GSI_CONTINUE_LINKING); - - gsi = gsi_last (finally); - tmp = build0 (EXC_PTR_EXPR, ptr_type_node); - x = gimple_build_assign (tmp, save_eptr); - gsi_insert_after (&gsi, x, GSI_CONTINUE_LINKING); - - tmp = build0 (FILTER_EXPR, integer_type_node); - x = gimple_build_assign (tmp, save_filt); - gsi_insert_after (&gsi, x, GSI_CONTINUE_LINKING); - - x = gimple_build_resx (get_eh_region_number (tf->region)); - gsi_insert_after (&gsi, x, GSI_CONTINUE_LINKING); - } - /* Wrap the block with protect_cleanup_actions as the action. */ - if (protect_cleanup_actions) - { - gimple_seq seq = NULL, failure = NULL; - - gimple_seq_add_stmt (&failure, protect_cleanup_actions); - x = gimple_build_eh_filter (NULL, failure); - gimple_eh_filter_set_must_not_throw (x, 1); - - gimple_seq_add_stmt (&seq, x); - x = gimple_build_try (finally, seq, GIMPLE_TRY_CATCH); - finally = lower_eh_filter (outer_state, x); - } - else - lower_eh_constructs_1 (outer_state, finally); - - /* Hook this up to the end of the existing try block. If we - previously fell through the end, we'll have to branch around. - This means adding a new goto, and adding it to the queue. */ - - gsi = gsi_last (gimple_try_eval (tf->top_p)); - - if (tf->may_fallthru) - { - tree tmp; - tmp = lower_try_finally_fallthru_label (tf); - x = gimple_build_goto (tmp); - gsi_insert_after (&gsi, x, GSI_CONTINUE_LINKING); - - if (this_state) - maybe_record_in_goto_queue (this_state, x); - - tf->may_fallthru = false; - } - - x = gimple_build_label (tf->eh_label); - gsi_insert_after (&gsi, x, GSI_CONTINUE_LINKING); - gsi_insert_seq_after (&gsi, finally, GSI_CONTINUE_LINKING); + x = gimple_build_eh_must_not_throw (protect_cleanup_actions); + x = gimple_build_try (finally, gimple_seq_alloc_with_stmt (x), + GIMPLE_TRY_CATCH); + finally = lower_eh_must_not_throw (outer_state, x); + + /* Drop all of this into the exception sequence. */ + emit_post_landing_pad (&eh_seq, tf->region); + gimple_seq_add_seq (&eh_seq, finally); + if (finally_may_fallthru) + emit_resx (&eh_seq, tf->region); /* Having now been handled, EH isn't to be considered with the rest of the outgoing edges. */ @@ -1081,10 +1052,7 @@ lower_try_finally_nofallthru (struct leh_state *state, gimple_seq finally; struct goto_queue_node *q, *qe; - if (tf->may_throw) - lab = tf->eh_label; - else - lab = create_artificial_label (gimple_location (tf->try_finally_expr)); + lab = create_artificial_label (gimple_location (tf->try_finally_expr)); /* We expect that tf->top_p is a GIMPLE_TRY. */ finally = gimple_try_cleanup (tf->top_p); @@ -1106,6 +1074,14 @@ lower_try_finally_nofallthru (struct leh_state *state, lower_eh_constructs_1 (state, finally); gimple_seq_add_seq (&tf->top_p_seq, finally); + + if (tf->may_throw) + { + emit_post_landing_pad (&eh_seq, tf->region); + + x = gimple_build_goto (lab); + gimple_seq_add_stmt (&eh_seq, x); + } } /* A subroutine of lower_try_finally. We have determined that there is @@ -1130,16 +1106,9 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf) { /* Only reachable via the exception edge. Add the given label to the head of the FINALLY block. Append a RESX at the end. */ - - x = gimple_build_label (tf->eh_label); - gimple_seq_add_stmt (&tf->top_p_seq, x); - - gimple_seq_add_seq (&tf->top_p_seq, finally); - - x = gimple_build_resx (get_eh_region_number (tf->region)); - - gimple_seq_add_stmt (&tf->top_p_seq, x); - + emit_post_landing_pad (&eh_seq, tf->region); + gimple_seq_add_seq (&eh_seq, finally); + emit_resx (&eh_seq, tf->region); return; } @@ -1223,15 +1192,13 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf) if (tf->may_throw) { - x = gimple_build_label (tf->eh_label); - gimple_seq_add_stmt (&new_stmt, x); + emit_post_landing_pad (&eh_seq, tf->region); seq = lower_try_finally_dup_block (finally, state); lower_eh_constructs_1 (state, seq); - gimple_seq_add_seq (&new_stmt, seq); + gimple_seq_add_seq (&eh_seq, seq); - x = gimple_build_resx (get_eh_region_number (tf->region)); - gimple_seq_add_stmt (&new_stmt, x); + emit_resx (&eh_seq, tf->region); } if (tf->goto_queue) @@ -1301,7 +1268,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf) else do_goto_redirection (q, lab, NULL, tf); } - + replace_goto_queue (tf); free (labels); } @@ -1375,20 +1342,13 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) if (tf->may_fallthru) { - x = gimple_build_assign (finally_tmp, build_int_cst (integer_type_node, - fallthru_index)); + x = gimple_build_assign (finally_tmp, + build_int_cst (NULL, fallthru_index)); gimple_seq_add_stmt (&tf->top_p_seq, x); - if (tf->may_throw) - { - x = gimple_build_goto (finally_label); - gimple_seq_add_stmt (&tf->top_p_seq, x); - } - - last_case = build3 (CASE_LABEL_EXPR, void_type_node, - build_int_cst (NULL_TREE, fallthru_index), NULL, - create_artificial_label (tf_loc)); + build_int_cst (NULL, fallthru_index), + NULL, create_artificial_label (tf_loc)); VEC_quick_push (tree, case_label_vec, last_case); last_case_index++; @@ -1402,23 +1362,24 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) if (tf->may_throw) { - x = gimple_build_label (tf->eh_label); - gimple_seq_add_stmt (&tf->top_p_seq, x); + emit_post_landing_pad (&eh_seq, tf->region); - x = gimple_build_assign (finally_tmp, build_int_cst (integer_type_node, - eh_index)); - gimple_seq_add_stmt (&tf->top_p_seq, x); + x = gimple_build_assign (finally_tmp, + build_int_cst (NULL, eh_index)); + gimple_seq_add_stmt (&eh_seq, x); + + x = gimple_build_goto (finally_label); + gimple_seq_add_stmt (&eh_seq, x); last_case = build3 (CASE_LABEL_EXPR, void_type_node, - build_int_cst (NULL_TREE, eh_index), NULL, - create_artificial_label (tf_loc)); + build_int_cst (NULL, eh_index), + NULL, create_artificial_label (tf_loc)); VEC_quick_push (tree, case_label_vec, last_case); last_case_index++; x = gimple_build_label (CASE_LABEL (last_case)); - gimple_seq_add_stmt (&switch_body, x); - x = gimple_build_resx (get_eh_region_number (tf->region)); - gimple_seq_add_stmt (&switch_body, x); + gimple_seq_add_stmt (&eh_seq, x); + emit_resx (&eh_seq, tf->region); } x = gimple_build_label (finally_label); @@ -1443,8 +1404,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) if (q->index < 0) { x = gimple_build_assign (finally_tmp, - build_int_cst (integer_type_node, - return_index)); + build_int_cst (NULL, return_index)); gimple_seq_add_stmt (&mod, x); do_return_redirection (q, finally_label, mod, &return_val); switch_id = return_index; @@ -1452,7 +1412,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) else { x = gimple_build_assign (finally_tmp, - build_int_cst (integer_type_node, q->index)); + build_int_cst (NULL, q->index)); gimple_seq_add_stmt (&mod, x); do_goto_redirection (q, finally_label, mod, tf); switch_id = q->index; @@ -1465,11 +1425,11 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) tree case_lab; void **slot; case_lab = build3 (CASE_LABEL_EXPR, void_type_node, - build_int_cst (NULL_TREE, switch_id), NULL, - NULL); + build_int_cst (NULL, switch_id), + NULL, NULL); /* We store the cont_stmt in the pointer map, so that we can recover it in the loop below. We don't create the new label while - walking the goto_queue because pointers don't offer a stable + walking the goto_queue because pointers don't offer a stable order. */ if (!cont_map) cont_map = pointer_map_create (); @@ -1577,7 +1537,6 @@ lower_try_finally (struct leh_state *state, gimple tp) struct leh_tf_state this_tf; struct leh_state this_state; int ndests; - location_t tf_loc = gimple_location (tp); /* Process the try block. */ @@ -1586,12 +1545,12 @@ lower_try_finally (struct leh_state *state, gimple tp) this_tf.top_p = tp; this_tf.outer = state; if (using_eh_for_cleanups_p) - this_tf.region - = gen_eh_region_cleanup (state->cur_region); + this_tf.region = gen_eh_region_cleanup (state->cur_region); else this_tf.region = NULL; this_state.cur_region = this_tf.region; + this_state.ehp_region = state->ehp_region; this_state.tf = &this_tf; lower_eh_constructs_1 (&this_state, gimple_try_eval(tp)); @@ -1601,13 +1560,10 @@ lower_try_finally (struct leh_state *state, gimple tp) /* Determine if any exceptions are possible within the try block. */ if (using_eh_for_cleanups_p) - this_tf.may_throw = get_eh_region_may_contain_throw (this_tf.region); + this_tf.may_throw = bitmap_bit_p (eh_region_may_contain_throw, + this_tf.region->index); if (this_tf.may_throw) - { - this_tf.eh_label = create_artificial_label (tf_loc); - set_eh_region_tree_label (this_tf.region, this_tf.eh_label); - honor_protect_cleanup_actions (state, &this_state, &this_tf); - } + honor_protect_cleanup_actions (state, &this_state, &this_tf); /* Determine how many edges (still) reach the finally block. Or rather, how many destinations are reached by the finally block. Use this to @@ -1663,58 +1619,65 @@ lower_try_finally (struct leh_state *state, gimple tp) static gimple_seq lower_catch (struct leh_state *state, gimple tp) { - struct eh_region_d *try_region; + eh_region try_region; struct leh_state this_state; gimple_stmt_iterator gsi; tree out_label; + gimple_seq new_seq; + gimple x; location_t try_catch_loc = gimple_location (tp); try_region = gen_eh_region_try (state->cur_region); + + this_state = *state; this_state.cur_region = try_region; - this_state.tf = state->tf; lower_eh_constructs_1 (&this_state, gimple_try_eval (tp)); - if (!get_eh_region_may_contain_throw (try_region)) - { - return gimple_try_eval (tp); - } + if (!bitmap_bit_p (eh_region_may_contain_throw, try_region->index)) + return gimple_try_eval (tp); + + new_seq = NULL; + emit_eh_dispatch (&new_seq, try_region); + emit_resx (&new_seq, try_region); + + this_state.cur_region = state->cur_region; + this_state.ehp_region = try_region; out_label = NULL; - for (gsi = gsi_start (gimple_try_cleanup (tp)); !gsi_end_p (gsi); ) + for (gsi = gsi_start (gimple_try_cleanup (tp)); + !gsi_end_p (gsi); + gsi_next (&gsi)) { - struct eh_region_d *catch_region; - tree eh_label; - gimple x, gcatch; + eh_catch c; + gimple gcatch; + gimple_seq handler; gcatch = gsi_stmt (gsi); - catch_region = gen_eh_region_catch (try_region, - gimple_catch_types (gcatch)); + c = gen_eh_region_catch (try_region, gimple_catch_types (gcatch)); - this_state.cur_region = catch_region; - lower_eh_constructs_1 (&this_state, gimple_catch_handler (gcatch)); + handler = gimple_catch_handler (gcatch); + lower_eh_constructs_1 (&this_state, handler); - eh_label = create_artificial_label (try_catch_loc); - set_eh_region_tree_label (catch_region, eh_label); + c->label = create_artificial_label (UNKNOWN_LOCATION); + x = gimple_build_label (c->label); + gimple_seq_add_stmt (&new_seq, x); - x = gimple_build_label (eh_label); - gsi_insert_before (&gsi, x, GSI_SAME_STMT); + gimple_seq_add_seq (&new_seq, handler); - if (gimple_seq_may_fallthru (gimple_catch_handler (gcatch))) + if (gimple_seq_may_fallthru (new_seq)) { if (!out_label) out_label = create_artificial_label (try_catch_loc); x = gimple_build_goto (out_label); - gimple_seq_add_stmt (gimple_catch_handler_ptr (gcatch), x); + gimple_seq_add_stmt (&new_seq, x); } - - gsi_insert_seq_before (&gsi, gimple_catch_handler (gcatch), - GSI_SAME_STMT); - gsi_remove (&gsi, false); } - return frob_into_branch_around (tp, NULL, out_label); + gimple_try_set_cleanup (tp, new_seq); + + return frob_into_branch_around (tp, try_region, out_label); } /* A subroutine of lower_eh_constructs_1. Lower a GIMPLE_TRY with a @@ -1725,34 +1688,70 @@ static gimple_seq lower_eh_filter (struct leh_state *state, gimple tp) { struct leh_state this_state; - struct eh_region_d *this_region; - gimple inner; - tree eh_label; + eh_region this_region; + gimple inner, x; + gimple_seq new_seq; inner = gimple_seq_first_stmt (gimple_try_cleanup (tp)); - if (gimple_eh_filter_must_not_throw (inner)) - this_region = gen_eh_region_must_not_throw (state->cur_region); - else - this_region = gen_eh_region_allowed (state->cur_region, - gimple_eh_filter_types (inner)); + this_region = gen_eh_region_allowed (state->cur_region, + gimple_eh_filter_types (inner)); this_state = *state; this_state.cur_region = this_region; lower_eh_constructs_1 (&this_state, gimple_try_eval (tp)); - if (!get_eh_region_may_contain_throw (this_region)) - { - return gimple_try_eval (tp); - } + if (!bitmap_bit_p (eh_region_may_contain_throw, this_region->index)) + return gimple_try_eval (tp); + + new_seq = NULL; + this_state.cur_region = state->cur_region; + this_state.ehp_region = this_region; + + emit_eh_dispatch (&new_seq, this_region); + emit_resx (&new_seq, this_region); + + this_region->u.allowed.label = create_artificial_label (UNKNOWN_LOCATION); + x = gimple_build_label (this_region->u.allowed.label); + gimple_seq_add_stmt (&new_seq, x); + + lower_eh_constructs_1 (&this_state, gimple_eh_filter_failure (inner)); + gimple_seq_add_seq (&new_seq, gimple_eh_filter_failure (inner)); + + gimple_try_set_cleanup (tp, new_seq); + + return frob_into_branch_around (tp, this_region, NULL); +} - lower_eh_constructs_1 (state, gimple_eh_filter_failure (inner)); - gimple_try_set_cleanup (tp, gimple_eh_filter_failure (inner)); +/* A subroutine of lower_eh_constructs_1. Lower a GIMPLE_TRY with + an GIMPLE_EH_MUST_NOT_THROW to a sequence of labels and blocks, + plus the exception region trees that record all the magic. */ - eh_label = create_artificial_label (gimple_location (inner)); - set_eh_region_tree_label (this_region, eh_label); +static gimple_seq +lower_eh_must_not_throw (struct leh_state *state, gimple tp) +{ + struct leh_state this_state; + eh_region this_region; + gimple inner; + + inner = gimple_seq_first_stmt (gimple_try_cleanup (tp)); + + this_region = gen_eh_region_must_not_throw (state->cur_region); + this_region->u.must_not_throw.failure_decl + = gimple_eh_must_not_throw_fndecl (inner); + this_region->u.must_not_throw.failure_loc = gimple_location (tp); - return frob_into_branch_around (tp, eh_label, NULL); + /* In order to get mangling applied to this decl, we must mark it + used now. Otherwise, pass_ipa_free_lang_data won't think it + needs to happen. */ + TREE_USED (this_region->u.must_not_throw.failure_decl) = 1; + + this_state = *state; + this_state.cur_region = this_region; + + lower_eh_constructs_1 (&this_state, gimple_try_eval (tp)); + + return gimple_try_eval (tp); } /* Implement a cleanup expression. This is similar to try-finally, @@ -1762,7 +1761,7 @@ static gimple_seq lower_cleanup (struct leh_state *state, gimple tp) { struct leh_state this_state; - struct eh_region_d *this_region; + eh_region this_region; struct leh_tf_state fake_tf; gimple_seq result; @@ -1780,10 +1779,8 @@ lower_cleanup (struct leh_state *state, gimple tp) lower_eh_constructs_1 (&this_state, gimple_try_eval (tp)); - if (!get_eh_region_may_contain_throw (this_region)) - { - return gimple_try_eval (tp); - } + if (!bitmap_bit_p (eh_region_may_contain_throw, this_region->index)) + return gimple_try_eval (tp); /* Build enough of a try-finally state so that we can reuse honor_protect_cleanup_actions. */ @@ -1794,9 +1791,6 @@ lower_cleanup (struct leh_state *state, gimple tp) fake_tf.may_fallthru = gimple_seq_may_fallthru (gimple_try_eval (tp)); fake_tf.may_throw = true; - fake_tf.eh_label = create_artificial_label (gimple_location (tp)); - set_eh_region_tree_label (this_region, fake_tf.eh_label); - honor_protect_cleanup_actions (state, NULL, &fake_tf); if (fake_tf.may_throw) @@ -1804,8 +1798,8 @@ lower_cleanup (struct leh_state *state, gimple tp) /* In this case honor_protect_cleanup_actions had nothing to do, and we should process this normally. */ lower_eh_constructs_1 (state, gimple_try_cleanup (tp)); - result = frob_into_branch_around (tp, fake_tf.eh_label, - fake_tf.fallthru_label); + result = frob_into_branch_around (tp, this_region, + fake_tf.fallthru_label); } else { @@ -1822,9 +1816,7 @@ lower_cleanup (struct leh_state *state, gimple tp) return result; } - - -/* Main loop for lowering eh constructs. Also moves gsi to the next +/* Main loop for lowering eh constructs. Also moves gsi to the next statement. */ static void @@ -1837,6 +1829,52 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) switch (gimple_code (stmt)) { case GIMPLE_CALL: + { + tree fndecl = gimple_call_fndecl (stmt); + tree rhs, lhs; + + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_POINTER: + /* The front end may have generated a call to + __builtin_eh_pointer (0) within a catch region. Replace + this zero argument with the current catch region number. */ + if (state->ehp_region) + { + tree nr = build_int_cst (NULL, state->ehp_region->index); + gimple_call_set_arg (stmt, 0, nr); + } + else + { + /* The user has dome something silly. Remove it. */ + rhs = build_int_cst (ptr_type_node, 0); + goto do_replace; + } + break; + + case BUILT_IN_EH_FILTER: + /* ??? This should never appear, but since it's a builtin it + is accessible to abuse by users. Just remove it and + replace the use with the arbitrary value zero. */ + rhs = build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + do_replace: + lhs = gimple_call_lhs (stmt); + x = gimple_build_assign (lhs, rhs); + gsi_insert_before (gsi, x, GSI_SAME_STMT); + /* FALLTHRU */ + + case BUILT_IN_EH_COPY_VALUES: + /* Likewise this should not appear. Remove it. */ + gsi_remove (gsi, true); + return; + + default: + break; + } + } + /* FALLTHRU */ + case GIMPLE_ASSIGN: /* If the stmt can throw use a new temporary for the assignment to a LHS. This makes sure the old value of the LHS is @@ -1889,6 +1927,9 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) case GIMPLE_EH_FILTER: replace = lower_eh_filter (state, stmt); break; + case GIMPLE_EH_MUST_NOT_THROW: + replace = lower_eh_must_not_throw (state, stmt); + break; default: replace = lower_cleanup (state, stmt); break; @@ -1926,19 +1967,32 @@ static unsigned int lower_eh_constructs (void) { struct leh_state null_state; + gimple_seq bodyp; - gimple_seq bodyp = gimple_body (current_function_decl); + bodyp = gimple_body (current_function_decl); + if (bodyp == NULL) + return 0; finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free); + eh_region_may_contain_throw = BITMAP_ALLOC (NULL); + memset (&null_state, 0, sizeof (null_state)); collect_finally_tree_1 (bodyp, NULL); - - memset (&null_state, 0, sizeof (null_state)); lower_eh_constructs_1 (&null_state, bodyp); - htab_delete (finally_tree); + /* We assume there's a return statement, or something, at the end of + the function, and thus ploping the EH sequence afterward won't + change anything. */ + gcc_assert (!gimple_seq_may_fallthru (bodyp)); + gimple_seq_add_seq (&bodyp, eh_seq); + + /* We assume that since BODYP already existed, adding EH_SEQ to it + didn't change its value, and we don't have to re-set the function. */ + gcc_assert (bodyp == gimple_body (current_function_decl)); - collect_eh_region_array (); + htab_delete (finally_tree); + BITMAP_FREE (eh_region_may_contain_throw); + eh_seq = NULL; /* If this function needs a language specific EH personality routine and the frontend didn't already set one do so now. */ @@ -1968,222 +2022,206 @@ struct gimple_opt_pass pass_lower_eh = TODO_dump_func /* todo_flags_finish */ } }; - -/* Construct EH edges for STMT. */ +/* Create the multiple edges from an EH_DISPATCH statement to all of + the possible handlers for its EH region. Return true if there's + no fallthru edge; false if there is. */ -static void -make_eh_edge (struct eh_region_d *region, void *data) +bool +make_eh_dispatch_edges (gimple stmt) { - gimple stmt; - tree lab; + eh_region r; + eh_catch c; basic_block src, dst; - stmt = (gimple) data; - lab = get_eh_region_tree_label (region); - + r = get_eh_region_from_number (gimple_eh_dispatch_region (stmt)); src = gimple_bb (stmt); - dst = label_to_block (lab); - make_edge (src, dst, EDGE_EH); -} - -/* See if STMT is call that might be inlined. */ + switch (r->type) + { + case ERT_TRY: + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + dst = label_to_block (c->label); + make_edge (src, dst, 0); -static bool -inlinable_call_p (gimple stmt) -{ - tree decl; - if (gimple_code (stmt) != GIMPLE_CALL) - return false; - if (cfun->after_inlining) - return false; - /* Indirect calls can be propagated to direct call - and inlined. */ - decl = gimple_call_fndecl (stmt); - if (!decl) - return true; - if (cgraph_function_flags_ready - && cgraph_function_body_availability (cgraph_node (decl)) - < AVAIL_OVERWRITABLE) - return false; - return !DECL_UNINLINABLE (decl); -} + /* A catch-all handler doesn't have a fallthru. */ + if (c->type_list == NULL) + return false; + } + break; -void -make_eh_edges (gimple stmt) -{ - int region_nr; - bool is_resx; - bool inlinable = false; - basic_block bb; + case ERT_ALLOWED_EXCEPTIONS: + dst = label_to_block (r->u.allowed.label); + make_edge (src, dst, 0); + break; - if (gimple_code (stmt) == GIMPLE_RESX) - { - region_nr = gimple_resx_region (stmt); - is_resx = true; - } - else - { - region_nr = lookup_stmt_eh_region (stmt); - if (region_nr < 0) - return; - is_resx = false; - inlinable = inlinable_call_p (stmt); + default: + gcc_unreachable (); } - foreach_reachable_handler (region_nr, is_resx, inlinable, make_eh_edge, stmt); - - /* Make CFG profile more consistent assuming that exception will resume to first - available EH handler. In practice this makes little difference, but we get - fewer consistency errors in the dumps. */ - bb = gimple_bb (stmt); - if (is_resx && EDGE_COUNT (bb->succs)) - EDGE_SUCC (bb, 0)->probability = REG_BR_PROB_BASE; + return true; } -/* Redirect EH edge E to NEW_BB. */ +/* Create the single EH edge from STMT to its nearest landing pad, + if there is such a landing pad within the current function. */ -edge -redirect_eh_edge (edge e, basic_block new_bb) +void +make_eh_edges (gimple stmt) { - gimple stmt = gsi_stmt (gsi_last_bb (e->src)); - int region_nr, new_region_nr; - bool is_resx; - bool inlinable = false; - tree label = gimple_block_label (new_bb); - struct eh_region_d *r; + basic_block src, dst; + eh_landing_pad lp; + int lp_nr; - if (gimple_code (stmt) == GIMPLE_RESX) - { - region_nr = gimple_resx_region (stmt); - is_resx = true; - } - else - { - region_nr = lookup_stmt_eh_region (stmt); - gcc_assert (region_nr >= 0); - is_resx = false; - inlinable = inlinable_call_p (stmt); - } + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr <= 0) + return; - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Redirecting EH edge %i->%i to %i, region %i, resx %i\n", - e->src->index, e->dest->index, new_bb->index, region_nr, is_resx); - r = redirect_eh_edge_to_label (e, label, is_resx, inlinable, region_nr); - new_region_nr = get_eh_region_number (r); - if (new_region_nr != region_nr) - { - if (is_resx) - gimple_resx_set_region (stmt, new_region_nr); - else - { - remove_stmt_from_eh_region (stmt); - add_stmt_to_eh_region (stmt, new_region_nr); - } - } - e = ssa_redirect_edge (e, new_bb); - return e; + lp = get_eh_landing_pad_from_number (lp_nr); + gcc_assert (lp != NULL); + + src = gimple_bb (stmt); + dst = label_to_block (lp->post_landing_pad); + make_edge (src, dst, EDGE_EH); } -static bool mark_eh_edge_found_error; +/* Do the work in redirecting EDGE_IN to NEW_BB within the EH region tree; + do not actually perform the final edge redirection. -/* Mark edge make_eh_edge would create for given region by setting it aux - field, output error if something goes wrong. */ + CHANGE_REGION is true when we're being called from cleanup_empty_eh and + we intend to change the destination EH region as well; this means + EH_LANDING_PAD_NR must already be set on the destination block label. + If false, we're being called from generic cfg manipulation code and we + should preserve our place within the region tree. */ static void -mark_eh_edge (struct eh_region_d *region, void *data) +redirect_eh_edge_1 (edge edge_in, basic_block new_bb, bool change_region) { - gimple stmt; - tree lab; - basic_block src, dst; + eh_landing_pad old_lp, new_lp; + basic_block old_bb; + gimple throw_stmt; + int old_lp_nr, new_lp_nr; + tree old_label, new_label; + edge_iterator ei; edge e; - stmt = (gimple) data; - lab = get_eh_region_tree_label (region); + old_bb = edge_in->dest; + old_label = gimple_block_label (old_bb); + old_lp_nr = EH_LANDING_PAD_NR (old_label); + gcc_assert (old_lp_nr > 0); + old_lp = get_eh_landing_pad_from_number (old_lp_nr); - src = gimple_bb (stmt); - dst = label_to_block (lab); + throw_stmt = last_stmt (edge_in->src); + gcc_assert (lookup_stmt_eh_lp (throw_stmt) == old_lp_nr); - e = find_edge (src, dst); - if (!e) - { - error ("EH edge %i->%i is missing", src->index, dst->index); - mark_eh_edge_found_error = true; - } - else if (!(e->flags & EDGE_EH)) + new_label = gimple_block_label (new_bb); + + /* Look for an existing region that might be using NEW_BB already. */ + new_lp_nr = EH_LANDING_PAD_NR (new_label); + if (new_lp_nr) { - error ("EH edge %i->%i miss EH flag", src->index, dst->index); - mark_eh_edge_found_error = true; + new_lp = get_eh_landing_pad_from_number (new_lp_nr); + gcc_assert (new_lp); + + /* Unless CHANGE_REGION is true, the new and old landing pad + had better be associated with the same EH region. */ + gcc_assert (change_region || new_lp->region == old_lp->region); } - else if (e->aux) + else { - /* ??? might not be mistake. */ - error ("EH edge %i->%i has duplicated regions", src->index, dst->index); - mark_eh_edge_found_error = true; + new_lp = NULL; + gcc_assert (!change_region); } - else - e->aux = (void *)1; -} - -/* Verify that BB containing STMT as the last statement, has precisely the - edges that make_eh_edges would create. */ -bool -verify_eh_edges (gimple stmt) -{ - int region_nr; - bool is_resx; - basic_block bb = gimple_bb (stmt); - edge_iterator ei; - edge e; - bool inlinable = false; + /* Notice when we redirect the last EH edge away from OLD_BB. */ + FOR_EACH_EDGE (e, ei, old_bb->preds) + if (e != edge_in && (e->flags & EDGE_EH)) + break; - FOR_EACH_EDGE (e, ei, bb->succs) - gcc_assert (!e->aux); - mark_eh_edge_found_error = false; - if (gimple_code (stmt) == GIMPLE_RESX) + if (new_lp) { - region_nr = gimple_resx_region (stmt); - is_resx = true; + /* NEW_LP already exists. If there are still edges into OLD_LP, + there's nothing to do with the EH tree. If there are no more + edges into OLD_LP, then we want to remove OLD_LP as it is unused. + If CHANGE_REGION is true, then our caller is expecting to remove + the landing pad. */ + if (e == NULL && !change_region) + remove_eh_landing_pad (old_lp); } else { - region_nr = lookup_stmt_eh_region (stmt); - if (region_nr < 0) - { - FOR_EACH_EDGE (e, ei, bb->succs) - if (e->flags & EDGE_EH) - { - error ("BB %i can not throw but has EH edges", bb->index); - return true; - } - return false; - } - if (!stmt_could_throw_p (stmt)) + /* No correct landing pad exists. If there are no more edges + into OLD_LP, then we can simply re-use the existing landing pad. + Otherwise, we have to create a new landing pad. */ + if (e == NULL) { - error ("BB %i last statement has incorrectly set region", bb->index); - return true; + EH_LANDING_PAD_NR (old_lp->post_landing_pad) = 0; + new_lp = old_lp; } - inlinable = inlinable_call_p (stmt); - is_resx = false; + else + new_lp = gen_eh_landing_pad (old_lp->region); + new_lp->post_landing_pad = new_label; + EH_LANDING_PAD_NR (new_label) = new_lp->index; } - foreach_reachable_handler (region_nr, is_resx, inlinable, mark_eh_edge, stmt); - FOR_EACH_EDGE (e, ei, bb->succs) + /* Maybe move the throwing statement to the new region. */ + if (old_lp != new_lp) + { + remove_stmt_from_eh_lp (throw_stmt); + add_stmt_to_eh_lp (throw_stmt, new_lp->index); + } +} + +/* Redirect EH edge E to NEW_BB. */ + +edge +redirect_eh_edge (edge edge_in, basic_block new_bb) +{ + redirect_eh_edge_1 (edge_in, new_bb, false); + return ssa_redirect_edge (edge_in, new_bb); +} + +/* This is a subroutine of gimple_redirect_edge_and_branch. Update the + labels for redirecting a non-fallthru EH_DISPATCH edge E to NEW_BB. + The actual edge update will happen in the caller. */ + +void +redirect_eh_dispatch_edge (gimple stmt, edge e, basic_block new_bb) +{ + tree new_lab = gimple_block_label (new_bb); + bool any_changed = false; + basic_block old_bb; + eh_region r; + eh_catch c; + + r = get_eh_region_from_number (gimple_eh_dispatch_region (stmt)); + switch (r->type) { - if ((e->flags & EDGE_EH) && !e->aux) + case ERT_TRY: + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) { - error ("unnecessary EH edge %i->%i", bb->index, e->dest->index); - mark_eh_edge_found_error = true; - return true; + old_bb = label_to_block (c->label); + if (old_bb == e->dest) + { + c->label = new_lab; + any_changed = true; + } } - e->aux = NULL; + break; + + case ERT_ALLOWED_EXCEPTIONS: + old_bb = label_to_block (r->u.allowed.label); + gcc_assert (old_bb == e->dest); + r->u.allowed.label = new_lab; + any_changed = true; + break; + + default: + gcc_unreachable (); } - return mark_eh_edge_found_error; + gcc_assert (any_changed); } - /* Helper function for operation_could_trap_p and stmt_could_throw_p. */ @@ -2307,7 +2345,7 @@ tree_could_trap_p (tree expr) if (!expr) return false; - + code = TREE_CODE (expr); t = TREE_TYPE (expr); @@ -2349,20 +2387,16 @@ tree_could_trap_p (tree expr) base = TREE_OPERAND (expr, 0); if (tree_could_trap_p (base)) return true; - if (TREE_THIS_NOTRAP (expr)) return false; - return !range_in_array_bounds_p (expr); case ARRAY_REF: base = TREE_OPERAND (expr, 0); if (tree_could_trap_p (base)) return true; - if (TREE_THIS_NOTRAP (expr)) return false; - return !in_array_bounds_p (expr); case INDIRECT_REF: @@ -2373,7 +2407,6 @@ tree_could_trap_p (tree expr) case ASM_EXPR: return TREE_THIS_VOLATILE (expr); - case CALL_EXPR: t = get_callee_fndecl (expr); /* Assume that calls to weak functions may trap. */ @@ -2440,35 +2473,33 @@ stmt_could_throw_1_p (gimple stmt) bool stmt_could_throw_p (gimple stmt) { - enum gimple_code code; - if (!flag_exceptions) return false; /* The only statements that can throw an exception are assignments, - conditionals, calls and asms. */ - code = gimple_code (stmt); - if (code != GIMPLE_ASSIGN - && code != GIMPLE_COND - && code != GIMPLE_CALL - && code != GIMPLE_ASM) - return false; + conditionals, calls, resx, and asms. */ + switch (gimple_code (stmt)) + { + case GIMPLE_RESX: + return true; - /* If exceptions can only be thrown by function calls and STMT is not a - GIMPLE_CALL, the statement cannot throw. */ - if (!flag_non_call_exceptions && code != GIMPLE_CALL) - return false; + case GIMPLE_CALL: + return !gimple_call_nothrow_p (stmt); - if (code == GIMPLE_ASSIGN || code == GIMPLE_COND) - return stmt_could_throw_1_p (stmt); - else if (is_gimple_call (stmt)) - return (gimple_call_flags (stmt) & ECF_NOTHROW) == 0; - else if (gimple_code (stmt) == GIMPLE_ASM) - return (gimple_asm_volatile_p (stmt)); - else - gcc_unreachable (); + case GIMPLE_ASSIGN: + case GIMPLE_COND: + if (!flag_non_call_exceptions) + return false; + return stmt_could_throw_1_p (stmt); - return false; + case GIMPLE_ASM: + if (!flag_non_call_exceptions) + return false; + return gimple_asm_volatile_p (stmt); + + default: + return false; + } } @@ -2482,8 +2513,8 @@ tree_could_throw_p (tree t) if (TREE_CODE (t) == MODIFY_EXPR) { if (flag_non_call_exceptions - && tree_could_trap_p (TREE_OPERAND (t, 0))) - return true; + && tree_could_trap_p (TREE_OPERAND (t, 0))) + return true; t = TREE_OPERAND (t, 1); } @@ -2502,25 +2533,13 @@ tree_could_throw_p (tree t) bool stmt_can_throw_external (gimple stmt) { - int region_nr; - bool is_resx = false; - bool inlinable_call = false; + int lp_nr; if (!stmt_could_throw_p (stmt)) return false; - if (gimple_code (stmt) == GIMPLE_RESX) - { - region_nr = gimple_resx_region (stmt); - is_resx = true; - } - else - region_nr = lookup_stmt_eh_region (stmt); - - if (region_nr < 0) - return true; - - return can_throw_external_1 (region_nr, is_resx, inlinable_call); + lp_nr = lookup_stmt_eh_lp (stmt); + return lp_nr == 0; } /* Return true if STMT can throw an exception that is caught within @@ -2529,49 +2548,56 @@ stmt_can_throw_external (gimple stmt) bool stmt_can_throw_internal (gimple stmt) { - int region_nr; - bool is_resx = false; - bool inlinable_call = false; + int lp_nr; - if (gimple_code (stmt) == GIMPLE_RESX) - { - region_nr = gimple_resx_region (stmt); - is_resx = true; - } - else - { - region_nr = lookup_stmt_eh_region (stmt); - inlinable_call = inlinable_call_p (stmt); - } - - if (region_nr < 0) + if (!stmt_could_throw_p (stmt)) return false; - return can_throw_internal_1 (region_nr, is_resx, inlinable_call); + lp_nr = lookup_stmt_eh_lp (stmt); + return lp_nr > 0; +} + +/* Given a statement STMT in IFUN, if STMT can no longer throw, then + remove any entry it might have from the EH table. Return true if + any change was made. */ + +bool +maybe_clean_eh_stmt_fn (struct function *ifun, gimple stmt) +{ + if (stmt_could_throw_p (stmt)) + return false; + return remove_stmt_from_eh_lp_fn (ifun, stmt); } +/* Likewise, but always use the current function. */ + +bool +maybe_clean_eh_stmt (gimple stmt) +{ + return maybe_clean_eh_stmt_fn (cfun, stmt); +} /* Given a statement OLD_STMT and a new statement NEW_STMT that has replaced OLD_STMT in the function, remove OLD_STMT from the EH table and put NEW_STMT in the table if it should be in there. Return TRUE if a replacement was done that my require an EH edge purge. */ -bool -maybe_clean_or_replace_eh_stmt (gimple old_stmt, gimple new_stmt) +bool +maybe_clean_or_replace_eh_stmt (gimple old_stmt, gimple new_stmt) { - int region_nr = lookup_stmt_eh_region (old_stmt); + int lp_nr = lookup_stmt_eh_lp (old_stmt); - if (region_nr >= 0) + if (lp_nr != 0) { bool new_stmt_could_throw = stmt_could_throw_p (new_stmt); if (new_stmt == old_stmt && new_stmt_could_throw) return false; - remove_stmt_from_eh_region (old_stmt); + remove_stmt_from_eh_lp (old_stmt); if (new_stmt_could_throw) { - add_stmt_to_eh_region (new_stmt, region_nr); + add_stmt_to_eh_lp (new_stmt, lp_nr); return false; } else @@ -2580,6 +2606,70 @@ maybe_clean_or_replace_eh_stmt (gimple old_stmt, gimple new_stmt) return false; } + +/* Given a statement OLD_STMT in OLD_FUN and a duplicate statment NEW_STMT + in NEW_FUN, copy the EH table data from OLD_STMT to NEW_STMT. The MAP + operand is the return value of duplicate_eh_regions. */ + +bool +maybe_duplicate_eh_stmt_fn (struct function *new_fun, gimple new_stmt, + struct function *old_fun, gimple old_stmt, + struct pointer_map_t *map, int default_lp_nr) +{ + int old_lp_nr, new_lp_nr; + void **slot; + + if (!stmt_could_throw_p (new_stmt)) + return false; + + old_lp_nr = lookup_stmt_eh_lp_fn (old_fun, old_stmt); + if (old_lp_nr == 0) + { + if (default_lp_nr == 0) + return false; + new_lp_nr = default_lp_nr; + } + else if (old_lp_nr > 0) + { + eh_landing_pad old_lp, new_lp; + + old_lp = VEC_index (eh_landing_pad, old_fun->eh->lp_array, old_lp_nr); + slot = pointer_map_contains (map, old_lp); + new_lp = (eh_landing_pad) *slot; + new_lp_nr = new_lp->index; + } + else + { + eh_region old_r, new_r; + + old_r = VEC_index (eh_region, old_fun->eh->region_array, -old_lp_nr); + slot = pointer_map_contains (map, old_r); + new_r = (eh_region) *slot; + new_lp_nr = -new_r->index; + } + + add_stmt_to_eh_lp_fn (new_fun, new_stmt, new_lp_nr); + return true; +} + +/* Similar, but both OLD_STMT and NEW_STMT are within the current function, + and thus no remapping is required. */ + +bool +maybe_duplicate_eh_stmt (gimple new_stmt, gimple old_stmt) +{ + int lp_nr; + + if (!stmt_could_throw_p (new_stmt)) + return false; + + lp_nr = lookup_stmt_eh_lp (old_stmt); + if (lp_nr == 0) + return false; + + add_stmt_to_eh_lp (new_stmt, lp_nr); + return true; +} /* Returns TRUE if oneh and twoh are exception handlers (gimple_try_cleanup of GIMPLE_TRY) that are similar enough to be considered the same. Currently @@ -2616,7 +2706,7 @@ same_handler_p (gimple_seq oneh, gimple_seq twoh) for (ai = 0; ai < gimple_call_num_args (ones); ++ai) if (!operand_equal_p (gimple_call_arg (ones, ai), - gimple_call_arg (twos, ai), 0)) + gimple_call_arg (twos, ai), 0)) return false; return true; @@ -2715,12 +2805,18 @@ refactor_eh (void) return 0; } +static bool +gate_refactor_eh (void) +{ + return flag_exceptions != 0; +} + struct gimple_opt_pass pass_refactor_eh = { { GIMPLE_PASS, "ehopt", /* name */ - NULL, /* gate */ + gate_refactor_eh, /* gate */ refactor_eh, /* execute */ NULL, /* sub */ NULL, /* next */ @@ -2733,512 +2829,919 @@ struct gimple_opt_pass pass_refactor_eh = TODO_dump_func /* todo_flags_finish */ } }; + +/* At the end of gimple optimization, we can lower RESX. */ -/* Walk statements, see what regions are really references and remove unreachable ones. */ +static bool +lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map) +{ + int lp_nr; + eh_region src_r, dst_r; + gimple_stmt_iterator gsi; + gimple x; + tree fn, src_nr; + bool ret = false; -static void -tree_remove_unreachable_handlers (void) + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr != 0) + dst_r = get_eh_region_from_lp_number (lp_nr); + else + dst_r = NULL; + + src_r = get_eh_region_from_number (gimple_resx_region (stmt)); + src_nr = build_int_cst (NULL, src_r->index); + gsi = gsi_last_bb (bb); + + if (dst_r) + { + /* When we have a destination region, we resolve this by copying + the excptr and filter values into place, and changing the edge + to immediately after the landing pad. */ + edge e; + + if (lp_nr < 0) + { + basic_block new_bb; + void **slot; + tree lab; + + /* We are resuming into a MUST_NOT_CALL region. Expand a call to + the failure decl into a new block, if needed. */ + gcc_assert (dst_r->type == ERT_MUST_NOT_THROW); + + slot = pointer_map_contains (mnt_map, dst_r); + if (slot == NULL) + { + gimple_stmt_iterator gsi2; + + new_bb = create_empty_bb (bb); + lab = gimple_block_label (new_bb); + gsi2 = gsi_start_bb (new_bb); + + fn = dst_r->u.must_not_throw.failure_decl; + x = gimple_build_call (fn, 0); + gimple_set_location (x, dst_r->u.must_not_throw.failure_loc); + gsi_insert_after (&gsi2, x, GSI_CONTINUE_LINKING); + + slot = pointer_map_insert (mnt_map, dst_r); + *slot = lab; + } + else + { + lab = (tree) *slot; + new_bb = label_to_block (lab); + } + + gcc_assert (EDGE_COUNT (bb->succs) == 0); + e = make_edge (bb, new_bb, EDGE_FALLTHRU); + e->count = bb->count; + e->probability = REG_BR_PROB_BASE; + } + else + { + edge_iterator ei; + tree dst_nr = build_int_cst (NULL, dst_r->index); + + fn = implicit_built_in_decls[BUILT_IN_EH_COPY_VALUES]; + x = gimple_build_call (fn, 2, dst_nr, src_nr); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + /* Update the flags for the outgoing edge. */ + e = single_succ_edge (bb); + gcc_assert (e->flags & EDGE_EH); + e->flags = (e->flags & ~EDGE_EH) | EDGE_FALLTHRU; + + /* If there are no more EH users of the landing pad, delete it. */ + FOR_EACH_EDGE (e, ei, e->dest->preds) + if (e->flags & EDGE_EH) + break; + if (e == NULL) + { + eh_landing_pad lp = get_eh_landing_pad_from_number (lp_nr); + remove_eh_landing_pad (lp); + } + } + + ret = true; + } + else + { + tree var; + + /* When we don't have a destination region, this exception escapes + up the call chain. We resolve this by generating a call to the + _Unwind_Resume library function. */ + + /* ??? The ARM EABI redefines _Unwind_Resume as __cxa_end_cleanup + with no arguments for C++ and Java. Check for that. */ + switch (targetm.arm_eabi_unwinder) + { + default: + fn = implicit_built_in_decls[BUILT_IN_UNWIND_RESUME]; + if (TYPE_ARG_TYPES (TREE_TYPE (fn)) == void_list_node) + { + x = gimple_build_call (fn, 0); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + break; + } + /* FALLTHRU */ + + case 0: + fn = implicit_built_in_decls[BUILT_IN_EH_POINTER]; + x = gimple_build_call (fn, 1, src_nr); + var = create_tmp_var (ptr_type_node, NULL); + var = make_ssa_name (var, x); + gimple_call_set_lhs (x, var); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + fn = implicit_built_in_decls[BUILT_IN_UNWIND_RESUME]; + x = gimple_build_call (fn, 1, var); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + break; + } + + gcc_assert (EDGE_COUNT (bb->succs) == 0); + } + + gsi_remove (&gsi, true); + + return ret; +} + +static unsigned +execute_lower_resx (void) { - sbitmap reachable, contains_stmt; - VEC(int,heap) * label_to_region; basic_block bb; + struct pointer_map_t *mnt_map; + bool dominance_invalidated = false; + bool any_rewritten = false; - label_to_region = label_to_region_map (); - reachable = sbitmap_alloc (num_eh_regions ()); - sbitmap_zero (reachable); - contains_stmt = sbitmap_alloc (num_eh_regions ()); - sbitmap_zero (contains_stmt); + mnt_map = pointer_map_create (); FOR_EACH_BB (bb) - { - gimple_stmt_iterator gsi; - int region; - bool has_eh_preds = bb_has_eh_pred (bb); + { + gimple last = last_stmt (bb); + if (last && is_gimple_resx (last)) + { + dominance_invalidated |= lower_resx (bb, last, mnt_map); + any_rewritten = true; + } + } + + pointer_map_destroy (mnt_map); + + if (dominance_invalidated) + { + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + } + + return any_rewritten ? TODO_update_ssa_only_virtuals : 0; +} + +static bool +gate_lower_ehcontrol (void) +{ + return cfun->eh->region_tree != NULL; +} + +struct gimple_opt_pass pass_lower_resx = +{ + { + GIMPLE_PASS, + "resx", /* name */ + gate_lower_ehcontrol, /* gate */ + execute_lower_resx, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_flow /* todo_flags_finish */ + } +}; + + +/* At the end of inlining, we can lower EH_DISPATCH. */ + +static void +lower_eh_dispatch (basic_block src, gimple stmt) +{ + gimple_stmt_iterator gsi; + int region_nr; + eh_region r; + tree filter, fn; + gimple x; - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + region_nr = gimple_eh_dispatch_region (stmt); + r = get_eh_region_from_number (region_nr); + + gsi = gsi_last_bb (src); + + switch (r->type) + { + case ERT_TRY: { - gimple stmt = gsi_stmt (gsi); + VEC (tree, heap) *labels = NULL; + tree default_label = NULL; + eh_catch c; + edge_iterator ei; + edge e; + + /* Collect the labels for a switch. Zero the post_landing_pad + field becase we'll no longer have anything keeping these labels + in existance and the optimizer will be free to merge these + blocks at will. */ + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + tree tp_node, flt_node, lab = c->label; + + c->label = NULL; + tp_node = c->type_list; + flt_node = c->filter_list; - if (gimple_code (stmt) == GIMPLE_LABEL && has_eh_preds) + if (tp_node == NULL) + { + default_label = lab; + break; + } + do + { + tree t = build3 (CASE_LABEL_EXPR, void_type_node, + TREE_VALUE (flt_node), NULL, lab); + VEC_safe_push (tree, heap, labels, t); + + tp_node = TREE_CHAIN (tp_node); + flt_node = TREE_CHAIN (flt_node); + } + while (tp_node); + } + + /* Clean up the edge flags. */ + FOR_EACH_EDGE (e, ei, src->succs) { - int uid = LABEL_DECL_UID (gimple_label_label (stmt)); - int region; + if (e->flags & EDGE_FALLTHRU) + { + /* If there was no catch-all, use the fallthru edge. */ + if (default_label == NULL) + default_label = gimple_block_label (e->dest); + e->flags &= ~EDGE_FALLTHRU; + } + } + gcc_assert (default_label != NULL); - for (region = VEC_index (int, label_to_region, uid); - region; region = get_next_region_sharing_label (region)) - SET_BIT (reachable, region); + /* Don't generate a switch if there's only a default case. + This is common in the form of try { A; } catch (...) { B; }. */ + if (labels == NULL) + { + e = single_succ_edge (src); + e->flags |= EDGE_FALLTHRU; } - if (gimple_code (stmt) == GIMPLE_RESX) - SET_BIT (reachable, - VEC_index (eh_region, cfun->eh->region_array, - gimple_resx_region (stmt))->region_number); - if ((region = lookup_stmt_eh_region (stmt)) >= 0) - SET_BIT (contains_stmt, region); + else + { + fn = implicit_built_in_decls[BUILT_IN_EH_FILTER]; + x = gimple_build_call (fn, 1, build_int_cst (NULL, region_nr)); + filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL); + filter = make_ssa_name (filter, x); + gimple_call_set_lhs (x, filter); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + /* Turn the default label into a default case. */ + default_label = build3 (CASE_LABEL_EXPR, void_type_node, + NULL, NULL, default_label); + sort_case_labels (labels); + + x = gimple_build_switch_vec (filter, default_label, labels); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + VEC_free (tree, heap, labels); + } + } + break; + + case ERT_ALLOWED_EXCEPTIONS: + { + edge b_e = BRANCH_EDGE (src); + edge f_e = FALLTHRU_EDGE (src); + + fn = implicit_built_in_decls[BUILT_IN_EH_FILTER]; + x = gimple_build_call (fn, 1, build_int_cst (NULL, region_nr)); + filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL); + filter = make_ssa_name (filter, x); + gimple_call_set_lhs (x, filter); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + r->u.allowed.label = NULL; + x = gimple_build_cond (EQ_EXPR, filter, + build_int_cst (TREE_TYPE (filter), + r->u.allowed.filter), + NULL_TREE, NULL_TREE); + gsi_insert_before (&gsi, x, GSI_SAME_STMT); + + b_e->flags = b_e->flags | EDGE_TRUE_VALUE; + f_e->flags = (f_e->flags & ~EDGE_FALLTHRU) | EDGE_FALSE_VALUE; } - } + break; + + default: + gcc_unreachable (); + } + + /* Replace the EH_DISPATCH with the SWITCH or COND generated above. */ + gsi_remove (&gsi, true); +} + +static unsigned +execute_lower_eh_dispatch (void) +{ + basic_block bb; + bool any_rewritten = false; + + assign_filter_values (); + + FOR_EACH_BB (bb) + { + gimple last = last_stmt (bb); + if (last && gimple_code (last) == GIMPLE_EH_DISPATCH) + { + lower_eh_dispatch (bb, last); + any_rewritten = true; + } + } + + return any_rewritten ? TODO_update_ssa_only_virtuals : 0; +} + +struct gimple_opt_pass pass_lower_eh_dispatch = +{ + { + GIMPLE_PASS, + "ehdisp", /* name */ + gate_lower_ehcontrol, /* gate */ + execute_lower_eh_dispatch, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_flow /* todo_flags_finish */ + } +}; + +/* Walk statements, see what regions are really referenced and remove + those that are unused. */ + +static void +remove_unreachable_handlers (void) +{ + sbitmap r_reachable, lp_reachable; + eh_region region; + eh_landing_pad lp; + basic_block bb; + int lp_nr, r_nr; + + r_reachable = sbitmap_alloc (VEC_length (eh_region, cfun->eh->region_array)); + lp_reachable + = sbitmap_alloc (VEC_length (eh_landing_pad, cfun->eh->lp_array)); + sbitmap_zero (r_reachable); + sbitmap_zero (lp_reachable); + + FOR_EACH_BB (bb) + { + gimple_stmt_iterator gsi = gsi_start_bb (bb); + + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + lp_nr = lookup_stmt_eh_lp (stmt); + + /* Negative LP numbers are MUST_NOT_THROW regions which + are not considered BB enders. */ + if (lp_nr < 0) + SET_BIT (r_reachable, -lp_nr); + + /* Positive LP numbers are real landing pads, are are BB enders. */ + else if (lp_nr > 0) + { + gcc_assert (gsi_one_before_end_p (gsi)); + region = get_eh_region_from_lp_number (lp_nr); + SET_BIT (r_reachable, region->index); + SET_BIT (lp_reachable, lp_nr); + } + } + } if (dump_file) { fprintf (dump_file, "Before removal of unreachable regions:\n"); dump_eh_tree (dump_file, cfun); fprintf (dump_file, "Reachable regions: "); - dump_sbitmap_file (dump_file, reachable); - fprintf (dump_file, "Regions containing insns: "); - dump_sbitmap_file (dump_file, contains_stmt); + dump_sbitmap_file (dump_file, r_reachable); + fprintf (dump_file, "Reachable landing pads: "); + dump_sbitmap_file (dump_file, lp_reachable); } - remove_unreachable_regions (reachable, contains_stmt); - sbitmap_free (reachable); - sbitmap_free (contains_stmt); - VEC_free (int, heap, label_to_region); + for (r_nr = 1; + VEC_iterate (eh_region, cfun->eh->region_array, r_nr, region); ++r_nr) + if (region && !TEST_BIT (r_reachable, r_nr)) + { + if (dump_file) + fprintf (dump_file, "Removing unreachable region %d\n", r_nr); + remove_eh_handler (region); + } + + for (lp_nr = 1; + VEC_iterate (eh_landing_pad, cfun->eh->lp_array, lp_nr, lp); ++lp_nr) + if (lp && !TEST_BIT (lp_reachable, lp_nr)) + { + if (dump_file) + fprintf (dump_file, "Removing unreachable landing pad %d\n", lp_nr); + remove_eh_landing_pad (lp); + } + if (dump_file) { fprintf (dump_file, "\n\nAfter removal of unreachable regions:\n"); dump_eh_tree (dump_file, cfun); fprintf (dump_file, "\n\n"); } + + sbitmap_free (r_reachable); + sbitmap_free (lp_reachable); + +#ifdef ENABLE_CHECKING + verify_eh_tree (cfun); +#endif } -/* Pattern match emtpy EH receiver looking like: - - save_filt.6352_662 = [filter_expr] <<<filter object>>>; - save_eptr.6351_663 = [exc_ptr_expr] <<<exception object>>>; - <<<exception object>>> = save_eptr.6351_663; - <<<filter object>>> = save_filt.6352_662; - resx 1 +/* Remove regions that do not have landing pads. This assumes + that remove_unreachable_handlers has already been run, and + that we've just manipulated the landing pads since then. */ - And various minor variants after DCE or copy propagation. - */ +static void +remove_unreachable_handlers_no_lp (void) +{ + eh_region r; + int i; -static int -tree_empty_eh_handler_p (basic_block bb) + for (i = 1; VEC_iterate (eh_region, cfun->eh->region_array, i, r); ++i) + if (r && r->landing_pads == NULL && r->type != ERT_MUST_NOT_THROW) + { + if (dump_file) + fprintf (dump_file, "Removing unreachable region %d\n", i); + remove_eh_handler (r); + } +} + +/* Undo critical edge splitting on an EH landing pad. Earlier, we + optimisticaly split all sorts of edges, including EH edges. The + optimization passes in between may not have needed them; if not, + we should undo the split. + + Recognize this case by having one EH edge incoming to the BB and + one normal edge outgoing; BB should be empty apart from the + post_landing_pad label. + + Note that this is slightly different from the empty handler case + handled by cleanup_empty_eh, in that the actual handler may yet + have actual code but the landing pad has been separated from the + handler. As such, cleanup_empty_eh relies on this transformation + having been done first. */ + +static bool +unsplit_eh (eh_landing_pad lp) { + basic_block bb = label_to_block (lp->post_landing_pad); gimple_stmt_iterator gsi; - int region; + edge e_in, e_out; + + /* Quickly check the edge counts on BB for singularity. */ + if (EDGE_COUNT (bb->preds) != 1 || EDGE_COUNT (bb->succs) != 1) + return false; + e_in = EDGE_PRED (bb, 0); + e_out = EDGE_SUCC (bb, 0); + + /* Input edge must be EH and output edge must be normal. */ + if ((e_in->flags & EDGE_EH) == 0 || (e_out->flags & EDGE_EH) != 0) + return false; + + /* The block must be empty except for the labels. */ + if (!gsi_end_p (gsi_after_labels (bb))) + return false; + + /* The destination block must not already have a landing pad + for a different region. */ + for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree lab; + int lp_nr; + + if (gimple_code (stmt) != GIMPLE_LABEL) + break; + lab = gimple_label_label (stmt); + lp_nr = EH_LANDING_PAD_NR (lab); + if (lp_nr && get_eh_region_from_lp_number (lp_nr) != lp->region) + return false; + } + + /* ??? I can't imagine there would be PHI nodes, since by nature + of critical edge splitting this block should never have been + a dominance frontier. If cfg cleanups somehow confuse this, + due to single edges in and out we ought to have degenerate PHIs + and can easily propagate the PHI arguments. */ + gcc_assert (gimple_seq_empty_p (phi_nodes (bb))); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Unsplit EH landing pad %d to block %i.\n", + lp->index, e_out->dest->index); + + /* Redirect the edge. Since redirect_eh_edge_1 expects to be moving + a successor edge, humor it. But do the real CFG change with the + predecessor of E_OUT in order to preserve the ordering of arguments + to the PHI nodes in E_OUT->DEST. */ + redirect_eh_edge_1 (e_in, e_out->dest, false); + redirect_edge_pred (e_out, e_in->src); + e_out->flags = e_in->flags; + e_out->probability = e_in->probability; + e_out->count = e_in->count; + remove_edge (e_in); + + return true; +} + +/* Examine each landing pad block and see if it matches unsplit_eh. */ + +static bool +unsplit_all_eh (void) +{ + bool changed = false; + eh_landing_pad lp; + int i; + + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp) + changed |= unsplit_eh (lp); + + return changed; +} + +/* A subroutine of cleanup_empty_eh. Redirect all EH edges incoming + to OLD_BB to NEW_BB; return true on success, false on failure. + + OLD_BB_OUT is the edge into NEW_BB from OLD_BB, so if we miss any + PHI variables from OLD_BB we can pick them up from OLD_BB_OUT. + Virtual PHIs may be deleted and marked for renaming. */ + +static bool +cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, + edge old_bb_out) +{ + gimple_stmt_iterator ngsi, ogsi; edge_iterator ei; edge e; - use_operand_p imm_use; - gimple use_stmt; - bool found = false; + bitmap rename_virts; + bitmap ophi_handled; - gsi = gsi_last_bb (bb); + FOR_EACH_EDGE (e, ei, old_bb->preds) + redirect_edge_var_map_clear (e); - /* RESX */ - if (gsi_end_p (gsi)) - return 0; - if (gimple_code (gsi_stmt (gsi)) != GIMPLE_RESX) - return 0; - region = gimple_resx_region (gsi_stmt (gsi)); + ophi_handled = BITMAP_ALLOC (NULL); + rename_virts = BITMAP_ALLOC (NULL); - /* filter_object set. */ - gsi_prev_nondebug (&gsi); - if (gsi_end_p (gsi)) - return 0; - if (gimple_code (gsi_stmt (gsi)) == GIMPLE_ASSIGN) + /* First, iterate through the PHIs on NEW_BB and set up the edge_var_map + for the edges we're going to move. */ + for (ngsi = gsi_start_phis (new_bb); !gsi_end_p (ngsi); gsi_next (&ngsi)) { - tree filter_tmp; - tree exc_ptr_tmp; + gimple ophi, nphi = gsi_stmt (ngsi); + tree nresult, nop; - if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != FILTER_EXPR) - return 0; - filter_tmp = gimple_assign_rhs1 (gsi_stmt (gsi)); + nresult = gimple_phi_result (nphi); + nop = gimple_phi_arg_def (nphi, old_bb_out->dest_idx); - /* filter_object set. */ - gsi_prev_nondebug (&gsi); - if (gsi_end_p (gsi)) - return 0; - if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN) - return 0; - if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != EXC_PTR_EXPR) - return 0; - exc_ptr_tmp = gimple_assign_rhs1 (gsi_stmt (gsi)); - - /* exc_ptr get. */ - if (TREE_CODE (exc_ptr_tmp) != EXC_PTR_EXPR) + /* Find the corresponding PHI in OLD_BB so we can forward-propagate + the source ssa_name. */ + ophi = NULL; + for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi)) { - gsi_prev_nondebug (&gsi); - if (gsi_end_p (gsi)) - return 0; - if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN) - return 0; - if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != EXC_PTR_EXPR) - return 0; - if (exc_ptr_tmp != gimple_assign_lhs (gsi_stmt (gsi))) - return 0; - if (!single_imm_use (exc_ptr_tmp, &imm_use, &use_stmt)) - return 0; + ophi = gsi_stmt (ogsi); + if (gimple_phi_result (ophi) == nop) + break; + ophi = NULL; } - /* filter_object get. */ - if (TREE_CODE (filter_tmp) != FILTER_EXPR) + /* If we did find the corresponding PHI, copy those inputs. */ + if (ophi) + { + bitmap_set_bit (ophi_handled, SSA_NAME_VERSION (nop)); + FOR_EACH_EDGE (e, ei, old_bb->preds) + { + location_t oloc; + tree oop; + + if ((e->flags & EDGE_EH) == 0) + continue; + oop = gimple_phi_arg_def (ophi, e->dest_idx); + oloc = gimple_phi_arg_location (ophi, e->dest_idx); + redirect_edge_var_map_add (e, nresult, oop, oloc); + } + } + /* If we didn't find the PHI, but it's a VOP, remember to rename + it later, assuming all other tests succeed. */ + else if (!is_gimple_reg (nresult)) + bitmap_set_bit (rename_virts, SSA_NAME_VERSION (nresult)); + /* If we didn't find the PHI, and it's a real variable, we know + from the fact that OLD_BB is tree_empty_eh_handler_p that the + variable is unchanged from input to the block and we can simply + re-use the input to NEW_BB from the OLD_BB_OUT edge. */ + else { - gsi_prev_nondebug (&gsi); - if (gsi_end_p (gsi)) - return 0; - if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN) - return 0; - if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != FILTER_EXPR) - return 0; - if (filter_tmp != gimple_assign_lhs (gsi_stmt (gsi))) - return 0; - if (!single_imm_use (filter_tmp, &imm_use, &use_stmt)) - return 0; + location_t nloc + = gimple_phi_arg_location (nphi, old_bb_out->dest_idx); + FOR_EACH_EDGE (e, ei, old_bb->preds) + redirect_edge_var_map_add (e, nresult, nop, nloc); } - - /* label. */ - gsi_prev_nondebug (&gsi); - if (gsi_end_p (gsi)) - return 0; } - if (gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL) - return 0; - /* Be sure that there is at least on EH region reaching the block directly. - After EH edge redirection, it is possible that block is reached by one handler - but resumed by different. */ - FOR_EACH_EDGE (e, ei, bb->preds) - if ((e->flags & EDGE_EH)) - found = true; - if (found) - return region; - return 0; -} - -/* Return true if it is possible to remove basic block BB and propagate - through PHIs. - - This means that every PHI in BB has all uses such that they are PHIs - of basic blocks reachable througt BB and they appears only in use - reachable by the edge from BB to the block contianing the use. - - This is same as in merge-phi code, but in slightly more general setting - because BB can have multiple successors. */ + /* Second, verify that all PHIs from OLD_BB have been handled. If not, + we don't know what values from the other edges into NEW_BB to use. */ + for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi)) + { + gimple ophi = gsi_stmt (ogsi); + tree oresult = gimple_phi_result (ophi); + if (!bitmap_bit_p (ophi_handled, SSA_NAME_VERSION (oresult))) + goto fail; + } -static bool -all_phis_safe_to_merge (basic_block bb) -{ - gimple_stmt_iterator si; - bool ok = true; - - for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) - { - gimple phi = gsi_stmt (si); - tree result = gimple_phi_result (phi); - gimple stmt; - use_operand_p imm_use; - imm_use_iterator imm_iter; - - /* If the PHI's result is never used, then we can just - ignore it. */ - if (has_zero_uses (result)) - continue; - /* We can always rebuild virtuals if needed. */ - if (!is_gimple_reg (result)) - continue; - FOR_EACH_IMM_USE_STMT (stmt, imm_iter, result) - { - if (gimple_code (stmt) != GIMPLE_PHI) + /* At this point we know that the merge will succeed. Remove the PHI + nodes for the virtuals that we want to rename. */ + if (!bitmap_empty_p (rename_virts)) + { + for (ngsi = gsi_start_phis (new_bb); !gsi_end_p (ngsi); ) + { + gimple nphi = gsi_stmt (ngsi); + tree nresult = gimple_phi_result (nphi); + if (bitmap_bit_p (rename_virts, SSA_NAME_VERSION (nresult))) { - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, - "PHI result has use in non-PHI statement.\n"); - ok = false; - BREAK_FROM_IMM_USE_STMT (imm_iter); + mark_virtual_phi_result_for_renaming (nphi); + remove_phi_node (&ngsi, true); } else - FOR_EACH_IMM_USE_ON_STMT (imm_use, imm_iter) - { - edge e; - e = gimple_phi_arg_edge (stmt, PHI_ARG_INDEX_FROM_USE (imm_use)); - if (e->src != bb) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "PHI has use in PHI not reached from" - "empty cleanup itself.\n"); - ok = false; - break; - } - } - if (!ok) - BREAK_FROM_IMM_USE_STMT (imm_iter); - } - if (!ok) - return false; + gsi_next (&ngsi); + } } - return ok; -} -static bool dominance_info_invalidated; + /* Finally, move the edges and update the PHIs. */ + for (ei = ei_start (old_bb->preds); (e = ei_safe_edge (ei)); ) + if (e->flags & EDGE_EH) + { + redirect_eh_edge_1 (e, new_bb, true); + redirect_edge_succ (e, new_bb); + flush_pending_stmts (e); + } + else + ei_next (&ei); -/* Information to pass into make_eh_edge_and_update_phi. */ + BITMAP_FREE (ophi_handled); + BITMAP_FREE (rename_virts); + return true; -struct update_info -{ - basic_block bb_to_remove, bb; - edge edge_to_remove; -}; + fail: + FOR_EACH_EDGE (e, ei, old_bb->preds) + redirect_edge_var_map_clear (e); + BITMAP_FREE (ophi_handled); + BITMAP_FREE (rename_virts); + return false; +} -/* DATA points to update-info structure. - Like make_eh_edge create EH edge from DATA->bb to basic block containing - handler of REGION. In addition also update PHI operands by copying - operands from DATA->bb_to_remove. */ +/* A subroutine of cleanup_empty_eh. Move a landing pad LP from its + old region to NEW_REGION at BB. */ static void -make_eh_edge_and_update_phi (struct eh_region_d *region, void *data) +cleanup_empty_eh_move_lp (basic_block bb, edge e_out, + eh_landing_pad lp, eh_region new_region) { - struct update_info *info = (struct update_info *) data; - edge e, e2; - tree lab; - basic_block src, dst; - gimple_stmt_iterator si; + gimple_stmt_iterator gsi; + eh_landing_pad *pp; - lab = get_eh_region_tree_label (region); + for (pp = &lp->region->landing_pads; *pp != lp; pp = &(*pp)->next_lp) + continue; + *pp = lp->next_lp; - src = info->bb; - dst = label_to_block (lab); + lp->region = new_region; + lp->next_lp = new_region->landing_pads; + new_region->landing_pads = lp; - e = find_edge (src, dst); - if (e) - { - gcc_assert (e->flags & EDGE_EH); - e->aux = e; - return; - } - dominance_info_invalidated = true; - e2 = find_edge (info->bb_to_remove, dst); - e = make_edge (src, dst, EDGE_EH); - e->aux = e; - gcc_assert (e2); - for (si = gsi_start_phis (dst); !gsi_end_p (si); gsi_next (&si)) - { - gimple phi = gsi_stmt (si); - tree use = USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e2)); - gimple def = (TREE_CODE (use) == SSA_NAME - ? SSA_NAME_DEF_STMT (use) : NULL); + /* Delete the RESX that was matched within the empty handler block. */ + gsi = gsi_last_bb (bb); + mark_virtual_ops_for_renaming (gsi_stmt (gsi)); + gsi_remove (&gsi, true); - if (def && gimple_bb (def) == info->bb_to_remove) - { - use = USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (def, - info->edge_to_remove)); - gcc_assert (info->bb_to_remove == info->edge_to_remove->dest); - def = TREE_CODE (use) == SSA_NAME ? SSA_NAME_DEF_STMT (use) : NULL; - gcc_assert (!def - || gimple_bb (def) != info->bb_to_remove - || !is_gimple_reg (use)); - } - SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), use); - } + /* Clean up E_OUT for the fallthru. */ + e_out->flags = (e_out->flags & ~EDGE_EH) | EDGE_FALLTHRU; + e_out->probability = REG_BR_PROB_BASE; } -/* Make EH edges corresponding to STMT while updating PHI nodes after removal - empty cleanup BB_TO_REMOVE joined to BB containing STMT - by EDGE_TO_REMOVE. - - Return if EDGE_TO_REMOVE was really removed. It might stay reachable when - not all EH regions are cleaned up. */ +/* A subroutine of cleanup_empty_eh. Handle more complex cases of + unsplitting than unsplit_eh was prepared to handle, e.g. when + multiple incoming edges and phis are involved. */ static bool -update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove) +cleanup_empty_eh_unsplit (basic_block bb, edge e_out, eh_landing_pad olp) { - int region_nr; - bool is_resx; - bool inlinable = false; - struct update_info info; - edge_iterator ei; - edge e; - int probability_sum = 0; - bool removed = false; + gimple_stmt_iterator gsi; + eh_landing_pad nlp; + tree lab; - info.bb_to_remove = bb_to_remove; - info.bb = gimple_bb (stmt); - info.edge_to_remove = edge_to_remove; + /* We really ought not have totally lost everything following + a landing pad label. Given that BB is empty, there had better + be a successor. */ + gcc_assert (e_out != NULL); - if (gimple_code (stmt) == GIMPLE_RESX) - { - region_nr = gimple_resx_region (stmt); - is_resx = true; - } - else + /* Look for an EH label in the successor block. */ + lab = NULL; + for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi)) { - region_nr = lookup_stmt_eh_region (stmt); - is_resx = false; - inlinable = inlinable_call_p (stmt); + gimple stmt = gsi_stmt (gsi); + if (gimple_code (stmt) != GIMPLE_LABEL) + break; + lab = gimple_label_label (stmt); + if (EH_LANDING_PAD_NR (lab)) + goto found; } + return false; + found: - /* First add new edges as neccesary. */ - foreach_reachable_handler (region_nr, is_resx, inlinable, - make_eh_edge_and_update_phi, &info); + /* The other label had better be part of the same EH region. Given that + we've not lowered RESX, there should be no way to have a totally empty + landing pad that crosses to another EH region. */ + nlp = get_eh_landing_pad_from_number (EH_LANDING_PAD_NR (lab)); + gcc_assert (nlp->region == olp->region); - /* And remove edges we didn't marked. */ - for (ei = ei_start (info.bb->succs); (e = ei_safe_edge (ei)); ) + /* Attempt to move the PHIs into the successor block. */ + if (cleanup_empty_eh_merge_phis (e_out->dest, bb, e_out)) { - if ((e->flags & EDGE_EH) && !e->aux) - { - dominance_info_invalidated = true; - if (e == edge_to_remove) - removed = true; - remove_edge (e); - } - else - { - e->aux = NULL; - probability_sum += e->probability; - ei_next (&ei); - } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Unsplit EH landing pad %d to block %d via lp %d.\n", + olp->index, e_out->dest->index, nlp->index); + + remove_eh_landing_pad (olp); + return true; } - /* Make CFG profile more consistent assuming that exception will resume to - first available EH handler. In practice this makes little difference, but - we get fewer consistency errors in the dumps. */ - if (is_resx && EDGE_COUNT (info.bb->succs) && !probability_sum) - EDGE_SUCC (info.bb, 0)->probability = REG_BR_PROB_BASE; - return removed; + return false; } -/* Look for basic blocks containing empty exception handler and remove them. - This is similar to jump forwarding, just across EH edges. */ +/* Examine the block associated with LP to determine if it's an empty + handler for its EH region. If so, attempt to redirect EH edges to + an outer region. Return true the CFG was updated in any way. This + is similar to jump forwarding, just across EH edges. */ static bool -cleanup_empty_eh (basic_block bb, VEC(int,heap) * label_to_region) +cleanup_empty_eh (eh_landing_pad lp) { - int region; - gimple_stmt_iterator si; + basic_block bb = label_to_block (lp->post_landing_pad); + gimple_stmt_iterator gsi; + gimple resx; + eh_region new_region; edge_iterator ei; + edge e, e_out; + bool has_non_eh_pred; + int new_lp_nr; - /* When handler of EH region winds up to be empty, we can safely - remove it. This leads to inner EH regions to be redirected - to outer one, if present in function. So we need to rebuild - EH edges in all sources. */ - if ((region = tree_empty_eh_handler_p (bb)) - && all_phis_safe_to_merge (bb)) + /* There can be zero or one edges out of BB. This is the quickest test. */ + switch (EDGE_COUNT (bb->succs)) { - edge e; - bool found = false, removed_some = false, has_non_eh_preds = false; - gimple_stmt_iterator gsi; - - /* Look for all EH regions sharing label of this block. - If they are not same as REGION, remove them and replace them - by outer region of REGION. Also note if REGION itself is one - of them. */ - - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) - if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL) - { - int uid = LABEL_DECL_UID (gimple_label_label (gsi_stmt (gsi))); - int r = VEC_index (int, label_to_region, uid); - int next; + case 0: + e_out = NULL; + break; + case 1: + e_out = EDGE_SUCC (bb, 0); + break; + default: + return false; + } + gsi = gsi_after_labels (bb); - while (r) - { - next = get_next_region_sharing_label (r); - if (r == region) - found = true; - else - { - removed_some = true; - remove_eh_region_and_replace_by_outer_of (r, region); - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Empty EH handler %i removed and " - "replaced by %i\n", r, region); - } - r = next; - } - } - else - break; + /* Make sure to skip debug statements. */ + if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi))) + gsi_next_nondebug (&gsi); - gcc_assert (found || removed_some); - FOR_EACH_EDGE (e, ei, bb->preds) - if (!(e->flags & EDGE_EH)) - has_non_eh_preds = true; + /* If the block is totally empty, look for more unsplitting cases. */ + if (gsi_end_p (gsi)) + return cleanup_empty_eh_unsplit (bb, e_out, lp); - /* When block is empty EH cleanup, but it is reachable via non-EH code too, - we can not remove the region it is resumed via, because doing so will - lead to redirection of its RESX edges. + /* The block should consist only of a single RESX statement. */ + resx = gsi_stmt (gsi); + if (!is_gimple_resx (resx)) + return false; + gcc_assert (gsi_one_before_end_p (gsi)); - This case will be handled later after edge forwarding if the EH cleanup - is really dead. */ + /* Determine if there are non-EH edges, or resx edges into the handler. */ + has_non_eh_pred = false; + FOR_EACH_EDGE (e, ei, bb->preds) + if (!(e->flags & EDGE_EH)) + has_non_eh_pred = true; - if (found && !has_non_eh_preds) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Empty EH handler %i removed.\n", region); - remove_eh_region (region); - } - else if (!removed_some) - return false; + /* Find the handler that's outer of the empty handler by looking at + where the RESX instruction was vectored. */ + new_lp_nr = lookup_stmt_eh_lp (resx); + new_region = get_eh_region_from_lp_number (new_lp_nr); + /* If there's no destination region within the current function, + redirection is trivial via removing the throwing statements from + the EH region, removing the EH edges, and allowing the block + to go unreachable. */ + if (new_region == NULL) + { + gcc_assert (e_out == NULL); for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); ) - { - basic_block src = e->src; - if (!(e->flags & EDGE_EH)) - { - ei_next (&ei); - continue; - } - if (stmt_can_throw_internal (last_stmt (src))) - { - if (!update_eh_edges (last_stmt (src), bb, e)) - ei_next (&ei); - } - else + if (e->flags & EDGE_EH) + { + gimple stmt = last_stmt (e->src); + remove_stmt_from_eh_lp (stmt); remove_edge (e); - } - - /* Verify that we eliminated all uses of PHI we are going to remove. - If we didn't, rebuild SSA on affected variable (this is allowed only - for virtuals). */ - for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) - { - gimple phi = gsi_stmt (si); - tree result = gimple_phi_result (phi); - if (!has_zero_uses (result)) - { - use_operand_p use_p; - imm_use_iterator iter; - gimple stmt; + } + else + ei_next (&ei); + goto succeed; + } - FOR_EACH_IMM_USE_STMT (stmt, iter, result) - { - /* We have use, see if it won't disappear after - removing BB. */ - if (gimple_bb (stmt) == bb) - continue; - if (gimple_code (stmt) == GIMPLE_PHI) - { - bool bad = false; - - FOR_EACH_IMM_USE_ON_STMT (use_p, iter) - if (gimple_phi_arg_edge (stmt, - PHI_ARG_INDEX_FROM_USE (use_p))->src != bb) - { - bad = true; - break; - } - - if (!bad) - continue; - } - - gcc_assert (!is_gimple_reg (result)); - mark_sym_for_renaming (SSA_NAME_VAR (result)); - /* As we are going to delete this block we will release all - defs which makes the immediate uses on use stmts invalid. - Avoid that by replacing all uses with the bare variable - and updating the stmts. */ - FOR_EACH_IMM_USE_ON_STMT (use_p, iter) - SET_USE (use_p, SSA_NAME_VAR (result)); - update_stmt (stmt); - } - } - } - if (!ei_safe_edge (ei_start (bb->preds))) - delete_basic_block (bb); + /* If the destination region is a MUST_NOT_THROW, allow the runtime + to handle the abort and allow the blocks to go unreachable. */ + if (new_region->type == ERT_MUST_NOT_THROW) + { + for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); ) + if (e->flags & EDGE_EH) + { + gimple stmt = last_stmt (e->src); + remove_stmt_from_eh_lp (stmt); + add_stmt_to_eh_lp (stmt, new_lp_nr); + remove_edge (e); + } + else + ei_next (&ei); + goto succeed; + } + + /* Try to redirect the EH edges and merge the PHIs into the destination + landing pad block. If the merge succeeds, we'll already have redirected + all the EH edges. The handler itself will go unreachable if there were + no normal edges. */ + if (cleanup_empty_eh_merge_phis (e_out->dest, bb, e_out)) + goto succeed; + + /* Finally, if all input edges are EH edges, then we can (potentially) + reduce the number of transfers from the runtime by moving the landing + pad from the original region to the new region. This is a win when + we remove the last CLEANUP region along a particular exception + propagation path. Since nothing changes except for the region with + which the landing pad is associated, the PHI nodes do not need to be + adjusted at all. */ + if (!has_non_eh_pred) + { + cleanup_empty_eh_move_lp (bb, e_out, lp, new_region); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Empty EH handler %i moved to EH region %i.\n", + lp->index, new_region->index); + + /* ??? The CFG didn't change, but we may have rendered the + old EH region unreachable. Trigger a cleanup there. */ return true; } + return false; + + succeed: + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Empty EH handler %i removed.\n", lp->index); + remove_eh_landing_pad (lp); + return true; } +/* Do a post-order traversal of the EH region tree. Examine each + post_landing_pad block and see if we can eliminate it as empty. */ + +static bool +cleanup_all_empty_eh (void) +{ + bool changed = false; + eh_landing_pad lp; + int i; + + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp) + changed |= cleanup_empty_eh (lp); + + return changed; +} /* Perform cleanups and lowering of exception handling 1) cleanups regions with handlers doing nothing are optimized out @@ -3246,63 +3749,58 @@ cleanup_empty_eh (basic_block bb, VEC(int,heap) * label_to_region) 3) Info about regions that are containing instructions, and regions reachable via local EH edges is collected 4) Eh tree is pruned for regions no longer neccesary. - */ + + TODO: Push MUST_NOT_THROW regions to the root of the EH tree. + Unify those that have the same failure decl and locus. +*/ static unsigned int -cleanup_eh (void) +execute_cleanup_eh (void) { - bool changed = false; - basic_block bb; - VEC(int,heap) * label_to_region; - int i; + /* Do this first: unsplit_all_eh and cleanup_all_empty_eh can die + looking up unreachable landing pads. */ + remove_unreachable_handlers (); - if (!cfun->eh) - return 0; - if (dump_file) + /* Watch out for the region tree vanishing due to all unreachable. */ + if (cfun->eh->region_tree && optimize) { - fprintf (dump_file, "Before cleanups:\n"); - dump_eh_tree (dump_file, cfun); - } + bool changed = false; - if (optimize) - { - label_to_region = label_to_region_map (); - dominance_info_invalidated = false; - /* We cannot use FOR_EACH_BB, since the basic blocks may get removed. */ - for (i = NUM_FIXED_BLOCKS; i < last_basic_block; i++) - { - bb = BASIC_BLOCK (i); - if (bb) - changed |= cleanup_empty_eh (bb, label_to_region); - } - VEC_free (int, heap, label_to_region); - if (dominance_info_invalidated) + changed |= unsplit_all_eh (); + changed |= cleanup_all_empty_eh (); + + if (changed) { free_dominance_info (CDI_DOMINATORS); free_dominance_info (CDI_POST_DOMINATORS); - } - /* Removing contained cleanup can render MUST_NOT_THROW regions empty. */ - if (changed) - delete_unreachable_blocks (); - } + /* We delayed all basic block deletion, as we may have performed + cleanups on EH edges while non-EH edges were still present. */ + delete_unreachable_blocks (); - tree_remove_unreachable_handlers (); - if (dump_file) - { - fprintf (dump_file, "After cleanups:\n"); - dump_eh_tree (dump_file, cfun); + /* We manipulated the landing pads. Remove any region that no + longer has a landing pad. */ + remove_unreachable_handlers_no_lp (); + + return TODO_cleanup_cfg | TODO_update_ssa_only_virtuals; + } } - return (changed ? TODO_cleanup_cfg | TODO_update_ssa : 0); + return 0; +} + +static bool +gate_cleanup_eh (void) +{ + return cfun->eh != NULL && cfun->eh->region_tree != NULL; } struct gimple_opt_pass pass_cleanup_eh = { { GIMPLE_PASS, "ehcleanup", /* name */ - NULL, /* gate */ - cleanup_eh, /* execute */ + gate_cleanup_eh, /* gate */ + execute_cleanup_eh, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -3314,3 +3812,150 @@ struct gimple_opt_pass pass_cleanup_eh = { TODO_dump_func /* todo_flags_finish */ } }; + +/* Verify that BB containing STMT as the last statement, has precisely the + edge that make_eh_edges would create. */ + +bool +verify_eh_edges (gimple stmt) +{ + basic_block bb = gimple_bb (stmt); + eh_landing_pad lp = NULL; + int lp_nr; + edge_iterator ei; + edge e, eh_edge; + + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr > 0) + lp = get_eh_landing_pad_from_number (lp_nr); + + eh_edge = NULL; + FOR_EACH_EDGE (e, ei, bb->succs) + { + if (e->flags & EDGE_EH) + { + if (eh_edge) + { + error ("BB %i has multiple EH edges", bb->index); + return true; + } + else + eh_edge = e; + } + } + + if (lp == NULL) + { + if (eh_edge) + { + error ("BB %i can not throw but has an EH edge", bb->index); + return true; + } + return false; + } + + if (!stmt_could_throw_p (stmt)) + { + error ("BB %i last statement has incorrectly set lp", bb->index); + return true; + } + + if (eh_edge == NULL) + { + error ("BB %i is missing an EH edge", bb->index); + return true; + } + + if (eh_edge->dest != label_to_block (lp->post_landing_pad)) + { + error ("Incorrect EH edge %i->%i", bb->index, eh_edge->dest->index); + return true; + } + + return false; +} + +/* Similarly, but handle GIMPLE_EH_DISPATCH specifically. */ + +bool +verify_eh_dispatch_edge (gimple stmt) +{ + eh_region r; + eh_catch c; + basic_block src, dst; + bool want_fallthru = true; + edge_iterator ei; + edge e, fall_edge; + + r = get_eh_region_from_number (gimple_eh_dispatch_region (stmt)); + src = gimple_bb (stmt); + + FOR_EACH_EDGE (e, ei, src->succs) + gcc_assert (e->aux == NULL); + + switch (r->type) + { + case ERT_TRY: + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + dst = label_to_block (c->label); + e = find_edge (src, dst); + if (e == NULL) + { + error ("BB %i is missing an edge", src->index); + return true; + } + e->aux = (void *)e; + + /* A catch-all handler doesn't have a fallthru. */ + if (c->type_list == NULL) + { + want_fallthru = false; + break; + } + } + break; + + case ERT_ALLOWED_EXCEPTIONS: + dst = label_to_block (r->u.allowed.label); + e = find_edge (src, dst); + if (e == NULL) + { + error ("BB %i is missing an edge", src->index); + return true; + } + e->aux = (void *)e; + break; + + default: + gcc_unreachable (); + } + + fall_edge = NULL; + FOR_EACH_EDGE (e, ei, src->succs) + { + if (e->flags & EDGE_FALLTHRU) + { + if (fall_edge != NULL) + { + error ("BB %i too many fallthru edges", src->index); + return true; + } + fall_edge = e; + } + else if (e->aux) + e->aux = NULL; + else + { + error ("BB %i has incorrect edge", src->index); + return true; + } + } + if ((fall_edge != NULL) ^ want_fallthru) + { + error ("BB %i has incorrect fallthru edge", src->index); + return true; + } + + return false; +} diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index 9af6cbd..b93e2f4 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -134,9 +134,9 @@ struct GTY(()) tree_ann_common_d { /* Annotation type. */ enum tree_ann_type type; - /* Record EH region number into a statement tree created during RTL - expansion (see gimple_to_tree). */ - int rn; + /* Record EH landing pad number into a statement tree created + during RTL expansion (see gimple_to_tree). */ + int lp_nr; /* Pointer to original GIMPLE statement. Used during RTL expansion (see gimple_to_tree). */ @@ -807,6 +807,9 @@ bool contains_abnormal_ssa_name_p (tree); bool stmt_dominates_stmt_p (gimple, gimple); void mark_virtual_ops_for_renaming (gimple); +/* In tree-ssa-dce.c */ +void mark_virtual_phi_result_for_renaming (gimple); + /* In tree-ssa-threadedge.c */ extern void threadedge_initialize_values (void); extern void threadedge_finalize_values (void); @@ -842,6 +845,9 @@ static inline bool array_ref_contains_indirect_ref (const_tree); /* In tree-eh.c */ extern void make_eh_edges (gimple); +extern bool make_eh_dispatch_edges (gimple); +extern edge redirect_eh_edge (edge, basic_block); +extern void redirect_eh_dispatch_edge (gimple, edge, basic_block); extern bool tree_could_trap_p (tree); extern bool operation_could_trap_helper_p (enum tree_code, bool, bool, bool, bool, tree, bool *); @@ -850,16 +856,22 @@ extern bool stmt_could_throw_p (gimple); extern bool tree_could_throw_p (tree); extern bool stmt_can_throw_internal (gimple); extern bool stmt_can_throw_external (gimple); -extern void add_stmt_to_eh_region (gimple, int); -extern bool remove_stmt_from_eh_region (gimple); +extern void add_stmt_to_eh_lp_fn (struct function *, gimple, int); +extern void add_stmt_to_eh_lp (gimple, int); +extern bool remove_stmt_from_eh_lp (gimple); +extern bool remove_stmt_from_eh_lp_fn (struct function *, gimple); +extern int lookup_stmt_eh_lp_fn (struct function *, gimple); +extern int lookup_expr_eh_lp (tree); +extern int lookup_stmt_eh_lp (gimple); +extern bool maybe_clean_eh_stmt_fn (struct function *, gimple); +extern bool maybe_clean_eh_stmt (gimple); extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple); -extern void add_stmt_to_eh_region_fn (struct function *, gimple, int); -extern bool remove_stmt_from_eh_region_fn (struct function *, gimple); -extern int lookup_stmt_eh_region_fn (struct function *, gimple); -extern int lookup_expr_eh_region (tree); -extern int lookup_stmt_eh_region (gimple); +extern bool maybe_duplicate_eh_stmt_fn (struct function *, gimple, + struct function *, gimple, + struct pointer_map_t *, int); +extern bool maybe_duplicate_eh_stmt (gimple, gimple); extern bool verify_eh_edges (gimple); - +extern bool verify_eh_dispatch_edge (gimple); /* In tree-ssa-pre.c */ struct pre_expr_d; @@ -926,6 +938,5 @@ unsigned int execute_fixup_cfg (void); void swap_tree_operands (gimple, tree *, tree *); int least_common_multiple (int, int); -edge redirect_eh_edge (edge e, basic_block new_bb); #endif /* _TREE_FLOW_H */ diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index b83c52f..5ada378 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -64,7 +64,7 @@ along with GCC; see the file COPYING3. If not see MODIFY_EXPRs that store to a dedicated returned-value variable. The duplicated eh_region info of the copy will later be appended to the info for the caller; the eh_region info in copied throwing - statements and RESX_EXPRs is adjusted accordingly. + statements and RESX statements are adjusted accordingly. Cloning: (only in C++) We have one body for a con/de/structor, and multiple function decls, each with a unique parameter list. @@ -1105,12 +1105,6 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) TREE_BLOCK (*tp) = new_block; } - if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset) - TREE_OPERAND (*tp, 0) = - build_int_cst (NULL_TREE, - id->eh_region_offset - + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0))); - if (TREE_CODE (*tp) != OMP_CLAUSE) TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id); @@ -1150,6 +1144,35 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +/* Helper for remap_gimple_stmt. Given an EH region number for the + source function, map that to the duplicate EH region number in + the destination function. */ + +static int +remap_eh_region_nr (int old_nr, copy_body_data *id) +{ + eh_region old_r, new_r; + void **slot; + + old_r = get_eh_region_from_number_fn (id->src_cfun, old_nr); + slot = pointer_map_contains (id->eh_map, old_r); + new_r = (eh_region) *slot; + + return new_r->index; +} + +/* Similar, but operate on INTEGER_CSTs. */ + +static tree +remap_eh_region_tree_nr (tree old_t_nr, copy_body_data *id) +{ + int old_nr, new_nr; + + old_nr = tree_low_cst (old_t_nr, 0); + new_nr = remap_eh_region_nr (old_nr, id); + + return build_int_cst (NULL, new_nr); +} /* Helper for copy_bb. Remap statement STMT using the inlining information in ID. Return the new statement copy. */ @@ -1339,9 +1362,59 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) VEC_safe_push (gimple, heap, id->debug_stmts, copy); return copy; } - else - /* Create a new deep copy of the statement. */ - copy = gimple_copy (stmt); + + /* Create a new deep copy of the statement. */ + copy = gimple_copy (stmt); + + /* Remap the region numbers for __builtin_eh_{pointer,filter}, + RESX and EH_DISPATCH. */ + if (id->eh_map) + switch (gimple_code (copy)) + { + case GIMPLE_CALL: + { + tree r, fndecl = gimple_call_fndecl (copy); + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_COPY_VALUES: + r = gimple_call_arg (copy, 1); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 1, r); + /* FALLTHRU */ + + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_FILTER: + r = gimple_call_arg (copy, 0); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 0, r); + break; + + default: + break; + } + } + break; + + case GIMPLE_RESX: + { + int r = gimple_resx_region (copy); + r = remap_eh_region_nr (r, id); + gimple_resx_set_region (copy, r); + } + break; + + case GIMPLE_EH_DISPATCH: + { + int r = gimple_eh_dispatch_region (copy); + r = remap_eh_region_nr (r, id); + gimple_eh_dispatch_set_region (copy, r); + } + break; + + default: + break; + } } /* If STMT has a block defined, map it to the newly constructed @@ -1377,12 +1450,6 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) gimple_set_vuse (copy, NULL_TREE); } - /* We have to handle EH region remapping of GIMPLE_RESX specially because - the region number is not an operand. */ - if (gimple_code (stmt) == GIMPLE_RESX && id->eh_region_offset) - { - gimple_resx_set_region (copy, gimple_resx_region (stmt) + id->eh_region_offset); - } return copy; } @@ -1617,43 +1684,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, cfun->calls_setjmp = true; } - /* If you think we can abort here, you are wrong. - There is no region 0 in gimple. */ - gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) != 0); - - if (stmt_could_throw_p (stmt) - /* When we are cloning for inlining, we are supposed to - construct a clone that calls precisely the same functions - as original. However IPA optimizers might've proved - earlier some function calls as non-trapping that might - render some basic blocks dead that might become - unreachable. - - We can't update SSA with unreachable blocks in CFG and thus - we prevent the scenario by preserving even the "dead" eh - edges until the point they are later removed by - fixup_cfg pass. */ - || (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES - && lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) > 0)) - { - int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt); - - /* Add an entry for the copied tree in the EH hashtable. - When cloning or versioning, use the hashtable in - cfun, and just copy the EH number. When inlining, use the - hashtable in the caller, and adjust the region number. */ - if (region > 0) - add_stmt_to_eh_region (stmt, region + id->eh_region_offset); - - /* If this tree doesn't have a region associated with it, - and there is a "current region," - then associate this tree with the current region - and add edges associated with this region. */ - if (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) <= 0 - && id->eh_region > 0 - && stmt_could_throw_p (stmt)) - add_stmt_to_eh_region (stmt, id->eh_region); - } + maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt, + id->eh_map, id->eh_lp_nr); if (gimple_in_ssa_p (cfun) && !is_gimple_debug (stmt)) { @@ -1822,7 +1854,9 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) } } - if (can_throw) + if (gimple_code (copy_stmt) == GIMPLE_EH_DISPATCH) + make_eh_dispatch_edges (copy_stmt); + else if (can_throw) make_eh_edges (copy_stmt); if (nonlocal_goto) @@ -2025,11 +2059,8 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, /* Duplicate any exception-handling regions. */ if (cfun->eh) - { - id->eh_region_offset - = duplicate_eh_regions (cfun_to_copy, remap_decl_1, id, - 0, id->eh_region); - } + id->eh_map = duplicate_eh_regions (cfun_to_copy, NULL, id->eh_lp_nr, + remap_decl_1, id); /* Use aux pointers to map the original blocks to copy. */ FOR_EACH_BB_FN (bb, cfun_to_copy) @@ -2062,6 +2093,12 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, entry_block_map->aux = NULL; exit_block_map->aux = NULL; + if (id->eh_map) + { + pointer_map_destroy (id->eh_map); + id->eh_map = NULL; + } + return new_fndecl; } @@ -3190,14 +3227,6 @@ estimate_num_insns (gimple stmt, eni_weights *weights) lhs = gimple_assign_lhs (stmt); rhs = gimple_assign_rhs1 (stmt); - /* EH magic stuff is most probably going to be optimized out. - We rarely really need to save EH info for unwinding - nested exceptions. */ - if (TREE_CODE (lhs) == FILTER_EXPR - || TREE_CODE (lhs) == EXC_PTR_EXPR - || TREE_CODE (rhs) == FILTER_EXPR - || TREE_CODE (rhs) == EXC_PTR_EXPR) - return 0; if (is_gimple_reg (lhs)) cost = 0; else @@ -3308,9 +3337,19 @@ estimate_num_insns (gimple stmt, eni_weights *weights) return 0; case GIMPLE_ASM: - case GIMPLE_RESX: return 1; + case GIMPLE_RESX: + /* This is either going to be an external function call with one + argument, or two register copy statements plus a goto. */ + return 2; + + case GIMPLE_EH_DISPATCH: + /* ??? This is going to turn into a switch statement. Ideally + we'd have a look at the eh region and estimate the number of + edges involved. */ + return 10; + case GIMPLE_BIND: return estimate_num_insns_seq (gimple_bind_body (stmt), weights); @@ -3551,7 +3590,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) #endif /* We will be inlining this callee. */ - id->eh_region = lookup_stmt_eh_region (stmt); + id->eh_lp_nr = lookup_stmt_eh_lp (stmt); /* Update the callers EH personality. */ if (DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl)) @@ -4935,7 +4974,7 @@ maybe_inline_call_in_expr (tree exp) id.do_not_unshare = true; /* We're not inside any EH region. */ - id.eh_region = -1; + id.eh_lp_nr = 0; t = copy_tree_body (&id); pointer_map_destroy (decl_map); diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index f04a3f0..29932e8 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -77,12 +77,12 @@ typedef struct copy_body_data is not. */ gimple gimple_call; - /* Exception region the inlined call lie in. */ - int eh_region; + /* Exception landing pad the inlined call lies in. */ + int eh_lp_nr; - /* Take region number in the function being copied, add this value and - get eh region number of the duplicate in the function we inline into. */ - int eh_region_offset; + /* Maps region and landing pad structures from the function being copied + to duplicates created within the function we inline into. */ + struct pointer_map_t *eh_map; /* We use the same mechanism do all sorts of different things. Rather than enumerating the different cases, we categorize the behavior diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 7a9d2bd..73eacf6 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -269,8 +269,7 @@ execute_fixup_cfg (void) } } - if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt)) - remove_stmt_from_eh_region (stmt); + maybe_clean_eh_stmt (stmt); } if (gimple_purge_dead_eh_edges (bb)) diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 4bb3364..9ea70e3 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -316,6 +316,8 @@ extern struct gimple_opt_pass pass_remove_useless_stmts; extern struct gimple_opt_pass pass_lower_cf; extern struct gimple_opt_pass pass_refactor_eh; extern struct gimple_opt_pass pass_lower_eh; +extern struct gimple_opt_pass pass_lower_eh_dispatch; +extern struct gimple_opt_pass pass_lower_resx; extern struct gimple_opt_pass pass_build_cfg; extern struct gimple_opt_pass pass_tree_profile; extern struct gimple_opt_pass pass_early_tree_profile; diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 2c64ab9..a325d75 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -1687,14 +1687,6 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, pp_string (buffer, " [non-local]"); break; - case EXC_PTR_EXPR: - pp_string (buffer, "<<<exception object>>>"); - break; - - case FILTER_EXPR: - pp_string (buffer, "<<<filter object>>>"); - break; - case LOOP_EXPR: pp_string (buffer, "while (1)"); if (!(flags & TDF_SLIM)) @@ -1795,11 +1787,6 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, dump_generic_node (buffer, op0, spc, flags, false); break; - case RESX_EXPR: - pp_string (buffer, "resx "); - dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); - break; - case ASM_EXPR: pp_string (buffer, "__asm__"); if (ASM_VOLATILE_P (node)) diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 1a0622e..00686c8 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -890,8 +890,7 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *), if (!analysis_stage) { update_stmt (stmt); - if (!stmt_could_throw_p (stmt)) - remove_stmt_from_eh_region (stmt); + maybe_clean_eh_stmt (stmt); } } if (deleted) diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 89804a9..9522b28 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -1028,10 +1028,6 @@ process_args: { tree op = gimple_call_arg (call, i); - if (TREE_CODE (op) == EXC_PTR_EXPR - || TREE_CODE (op) == FILTER_EXPR) - continue; - if (TREE_CODE (op) == WITH_SIZE_EXPR) op = TREE_OPERAND (op, 0); diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index 99a039f..67d0472 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -322,15 +322,6 @@ mark_stmt_if_obviously_necessary (gimple stmt, bool aggressive) case GIMPLE_ASSIGN: if (!lhs) lhs = gimple_assign_lhs (stmt); - /* These values are mildly magic bits of the EH runtime. We can't - see the entire lifetime of these values until landing pads are - generated. */ - if (TREE_CODE (lhs) == EXC_PTR_EXPR - || TREE_CODE (lhs) == FILTER_EXPR) - { - mark_stmt_necessary (stmt, true); - return; - } break; case GIMPLE_DEBUG: @@ -817,28 +808,33 @@ propagate_necessity (struct edge_list *el) /* Replace all uses of result of PHI by underlying variable and mark it for renaming. */ -static void +void mark_virtual_phi_result_for_renaming (gimple phi) { bool used = false; imm_use_iterator iter; use_operand_p use_p; gimple stmt; + tree result_ssa, result_var; + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Marking result for renaming : "); print_gimple_stmt (dump_file, phi, 0, TDF_SLIM); fprintf (dump_file, "\n"); } - FOR_EACH_IMM_USE_STMT (stmt, iter, gimple_phi_result (phi)) + + result_ssa = gimple_phi_result (phi); + result_var = SSA_NAME_VAR (result_ssa); + FOR_EACH_IMM_USE_STMT (stmt, iter, result_ssa) { FOR_EACH_IMM_USE_ON_STMT (use_p, iter) - SET_USE (use_p, SSA_NAME_VAR (gimple_phi_result (phi))); + SET_USE (use_p, result_var); update_stmt (stmt); used = true; } if (used) - mark_sym_for_renaming (SSA_NAME_VAR (PHI_RESULT (phi))); + mark_sym_for_renaming (result_var); } /* Remove dead PHI nodes from block BB. */ diff --git a/gcc/tree-ssa-operands.c b/gcc/tree-ssa-operands.c index 16f4a43..28e6ec6 100644 --- a/gcc/tree-ssa-operands.c +++ b/gcc/tree-ssa-operands.c @@ -1000,8 +1000,6 @@ get_expr_operands (gimple stmt, tree *expr_p, int flags) case LABEL_DECL: case CONST_DECL: case CASE_LABEL_EXPR: - case FILTER_EXPR: - case EXC_PTR_EXPR: /* Expressions that make no memory references. */ return; diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 267aeb5..5da6c63 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -1068,9 +1068,7 @@ get_or_alloc_expr_for (tree t) { if (TREE_CODE (t) == SSA_NAME) return get_or_alloc_expr_for_name (t); - else if (is_gimple_min_invariant (t) - || TREE_CODE (t) == EXC_PTR_EXPR - || TREE_CODE (t) == FILTER_EXPR) + else if (is_gimple_min_invariant (t)) return get_or_alloc_expr_for_constant (t); else { @@ -2549,17 +2547,6 @@ can_value_number_call (gimple stmt) return false; } -/* Return true if OP is an exception handler related operation, such as - FILTER_EXPR or EXC_PTR_EXPR. */ - -static bool -is_exception_related (gimple stmt) -{ - return (is_gimple_assign (stmt) - && (gimple_assign_rhs_code (stmt) == FILTER_EXPR - || gimple_assign_rhs_code (stmt) == EXC_PTR_EXPR)); -} - /* Return true if OP is a tree which we can perform PRE on. This may not match the operations we can value number, but in a perfect world would. */ @@ -3885,8 +3872,6 @@ compute_avail (void) switch (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))) { case tcc_unary: - if (is_exception_related (stmt)) - continue; case tcc_binary: case tcc_comparison: { diff --git a/gcc/tree-ssa-propagate.c b/gcc/tree-ssa-propagate.c index ab9cee3..42d89e9 100644 --- a/gcc/tree-ssa-propagate.c +++ b/gcc/tree-ssa-propagate.c @@ -617,10 +617,6 @@ valid_gimple_rhs_p (tree expr) return false; break; - case EXC_PTR_EXPR: - case FILTER_EXPR: - break; - default: return false; } diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c index 255e8a5..4158fbd 100644 --- a/gcc/tree-ssa-sccvn.c +++ b/gcc/tree-ssa-sccvn.c @@ -576,8 +576,6 @@ copy_reference_ops_from_ref (tree ref, VEC(vn_reference_op_s, heap) **result) case CONST_DECL: case RESULT_DECL: case SSA_NAME: - case EXC_PTR_EXPR: - case FILTER_EXPR: temp.op0 = ref; break; case ADDR_EXPR: @@ -688,8 +686,6 @@ ao_ref_init_from_vn_reference (ao_ref *ref, case PARM_DECL: case RESULT_DECL: case SSA_NAME: - case FILTER_EXPR: - case EXC_PTR_EXPR: *op0_p = op->op0; break; diff --git a/gcc/tree-ssa-sink.c b/gcc/tree-ssa-sink.c index 5b9b4be..a9b4b67 100644 --- a/gcc/tree-ssa-sink.c +++ b/gcc/tree-ssa-sink.c @@ -323,8 +323,6 @@ statement_sink_location (gimple stmt, basic_block frombb, code = gimple_assign_rhs_code (stmt); if (stmt_ends_bb_p (stmt) || gimple_has_side_effects (stmt) - || code == EXC_PTR_EXPR - || code == FILTER_EXPR || is_hidden_global_store (stmt) || gimple_has_volatile_ops (stmt) || gimple_vuse (stmt) diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index a9d3132..e5f4a29 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -425,10 +425,6 @@ struct constraint static VEC(constraint_t,heap) *constraints; static alloc_pool constraint_pool; - -DEF_VEC_I(int); -DEF_VEC_ALLOC_I(int, heap); - /* The constraint graph is represented as an array of bitmaps containing successor nodes. */ @@ -1287,10 +1283,6 @@ build_succ_graph (void) static unsigned int changed_count; static sbitmap changed; -DEF_VEC_I(unsigned); -DEF_VEC_ALLOC_I(unsigned,heap); - - /* Strongly Connected Component visitation info. */ struct scc_info @@ -4667,23 +4667,36 @@ get_eh_types_for_runtime (tree list) static void find_decls_types_in_eh_region (eh_region r, struct free_lang_data_d *fld) { - if (r == NULL) - return; - - /* The types referenced in R must first be changed to the EH types - used at runtime. This removes references to FE types in the - region. */ - if (r->type == ERT_CATCH) + switch (r->type) { - tree list = r->u.eh_catch.type_list; - r->u.eh_catch.type_list = get_eh_types_for_runtime (list); - find_decls_types (r->u.eh_catch.type_list, fld); - } - else if (r->type == ERT_ALLOWED_EXCEPTIONS) - { - tree list = r->u.allowed.type_list; - r->u.allowed.type_list = get_eh_types_for_runtime (list); - find_decls_types (r->u.allowed.type_list, fld); + case ERT_CLEANUP: + break; + + case ERT_TRY: + { + eh_catch c; + + /* The types referenced in each catch must first be changed to the + EH types used at runtime. This removes references to FE types + in the region. */ + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + c->type_list = get_eh_types_for_runtime (c->type_list); + walk_tree (&c->type_list, find_decls_types_r, fld, fld->pset); + } + } + break; + + case ERT_ALLOWED_EXCEPTIONS: + r->u.allowed.type_list + = get_eh_types_for_runtime (r->u.allowed.type_list); + walk_tree (&r->u.allowed.type_list, find_decls_types_r, fld, fld->pset); + break; + + case ERT_MUST_NOT_THROW: + walk_tree (&r->u.must_not_throw.failure_decl, + find_decls_types_r, fld, fld->pset); + break; } } @@ -4715,14 +4728,11 @@ find_decls_types_in_node (struct cgraph_node *n, struct free_lang_data_d *fld) find_decls_types (TREE_VALUE (t), fld); /* Traverse EH regions in FN. */ - if (fn->eh->region_array) - { - unsigned i; - eh_region r; - - for (i = 0; VEC_iterate (eh_region, fn->eh->region_array, i, r); i++) - find_decls_types_in_eh_region (r, fld); - } + { + eh_region r; + FOR_ALL_EH_REGION_FN (r, fn) + find_decls_types_in_eh_region (r, fld); + } /* Traverse every statement in FN. */ FOR_EACH_BB_FN (bb, fn) @@ -8880,12 +8890,15 @@ local_define_builtin (const char *name, tree type, enum built_in_function code, /* Call this function after instantiating all builtins that the language front end cares about. This will build the rest of the builtins that - are relied upon by the tree optimizers and the middle-end. */ + are relied upon by the tree optimizers and the middle-end. + + ENABLE_CXA_END_CLEANUP should be true for C++ and Java, where the ARM + EABI requires a slightly different implementation of _Unwind_Resume. */ void -build_common_builtin_nodes (void) +build_common_builtin_nodes (bool enable_cxa_end_cleanup) { - tree tmp, ftype; + tree tmp, tmp2, ftype; if (built_in_decls[BUILT_IN_MEMCPY] == NULL || built_in_decls[BUILT_IN_MEMMOVE] == NULL) @@ -8990,6 +9003,47 @@ build_common_builtin_nodes (void) local_define_builtin ("__builtin_profile_func_exit", ftype, BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit", 0); + if (enable_cxa_end_cleanup && targetm.arm_eabi_unwinder) + { + ftype = build_function_type (void_type_node, void_list_node); + local_define_builtin ("__builtin_unwind_resume", ftype, + BUILT_IN_UNWIND_RESUME, + "__cxa_end_cleanup", ECF_NORETURN); + } + else + { + tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + ftype = build_function_type (void_type_node, tmp); + local_define_builtin ("__builtin_unwind_resume", ftype, + BUILT_IN_UNWIND_RESUME, + (USING_SJLJ_EXCEPTIONS + ? "_Unwind_SjLj_Resume" : "_Unwind_Resume"), + ECF_NORETURN); + } + + /* The exception object and filter values from the runtime. The argument + must be zero before exception lowering, i.e. from the front end. After + exception lowering, it will be the region number for the exception + landing pad. These functions are PURE instead of CONST to prevent + them from being hoisted past the exception edge that will initialize + its value in the landing pad. */ + tmp = tree_cons (NULL_TREE, integer_type_node, void_list_node); + ftype = build_function_type (ptr_type_node, tmp); + local_define_builtin ("__builtin_eh_pointer", ftype, BUILT_IN_EH_POINTER, + "__builtin_eh_pointer", ECF_PURE | ECF_NOTHROW); + + tmp2 = lang_hooks.types.type_for_mode (targetm.eh_return_filter_mode (), 0); + ftype = build_function_type (tmp2, tmp); + local_define_builtin ("__builtin_eh_filter", ftype, BUILT_IN_EH_FILTER, + "__builtin_eh_filter", ECF_PURE | ECF_NOTHROW); + + tmp = tree_cons (NULL_TREE, integer_type_node, void_list_node); + tmp = tree_cons (NULL_TREE, integer_type_node, tmp); + ftype = build_function_type (void_type_node, tmp); + local_define_builtin ("__builtin_eh_copy_values", ftype, + BUILT_IN_EH_COPY_VALUES, + "__builtin_eh_copy_values", ECF_NOTHROW); + /* Complex multiplication and division. These are handled as builtins rather than optabs because emit_library_call_value doesn't support complex. Further, we can do slightly better with folding these @@ -9151,16 +9205,6 @@ build_opaque_vector_type (tree innertype, int nunits) } -/* Build RESX_EXPR with given REGION_NUMBER. */ -tree -build_resx (int region_number) -{ - tree t; - t = build1 (RESX_EXPR, void_type_node, - build_int_cst (NULL_TREE, region_number)); - return t; -} - /* Given an initializer INIT, return TRUE if INIT is zero or some aggregate of zeros. Otherwise return FALSE. */ bool diff --git a/gcc/tree.def b/gcc/tree.def index e7be1d0..71987ef 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -440,12 +440,6 @@ DEFTREECODE (MISALIGNED_INDIRECT_REF, "misaligned_indirect_ref", tcc_reference, identifier or a vtable index. */ DEFTREECODE (OBJ_TYPE_REF, "obj_type_ref", tcc_expression, 3) -/* The exception object from the runtime. */ -DEFTREECODE (EXC_PTR_EXPR, "exc_ptr_expr", tcc_expression, 0) - -/* The filter object from the runtime. */ -DEFTREECODE (FILTER_EXPR, "filter_expr", tcc_expression, 0) - /* Constructor: return an aggregate value made from specified components. In C, this is used only for structure and array initializers. The operand is a sequence of component values made out of a VEC of @@ -878,10 +872,6 @@ DEFTREECODE (SWITCH_EXPR, "switch_expr", tcc_statement, 3) label. CASE_LABEL is the corresponding LABEL_DECL. */ DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 3) -/* RESX. Resume execution after an exception. Operand 0 is a - number indicating the exception region that is being left. */ -DEFTREECODE (RESX_EXPR, "resx_expr", tcc_statement, 1) - /* Used to represent an inline assembly statement. ASM_STRING returns a STRING_CST for the instruction (e.g., "mov x, y"). ASM_OUTPUTS, ASM_INPUTS, and ASM_CLOBBERS represent the outputs, inputs, and clobbers @@ -444,9 +444,6 @@ struct GTY(()) tree_common { ASM_INPUT_P in ASM_EXPR - EH_FILTER_MUST_NOT_THROW in - EH_FILTER_EXPR - TYPE_REF_CAN_ALIAS_ALL in POINTER_TYPE, REFERENCE_TYPE @@ -1659,8 +1656,6 @@ extern void protected_set_expr_location (tree, location_t); /* EH_FILTER_EXPR accessors. */ #define EH_FILTER_TYPES(NODE) TREE_OPERAND (EH_FILTER_EXPR_CHECK (NODE), 0) #define EH_FILTER_FAILURE(NODE) TREE_OPERAND (EH_FILTER_EXPR_CHECK (NODE), 1) -#define EH_FILTER_MUST_NOT_THROW(NODE) \ - (EH_FILTER_EXPR_CHECK (NODE)->base.static_flag) /* OBJ_TYPE_REF accessors. */ #define OBJ_TYPE_REF_EXPR(NODE) TREE_OPERAND (OBJ_TYPE_REF_CHECK (NODE), 0) @@ -2796,6 +2791,11 @@ struct GTY(()) tree_field_decl { #define LABEL_DECL_UID(NODE) \ (LABEL_DECL_CHECK (NODE)->label_decl.label_decl_uid) +/* In a LABEL_DECL, the EH region number for which the label is the + post_landing_pad. */ +#define EH_LANDING_PAD_NR(NODE) \ + (LABEL_DECL_CHECK (NODE)->label_decl.eh_landing_pad_nr) + /* In LABEL_DECL nodes, nonzero means that an error message about jumping into such a binding contour has been printed for this label. */ #define DECL_ERROR_ISSUED(NODE) \ @@ -2804,6 +2804,7 @@ struct GTY(()) tree_field_decl { struct GTY(()) tree_label_decl { struct tree_decl_with_rtl common; int label_decl_uid; + int eh_landing_pad_nr; }; struct GTY(()) tree_result_decl { @@ -3914,7 +3915,6 @@ extern tree build_method_type_directly (tree, tree, tree); extern tree build_method_type (tree, tree); extern tree build_offset_type (tree, tree); extern tree build_complex_type (tree); -extern tree build_resx (int); extern tree array_type_nelts (const_tree); extern bool in_array_bounds_p (tree); extern bool range_in_array_bounds_p (tree); @@ -4937,7 +4937,7 @@ extern int real_minus_onep (const_tree); extern void init_ttree (void); extern void build_common_tree_nodes (bool, bool); extern void build_common_tree_nodes_2 (int); -extern void build_common_builtin_nodes (void); +extern void build_common_builtin_nodes (bool); extern tree build_nonstandard_integer_type (unsigned HOST_WIDE_INT, int); extern tree build_range_type (tree, tree, tree); extern bool subrange_type_for_debug_p (const_tree, tree *, tree *); diff --git a/gcc/value-prof.c b/gcc/value-prof.c index 9774ca2..9109286 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -1087,84 +1087,86 @@ find_func_by_pid (int pid) */ static gimple -gimple_ic (gimple stmt, gimple call, struct cgraph_node *direct_call, +gimple_ic (gimple icall_stmt, struct cgraph_node *direct_call, int prob, gcov_type count, gcov_type all) { - gimple stmt1, stmt2, stmt3; + gimple dcall_stmt, load_stmt, cond_stmt; tree tmp1, tmpv, tmp; - gimple bb1end, bb2end, bb3end; - basic_block bb, bb2, bb3, bb4; + basic_block cond_bb, dcall_bb, icall_bb, join_bb; tree optype = build_pointer_type (void_type_node); - edge e12, e13, e23, e24, e34; + edge e_cd, e_ci, e_di, e_dj, e_ij; gimple_stmt_iterator gsi; - int region; + int lp_nr; - bb = gimple_bb (stmt); - gsi = gsi_for_stmt (stmt); + cond_bb = gimple_bb (icall_stmt); + gsi = gsi_for_stmt (icall_stmt); tmpv = create_tmp_var (optype, "PROF"); tmp1 = create_tmp_var (optype, "PROF"); - stmt1 = gimple_build_assign (tmpv, unshare_expr (gimple_call_fn (call))); + tmp = unshare_expr (gimple_call_fn (icall_stmt)); + load_stmt = gimple_build_assign (tmpv, tmp); + gsi_insert_before (&gsi, load_stmt, GSI_SAME_STMT); tmp = fold_convert (optype, build_addr (direct_call->decl, current_function_decl)); - stmt2 = gimple_build_assign (tmp1, tmp); - stmt3 = gimple_build_cond (NE_EXPR, tmp1, tmpv, NULL_TREE, NULL_TREE); - gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); - gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT); - gsi_insert_before (&gsi, stmt3, GSI_SAME_STMT); - bb1end = stmt3; + load_stmt = gimple_build_assign (tmp1, tmp); + gsi_insert_before (&gsi, load_stmt, GSI_SAME_STMT); - stmt1 = gimple_copy (stmt); - gimple_call_set_fndecl (stmt1, direct_call->decl); - gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); - bb2end = stmt1; - bb3end = stmt; + cond_stmt = gimple_build_cond (EQ_EXPR, tmp1, tmpv, NULL_TREE, NULL_TREE); + gsi_insert_before (&gsi, cond_stmt, GSI_SAME_STMT); + + dcall_stmt = gimple_copy (icall_stmt); + gimple_call_set_fndecl (dcall_stmt, direct_call->decl); + gsi_insert_before (&gsi, dcall_stmt, GSI_SAME_STMT); /* Fix CFG. */ - /* Edge e23 connects bb2 to bb3, etc. */ - e12 = split_block (bb, bb1end); - bb2 = e12->dest; - bb2->count = count; - e23 = split_block (bb2, bb2end); - bb3 = e23->dest; - bb3->count = all - count; - e34 = split_block (bb3, bb3end); - bb4 = e34->dest; - bb4->count = all; + /* Edge e_cd connects cond_bb to dcall_bb, etc; note the first letters. */ + e_cd = split_block (cond_bb, cond_stmt); + dcall_bb = e_cd->dest; + dcall_bb->count = count; - e12->flags &= ~EDGE_FALLTHRU; - e12->flags |= EDGE_FALSE_VALUE; - e12->probability = prob; - e12->count = count; + e_di = split_block (dcall_bb, dcall_stmt); + icall_bb = e_di->dest; + icall_bb->count = all - count; - e13 = make_edge (bb, bb3, EDGE_TRUE_VALUE); - e13->probability = REG_BR_PROB_BASE - prob; - e13->count = all - count; + e_ij = split_block (icall_bb, icall_stmt); + join_bb = e_ij->dest; + join_bb->count = all; - remove_edge (e23); + e_cd->flags = (e_cd->flags & ~EDGE_FALLTHRU) | EDGE_TRUE_VALUE; + e_cd->probability = prob; + e_cd->count = count; + + e_ci = make_edge (cond_bb, icall_bb, EDGE_FALSE_VALUE); + e_ci->probability = REG_BR_PROB_BASE - prob; + e_ci->count = all - count; + + remove_edge (e_di); - e24 = make_edge (bb2, bb4, EDGE_FALLTHRU); - e24->probability = REG_BR_PROB_BASE; - e24->count = count; - e34->probability = REG_BR_PROB_BASE; - e34->count = all - count; + e_dj = make_edge (dcall_bb, join_bb, EDGE_FALLTHRU); + e_dj->probability = REG_BR_PROB_BASE; + e_dj->count = count; + + e_ij->probability = REG_BR_PROB_BASE; + e_ij->count = all - count; /* Fix eh edges */ - region = lookup_stmt_eh_region (stmt); - if (region >= 0 && stmt_could_throw_p (stmt1)) + lp_nr = lookup_stmt_eh_lp (icall_stmt); + if (lp_nr != 0) { - add_stmt_to_eh_region (stmt1, region); - make_eh_edges (stmt1); - } + gimple_purge_dead_eh_edges (join_bb); - if (region >= 0 && stmt_could_throw_p (stmt)) - { - gimple_purge_dead_eh_edges (bb4); - make_eh_edges (stmt); + if (stmt_could_throw_p (dcall_stmt)) + { + add_stmt_to_eh_lp (dcall_stmt, lp_nr); + make_eh_edges (dcall_stmt); + } + + gcc_assert (stmt_could_throw_p (icall_stmt)); + make_eh_edges (icall_stmt); } - return stmt1; + return dcall_stmt; } /* @@ -1220,7 +1222,7 @@ gimple_ic_transform (gimple stmt) if (direct_call == NULL) return false; - modify = gimple_ic (stmt, stmt, direct_call, prob, count, all); + modify = gimple_ic (stmt, direct_call, prob, count, all); if (dump_file) { @@ -1266,89 +1268,79 @@ interesting_stringop_to_profile_p (tree fndecl, gimple call) } } -/* Convert stringop (..., size) +/* Convert stringop (..., vcall_size) into - if (size == VALUE) - stringop (...., VALUE); + if (vcall_size == icall_size) + stringop (..., icall_size); else - stringop (...., size); - assuming constant propagation of VALUE will happen later. -*/ + stringop (..., vcall_size); + assuming we'll propagate a true constant into ICALL_SIZE later. */ + static void -gimple_stringop_fixed_value (gimple stmt, tree value, int prob, gcov_type count, - gcov_type all) +gimple_stringop_fixed_value (gimple vcall_stmt, tree icall_size, int prob, + gcov_type count, gcov_type all) { - gimple stmt1, stmt2, stmt3; - tree tmp1, tmpv; - gimple bb1end, bb2end; - basic_block bb, bb2, bb3, bb4; - edge e12, e13, e23, e24, e34; + gimple tmp_stmt, cond_stmt, icall_stmt; + tree tmp1, tmpv, vcall_size, optype; + basic_block cond_bb, icall_bb, vcall_bb, join_bb; + edge e_ci, e_cv, e_iv, e_ij, e_vj; gimple_stmt_iterator gsi; - tree blck_size = gimple_call_arg (stmt, 2); - tree optype = TREE_TYPE (blck_size); - int region; - bb = gimple_bb (stmt); - gsi = gsi_for_stmt (stmt); + cond_bb = gimple_bb (vcall_stmt); + gsi = gsi_for_stmt (vcall_stmt); - if (gsi_end_p (gsi)) - { - edge_iterator ei; - for (ei = ei_start (bb->succs); (e34 = ei_safe_edge (ei)); ) - if (!(e34->flags & EDGE_ABNORMAL)) - break; - } - else - { - e34 = split_block (bb, stmt); - gsi = gsi_for_stmt (stmt); - } - bb4 = e34->dest; + vcall_size = gimple_call_arg (vcall_stmt, 2); + optype = TREE_TYPE (vcall_size); tmpv = create_tmp_var (optype, "PROF"); tmp1 = create_tmp_var (optype, "PROF"); - stmt1 = gimple_build_assign (tmpv, fold_convert (optype, value)); - stmt2 = gimple_build_assign (tmp1, blck_size); - stmt3 = gimple_build_cond (NE_EXPR, tmp1, tmpv, NULL_TREE, NULL_TREE); - gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); - gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT); - gsi_insert_before (&gsi, stmt3, GSI_SAME_STMT); - bb1end = stmt3; + tmp_stmt = gimple_build_assign (tmpv, fold_convert (optype, icall_size)); + gsi_insert_before (&gsi, tmp_stmt, GSI_SAME_STMT); - stmt1 = gimple_copy (stmt); - gimple_call_set_arg (stmt1, 2, value); - gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT); - region = lookup_stmt_eh_region (stmt); - if (region >= 0) - add_stmt_to_eh_region (stmt1, region); - bb2end = stmt1; + tmp_stmt = gimple_build_assign (tmp1, vcall_size); + gsi_insert_before (&gsi, tmp_stmt, GSI_SAME_STMT); + + cond_stmt = gimple_build_cond (EQ_EXPR, tmp1, tmpv, NULL_TREE, NULL_TREE); + gsi_insert_before (&gsi, cond_stmt, GSI_SAME_STMT); + + icall_stmt = gimple_copy (vcall_stmt); + gimple_call_set_arg (icall_stmt, 2, icall_size); + gsi_insert_before (&gsi, icall_stmt, GSI_SAME_STMT); /* Fix CFG. */ - /* Edge e23 connects bb2 to bb3, etc. */ - e12 = split_block (bb, bb1end); - bb2 = e12->dest; - bb2->count = count; - e23 = split_block (bb2, bb2end); - bb3 = e23->dest; - bb3->count = all - count; + /* Edge e_ci connects cond_bb to icall_bb, etc. */ + e_ci = split_block (cond_bb, cond_stmt); + icall_bb = e_ci->dest; + icall_bb->count = count; - e12->flags &= ~EDGE_FALLTHRU; - e12->flags |= EDGE_FALSE_VALUE; - e12->probability = prob; - e12->count = count; + e_iv = split_block (icall_bb, icall_stmt); + vcall_bb = e_iv->dest; + vcall_bb->count = all - count; - e13 = make_edge (bb, bb3, EDGE_TRUE_VALUE); - e13->probability = REG_BR_PROB_BASE - prob; - e13->count = all - count; + e_vj = split_block (vcall_bb, vcall_stmt); + join_bb = e_vj->dest; + join_bb->count = all; - remove_edge (e23); + e_ci->flags = (e_ci->flags & ~EDGE_FALLTHRU) | EDGE_TRUE_VALUE; + e_ci->probability = prob; + e_ci->count = count; + + e_cv = make_edge (cond_bb, vcall_bb, EDGE_FALSE_VALUE); + e_cv->probability = REG_BR_PROB_BASE - prob; + e_cv->count = all - count; + + remove_edge (e_iv); - e24 = make_edge (bb2, bb4, EDGE_FALLTHRU); - e24->probability = REG_BR_PROB_BASE; - e24->count = count; + e_ij = make_edge (icall_bb, join_bb, EDGE_FALLTHRU); + e_ij->probability = REG_BR_PROB_BASE; + e_ij->count = count; - e34->probability = REG_BR_PROB_BASE; - e34->count = all - count; + e_vj->probability = REG_BR_PROB_BASE; + e_vj->count = all - count; + + /* Because these are all string op builtins, they're all nothrow. */ + gcc_assert (!stmt_could_throw_p (vcall_stmt)); + gcc_assert (!stmt_could_throw_p (icall_stmt)); } /* Find values inside STMT for that we want to measure histograms for diff --git a/gcc/vecprim.h b/gcc/vecprim.h index c546a2b..e9ccc52 100644 --- a/gcc/vecprim.h +++ b/gcc/vecprim.h @@ -23,6 +23,11 @@ along with GCC; see the file COPYING3. If not see DEF_VEC_I(char); DEF_VEC_ALLOC_I(char,heap); +typedef unsigned char uchar; +DEF_VEC_I(uchar); +DEF_VEC_ALLOC_I(uchar,heap); +DEF_VEC_ALLOC_I(uchar,gc); + DEF_VEC_I(int); DEF_VEC_ALLOC_I(int,heap); |