138fd1498Szrj /* Detect paths through the CFG which can never be executed in a conforming
238fd1498Szrj program and isolate them.
338fd1498Szrj
438fd1498Szrj Copyright (C) 2013-2018 Free Software Foundation, Inc.
538fd1498Szrj
638fd1498Szrj This file is part of GCC.
738fd1498Szrj
838fd1498Szrj GCC is free software; you can redistribute it and/or modify
938fd1498Szrj it under the terms of the GNU General Public License as published by
1038fd1498Szrj the Free Software Foundation; either version 3, or (at your option)
1138fd1498Szrj any later version.
1238fd1498Szrj
1338fd1498Szrj GCC is distributed in the hope that it will be useful,
1438fd1498Szrj but WITHOUT ANY WARRANTY; without even the implied warranty of
1538fd1498Szrj MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1638fd1498Szrj GNU General Public License for more details.
1738fd1498Szrj
1838fd1498Szrj You should have received a copy of the GNU General Public License
1938fd1498Szrj along with GCC; see the file COPYING3. If not see
2038fd1498Szrj <http://www.gnu.org/licenses/>. */
2138fd1498Szrj
2238fd1498Szrj #include "config.h"
2338fd1498Szrj #include "system.h"
2438fd1498Szrj #include "coretypes.h"
2538fd1498Szrj #include "backend.h"
2638fd1498Szrj #include "tree.h"
2738fd1498Szrj #include "gimple.h"
2838fd1498Szrj #include "cfghooks.h"
2938fd1498Szrj #include "tree-pass.h"
3038fd1498Szrj #include "ssa.h"
3138fd1498Szrj #include "diagnostic-core.h"
3238fd1498Szrj #include "fold-const.h"
3338fd1498Szrj #include "gimple-iterator.h"
3438fd1498Szrj #include "gimple-walk.h"
3538fd1498Szrj #include "tree-ssa.h"
3638fd1498Szrj #include "cfgloop.h"
3738fd1498Szrj #include "tree-cfg.h"
3838fd1498Szrj #include "cfganal.h"
3938fd1498Szrj #include "intl.h"
4038fd1498Szrj
4138fd1498Szrj
4238fd1498Szrj static bool cfg_altered;
4338fd1498Szrj
4438fd1498Szrj /* Callback for walk_stmt_load_store_ops.
4538fd1498Szrj
4638fd1498Szrj Return TRUE if OP will dereference the tree stored in DATA, FALSE
4738fd1498Szrj otherwise.
4838fd1498Szrj
4938fd1498Szrj This routine only makes a superficial check for a dereference. Thus,
5038fd1498Szrj it must only be used if it is safe to return a false negative. */
5138fd1498Szrj static bool
check_loadstore(gimple * stmt,tree op,tree,void * data)5238fd1498Szrj check_loadstore (gimple *stmt, tree op, tree, void *data)
5338fd1498Szrj {
5438fd1498Szrj if ((TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF)
5538fd1498Szrj && operand_equal_p (TREE_OPERAND (op, 0), (tree)data, 0))
5638fd1498Szrj {
5738fd1498Szrj TREE_THIS_VOLATILE (op) = 1;
5838fd1498Szrj TREE_SIDE_EFFECTS (op) = 1;
5938fd1498Szrj update_stmt (stmt);
6038fd1498Szrj return true;
6138fd1498Szrj }
6238fd1498Szrj return false;
6338fd1498Szrj }
6438fd1498Szrj
6538fd1498Szrj /* Insert a trap after SI and split the block after the trap. */
6638fd1498Szrj
6738fd1498Szrj static void
insert_trap(gimple_stmt_iterator * si_p,tree op)6838fd1498Szrj insert_trap (gimple_stmt_iterator *si_p, tree op)
6938fd1498Szrj {
7038fd1498Szrj /* We want the NULL pointer dereference to actually occur so that
7138fd1498Szrj code that wishes to catch the signal can do so.
7238fd1498Szrj
7338fd1498Szrj If the dereference is a load, then there's nothing to do as the
7438fd1498Szrj LHS will be a throw-away SSA_NAME and the RHS is the NULL dereference.
7538fd1498Szrj
7638fd1498Szrj If the dereference is a store and we can easily transform the RHS,
7738fd1498Szrj then simplify the RHS to enable more DCE. Note that we require the
7838fd1498Szrj statement to be a GIMPLE_ASSIGN which filters out calls on the RHS. */
7938fd1498Szrj gimple *stmt = gsi_stmt (*si_p);
8038fd1498Szrj if (walk_stmt_load_store_ops (stmt, (void *)op, NULL, check_loadstore)
8138fd1498Szrj && is_gimple_assign (stmt)
8238fd1498Szrj && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (stmt))))
8338fd1498Szrj {
8438fd1498Szrj /* We just need to turn the RHS into zero converted to the proper
8538fd1498Szrj type. */
8638fd1498Szrj tree type = TREE_TYPE (gimple_assign_lhs (stmt));
8738fd1498Szrj gimple_assign_set_rhs_code (stmt, INTEGER_CST);
8838fd1498Szrj gimple_assign_set_rhs1 (stmt, fold_convert (type, integer_zero_node));
8938fd1498Szrj update_stmt (stmt);
9038fd1498Szrj }
9138fd1498Szrj
9238fd1498Szrj gcall *new_stmt
9338fd1498Szrj = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
9438fd1498Szrj gimple_seq seq = NULL;
9538fd1498Szrj gimple_seq_add_stmt (&seq, new_stmt);
9638fd1498Szrj
9738fd1498Szrj /* If we had a NULL pointer dereference, then we want to insert the
9838fd1498Szrj __builtin_trap after the statement, for the other cases we want
9938fd1498Szrj to insert before the statement. */
10038fd1498Szrj if (walk_stmt_load_store_ops (stmt, (void *)op,
10138fd1498Szrj check_loadstore,
10238fd1498Szrj check_loadstore))
10338fd1498Szrj {
10438fd1498Szrj gsi_insert_after (si_p, seq, GSI_NEW_STMT);
10538fd1498Szrj if (stmt_ends_bb_p (stmt))
10638fd1498Szrj {
10738fd1498Szrj split_block (gimple_bb (stmt), stmt);
10838fd1498Szrj return;
10938fd1498Szrj }
11038fd1498Szrj }
11138fd1498Szrj else
11238fd1498Szrj gsi_insert_before (si_p, seq, GSI_NEW_STMT);
11338fd1498Szrj
11438fd1498Szrj split_block (gimple_bb (new_stmt), new_stmt);
11538fd1498Szrj *si_p = gsi_for_stmt (stmt);
11638fd1498Szrj }
11738fd1498Szrj
11838fd1498Szrj /* BB when reached via incoming edge E will exhibit undefined behavior
11938fd1498Szrj at STMT. Isolate and optimize the path which exhibits undefined
12038fd1498Szrj behavior.
12138fd1498Szrj
12238fd1498Szrj Isolation is simple. Duplicate BB and redirect E to BB'.
12338fd1498Szrj
12438fd1498Szrj Optimization is simple as well. Replace STMT in BB' with an
12538fd1498Szrj unconditional trap and remove all outgoing edges from BB'.
12638fd1498Szrj
12738fd1498Szrj If RET_ZERO, do not trap, only return NULL.
12838fd1498Szrj
12938fd1498Szrj DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
13038fd1498Szrj
13138fd1498Szrj Return BB'. */
13238fd1498Szrj
13338fd1498Szrj basic_block
isolate_path(basic_block bb,basic_block duplicate,edge e,gimple * stmt,tree op,bool ret_zero)13438fd1498Szrj isolate_path (basic_block bb, basic_block duplicate,
13538fd1498Szrj edge e, gimple *stmt, tree op, bool ret_zero)
13638fd1498Szrj {
13738fd1498Szrj gimple_stmt_iterator si, si2;
13838fd1498Szrj edge_iterator ei;
13938fd1498Szrj edge e2;
14038fd1498Szrj bool impossible = true;
14138fd1498Szrj profile_count count = e->count ();
14238fd1498Szrj
14338fd1498Szrj for (si = gsi_start_bb (bb); gsi_stmt (si) != stmt; gsi_next (&si))
14438fd1498Szrj if (stmt_can_terminate_bb_p (gsi_stmt (si)))
14538fd1498Szrj {
14638fd1498Szrj impossible = false;
14738fd1498Szrj break;
14838fd1498Szrj }
14938fd1498Szrj force_edge_cold (e, impossible);
15038fd1498Szrj
15138fd1498Szrj /* First duplicate BB if we have not done so already and remove all
15238fd1498Szrj the duplicate's outgoing edges as duplicate is going to unconditionally
15338fd1498Szrj trap. Removing the outgoing edges is both an optimization and ensures
15438fd1498Szrj we don't need to do any PHI node updates. */
15538fd1498Szrj if (!duplicate)
15638fd1498Szrj {
15738fd1498Szrj duplicate = duplicate_block (bb, NULL, NULL);
15838fd1498Szrj duplicate->count = profile_count::zero ();
15938fd1498Szrj if (!ret_zero)
16038fd1498Szrj for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
16138fd1498Szrj remove_edge (e2);
16238fd1498Szrj }
16338fd1498Szrj bb->count -= count;
16438fd1498Szrj
16538fd1498Szrj /* Complete the isolation step by redirecting E to reach DUPLICATE. */
16638fd1498Szrj e2 = redirect_edge_and_branch (e, duplicate);
16738fd1498Szrj if (e2)
16838fd1498Szrj {
16938fd1498Szrj flush_pending_stmts (e2);
17038fd1498Szrj
17138fd1498Szrj /* Update profile only when redirection is really processed. */
17238fd1498Szrj bb->count += e->count ();
17338fd1498Szrj }
17438fd1498Szrj
17538fd1498Szrj /* There may be more than one statement in DUPLICATE which exhibits
17638fd1498Szrj undefined behavior. Ultimately we want the first such statement in
17738fd1498Szrj DUPLCIATE so that we're able to delete as much code as possible.
17838fd1498Szrj
17938fd1498Szrj So each time we discover undefined behavior in DUPLICATE, search for
18038fd1498Szrj the statement which triggers undefined behavior. If found, then
18138fd1498Szrj transform the statement into a trap and delete everything after the
18238fd1498Szrj statement. If not found, then this particular instance was subsumed by
18338fd1498Szrj an earlier instance of undefined behavior and there's nothing to do.
18438fd1498Szrj
18538fd1498Szrj This is made more complicated by the fact that we have STMT, which is in
18638fd1498Szrj BB rather than in DUPLICATE. So we set up two iterators, one for each
18738fd1498Szrj block and walk forward looking for STMT in BB, advancing each iterator at
18838fd1498Szrj each step.
18938fd1498Szrj
19038fd1498Szrj When we find STMT the second iterator should point to STMT's equivalent in
19138fd1498Szrj duplicate. If DUPLICATE ends before STMT is found in BB, then there's
19238fd1498Szrj nothing to do.
19338fd1498Szrj
19438fd1498Szrj Ignore labels and debug statements. */
19538fd1498Szrj si = gsi_start_nondebug_after_labels_bb (bb);
19638fd1498Szrj si2 = gsi_start_nondebug_after_labels_bb (duplicate);
19738fd1498Szrj while (!gsi_end_p (si) && !gsi_end_p (si2) && gsi_stmt (si) != stmt)
19838fd1498Szrj {
19938fd1498Szrj gsi_next_nondebug (&si);
20038fd1498Szrj gsi_next_nondebug (&si2);
20138fd1498Szrj }
20238fd1498Szrj
20338fd1498Szrj /* This would be an indicator that we never found STMT in BB, which should
20438fd1498Szrj never happen. */
20538fd1498Szrj gcc_assert (!gsi_end_p (si));
20638fd1498Szrj
20738fd1498Szrj /* If we did not run to the end of DUPLICATE, then SI points to STMT and
20838fd1498Szrj SI2 points to the duplicate of STMT in DUPLICATE. Insert a trap
20938fd1498Szrj before SI2 and remove SI2 and all trailing statements. */
21038fd1498Szrj if (!gsi_end_p (si2))
21138fd1498Szrj {
21238fd1498Szrj if (ret_zero)
21338fd1498Szrj {
21438fd1498Szrj greturn *ret = as_a <greturn *> (gsi_stmt (si2));
21538fd1498Szrj tree zero = build_zero_cst (TREE_TYPE (gimple_return_retval (ret)));
21638fd1498Szrj gimple_return_set_retval (ret, zero);
21738fd1498Szrj update_stmt (ret);
21838fd1498Szrj }
21938fd1498Szrj else
22038fd1498Szrj insert_trap (&si2, op);
22138fd1498Szrj }
22238fd1498Szrj
22338fd1498Szrj return duplicate;
22438fd1498Szrj }
22538fd1498Szrj
22638fd1498Szrj /* Return TRUE if STMT is a div/mod operation using DIVISOR as the divisor.
22738fd1498Szrj FALSE otherwise. */
22838fd1498Szrj
22938fd1498Szrj static bool
is_divmod_with_given_divisor(gimple * stmt,tree divisor)23038fd1498Szrj is_divmod_with_given_divisor (gimple *stmt, tree divisor)
23138fd1498Szrj {
23238fd1498Szrj /* Only assignments matter. */
23338fd1498Szrj if (!is_gimple_assign (stmt))
23438fd1498Szrj return false;
23538fd1498Szrj
23638fd1498Szrj /* Check for every DIV/MOD expression. */
23738fd1498Szrj enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
23838fd1498Szrj if (rhs_code == TRUNC_DIV_EXPR
23938fd1498Szrj || rhs_code == FLOOR_DIV_EXPR
24038fd1498Szrj || rhs_code == CEIL_DIV_EXPR
24138fd1498Szrj || rhs_code == EXACT_DIV_EXPR
24238fd1498Szrj || rhs_code == ROUND_DIV_EXPR
24338fd1498Szrj || rhs_code == TRUNC_MOD_EXPR
24438fd1498Szrj || rhs_code == FLOOR_MOD_EXPR
24538fd1498Szrj || rhs_code == CEIL_MOD_EXPR
24638fd1498Szrj || rhs_code == ROUND_MOD_EXPR)
24738fd1498Szrj {
24838fd1498Szrj /* Pointer equality is fine when DIVISOR is an SSA_NAME, but
24938fd1498Szrj not sufficient for constants which may have different types. */
25038fd1498Szrj if (operand_equal_p (gimple_assign_rhs2 (stmt), divisor, 0))
25138fd1498Szrj return true;
25238fd1498Szrj }
25338fd1498Szrj return false;
25438fd1498Szrj }
25538fd1498Szrj
25638fd1498Szrj /* NAME is an SSA_NAME that we have already determined has the value 0 or NULL.
25738fd1498Szrj
25838fd1498Szrj Return TRUE if USE_STMT uses NAME in a way where a 0 or NULL value results
25938fd1498Szrj in undefined behavior, FALSE otherwise
26038fd1498Szrj
26138fd1498Szrj LOC is used for issuing diagnostics. This case represents potential
26238fd1498Szrj undefined behavior exposed by path splitting and that's reflected in
26338fd1498Szrj the diagnostic. */
26438fd1498Szrj
26538fd1498Szrj bool
stmt_uses_name_in_undefined_way(gimple * use_stmt,tree name,location_t loc)26638fd1498Szrj stmt_uses_name_in_undefined_way (gimple *use_stmt, tree name, location_t loc)
26738fd1498Szrj {
26838fd1498Szrj /* If we are working with a non pointer type, then see
26938fd1498Szrj if this use is a DIV/MOD operation using NAME as the
27038fd1498Szrj divisor. */
27138fd1498Szrj if (!POINTER_TYPE_P (TREE_TYPE (name)))
27238fd1498Szrj {
273*58e805e6Szrj if (!cfun->can_throw_non_call_exceptions)
27438fd1498Szrj return is_divmod_with_given_divisor (use_stmt, name);
27538fd1498Szrj return false;
27638fd1498Szrj }
27738fd1498Szrj
27838fd1498Szrj /* NAME is a pointer, so see if it's used in a context where it must
27938fd1498Szrj be non-NULL. */
28038fd1498Szrj bool by_dereference
28138fd1498Szrj = infer_nonnull_range_by_dereference (use_stmt, name);
28238fd1498Szrj
28338fd1498Szrj if (by_dereference
28438fd1498Szrj || infer_nonnull_range_by_attribute (use_stmt, name))
28538fd1498Szrj {
28638fd1498Szrj
28738fd1498Szrj if (by_dereference)
28838fd1498Szrj {
28938fd1498Szrj warning_at (loc, OPT_Wnull_dereference,
29038fd1498Szrj "potential null pointer dereference");
29138fd1498Szrj if (!flag_isolate_erroneous_paths_dereference)
29238fd1498Szrj return false;
29338fd1498Szrj }
29438fd1498Szrj else
29538fd1498Szrj {
29638fd1498Szrj if (!flag_isolate_erroneous_paths_attribute)
29738fd1498Szrj return false;
29838fd1498Szrj }
29938fd1498Szrj return true;
30038fd1498Szrj }
30138fd1498Szrj return false;
30238fd1498Szrj }
30338fd1498Szrj
30438fd1498Szrj /* Return TRUE if USE_STMT uses 0 or NULL in a context which results in
30538fd1498Szrj undefined behavior, FALSE otherwise.
30638fd1498Szrj
30738fd1498Szrj These cases are explicit in the IL. */
30838fd1498Szrj
30938fd1498Szrj bool
stmt_uses_0_or_null_in_undefined_way(gimple * stmt)31038fd1498Szrj stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
31138fd1498Szrj {
312*58e805e6Szrj if (!cfun->can_throw_non_call_exceptions
31338fd1498Szrj && is_divmod_with_given_divisor (stmt, integer_zero_node))
31438fd1498Szrj return true;
31538fd1498Szrj
31638fd1498Szrj /* By passing null_pointer_node, we can use the
31738fd1498Szrj infer_nonnull_range functions to detect explicit NULL
31838fd1498Szrj pointer dereferences and other uses where a non-NULL
31938fd1498Szrj value is required. */
32038fd1498Szrj
32138fd1498Szrj bool by_dereference
32238fd1498Szrj = infer_nonnull_range_by_dereference (stmt, null_pointer_node);
32338fd1498Szrj if (by_dereference
32438fd1498Szrj || infer_nonnull_range_by_attribute (stmt, null_pointer_node))
32538fd1498Szrj {
32638fd1498Szrj if (by_dereference)
32738fd1498Szrj {
32838fd1498Szrj location_t loc = gimple_location (stmt);
32938fd1498Szrj warning_at (loc, OPT_Wnull_dereference,
33038fd1498Szrj "null pointer dereference");
33138fd1498Szrj if (!flag_isolate_erroneous_paths_dereference)
33238fd1498Szrj return false;
33338fd1498Szrj }
33438fd1498Szrj else
33538fd1498Szrj {
33638fd1498Szrj if (!flag_isolate_erroneous_paths_attribute)
33738fd1498Szrj return false;
33838fd1498Szrj }
33938fd1498Szrj return true;
34038fd1498Szrj }
34138fd1498Szrj return false;
34238fd1498Szrj }
34338fd1498Szrj
34438fd1498Szrj /* Look for PHI nodes which feed statements in the same block where
34538fd1498Szrj the value of the PHI node implies the statement is erroneous.
34638fd1498Szrj
34738fd1498Szrj For example, a NULL PHI arg value which then feeds a pointer
34838fd1498Szrj dereference.
34938fd1498Szrj
35038fd1498Szrj When found isolate and optimize the path associated with the PHI
35138fd1498Szrj argument feeding the erroneous statement. */
35238fd1498Szrj static void
find_implicit_erroneous_behavior(void)35338fd1498Szrj find_implicit_erroneous_behavior (void)
35438fd1498Szrj {
35538fd1498Szrj basic_block bb;
35638fd1498Szrj
35738fd1498Szrj FOR_EACH_BB_FN (bb, cfun)
35838fd1498Szrj {
35938fd1498Szrj gphi_iterator si;
36038fd1498Szrj
36138fd1498Szrj /* Out of an abundance of caution, do not isolate paths to a
36238fd1498Szrj block where the block has any abnormal outgoing edges.
36338fd1498Szrj
36438fd1498Szrj We might be able to relax this in the future. We have to detect
36538fd1498Szrj when we have to split the block with the NULL dereference and
36638fd1498Szrj the trap we insert. We have to preserve abnormal edges out
36738fd1498Szrj of the isolated block which in turn means updating PHIs at
36838fd1498Szrj the targets of those abnormal outgoing edges. */
36938fd1498Szrj if (has_abnormal_or_eh_outgoing_edge_p (bb))
37038fd1498Szrj continue;
37138fd1498Szrj
37238fd1498Szrj
37338fd1498Szrj /* If BB has an edge to itself, then duplication of BB below
37438fd1498Szrj could result in reallocation of BB's PHI nodes. If that happens
37538fd1498Szrj then the loop below over the PHIs would use the old PHI and
37638fd1498Szrj thus invalid information. We don't have a good way to know
37738fd1498Szrj if a PHI has been reallocated, so just avoid isolation in
37838fd1498Szrj this case. */
37938fd1498Szrj if (find_edge (bb, bb))
38038fd1498Szrj continue;
38138fd1498Szrj
38238fd1498Szrj /* First look for a PHI which sets a pointer to NULL and which
38338fd1498Szrj is then dereferenced within BB. This is somewhat overly
38438fd1498Szrj conservative, but probably catches most of the interesting
38538fd1498Szrj cases. */
38638fd1498Szrj for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
38738fd1498Szrj {
38838fd1498Szrj gphi *phi = si.phi ();
38938fd1498Szrj tree lhs = gimple_phi_result (phi);
39038fd1498Szrj
39138fd1498Szrj /* PHI produces a pointer result. See if any of the PHI's
39238fd1498Szrj arguments are NULL.
39338fd1498Szrj
39438fd1498Szrj When we remove an edge, we want to reprocess the current
39538fd1498Szrj index, hence the ugly way we update I for each iteration. */
39638fd1498Szrj basic_block duplicate = NULL;
39738fd1498Szrj for (unsigned i = 0, next_i = 0;
39838fd1498Szrj i < gimple_phi_num_args (phi);
39938fd1498Szrj i = next_i)
40038fd1498Szrj {
40138fd1498Szrj tree op = gimple_phi_arg_def (phi, i);
40238fd1498Szrj edge e = gimple_phi_arg_edge (phi, i);
40338fd1498Szrj imm_use_iterator iter;
40438fd1498Szrj gimple *use_stmt;
40538fd1498Szrj
40638fd1498Szrj next_i = i + 1;
40738fd1498Szrj
40838fd1498Szrj if (TREE_CODE (op) == ADDR_EXPR)
40938fd1498Szrj {
41038fd1498Szrj tree valbase = get_base_address (TREE_OPERAND (op, 0));
41138fd1498Szrj if ((VAR_P (valbase) && !is_global_var (valbase))
41238fd1498Szrj || TREE_CODE (valbase) == PARM_DECL)
41338fd1498Szrj {
41438fd1498Szrj FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
41538fd1498Szrj {
41638fd1498Szrj greturn *return_stmt
41738fd1498Szrj = dyn_cast <greturn *> (use_stmt);
41838fd1498Szrj if (!return_stmt)
41938fd1498Szrj continue;
42038fd1498Szrj
42138fd1498Szrj if (gimple_return_retval (return_stmt) != lhs)
42238fd1498Szrj continue;
42338fd1498Szrj
42438fd1498Szrj if (warning_at (gimple_location (use_stmt),
42538fd1498Szrj OPT_Wreturn_local_addr,
42638fd1498Szrj "function may return address "
42738fd1498Szrj "of local variable"))
42838fd1498Szrj inform (DECL_SOURCE_LOCATION(valbase),
42938fd1498Szrj "declared here");
43038fd1498Szrj
43138fd1498Szrj if (gimple_bb (use_stmt) == bb)
43238fd1498Szrj {
43338fd1498Szrj duplicate = isolate_path (bb, duplicate, e,
43438fd1498Szrj use_stmt, lhs, true);
43538fd1498Szrj
43638fd1498Szrj /* When we remove an incoming edge, we need to
43738fd1498Szrj reprocess the Ith element. */
43838fd1498Szrj next_i = i;
43938fd1498Szrj cfg_altered = true;
44038fd1498Szrj }
44138fd1498Szrj }
44238fd1498Szrj }
44338fd1498Szrj }
44438fd1498Szrj
44538fd1498Szrj if (!integer_zerop (op))
44638fd1498Szrj continue;
44738fd1498Szrj
44838fd1498Szrj location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
44938fd1498Szrj
45038fd1498Szrj /* We've got a NULL PHI argument. Now see if the
45138fd1498Szrj PHI's result is dereferenced within BB. */
45238fd1498Szrj FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
45338fd1498Szrj {
45438fd1498Szrj /* We only care about uses in BB. Catching cases in
45538fd1498Szrj in other blocks would require more complex path
45638fd1498Szrj isolation code. */
45738fd1498Szrj if (gimple_bb (use_stmt) != bb)
45838fd1498Szrj continue;
45938fd1498Szrj
46038fd1498Szrj location_t loc = gimple_location (use_stmt)
46138fd1498Szrj ? gimple_location (use_stmt)
46238fd1498Szrj : phi_arg_loc;
46338fd1498Szrj
46438fd1498Szrj if (stmt_uses_name_in_undefined_way (use_stmt, lhs, loc))
46538fd1498Szrj {
46638fd1498Szrj duplicate = isolate_path (bb, duplicate, e,
46738fd1498Szrj use_stmt, lhs, false);
46838fd1498Szrj
46938fd1498Szrj /* When we remove an incoming edge, we need to
47038fd1498Szrj reprocess the Ith element. */
47138fd1498Szrj next_i = i;
47238fd1498Szrj cfg_altered = true;
47338fd1498Szrj }
47438fd1498Szrj }
47538fd1498Szrj }
47638fd1498Szrj }
47738fd1498Szrj }
47838fd1498Szrj }
47938fd1498Szrj
48038fd1498Szrj /* Look for statements which exhibit erroneous behavior. For example
48138fd1498Szrj a NULL pointer dereference.
48238fd1498Szrj
48338fd1498Szrj When found, optimize the block containing the erroneous behavior. */
48438fd1498Szrj static void
find_explicit_erroneous_behavior(void)48538fd1498Szrj find_explicit_erroneous_behavior (void)
48638fd1498Szrj {
48738fd1498Szrj basic_block bb;
48838fd1498Szrj
48938fd1498Szrj FOR_EACH_BB_FN (bb, cfun)
49038fd1498Szrj {
49138fd1498Szrj gimple_stmt_iterator si;
49238fd1498Szrj
49338fd1498Szrj /* Out of an abundance of caution, do not isolate paths to a
49438fd1498Szrj block where the block has any abnormal outgoing edges.
49538fd1498Szrj
49638fd1498Szrj We might be able to relax this in the future. We have to detect
49738fd1498Szrj when we have to split the block with the NULL dereference and
49838fd1498Szrj the trap we insert. We have to preserve abnormal edges out
49938fd1498Szrj of the isolated block which in turn means updating PHIs at
50038fd1498Szrj the targets of those abnormal outgoing edges. */
50138fd1498Szrj if (has_abnormal_or_eh_outgoing_edge_p (bb))
50238fd1498Szrj continue;
50338fd1498Szrj
50438fd1498Szrj /* Now look at the statements in the block and see if any of
50538fd1498Szrj them explicitly dereference a NULL pointer. This happens
50638fd1498Szrj because of jump threading and constant propagation. */
50738fd1498Szrj for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
50838fd1498Szrj {
50938fd1498Szrj gimple *stmt = gsi_stmt (si);
51038fd1498Szrj
51138fd1498Szrj if (stmt_uses_0_or_null_in_undefined_way (stmt))
51238fd1498Szrj {
51338fd1498Szrj insert_trap (&si, null_pointer_node);
51438fd1498Szrj bb = gimple_bb (gsi_stmt (si));
51538fd1498Szrj
51638fd1498Szrj /* Ignore any more operands on this statement and
51738fd1498Szrj continue the statement iterator (which should
51838fd1498Szrj terminate its loop immediately. */
51938fd1498Szrj cfg_altered = true;
52038fd1498Szrj break;
52138fd1498Szrj }
52238fd1498Szrj
52338fd1498Szrj /* Detect returning the address of a local variable. This only
52438fd1498Szrj becomes undefined behavior if the result is used, so we do not
52538fd1498Szrj insert a trap and only return NULL instead. */
52638fd1498Szrj if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
52738fd1498Szrj {
52838fd1498Szrj tree val = gimple_return_retval (return_stmt);
52938fd1498Szrj if (val && TREE_CODE (val) == ADDR_EXPR)
53038fd1498Szrj {
53138fd1498Szrj tree valbase = get_base_address (TREE_OPERAND (val, 0));
53238fd1498Szrj if ((VAR_P (valbase) && !is_global_var (valbase))
53338fd1498Szrj || TREE_CODE (valbase) == PARM_DECL)
53438fd1498Szrj {
53538fd1498Szrj /* We only need it for this particular case. */
53638fd1498Szrj calculate_dominance_info (CDI_POST_DOMINATORS);
53738fd1498Szrj const char* msg;
53838fd1498Szrj bool always_executed = dominated_by_p
53938fd1498Szrj (CDI_POST_DOMINATORS,
54038fd1498Szrj single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
54138fd1498Szrj if (always_executed)
54238fd1498Szrj msg = N_("function returns address of local variable");
54338fd1498Szrj else
54438fd1498Szrj msg = N_("function may return address of "
54538fd1498Szrj "local variable");
54638fd1498Szrj
54738fd1498Szrj if (warning_at (gimple_location (stmt),
54838fd1498Szrj OPT_Wreturn_local_addr, msg))
54938fd1498Szrj inform (DECL_SOURCE_LOCATION(valbase), "declared here");
55038fd1498Szrj tree zero = build_zero_cst (TREE_TYPE (val));
55138fd1498Szrj gimple_return_set_retval (return_stmt, zero);
55238fd1498Szrj update_stmt (stmt);
55338fd1498Szrj }
55438fd1498Szrj }
55538fd1498Szrj }
55638fd1498Szrj }
55738fd1498Szrj }
55838fd1498Szrj }
55938fd1498Szrj
56038fd1498Szrj /* Search the function for statements which, if executed, would cause
56138fd1498Szrj the program to fault such as a dereference of a NULL pointer.
56238fd1498Szrj
56338fd1498Szrj Such a program can't be valid if such a statement was to execute
56438fd1498Szrj according to ISO standards.
56538fd1498Szrj
56638fd1498Szrj We detect explicit NULL pointer dereferences as well as those implied
56738fd1498Szrj by a PHI argument having a NULL value which unconditionally flows into
56838fd1498Szrj a dereference in the same block as the PHI.
56938fd1498Szrj
57038fd1498Szrj In the former case we replace the offending statement with an
57138fd1498Szrj unconditional trap and eliminate the outgoing edges from the statement's
57238fd1498Szrj basic block. This may expose secondary optimization opportunities.
57338fd1498Szrj
57438fd1498Szrj In the latter case, we isolate the path(s) with the NULL PHI
57538fd1498Szrj feeding the dereference. We can then replace the offending statement
57638fd1498Szrj and eliminate the outgoing edges in the duplicate. Again, this may
57738fd1498Szrj expose secondary optimization opportunities.
57838fd1498Szrj
57938fd1498Szrj A warning for both cases may be advisable as well.
58038fd1498Szrj
58138fd1498Szrj Other statically detectable violations of the ISO standard could be
58238fd1498Szrj handled in a similar way, such as out-of-bounds array indexing. */
58338fd1498Szrj
58438fd1498Szrj static unsigned int
gimple_ssa_isolate_erroneous_paths(void)58538fd1498Szrj gimple_ssa_isolate_erroneous_paths (void)
58638fd1498Szrj {
58738fd1498Szrj initialize_original_copy_tables ();
58838fd1498Szrj
58938fd1498Szrj /* Search all the blocks for edges which, if traversed, will
59038fd1498Szrj result in undefined behavior. */
59138fd1498Szrj cfg_altered = false;
59238fd1498Szrj
59338fd1498Szrj /* First handle cases where traversal of a particular edge
59438fd1498Szrj triggers undefined behavior. These cases require creating
59538fd1498Szrj duplicate blocks and thus new SSA_NAMEs.
59638fd1498Szrj
59738fd1498Szrj We want that process complete prior to the phase where we start
59838fd1498Szrj removing edges from the CFG. Edge removal may ultimately result in
59938fd1498Szrj removal of PHI nodes and thus releasing SSA_NAMEs back to the
60038fd1498Szrj name manager.
60138fd1498Szrj
60238fd1498Szrj If the two processes run in parallel we could release an SSA_NAME
60338fd1498Szrj back to the manager but we could still have dangling references
60438fd1498Szrj to the released SSA_NAME in unreachable blocks.
60538fd1498Szrj that any released names not have dangling references in the IL. */
60638fd1498Szrj find_implicit_erroneous_behavior ();
60738fd1498Szrj find_explicit_erroneous_behavior ();
60838fd1498Szrj
60938fd1498Szrj free_original_copy_tables ();
61038fd1498Szrj
61138fd1498Szrj /* We scramble the CFG and loop structures a bit, clean up
61238fd1498Szrj appropriately. We really should incrementally update the
61338fd1498Szrj loop structures, in theory it shouldn't be that hard. */
61438fd1498Szrj free_dominance_info (CDI_POST_DOMINATORS);
61538fd1498Szrj if (cfg_altered)
61638fd1498Szrj {
61738fd1498Szrj free_dominance_info (CDI_DOMINATORS);
61838fd1498Szrj loops_state_set (LOOPS_NEED_FIXUP);
61938fd1498Szrj return TODO_cleanup_cfg | TODO_update_ssa;
62038fd1498Szrj }
62138fd1498Szrj return 0;
62238fd1498Szrj }
62338fd1498Szrj
62438fd1498Szrj namespace {
62538fd1498Szrj const pass_data pass_data_isolate_erroneous_paths =
62638fd1498Szrj {
62738fd1498Szrj GIMPLE_PASS, /* type */
62838fd1498Szrj "isolate-paths", /* name */
62938fd1498Szrj OPTGROUP_NONE, /* optinfo_flags */
63038fd1498Szrj TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */
63138fd1498Szrj ( PROP_cfg | PROP_ssa ), /* properties_required */
63238fd1498Szrj 0, /* properties_provided */
63338fd1498Szrj 0, /* properties_destroyed */
63438fd1498Szrj 0, /* todo_flags_start */
63538fd1498Szrj 0, /* todo_flags_finish */
63638fd1498Szrj };
63738fd1498Szrj
63838fd1498Szrj class pass_isolate_erroneous_paths : public gimple_opt_pass
63938fd1498Szrj {
64038fd1498Szrj public:
pass_isolate_erroneous_paths(gcc::context * ctxt)64138fd1498Szrj pass_isolate_erroneous_paths (gcc::context *ctxt)
64238fd1498Szrj : gimple_opt_pass (pass_data_isolate_erroneous_paths, ctxt)
64338fd1498Szrj {}
64438fd1498Szrj
64538fd1498Szrj /* opt_pass methods: */
clone()64638fd1498Szrj opt_pass * clone () { return new pass_isolate_erroneous_paths (m_ctxt); }
gate(function *)64738fd1498Szrj virtual bool gate (function *)
64838fd1498Szrj {
64938fd1498Szrj /* If we do not have a suitable builtin function for the trap statement,
65038fd1498Szrj then do not perform the optimization. */
65138fd1498Szrj return (flag_isolate_erroneous_paths_dereference != 0
65238fd1498Szrj || flag_isolate_erroneous_paths_attribute != 0
65338fd1498Szrj || warn_null_dereference);
65438fd1498Szrj }
65538fd1498Szrj
execute(function *)65638fd1498Szrj virtual unsigned int execute (function *)
65738fd1498Szrj {
65838fd1498Szrj return gimple_ssa_isolate_erroneous_paths ();
65938fd1498Szrj }
66038fd1498Szrj
66138fd1498Szrj }; // class pass_isolate_erroneous_paths
66238fd1498Szrj }
66338fd1498Szrj
66438fd1498Szrj gimple_opt_pass *
make_pass_isolate_erroneous_paths(gcc::context * ctxt)66538fd1498Szrj make_pass_isolate_erroneous_paths (gcc::context *ctxt)
66638fd1498Szrj {
66738fd1498Szrj return new pass_isolate_erroneous_paths (ctxt);
66838fd1498Szrj }
669