X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gcc%2Ftree-eh.c;h=db02cbdb1b577d1809df7a3313013bf8102a7223;hb=34dbb287c7d6bc68db6bc2eb83b1275dfe99e923;hp=973782ba5a86fd832db34156dbba5d21d01c8d67;hpb=c16fd67643acd718a5355e96c7759b42e8f211f6;p=platform%2Fupstream%2Fgcc.git diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index 973782b..db02cbd 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -1,5 +1,5 @@ /* Exception handling semantics and decomposition for trees. - Copyright (C) 2003-2013 Free Software Foundation, Inc. + Copyright (C) 2003-2014 Free Software Foundation, Inc. This file is part of GCC. @@ -20,35 +20,44 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" +#include "hash-table.h" #include "tm.h" #include "tree.h" +#include "expr.h" +#include "calls.h" #include "flags.h" #include "function.h" #include "except.h" #include "pointer-set.h" -#include "tree-flow.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "cgraph.h" +#include "tree-cfg.h" +#include "tree-phinodes.h" +#include "ssa-iterators.h" +#include "stringpool.h" +#include "tree-ssanames.h" +#include "tree-into-ssa.h" +#include "tree-ssa.h" #include "tree-inline.h" #include "tree-pass.h" #include "langhooks.h" -#include "ggc.h" #include "diagnostic-core.h" -#include "gimple.h" #include "target.h" #include "cfgloop.h" +#include "gimple-low.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. */ typedef union {tree *tp; tree t; gimple g;} treemple; -/* Nonzero if we are using EH to handle cleanups. */ -static int using_eh_for_cleanups_p = 0; - -void -using_eh_for_cleanups (void) -{ - using_eh_for_cleanups_p = 1; -} - /* Misc functions used in this file. */ /* Remember and lookup EH landing pad data for arbitrary statements. @@ -65,7 +74,7 @@ using_eh_for_cleanups (void) /* Add statement T in function IFUN to landing pad NUM. */ -void +static void add_stmt_to_eh_lp_fn (struct function *ifun, gimple t, int num) { struct throw_stmt_node *n; @@ -73,7 +82,7 @@ add_stmt_to_eh_lp_fn (struct function *ifun, gimple t, int num) gcc_assert (num != 0); - n = ggc_alloc_throw_stmt_node (); + n = ggc_alloc (); n->stmt = t; n->lp_nr = num; @@ -193,20 +202,42 @@ struct finally_tree_node gimple parent; }; +/* Hashtable helpers. */ + +struct finally_tree_hasher : typed_free_remove +{ + typedef finally_tree_node value_type; + typedef finally_tree_node compare_type; + static inline hashval_t hash (const value_type *); + static inline bool equal (const value_type *, const compare_type *); +}; + +inline hashval_t +finally_tree_hasher::hash (const value_type *v) +{ + return (intptr_t)v->child.t >> 4; +} + +inline bool +finally_tree_hasher::equal (const value_type *v, const compare_type *c) +{ + return v->child.t == c->child.t; +} + /* Note that this table is *not* marked GTY. It is short-lived. */ -static htab_t finally_tree; +static hash_table finally_tree; static void record_in_finally_tree (treemple child, gimple parent) { struct finally_tree_node *n; - void **slot; + finally_tree_node **slot; n = XNEW (struct finally_tree_node); n->child = child; n->parent = parent; - slot = htab_find_slot (finally_tree, n, INSERT); + slot = finally_tree.find_slot (n, INSERT); gcc_assert (!*slot); *slot = n; } @@ -285,7 +316,7 @@ outside_finally_tree (treemple start, gimple target) do { n.child = start; - p = (struct finally_tree_node *) htab_find (finally_tree, &n); + p = finally_tree.find (&n); if (!p) return true; start.g = p->parent; @@ -1357,9 +1388,6 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) x = gimple_seq_last_stmt (finally); finally_loc = x ? gimple_location (x) : tf_loc; - /* Lower the finally block itself. */ - lower_eh_constructs_1 (state, &finally); - /* Prepare for switch statement generation. */ nlabels = tf->dest_array.length (); return_index = nlabels; @@ -1445,6 +1473,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) x = gimple_build_label (finally_label); gimple_seq_add_stmt (&tf->top_p_seq, x); + lower_eh_constructs_1 (state, &finally); gimple_seq_add_seq (&tf->top_p_seq, finally); /* Redirect each incoming goto edge. */ @@ -1521,6 +1550,8 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) /* Make sure that the last case is the default label, as one is required. Then sort the labels, which is also required in GIMPLE. */ CASE_LOW (last_case) = NULL; + tree tem = case_label_vec.pop (); + gcc_assert (tem == last_case); sort_case_labels (case_label_vec); /* Build the switch statement, setting last_case to be the default @@ -1632,7 +1663,7 @@ lower_try_finally (struct leh_state *state, gimple tp) this_tf.try_finally_expr = tp; this_tf.top_p = tp; this_tf.outer = state; - if (using_eh_for_cleanups_p && !cleanup_is_dead_in (state->cur_region)) + if (using_eh_for_cleanups_p () && !cleanup_is_dead_in (state->cur_region)) { this_tf.region = gen_eh_region_cleanup (state->cur_region); this_state.cur_region = this_tf.region; @@ -1714,7 +1745,7 @@ lower_try_finally (struct leh_state *state, gimple tp) { gimple_seq new_eh_seq = eh_seq; eh_seq = old_eh_seq; - gimple_seq_add_seq(&eh_seq, new_eh_seq); + gimple_seq_add_seq (&eh_seq, new_eh_seq); } } @@ -2092,8 +2123,36 @@ lower_eh_constructs_1 (struct leh_state *state, gimple_seq *pseq) lower_eh_constructs_2 (state, &gsi); } -static unsigned int -lower_eh_constructs (void) +namespace { + +const pass_data pass_data_lower_eh = +{ + GIMPLE_PASS, /* type */ + "eh", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_execute */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + PROP_gimple_leh, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_lower_eh : public gimple_opt_pass +{ +public: + pass_lower_eh (gcc::context *ctxt) + : gimple_opt_pass (pass_data_lower_eh, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_lower_eh + +unsigned int +pass_lower_eh::execute (function *fun) { struct leh_state null_state; gimple_seq bodyp; @@ -2102,7 +2161,7 @@ lower_eh_constructs (void) if (bodyp == NULL) return 0; - finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free); + finally_tree.create (31); eh_region_may_contain_throw_map = BITMAP_ALLOC (NULL); memset (&null_state, 0, sizeof (null_state)); @@ -2120,13 +2179,13 @@ lower_eh_constructs (void) didn't change its value, and we don't have to re-set the function. */ gcc_assert (bodyp == gimple_body (current_function_decl)); - htab_delete (finally_tree); + finally_tree.dispose (); BITMAP_FREE (eh_region_may_contain_throw_map); eh_seq = NULL; /* If this function needs a language specific EH personality routine and the frontend didn't already set one do so now. */ - if (function_needs_eh_personality (cfun) == eh_personality_lang + if (function_needs_eh_personality (fun) == eh_personality_lang && !DECL_FUNCTION_PERSONALITY (current_function_decl)) DECL_FUNCTION_PERSONALITY (current_function_decl) = lang_hooks.eh_personality (); @@ -2134,25 +2193,13 @@ lower_eh_constructs (void) return 0; } -struct gimple_opt_pass pass_lower_eh = +} // anon namespace + +gimple_opt_pass * +make_pass_lower_eh (gcc::context *ctxt) { - { - GIMPLE_PASS, - "eh", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - NULL, /* gate */ - lower_eh_constructs, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_TREE_EH, /* tv_id */ - PROP_gimple_lcf, /* properties_required */ - PROP_gimple_leh, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0 /* todo_flags_finish */ - } -}; + return new pass_lower_eh (ctxt); +} /* Create the multiple edges from an EH_DISPATCH statement to all of the possible handlers for its EH region. Return true if there's @@ -2467,6 +2514,68 @@ operation_could_trap_p (enum tree_code op, bool fp_operation, bool honor_trapv, &handled); } + +/* Returns true if it is possible to prove that the index of + an array access REF (an ARRAY_REF expression) falls into the + array bounds. */ + +static bool +in_array_bounds_p (tree ref) +{ + tree idx = TREE_OPERAND (ref, 1); + tree min, max; + + if (TREE_CODE (idx) != INTEGER_CST) + return false; + + min = array_ref_low_bound (ref); + max = array_ref_up_bound (ref); + if (!min + || !max + || TREE_CODE (min) != INTEGER_CST + || TREE_CODE (max) != INTEGER_CST) + return false; + + if (tree_int_cst_lt (idx, min) + || tree_int_cst_lt (max, idx)) + return false; + + return true; +} + +/* Returns true if it is possible to prove that the range of + an array access REF (an ARRAY_RANGE_REF expression) falls + into the array bounds. */ + +static bool +range_in_array_bounds_p (tree ref) +{ + tree domain_type = TYPE_DOMAIN (TREE_TYPE (ref)); + tree range_min, range_max, min, max; + + range_min = TYPE_MIN_VALUE (domain_type); + range_max = TYPE_MAX_VALUE (domain_type); + if (!range_min + || !range_max + || TREE_CODE (range_min) != INTEGER_CST + || TREE_CODE (range_max) != INTEGER_CST) + return false; + + min = array_ref_low_bound (ref); + max = array_ref_up_bound (ref); + if (!min + || !max + || TREE_CODE (min) != INTEGER_CST + || TREE_CODE (max) != INTEGER_CST) + return false; + + if (tree_int_cst_lt (range_min, min) + || tree_int_cst_lt (max, range_max)) + return false; + + return true; +} + /* Return true if EXPR can trap, as in dereferencing an invalid pointer location or floating point arithmetic. C.f. the rtl version, may_trap_p. This routine expects only GIMPLE lhs or rhs input. */ @@ -2502,12 +2611,6 @@ tree_could_trap_p (tree expr) restart: switch (code) { - case TARGET_MEM_REF: - if (TREE_CODE (TMR_BASE (expr)) == ADDR_EXPR - && !TMR_INDEX (expr) && !TMR_INDEX2 (expr)) - return false; - return !TREE_THIS_NOTRAP (expr); - case COMPONENT_REF: case REALPART_EXPR: case IMAGPART_EXPR: @@ -2534,10 +2637,36 @@ tree_could_trap_p (tree expr) return false; return !in_array_bounds_p (expr); + case TARGET_MEM_REF: case MEM_REF: - if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR) + if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR + && tree_could_trap_p (TREE_OPERAND (TREE_OPERAND (expr, 0), 0))) + return true; + if (TREE_THIS_NOTRAP (expr)) return false; - /* Fallthru. */ + /* We cannot prove that the access is in-bounds when we have + variable-index TARGET_MEM_REFs. */ + if (code == TARGET_MEM_REF + && (TMR_INDEX (expr) || TMR_INDEX2 (expr))) + return true; + if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR) + { + tree base = TREE_OPERAND (TREE_OPERAND (expr, 0), 0); + offset_int off = mem_ref_offset (expr); + if (wi::neg_p (off, SIGNED)) + return true; + if (TREE_CODE (base) == STRING_CST) + return wi::leu_p (TREE_STRING_LENGTH (base), off); + else if (DECL_SIZE_UNIT (base) == NULL_TREE + || TREE_CODE (DECL_SIZE_UNIT (base)) != INTEGER_CST + || wi::leu_p (wi::to_offset (DECL_SIZE_UNIT (base)), off)) + return true; + /* Now we are sure the first byte of the access is inside + the object. */ + return false; + } + return true; + case INDIRECT_REF: return !TREE_THIS_NOTRAP (expr); @@ -2557,13 +2686,13 @@ tree_could_trap_p (tree expr) /* Assume that accesses to weak functions may trap, unless we know they are certainly defined in current TU or in some other LTO partition. */ - if (DECL_WEAK (expr)) + if (DECL_WEAK (expr) && !DECL_COMDAT (expr)) { struct cgraph_node *node; if (!DECL_EXTERNAL (expr)) return false; node = cgraph_function_node (cgraph_get_node (expr), NULL); - if (node && node->symbol.in_other_partition) + if (node && node->in_other_partition) return false; return true; } @@ -2573,13 +2702,13 @@ tree_could_trap_p (tree expr) /* Assume that accesses to weak vars may trap, unless we know they are certainly defined in current TU or in some other LTO partition. */ - if (DECL_WEAK (expr)) + if (DECL_WEAK (expr) && !DECL_COMDAT (expr)) { - struct varpool_node *node; + varpool_node *node; if (!DECL_EXTERNAL (expr)) return false; node = varpool_variable_node (varpool_get_node (expr), NULL); - if (node && node->symbol.in_other_partition) + if (node && node->in_other_partition) return false; return true; } @@ -2981,38 +3110,46 @@ refactor_eh_r (gimple_seq seq) } } -static unsigned -refactor_eh (void) +namespace { + +const pass_data pass_data_refactor_eh = { - refactor_eh_r (gimple_body (current_function_decl)); - return 0; -} + GIMPLE_PASS, /* type */ + "ehopt", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_execute */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; -static bool -gate_refactor_eh (void) +class pass_refactor_eh : public gimple_opt_pass { - return flag_exceptions != 0; -} +public: + pass_refactor_eh (gcc::context *ctxt) + : gimple_opt_pass (pass_data_refactor_eh, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return flag_exceptions != 0; } + virtual unsigned int execute (function *) + { + refactor_eh_r (gimple_body (current_function_decl)); + return 0; + } + +}; // class pass_refactor_eh + +} // anon namespace -struct gimple_opt_pass pass_refactor_eh = +gimple_opt_pass * +make_pass_refactor_eh (gcc::context *ctxt) { - { - GIMPLE_PASS, - "ehopt", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - gate_refactor_eh, /* gate */ - refactor_eh, /* 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 */ - 0 /* todo_flags_finish */ - } -}; + return new pass_refactor_eh (ctxt); +} /* At the end of gimple optimization, we can lower RESX. */ @@ -3166,8 +3303,37 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map) return ret; } -static unsigned -execute_lower_resx (void) +namespace { + +const pass_data pass_data_lower_resx = +{ + GIMPLE_PASS, /* type */ + "resx", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_execute */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_lower_resx : public gimple_opt_pass +{ +public: + pass_lower_resx (gcc::context *ctxt) + : gimple_opt_pass (pass_data_lower_resx, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return flag_exceptions != 0; } + virtual unsigned int execute (function *); + +}; // class pass_lower_resx + +unsigned +pass_lower_resx::execute (function *fun) { basic_block bb; struct pointer_map_t *mnt_map; @@ -3176,7 +3342,7 @@ execute_lower_resx (void) mnt_map = pointer_map_create (); - FOR_EACH_BB (bb) + FOR_EACH_BB_FN (bb, fun) { gimple last = last_stmt (bb); if (last && is_gimple_resx (last)) @@ -3197,31 +3363,13 @@ execute_lower_resx (void) return any_rewritten ? TODO_update_ssa_only_virtuals : 0; } -static bool -gate_lower_resx (void) -{ - return flag_exceptions != 0; -} +} // anon namespace -struct gimple_opt_pass pass_lower_resx = +gimple_opt_pass * +make_pass_lower_resx (gcc::context *ctxt) { - { - GIMPLE_PASS, - "resx", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - gate_lower_resx, /* 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_verify_flow /* todo_flags_finish */ - } -}; + return new pass_lower_resx (ctxt); +} /* Try to optimize var = {v} {CLOBBER} stmts followed just by external throw. */ @@ -3230,14 +3378,48 @@ static void optimize_clobbers (basic_block bb) { gimple_stmt_iterator gsi = gsi_last_bb (bb); + bool any_clobbers = false; + bool seen_stack_restore = false; + edge_iterator ei; + edge e; + + /* Only optimize anything if the bb contains at least one clobber, + ends with resx (checked by caller), optionally contains some + debug stmts or labels, or at most one __builtin_stack_restore + call, and has an incoming EH edge. */ for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi)) { gimple stmt = gsi_stmt (gsi); if (is_gimple_debug (stmt)) continue; - if (!gimple_clobber_p (stmt) - || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME) - return; + if (gimple_clobber_p (stmt)) + { + any_clobbers = true; + continue; + } + if (!seen_stack_restore + && gimple_call_builtin_p (stmt, BUILT_IN_STACK_RESTORE)) + { + seen_stack_restore = true; + continue; + } + if (gimple_code (stmt) == GIMPLE_LABEL) + break; + return; + } + if (!any_clobbers) + return; + FOR_EACH_EDGE (e, ei, bb->preds) + if (e->flags & EDGE_EH) + break; + if (e == NULL) + return; + gsi = gsi_last_bb (bb); + for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + if (!gimple_clobber_p (stmt)) + continue; unlink_stmt_vdef (stmt); gsi_remove (&gsi, true); release_defs (stmt); @@ -3255,6 +3437,7 @@ sink_clobbers (basic_block bb) gimple_stmt_iterator gsi, dgsi; basic_block succbb; bool any_clobbers = false; + unsigned todo = 0; /* Only optimize if BB has a single EH successor and all predecessor edges are EH too. */ @@ -3278,36 +3461,97 @@ sink_clobbers (basic_block bb) continue; if (gimple_code (stmt) == GIMPLE_LABEL) break; - if (!gimple_clobber_p (stmt) - || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME) + if (!gimple_clobber_p (stmt)) return 0; any_clobbers = true; } if (!any_clobbers) return 0; - succbb = single_succ (bb); + edge succe = single_succ_edge (bb); + succbb = succe->dest; + + /* See if there is a virtual PHI node to take an updated virtual + operand from. */ + gimple vphi = NULL; + tree vuse = NULL_TREE; + for (gsi = gsi_start_phis (succbb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + tree res = gimple_phi_result (gsi_stmt (gsi)); + if (virtual_operand_p (res)) + { + vphi = gsi_stmt (gsi); + vuse = res; + break; + } + } + dgsi = gsi_after_labels (succbb); gsi = gsi_last_bb (bb); for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi)) { gimple stmt = gsi_stmt (gsi); + tree lhs; if (is_gimple_debug (stmt)) continue; if (gimple_code (stmt) == GIMPLE_LABEL) break; - unlink_stmt_vdef (stmt); + lhs = gimple_assign_lhs (stmt); + /* Unfortunately we don't have dominance info updated at this + point, so checking if + dominated_by_p (CDI_DOMINATORS, succbb, + gimple_bb (SSA_NAME_DEF_STMT (TREE_OPERAND (lhs, 0))) + would be too costly. Thus, avoid sinking any clobbers that + refer to non-(D) SSA_NAMEs. */ + if (TREE_CODE (lhs) == MEM_REF + && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (lhs, 0))) + { + unlink_stmt_vdef (stmt); + gsi_remove (&gsi, true); + release_defs (stmt); + continue; + } + + /* As we do not change stmt order when sinking across a + forwarder edge we can keep virtual operands in place. */ gsi_remove (&gsi, false); - /* Trigger the operand scanner to cause renaming for virtual - operands for this statement. - ??? Given the simple structure of this code manually - figuring out the reaching definition should not be too hard. */ - if (gimple_vuse (stmt)) - gimple_set_vuse (stmt, NULL_TREE); - gsi_insert_before (&dgsi, stmt, GSI_SAME_STMT); + gsi_insert_before (&dgsi, stmt, GSI_NEW_STMT); + + /* But adjust virtual operands if we sunk across a PHI node. */ + if (vuse) + { + gimple use_stmt; + imm_use_iterator iter; + use_operand_p use_p; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse) + FOR_EACH_IMM_USE_ON_STMT (use_p, iter) + SET_USE (use_p, gimple_vdef (stmt)); + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse)) + { + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (stmt)) = 1; + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse) = 0; + } + /* Adjust the incoming virtual operand. */ + SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe), gimple_vuse (stmt)); + SET_USE (gimple_vuse_op (stmt), vuse); + } + /* If there isn't a single predecessor but no virtual PHI node + arrange for virtual operands to be renamed. */ + else if (gimple_vuse_op (stmt) != NULL_USE_OPERAND_P + && !single_pred_p (succbb)) + { + /* In this case there will be no use of the VDEF of this stmt. + ??? Unless this is a secondary opportunity and we have not + removed unreachable blocks yet, so we cannot assert this. + Which also means we will end up renaming too many times. */ + SET_USE (gimple_vuse_op (stmt), gimple_vop (cfun)); + mark_virtual_operands_for_renaming (cfun); + todo |= TODO_update_ssa_only_virtuals; + } } - return TODO_update_ssa_only_virtuals; + return todo; } /* At the end of inlining, we can lower EH_DISPATCH. Return true when @@ -3332,7 +3576,7 @@ lower_eh_dispatch (basic_block src, gimple stmt) { case ERT_TRY: { - vec labels = vNULL; + auto_vec labels; tree default_label = NULL; eh_catch c; edge_iterator ei; @@ -3420,8 +3664,6 @@ lower_eh_dispatch (basic_block src, gimple stmt) x = gimple_build_switch (filter, default_label, labels); gsi_insert_before (&gsi, x, GSI_SAME_STMT); - - labels.release (); } pointer_set_destroy (seen_values); } @@ -3461,8 +3703,37 @@ lower_eh_dispatch (basic_block src, gimple stmt) return redirected; } -static unsigned -execute_lower_eh_dispatch (void) +namespace { + +const pass_data pass_data_lower_eh_dispatch = +{ + GIMPLE_PASS, /* type */ + "ehdisp", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_execute */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_lower_eh_dispatch : public gimple_opt_pass +{ +public: + pass_lower_eh_dispatch (gcc::context *ctxt) + : gimple_opt_pass (pass_data_lower_eh_dispatch, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *fun) { return fun->eh->region_tree != NULL; } + virtual unsigned int execute (function *); + +}; // class pass_lower_eh_dispatch + +unsigned +pass_lower_eh_dispatch::execute (function *fun) { basic_block bb; int flags = 0; @@ -3470,7 +3741,7 @@ execute_lower_eh_dispatch (void) assign_filter_values (); - FOR_EACH_BB (bb) + FOR_EACH_BB_FN (bb, fun) { gimple last = last_stmt (bb); if (last == NULL) @@ -3494,31 +3765,13 @@ execute_lower_eh_dispatch (void) return flags; } -static bool -gate_lower_eh_dispatch (void) -{ - return cfun->eh->region_tree != NULL; -} +} // anon namespace -struct gimple_opt_pass pass_lower_eh_dispatch = +gimple_opt_pass * +make_pass_lower_eh_dispatch (gcc::context *ctxt) { - { - GIMPLE_PASS, - "ehdisp", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - gate_lower_eh_dispatch, /* 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_verify_flow /* todo_flags_finish */ - } -}; + return new pass_lower_eh_dispatch (ctxt); +} /* Walk statements, see what regions and, optionally, landing pads are really referenced. @@ -3552,7 +3805,7 @@ mark_reachable_handlers (sbitmap *r_reachablep, sbitmap *lp_reachablep) else lp_reachable = NULL; - FOR_EACH_BB (bb) + FOR_EACH_BB_FN (bb, cfun) { gimple_stmt_iterator gsi; @@ -3737,10 +3990,10 @@ unsplit_eh (eh_landing_pad lp) 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) + if (!single_pred_p (bb) || !single_succ_p (bb)) return false; - e_in = EDGE_PRED (bb, 0); - e_out = EDGE_SUCC (bb, 0); + e_in = single_pred_edge (bb); + e_out = single_succ_edge (bb); /* Input edge must be EH and output edge must be normal. */ if ((e_in->flags & EDGE_EH) == 0 || (e_out->flags & EDGE_EH) != 0) @@ -3849,7 +4102,6 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, gimple_stmt_iterator ngsi, ogsi; edge_iterator ei; edge e; - bitmap rename_virts; bitmap ophi_handled; /* The destination block must not be a regular successor for any @@ -3872,7 +4124,6 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, redirect_edge_var_map_clear (e); ophi_handled = BITMAP_ALLOC (NULL); - rename_virts = BITMAP_ALLOC (NULL); /* First, iterate through the PHIs on NEW_BB and set up the edge_var_map for the edges we're going to move. */ @@ -3925,11 +4176,7 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, 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 (virtual_operand_p (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 + /* If we didn't find the PHI, if it's a real variable or a VOP, 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. */ @@ -3952,24 +4199,6 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, goto fail; } - /* 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))) - { - mark_virtual_phi_result_for_renaming (nphi); - remove_phi_node (&ngsi, true); - } - else - gsi_next (&ngsi); - } - } - /* 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) @@ -3997,14 +4226,12 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb, ei_next (&ei); BITMAP_FREE (ophi_handled); - BITMAP_FREE (rename_virts); return true; 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; } @@ -4142,7 +4369,7 @@ cleanup_empty_eh (eh_landing_pad lp) e_out = NULL; break; case 1: - e_out = EDGE_SUCC (bb, 0); + e_out = single_succ_edge (bb); break; default: return false; @@ -4166,8 +4393,11 @@ cleanup_empty_eh (eh_landing_pad lp) /* If the block is totally empty, look for more unsplitting cases. */ if (gsi_end_p (gsi)) { - /* For the degenerate case of an infinite loop bail out. */ - if (infinite_empty_loop_p (e_out)) + /* For the degenerate case of an infinite loop bail out. + If bb has no successors and is totally empty, which can happen e.g. + because of incorrect noreturn attribute, bail out too. */ + if (e_out == NULL + || infinite_empty_loop_p (e_out)) return ret; return ret | cleanup_empty_eh_unsplit (bb, e_out, lp); @@ -4290,7 +4520,7 @@ cleanup_all_empty_eh (void) 2) MUST_NOT_THROW regions that became dead because of 1) are optimized out 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. + 4) Eh tree is pruned for regions no longer necessary. TODO: Push MUST_NOT_THROW regions to the root of the EH tree. Unify those that have the same failure decl and locus. @@ -4304,11 +4534,12 @@ execute_cleanup_eh_1 (void) remove_unreachable_handlers (); /* Watch out for the region tree vanishing due to all unreachable. */ - if (cfun->eh->region_tree && optimize) + if (cfun->eh->region_tree) { bool changed = false; - changed |= unsplit_all_eh (); + if (optimize) + changed |= unsplit_all_eh (); changed |= cleanup_all_empty_eh (); if (changed) @@ -4331,8 +4562,42 @@ execute_cleanup_eh_1 (void) return 0; } -static unsigned int -execute_cleanup_eh (void) +namespace { + +const pass_data pass_data_cleanup_eh = +{ + GIMPLE_PASS, /* type */ + "ehcleanup", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_execute */ + TV_TREE_EH, /* tv_id */ + PROP_gimple_lcf, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_cleanup_eh : public gimple_opt_pass +{ +public: + pass_cleanup_eh (gcc::context *ctxt) + : gimple_opt_pass (pass_data_cleanup_eh, ctxt) + {} + + /* opt_pass methods: */ + opt_pass * clone () { return new pass_cleanup_eh (m_ctxt); } + virtual bool gate (function *fun) + { + return fun->eh != NULL && fun->eh->region_tree != NULL; + } + + virtual unsigned int execute (function *); + +}; // class pass_cleanup_eh + +unsigned int +pass_cleanup_eh::execute (function *fun) { int ret = execute_cleanup_eh_1 (); @@ -4340,36 +4605,19 @@ execute_cleanup_eh (void) clear it. This exposes cross-language inlining opportunities and avoids references to a never defined personality routine. */ if (DECL_FUNCTION_PERSONALITY (current_function_decl) - && function_needs_eh_personality (cfun) != eh_personality_lang) + && function_needs_eh_personality (fun) != eh_personality_lang) DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE; return ret; } -static bool -gate_cleanup_eh (void) +} // anon namespace + +gimple_opt_pass * +make_pass_cleanup_eh (gcc::context *ctxt) { - return cfun->eh != NULL && cfun->eh->region_tree != NULL; + return new pass_cleanup_eh (ctxt); } - -struct gimple_opt_pass pass_cleanup_eh = { - { - GIMPLE_PASS, - "ehcleanup", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - gate_cleanup_eh, /* gate */ - execute_cleanup_eh, /* 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 */ - 0 /* todo_flags_finish */ - } -}; /* Verify that BB containing STMT as the last statement, has precisely the edge that make_eh_edges would create. */