/* Detect paths through the CFG which can never be executed in a conforming
program and isolate them.
- Copyright (C) 2013 Free Software Foundation, Inc.
+ Copyright (C) 2013-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "alias.h"
+#include "symtab.h"
+#include "options.h"
#include "tree.h"
+#include "fold-const.h"
#include "flags.h"
+#include "predict.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-walk.h"
#include "cfgloop.h"
#include "tree-pass.h"
#include "tree-cfg.h"
+#include "diagnostic-core.h"
+#include "intl.h"
static bool cfg_altered;
update_stmt (stmt);
}
- gimple new_stmt
+ gcall *new_stmt
= gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
gimple_seq seq = NULL;
gimple_seq_add_stmt (&seq, new_stmt);
Optimization is simple as well. Replace STMT in BB' with an
unconditional trap and remove all outgoing edges from BB'.
+ If RET_ZERO, do not trap, only return NULL.
+
DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
Return BB'. */
basic_block
isolate_path (basic_block bb, basic_block duplicate,
- edge e, gimple stmt, tree op)
+ edge e, gimple stmt, tree op, bool ret_zero)
{
gimple_stmt_iterator si, si2;
edge_iterator ei;
if (!duplicate)
{
duplicate = duplicate_block (bb, NULL, NULL);
- for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
- remove_edge (e2);
+ if (!ret_zero)
+ for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
+ remove_edge (e2);
}
/* Complete the isolation step by redirecting E to reach DUPLICATE. */
SI2 points to the duplicate of STMT in DUPLICATE. Insert a trap
before SI2 and remove SI2 and all trailing statements. */
if (!gsi_end_p (si2))
- insert_trap_and_remove_trailing_statements (&si2, op);
+ {
+ if (ret_zero)
+ {
+ greturn *ret = as_a <greturn *> (gsi_stmt (si2));
+ tree zero = build_zero_cst (TREE_TYPE (gimple_return_retval (ret)));
+ gimple_return_set_retval (ret, zero);
+ update_stmt (ret);
+ }
+ else
+ insert_trap_and_remove_trailing_statements (&si2, op);
+ }
return duplicate;
}
FOR_EACH_BB_FN (bb, cfun)
{
- gimple_stmt_iterator si;
+ gphi_iterator si;
/* Out of an abundance of caution, do not isolate paths to a
block where the block has any abnormal outgoing edges.
cases. */
for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
{
- gimple phi = gsi_stmt (si);
+ gphi *phi = si.phi ();
tree lhs = gimple_phi_result (phi);
/* If the result is not a pointer, then there is no need to
i = next_i)
{
tree op = gimple_phi_arg_def (phi, i);
+ edge e = gimple_phi_arg_edge (phi, i);
+ imm_use_iterator iter;
+ gimple use_stmt;
next_i = i + 1;
+ if (TREE_CODE (op) == ADDR_EXPR)
+ {
+ tree valbase = get_base_address (TREE_OPERAND (op, 0));
+ if ((TREE_CODE (valbase) == VAR_DECL
+ && !is_global_var (valbase))
+ || TREE_CODE (valbase) == PARM_DECL)
+ {
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+ {
+ greturn *return_stmt
+ = dyn_cast <greturn *> (use_stmt);
+ if (!return_stmt)
+ continue;
+
+ if (gimple_return_retval (return_stmt) != lhs)
+ continue;
+
+ if (warning_at (gimple_location (use_stmt),
+ OPT_Wreturn_local_addr,
+ "function may return address "
+ "of local variable"))
+ inform (DECL_SOURCE_LOCATION(valbase),
+ "declared here");
+
+ if (gimple_bb (use_stmt) == bb)
+ {
+ duplicate = isolate_path (bb, duplicate, e,
+ use_stmt, lhs, true);
+
+ /* When we remove an incoming edge, we need to
+ reprocess the Ith element. */
+ next_i = i;
+ cfg_altered = true;
+ }
+ }
+ }
+ }
+
if (!integer_zerop (op))
continue;
- edge e = gimple_phi_arg_edge (phi, i);
- imm_use_iterator iter;
- gimple use_stmt;
-
/* We've got a NULL PHI argument. Now see if the
PHI's result is dereferenced within BB. */
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
flag_isolate_erroneous_paths_attribute))
{
- duplicate = isolate_path (bb, duplicate,
- e, use_stmt, lhs);
+ duplicate = isolate_path (bb, duplicate, e,
+ use_stmt, lhs, false);
/* When we remove an incoming edge, we need to
reprocess the Ith element. */
cfg_altered = true;
break;
}
+
+ /* Detect returning the address of a local variable. This only
+ becomes undefined behavior if the result is used, so we do not
+ insert a trap and only return NULL instead. */
+ if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
+ {
+ tree val = gimple_return_retval (return_stmt);
+ if (val && TREE_CODE (val) == ADDR_EXPR)
+ {
+ tree valbase = get_base_address (TREE_OPERAND (val, 0));
+ if ((TREE_CODE (valbase) == VAR_DECL
+ && !is_global_var (valbase))
+ || TREE_CODE (valbase) == PARM_DECL)
+ {
+ /* We only need it for this particular case. */
+ calculate_dominance_info (CDI_POST_DOMINATORS);
+ const char* msg;
+ bool always_executed = dominated_by_p
+ (CDI_POST_DOMINATORS,
+ single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
+ if (always_executed)
+ msg = N_("function returns address of local variable");
+ else
+ msg = N_("function may return address of "
+ "local variable");
+
+ if (warning_at (gimple_location (stmt),
+ OPT_Wreturn_local_addr, msg))
+ inform (DECL_SOURCE_LOCATION(valbase), "declared here");
+ tree zero = build_zero_cst (TREE_TYPE (val));
+ gimple_return_set_retval (return_stmt, zero);
+ update_stmt (stmt);
+ }
+ }
+ }
}
}
}
+
/* Search the function for statements which, if executed, would cause
the program to fault such as a dereference of a NULL pointer.
return 0;
}
-static bool
-gate_isolate_erroneous_paths (void)
-{
- /* If we do not have a suitable builtin function for the trap statement,
- then do not perform the optimization. */
- return (flag_isolate_erroneous_paths_dereference != 0
- || flag_isolate_erroneous_paths_attribute != 0);
-}
-
namespace {
const pass_data pass_data_isolate_erroneous_paths =
{
GIMPLE_PASS, /* type */
"isolate-paths", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */
( PROP_cfg | PROP_ssa ), /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_ssa, /* todo_flags_finish */
+ 0, /* todo_flags_finish */
};
class pass_isolate_erroneous_paths : public gimple_opt_pass
/* opt_pass methods: */
opt_pass * clone () { return new pass_isolate_erroneous_paths (m_ctxt); }
- bool gate () { return gate_isolate_erroneous_paths (); }
- unsigned int execute () { return gimple_ssa_isolate_erroneous_paths (); }
+ virtual bool gate (function *)
+ {
+ /* If we do not have a suitable builtin function for the trap statement,
+ then do not perform the optimization. */
+ return (flag_isolate_erroneous_paths_dereference != 0
+ || flag_isolate_erroneous_paths_attribute != 0);
+ }
+
+ virtual unsigned int execute (function *)
+ {
+ return gimple_ssa_isolate_erroneous_paths ();
+ }
}; // class pass_isolate_erroneous_paths
}