/* 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.
#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 "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. */
gcc_assert (num != 0);
- n = ggc_alloc_throw_stmt_node ();
+ n = ggc_alloc<throw_stmt_node> ();
n->stmt = t;
n->lp_nr = num;
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;
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. */
/* 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
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;
/* 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 ();
return 0;
}
-namespace {
-
-const pass_data pass_data_lower_eh =
-{
- GIMPLE_PASS, /* type */
- "eh", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- false, /* has_gate */
- 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: */
- unsigned int execute () { return lower_eh_constructs (); }
-
-}; // class pass_lower_eh
-
} // anon namespace
gimple_opt_pass *
&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. */
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:
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);
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;
}
LTO partition. */
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;
}
}
}
-static unsigned
-refactor_eh (void)
-{
- refactor_eh_r (gimple_body (current_function_decl));
- return 0;
-}
-
-static bool
-gate_refactor_eh (void)
-{
- return flag_exceptions != 0;
-}
-
namespace {
const pass_data pass_data_refactor_eh =
GIMPLE_PASS, /* type */
"ehopt", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
{}
/* opt_pass methods: */
- bool gate () { return gate_refactor_eh (); }
- unsigned int execute () { return refactor_eh (); }
+ 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
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;
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))
return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
}
-static bool
-gate_lower_resx (void)
-{
- return flag_exceptions != 0;
-}
-
-namespace {
-
-const pass_data pass_data_lower_resx =
-{
- GIMPLE_PASS, /* type */
- "resx", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
- 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 */
-};
-
-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: */
- bool gate () { return gate_lower_resx (); }
- unsigned int execute () { return execute_lower_resx (); }
-
-}; // class pass_lower_resx
-
} // anon namespace
gimple_opt_pass *
{
case ERT_TRY:
{
- vec<tree> labels = vNULL;
+ auto_vec<tree> labels;
tree default_label = NULL;
eh_catch c;
edge_iterator ei;
x = gimple_build_switch (filter, default_label, labels);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
-
- labels.release ();
}
pointer_set_destroy (seen_values);
}
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;
assign_filter_values ();
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fun)
{
gimple last = last_stmt (bb);
if (last == NULL)
return flags;
}
-static bool
-gate_lower_eh_dispatch (void)
-{
- return cfun->eh->region_tree != NULL;
-}
-
-namespace {
-
-const pass_data pass_data_lower_eh_dispatch =
-{
- GIMPLE_PASS, /* type */
- "ehdisp", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
- 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 */
-};
-
-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: */
- bool gate () { return gate_lower_eh_dispatch (); }
- unsigned int execute () { return execute_lower_eh_dispatch (); }
-
-}; // class pass_lower_eh_dispatch
-
} // anon namespace
gimple_opt_pass *
else
lp_reachable = NULL;
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
gimple_stmt_iterator gsi;
/* 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);
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)
return 0;
}
-static unsigned int
-execute_cleanup_eh (void)
-{
- int ret = execute_cleanup_eh_1 ();
-
- /* If the function no longer needs an EH personality routine
- 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)
- DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE;
-
- return ret;
-}
-
-static bool
-gate_cleanup_eh (void)
-{
- return cfun->eh != NULL && cfun->eh->region_tree != NULL;
-}
-
namespace {
const pass_data pass_data_cleanup_eh =
GIMPLE_PASS, /* type */
"ehcleanup", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_ssa, /* todo_flags_finish */
+ 0, /* todo_flags_finish */
};
class pass_cleanup_eh : public gimple_opt_pass
/* opt_pass methods: */
opt_pass * clone () { return new pass_cleanup_eh (m_ctxt); }
- bool gate () { return gate_cleanup_eh (); }
- unsigned int execute () { return execute_cleanup_eh (); }
+ 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 ();
+
+ /* If the function no longer needs an EH personality routine
+ 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 (fun) != eh_personality_lang)
+ DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE;
+
+ return ret;
+}
+
} // anon namespace
gimple_opt_pass *