/* Support for simple predicate analysis. Copyright (C) 2001-2022 Free Software Foundation, Inc. Contributed by Xinliang David Li Generalized by Martin Sebor This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" #include "backend.h" #include "tree.h" #include "gimple.h" #include "tree-pass.h" #include "ssa.h" #include "gimple-pretty-print.h" #include "diagnostic-core.h" #include "fold-const.h" #include "gimple-iterator.h" #include "tree-ssa.h" #include "tree-cfg.h" #include "cfghooks.h" #include "attribs.h" #include "builtins.h" #include "calls.h" #include "value-query.h" #include "gimple-predicate-analysis.h" #define DEBUG_PREDICATE_ANALYZER 1 /* Return true if BB1 is postdominating BB2 and BB1 is not a loop exit bb. The loop exit bb check is simple and does not cover all cases. */ static bool is_non_loop_exit_postdominating (basic_block bb1, basic_block bb2) { if (!dominated_by_p (CDI_POST_DOMINATORS, bb2, bb1)) return false; if (single_pred_p (bb1) && !single_succ_p (bb2)) return false; return true; } /* Find BB's closest postdominator that is its control equivalent (i.e., that's controlled by the same predicate). */ static inline basic_block find_control_equiv_block (basic_block bb) { basic_block pdom = get_immediate_dominator (CDI_POST_DOMINATORS, bb); /* Skip the postdominating bb that is also a loop exit. */ if (!is_non_loop_exit_postdominating (pdom, bb)) return NULL; /* If the postdominator is dominated by BB, return it. */ if (dominated_by_p (CDI_DOMINATORS, pdom, bb)) return pdom; return NULL; } /* Return true if X1 is the negation of X2. */ static inline bool pred_neg_p (const pred_info &x1, const pred_info &x2) { if (!operand_equal_p (x1.pred_lhs, x2.pred_lhs, 0) || !operand_equal_p (x1.pred_rhs, x2.pred_rhs, 0)) return false; tree_code c1 = x1.cond_code, c2; if (x1.invert == x2.invert) c2 = invert_tree_comparison (x2.cond_code, false); else c2 = x2.cond_code; return c1 == c2; } /* Return whether the condition (VAL CMPC BOUNDARY) is true. */ static bool is_value_included_in (tree val, tree boundary, tree_code cmpc) { /* Only handle integer constant here. */ if (TREE_CODE (val) != INTEGER_CST || TREE_CODE (boundary) != INTEGER_CST) return true; bool inverted = false; if (cmpc == GE_EXPR || cmpc == GT_EXPR || cmpc == NE_EXPR) { cmpc = invert_tree_comparison (cmpc, false); inverted = true; } bool result; if (cmpc == EQ_EXPR) result = tree_int_cst_equal (val, boundary); else if (cmpc == LT_EXPR) result = tree_int_cst_lt (val, boundary); else { gcc_assert (cmpc == LE_EXPR); result = tree_int_cst_le (val, boundary); } if (inverted) result ^= 1; return result; } /* Format the vector of edges EV as a string. */ static std::string format_edge_vec (const vec &ev) { std::string str; unsigned n = ev.length (); for (unsigned i = 0; i < n; ++i) { char es[32]; const_edge e = ev[i]; sprintf (es, "%u", e->src->index); str += es; if (i + 1 < n) str += " -> "; } return str; } /* Format the first N elements of the array of vector of edges EVA as a string. */ static std::string format_edge_vecs (const vec eva[], unsigned n) { std::string str; for (unsigned i = 0; i != n; ++i) { str += '{'; str += format_edge_vec (eva[i]); str += '}'; if (i + 1 < n) str += ", "; } return str; } /* Dump a single pred_info to DUMP_FILE. */ static void dump_pred_info (const pred_info &pred) { if (pred.invert) fprintf (dump_file, "NOT ("); print_generic_expr (dump_file, pred.pred_lhs); fprintf (dump_file, " %s ", op_symbol_code (pred.cond_code)); print_generic_expr (dump_file, pred.pred_rhs); if (pred.invert) fputc (')', dump_file); } /* Dump a pred_chain to DUMP_FILE. */ static void dump_pred_chain (const pred_chain &chain) { unsigned np = chain.length (); if (np > 1) fprintf (dump_file, "AND ("); for (unsigned j = 0; j < np; j++) { dump_pred_info (chain[j]); if (j < np - 1) fprintf (dump_file, ", "); else if (j > 0) fputc (')', dump_file); } } /* Dump the predicate chain PREDS for STMT, prefixed by MSG. */ static void dump_predicates (gimple *stmt, const pred_chain_union &preds, const char *msg) { fprintf (dump_file, "%s", msg); if (stmt) { print_gimple_stmt (dump_file, stmt, 0); fprintf (dump_file, "is guarded by:\n"); } unsigned np = preds.length (); if (np > 1) fprintf (dump_file, "OR ("); for (unsigned i = 0; i < np; i++) { dump_pred_chain (preds[i]); if (i < np - 1) fprintf (dump_file, ", "); else if (i > 0) fputc (')', dump_file); } fputc ('\n', dump_file); } /* Dump the first NCHAINS elements of the DEP_CHAINS array into DUMP_FILE. */ static void dump_dep_chains (const auto_vec dep_chains[], unsigned nchains) { if (!dump_file) return; for (unsigned i = 0; i != nchains; ++i) { const auto_vec &v = dep_chains[i]; unsigned n = v.length (); for (unsigned j = 0; j != n; ++j) { fprintf (dump_file, "%u", v[j]->src->index); if (j + 1 < n) fprintf (dump_file, " -> "); } fputc ('\n', dump_file); } } /* Return the 'normalized' conditional code with operand swapping and condition inversion controlled by SWAP_COND and INVERT. */ static tree_code get_cmp_code (tree_code orig_cmp_code, bool swap_cond, bool invert) { tree_code tc = orig_cmp_code; if (swap_cond) tc = swap_tree_comparison (orig_cmp_code); if (invert) tc = invert_tree_comparison (tc, false); switch (tc) { case LT_EXPR: case LE_EXPR: case GT_EXPR: case GE_EXPR: case EQ_EXPR: case NE_EXPR: break; default: return ERROR_MARK; } return tc; } /* Return true if PRED is common among all predicate chains in PREDS (and therefore can be factored out). */ static bool find_matching_predicate_in_rest_chains (const pred_info &pred, const pred_chain_union &preds) { /* Trival case. */ if (preds.length () == 1) return true; for (unsigned i = 1; i < preds.length (); i++) { bool found = false; const pred_chain &chain = preds[i]; unsigned n = chain.length (); for (unsigned j = 0; j < n; j++) { const pred_info &pred2 = chain[j]; /* Can relax the condition comparison to not use address comparison. However, the most common case is that multiple control dependent paths share a common path prefix, so address comparison should be ok. */ if (operand_equal_p (pred2.pred_lhs, pred.pred_lhs, 0) && operand_equal_p (pred2.pred_rhs, pred.pred_rhs, 0) && pred2.invert == pred.invert) { found = true; break; } } if (!found) return false; } return true; } /* Find a predicate to examine against paths of interest. If there is no predicate of the "FLAG_VAR CMP CONST" form, try to find one of that's the form "FLAG_VAR CMP FLAG_VAR" with value range info. PHI is the phi node whose incoming (interesting) paths need to be examined. On success, return the comparison code, set defintion gimple of FLAG_DEF and BOUNDARY_CST. Otherwise return ERROR_MARK. */ static tree_code find_var_cmp_const (pred_chain_union preds, gphi *phi, gimple **flag_def, tree *boundary_cst) { tree_code vrinfo_code = ERROR_MARK; gimple *vrinfo_def = NULL; tree vrinfo_cst = NULL; gcc_assert (preds.length () > 0); pred_chain chain = preds[0]; for (unsigned i = 0; i < chain.length (); i++) { bool use_vrinfo_p = false; const pred_info &pred = chain[i]; tree cond_lhs = pred.pred_lhs; tree cond_rhs = pred.pred_rhs; if (cond_lhs == NULL_TREE || cond_rhs == NULL_TREE) continue; tree_code code = get_cmp_code (pred.cond_code, false, pred.invert); if (code == ERROR_MARK) continue; /* Convert to the canonical form SSA_NAME CMP CONSTANT. */ if (TREE_CODE (cond_lhs) == SSA_NAME && is_gimple_constant (cond_rhs)) ; else if (TREE_CODE (cond_rhs) == SSA_NAME && is_gimple_constant (cond_lhs)) { std::swap (cond_lhs, cond_rhs); if ((code = get_cmp_code (code, true, false)) == ERROR_MARK) continue; } /* Check if we can take advantage of FLAG_VAR COMP FLAG_VAR predicate with value range info. Note only first of such case is handled. */ else if (vrinfo_code == ERROR_MARK && TREE_CODE (cond_lhs) == SSA_NAME && TREE_CODE (cond_rhs) == SSA_NAME) { gimple* lhs_def = SSA_NAME_DEF_STMT (cond_lhs); if (!lhs_def || gimple_code (lhs_def) != GIMPLE_PHI || gimple_bb (lhs_def) != gimple_bb (phi)) { std::swap (cond_lhs, cond_rhs); if ((code = get_cmp_code (code, true, false)) == ERROR_MARK) continue; } /* Check value range info of rhs, do following transforms: flag_var < [min, max] -> flag_var < max flag_var > [min, max] -> flag_var > min We can also transform LE_EXPR/GE_EXPR to LT_EXPR/GT_EXPR: flag_var <= [min, max] -> flag_var < [min, max+1] flag_var >= [min, max] -> flag_var > [min-1, max] if no overflow/wrap. */ tree type = TREE_TYPE (cond_lhs); value_range r; if (!INTEGRAL_TYPE_P (type) || !get_range_query (cfun)->range_of_expr (r, cond_rhs) || r.kind () != VR_RANGE) continue; wide_int min = r.lower_bound (); wide_int max = r.upper_bound (); if (code == LE_EXPR && max != wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type))) { code = LT_EXPR; max = max + 1; } if (code == GE_EXPR && min != wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type))) { code = GT_EXPR; min = min - 1; } if (code == LT_EXPR) cond_rhs = wide_int_to_tree (type, max); else if (code == GT_EXPR) cond_rhs = wide_int_to_tree (type, min); else continue; use_vrinfo_p = true; } else continue; if ((*flag_def = SSA_NAME_DEF_STMT (cond_lhs)) == NULL) continue; if (gimple_code (*flag_def) != GIMPLE_PHI || gimple_bb (*flag_def) != gimple_bb (phi) || !find_matching_predicate_in_rest_chains (pred, preds)) continue; /* Return if any "flag_var comp const" predicate is found. */ if (!use_vrinfo_p) { *boundary_cst = cond_rhs; return code; } /* Record if any "flag_var comp flag_var[vinfo]" predicate is found. */ else if (vrinfo_code == ERROR_MARK) { vrinfo_code = code; vrinfo_def = *flag_def; vrinfo_cst = cond_rhs; } } /* Return the "flag_var cmp flag_var[vinfo]" predicate we found. */ if (vrinfo_code != ERROR_MARK) { *flag_def = vrinfo_def; *boundary_cst = vrinfo_cst; } return vrinfo_code; } /* Return true if all interesting opnds are pruned, false otherwise. PHI is the phi node with interesting operands, OPNDS is the bitmap of the interesting operand positions, FLAG_DEF is the statement defining the flag guarding the use of the PHI output, BOUNDARY_CST is the const value used in the predicate associated with the flag, CMP_CODE is the comparison code used in the predicate, VISITED_PHIS is the pointer set of phis visited, and VISITED_FLAG_PHIS is the pointer to the pointer set of flag definitions that are also phis. Example scenario: BB1: flag_1 = phi <0, 1> // (1) var_1 = phi BB2: flag_2 = phi <0, flag_1, flag_1> // (2) var_2 = phi if (flag_2 == 1) goto BB3; BB3: use of var_2 // (3) Because some flag arg in (1) is not constant, if we do not look into the flag phis recursively, it is conservatively treated as unknown and var_1 is thought to flow into use at (3). Since var_1 is potentially uninitialized a false warning will be emitted. Checking recursively into (1), the compiler can find out that only some_val (which is defined) can flow into (3) which is OK. */ static bool prune_phi_opnds (gphi *phi, unsigned opnds, gphi *flag_def, tree boundary_cst, tree_code cmp_code, predicate::func_t &eval, hash_set *visited_phis, bitmap *visited_flag_phis) { /* The Boolean predicate guarding the PHI definition. Initialized lazily from PHI in the first call to is_use_guarded() and cached for subsequent iterations. */ predicate def_preds (eval); unsigned n = MIN (eval.max_phi_args, gimple_phi_num_args (flag_def)); for (unsigned i = 0; i < n; i++) { if (!MASK_TEST_BIT (opnds, i)) continue; tree flag_arg = gimple_phi_arg_def (flag_def, i); if (!is_gimple_constant (flag_arg)) { if (TREE_CODE (flag_arg) != SSA_NAME) return false; gphi *flag_arg_def = dyn_cast (SSA_NAME_DEF_STMT (flag_arg)); if (!flag_arg_def) return false; tree phi_arg = gimple_phi_arg_def (phi, i); if (TREE_CODE (phi_arg) != SSA_NAME) return false; gphi *phi_arg_def = dyn_cast (SSA_NAME_DEF_STMT (phi_arg)); if (!phi_arg_def) return false; if (gimple_bb (phi_arg_def) != gimple_bb (flag_arg_def)) return false; if (!*visited_flag_phis) *visited_flag_phis = BITMAP_ALLOC (NULL); tree phi_result = gimple_phi_result (flag_arg_def); if (bitmap_bit_p (*visited_flag_phis, SSA_NAME_VERSION (phi_result))) return false; bitmap_set_bit (*visited_flag_phis, SSA_NAME_VERSION (phi_result)); /* Now recursively try to prune the interesting phi args. */ unsigned opnds_arg_phi = eval.phi_arg_set (phi_arg_def); if (!prune_phi_opnds (phi_arg_def, opnds_arg_phi, flag_arg_def, boundary_cst, cmp_code, eval, visited_phis, visited_flag_phis)) return false; bitmap_clear_bit (*visited_flag_phis, SSA_NAME_VERSION (phi_result)); continue; } /* Now check if the constant is in the guarded range. */ if (is_value_included_in (flag_arg, boundary_cst, cmp_code)) { /* Now that we know that this undefined edge is not pruned. If the operand is defined by another phi, we can further prune the incoming edges of that phi by checking the predicates of this operands. */ tree opnd = gimple_phi_arg_def (phi, i); gimple *opnd_def = SSA_NAME_DEF_STMT (opnd); if (gphi *opnd_def_phi = dyn_cast (opnd_def)) { unsigned opnds2 = eval.phi_arg_set (opnd_def_phi); if (!MASK_EMPTY (opnds2)) { edge opnd_edge = gimple_phi_arg_edge (phi, i); if (def_preds.is_use_guarded (phi, opnd_edge->src, opnd_def_phi, opnds2, visited_phis)) return false; } } else return false; } } return true; } /* Recursively compute the set PHI's incoming edges with "uninteresting" operands of a phi chain, i.e., those for which EVAL returns false. CD_ROOT is the control dependence root from which edges are collected up the CFG nodes that it's dominated by. *EDGES holds the result, and VISITED is used for detecting cycles. */ static void collect_phi_def_edges (gphi *phi, basic_block cd_root, auto_vec *edges, predicate::func_t &eval, hash_set *visited) { if (visited->elements () == 0 && DEBUG_PREDICATE_ANALYZER && dump_file) { fprintf (dump_file, "%s for cd_root %u and ", __func__, cd_root->index); print_gimple_stmt (dump_file, phi, 0); } if (visited->add (phi)) return; unsigned n = gimple_phi_num_args (phi); for (unsigned i = 0; i < n; i++) { edge opnd_edge = gimple_phi_arg_edge (phi, i); tree opnd = gimple_phi_arg_def (phi, i); if (TREE_CODE (opnd) == SSA_NAME) { gimple *def = SSA_NAME_DEF_STMT (opnd); if (gimple_code (def) == GIMPLE_PHI && dominated_by_p (CDI_DOMINATORS, gimple_bb (def), cd_root)) collect_phi_def_edges (as_a (def), cd_root, edges, eval, visited); else if (!eval (opnd)) { if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\tFound def edge %i -> %i for cd_root %i " "and operand %u of: ", opnd_edge->src->index, opnd_edge->dest->index, cd_root->index, i); print_gimple_stmt (dump_file, phi, 0); } edges->safe_push (opnd_edge); } } else { if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\tFound def edge %i -> %i for cd_root %i " "and operand %u of: ", opnd_edge->src->index, opnd_edge->dest->index, cd_root->index, i); print_gimple_stmt (dump_file, phi, 0); } if (!eval (opnd)) edges->safe_push (opnd_edge); } } } /* Return an expression corresponding to the predicate PRED. */ static tree build_pred_expr (const pred_info &pred) { tree_code cond_code = pred.cond_code; tree lhs = pred.pred_lhs; tree rhs = pred.pred_rhs; if (pred.invert) cond_code = invert_tree_comparison (cond_code, false); return build2 (cond_code, TREE_TYPE (lhs), lhs, rhs); } /* Return an expression corresponding to PREDS. */ static tree build_pred_expr (const pred_chain_union &preds, bool invert = false) { tree_code code = invert ? TRUTH_AND_EXPR : TRUTH_OR_EXPR; tree_code subcode = invert ? TRUTH_OR_EXPR : TRUTH_AND_EXPR; tree expr = NULL_TREE; for (unsigned i = 0; i != preds.length (); ++i) { tree subexpr = NULL_TREE; for (unsigned j = 0; j != preds[i].length (); ++j) { const pred_info &pi = preds[i][j]; tree cond = build_pred_expr (pi); if (invert) cond = invert_truthvalue (cond); subexpr = subexpr ? build2 (subcode, boolean_type_node, subexpr, cond) : cond; } if (expr) expr = build2 (code, boolean_type_node, expr, subexpr); else expr = subexpr; } return expr; } /* Return a bitset of all PHI arguments or zero if there are too many. */ unsigned predicate::func_t::phi_arg_set (gphi *phi) { unsigned n = gimple_phi_num_args (phi); if (max_phi_args < n) return 0; /* Set the least significant N bits. */ return (1U << n) - 1; } /* Determine if the predicate set of the use does not overlap with that of the interesting paths. The most common senario of guarded use is in Example 1: Example 1: if (some_cond) { x = ...; // set x to valid flag = true; } ... some code ... if (flag) use (x); // use when x is valid The real world examples are usually more complicated, but similar and usually result from inlining: bool init_func (int * x) { if (some_cond) return false; *x = ...; // set *x to valid return true; } void foo (..) { int x; if (!init_func (&x)) return; .. some_code ... use (x); // use when x is valid } Another possible use scenario is in the following trivial example: Example 2: if (n > 0) x = 1; ... if (n > 0) { if (m < 2) ... = x; } Predicate analysis needs to compute the composite predicate: 1) 'x' use predicate: (n > 0) .AND. (m < 2) 2) 'x' default value (non-def) predicate: .NOT. (n > 0) (the predicate chain for phi operand defs can be computed starting from a bb that is control equivalent to the phi's bb and is dominating the operand def.) and check overlapping: (n > 0) .AND. (m < 2) .AND. (.NOT. (n > 0)) <==> false This implementation provides a framework that can handle different scenarios. (Note that many simple cases are handled properly without the predicate analysis if jump threading eliminates the merge point thus makes path-sensitive analysis unnecessary.) PHI is the phi node whose incoming (undefined) paths need to be pruned, and OPNDS is the bitmap holding interesting operand positions. VISITED is the pointer set of phi stmts being checked. */ bool predicate::overlap (gphi *phi, unsigned opnds, hash_set *visited) { gimple *flag_def = NULL; tree boundary_cst = NULL_TREE; bitmap visited_flag_phis = NULL; /* Find within the common prefix of multiple predicate chains a predicate that is a comparison of a flag variable against a constant. */ tree_code cmp_code = find_var_cmp_const (m_preds, phi, &flag_def, &boundary_cst); if (cmp_code == ERROR_MARK) return true; /* Now check all the uninit incoming edges have a constant flag value that is in conflict with the use guard/predicate. */ gphi *phi_def = as_a (flag_def); bool all_pruned = prune_phi_opnds (phi, opnds, phi_def, boundary_cst, cmp_code, m_eval, visited, &visited_flag_phis); if (visited_flag_phis) BITMAP_FREE (visited_flag_phis); return !all_pruned; } /* Return true if two predicates PRED1 and X2 are equivalent. Assume the expressions have already properly re-associated. */ static inline bool pred_equal_p (const pred_info &pred1, const pred_info &pred2) { if (!operand_equal_p (pred1.pred_lhs, pred2.pred_lhs, 0) || !operand_equal_p (pred1.pred_rhs, pred2.pred_rhs, 0)) return false; tree_code c1 = pred1.cond_code, c2; if (pred1.invert != pred2.invert && TREE_CODE_CLASS (pred2.cond_code) == tcc_comparison) c2 = invert_tree_comparison (pred2.cond_code, false); else c2 = pred2.cond_code; return c1 == c2; } /* Return true if PRED tests inequality (i.e., X != Y). */ static inline bool is_neq_relop_p (const pred_info &pred) { return ((pred.cond_code == NE_EXPR && !pred.invert) || (pred.cond_code == EQ_EXPR && pred.invert)); } /* Returns true if PRED is of the form X != 0. */ static inline bool is_neq_zero_form_p (const pred_info &pred) { if (!is_neq_relop_p (pred) || !integer_zerop (pred.pred_rhs) || TREE_CODE (pred.pred_lhs) != SSA_NAME) return false; return true; } /* Return true if PRED is equivalent to X != 0. */ static inline bool pred_expr_equal_p (const pred_info &pred, tree expr) { if (!is_neq_zero_form_p (pred)) return false; return operand_equal_p (pred.pred_lhs, expr, 0); } /* Return true if VAL satisfies (x CMPC BOUNDARY) predicate. CMPC can be either one of the range comparison codes ({GE,LT,EQ,NE}_EXPR and the like), or BIT_AND_EXPR. EXACT_P is only meaningful for the latter. Modify the question from VAL & BOUNDARY != 0 to VAL & BOUNDARY == VAL. For other values of CMPC, EXACT_P is ignored. */ static bool value_sat_pred_p (tree val, tree boundary, tree_code cmpc, bool exact_p = false) { if (cmpc != BIT_AND_EXPR) return is_value_included_in (val, boundary, cmpc); wide_int andw = wi::to_wide (val) & wi::to_wide (boundary); if (exact_p) return andw == wi::to_wide (val); return andw.to_uhwi (); } /* Return true if the domain of single predicate expression PRED1 is a subset of that of PRED2, and false if it cannot be proved. */ static bool subset_of (const pred_info &pred1, const pred_info &pred2) { if (pred_equal_p (pred1, pred2)) return true; if ((TREE_CODE (pred1.pred_rhs) != INTEGER_CST) || (TREE_CODE (pred2.pred_rhs) != INTEGER_CST)) return false; if (!operand_equal_p (pred1.pred_lhs, pred2.pred_lhs, 0)) return false; tree_code code1 = pred1.cond_code; if (pred1.invert) code1 = invert_tree_comparison (code1, false); tree_code code2 = pred2.cond_code; if (pred2.invert) code2 = invert_tree_comparison (code2, false); if (code2 == NE_EXPR && code1 == NE_EXPR) return false; if (code2 == NE_EXPR) return !value_sat_pred_p (pred2.pred_rhs, pred1.pred_rhs, code1); if (code1 == EQ_EXPR) return value_sat_pred_p (pred1.pred_rhs, pred2.pred_rhs, code2); if (code1 == code2) return value_sat_pred_p (pred1.pred_rhs, pred2.pred_rhs, code2, code1 == BIT_AND_EXPR); return false; } /* Return true if the domain of CHAIN1 is a subset of that of CHAIN2. Return false if it cannot be proven so. */ static bool subset_of (const pred_chain &chain1, const pred_chain &chain2) { unsigned np1 = chain1.length (); unsigned np2 = chain2.length (); for (unsigned i2 = 0; i2 < np2; i2++) { bool found = false; const pred_info &info2 = chain2[i2]; for (unsigned i1 = 0; i1 < np1; i1++) { const pred_info &info1 = chain1[i1]; if (subset_of (info1, info2)) { found = true; break; } } if (!found) return false; } return true; } /* Return true if the domain defined by the predicate chain PREDS is a subset of the domain of *THIS. Return false if PREDS's domain is not a subset of any of the sub-domains of *THIS (corresponding to each individual chains in it), even though it may be still be a subset of whole domain of *THIS which is the union (ORed) of all its subdomains. In other words, the result is conservative. */ bool predicate::includes (const pred_chain &chain) const { for (unsigned i = 0; i < m_preds.length (); i++) if (subset_of (chain, m_preds[i])) return true; return false; } /* Return true if the domain defined by *THIS is a superset of PREDS's domain. Avoid building generic trees (and rely on the folding capability of the compiler), and instead perform brute force comparison of individual predicate chains (this won't be a computationally costly since the chains are pretty short). Returning false does not necessarily mean *THIS is not a superset of *PREDS, only that it need not be since the analysis cannot prove it. */ bool predicate::superset_of (const predicate &preds) const { for (unsigned i = 0; i < preds.m_preds.length (); i++) if (!includes (preds.m_preds[i])) return false; return true; } /* Create a predicate of the form OP != 0 and push it the work list CHAIN. */ static void push_to_worklist (tree op, pred_chain *chain, hash_set *mark_set) { if (mark_set->contains (op)) return; mark_set->add (op); pred_info arg_pred; arg_pred.pred_lhs = op; arg_pred.pred_rhs = integer_zero_node; arg_pred.cond_code = NE_EXPR; arg_pred.invert = false; chain->safe_push (arg_pred); } /* Return a pred_info for a gimple assignment CMP_ASSIGN with comparison rhs. */ static pred_info get_pred_info_from_cmp (const gimple *cmp_assign) { pred_info pred; pred.pred_lhs = gimple_assign_rhs1 (cmp_assign); pred.pred_rhs = gimple_assign_rhs2 (cmp_assign); pred.cond_code = gimple_assign_rhs_code (cmp_assign); pred.invert = false; return pred; } /* If PHI is a degenerate phi with all operands having the same value (relop) update *PRED to that value and return true. Otherwise return false. */ static bool is_degenerate_phi (gimple *phi, pred_info *pred) { tree op0 = gimple_phi_arg_def (phi, 0); if (TREE_CODE (op0) != SSA_NAME) return false; gimple *def0 = SSA_NAME_DEF_STMT (op0); if (gimple_code (def0) != GIMPLE_ASSIGN) return false; if (TREE_CODE_CLASS (gimple_assign_rhs_code (def0)) != tcc_comparison) return false; pred_info pred0 = get_pred_info_from_cmp (def0); unsigned n = gimple_phi_num_args (phi); for (unsigned i = 1; i < n; ++i) { tree op = gimple_phi_arg_def (phi, i); if (TREE_CODE (op) != SSA_NAME) return false; gimple *def = SSA_NAME_DEF_STMT (op); if (gimple_code (def) != GIMPLE_ASSIGN) return false; if (TREE_CODE_CLASS (gimple_assign_rhs_code (def)) != tcc_comparison) return false; pred_info pred = get_pred_info_from_cmp (def); if (!pred_equal_p (pred, pred0)) return false; } *pred = pred0; return true; } /* Recursively compute the control dependence chains (paths of edges) from the dependent basic block, DEP_BB, up to the dominating basic block, DOM_BB (the head node of a chain should be dominated by it), storing them in the CD_CHAINS array. CUR_CD_CHAIN is the current chain being computed. *NUM_CHAINS is total number of chains in the CD_CHAINS array. *NUM_CALLS is the number of recursive calls to control unbounded recursion. Return true if the information is successfully computed, false if there is no control dependence or not computed. */ static bool compute_control_dep_chain (basic_block dom_bb, const_basic_block dep_bb, vec cd_chains[], unsigned *num_chains, vec &cur_cd_chain, unsigned *num_calls, unsigned depth = 0) { if (*num_calls > (unsigned)param_uninit_control_dep_attempts) { if (dump_file) fprintf (dump_file, "param_uninit_control_dep_attempts exceeded: %u\n", *num_calls); return false; } ++*num_calls; /* FIXME: Use a set instead. */ unsigned cur_chain_len = cur_cd_chain.length (); if (cur_chain_len > MAX_CHAIN_LEN) { if (dump_file) fprintf (dump_file, "MAX_CHAIN_LEN exceeded: %u\n", cur_chain_len); return false; } if (cur_chain_len > 5) { if (dump_file) fprintf (dump_file, "chain length exceeds 5: %u\n", cur_chain_len); } for (unsigned i = 0; i < cur_chain_len; i++) { edge e = cur_cd_chain[i]; /* Cycle detected. */ if (e->src == dom_bb) { if (dump_file) fprintf (dump_file, "cycle detected\n"); return false; } } if (DEBUG_PREDICATE_ANALYZER && dump_file) fprintf (dump_file, "%*s%s (dom_bb = %u, dep_bb = %u, cd_chains = { %s }, ...)\n", depth, "", __func__, dom_bb->index, dep_bb->index, format_edge_vecs (cd_chains, *num_chains).c_str ()); bool found_cd_chain = false; /* Iterate over DOM_BB's successors. */ edge e; edge_iterator ei; FOR_EACH_EDGE (e, ei, dom_bb->succs) { int post_dom_check = 0; if (e->flags & (EDGE_FAKE | EDGE_ABNORMAL)) continue; basic_block cd_bb = e->dest; cur_cd_chain.safe_push (e); while (!is_non_loop_exit_postdominating (cd_bb, dom_bb)) { if (cd_bb == dep_bb) { /* Found a direct control dependence. */ if (*num_chains < MAX_NUM_CHAINS) { cd_chains[*num_chains] = cur_cd_chain.copy (); (*num_chains)++; } found_cd_chain = true; /* Check path from next edge. */ break; } /* Check if DEP_BB is indirectly control-dependent on DOM_BB. */ if (compute_control_dep_chain (cd_bb, dep_bb, cd_chains, num_chains, cur_cd_chain, num_calls, depth + 1)) { found_cd_chain = true; break; } cd_bb = get_immediate_dominator (CDI_POST_DOMINATORS, cd_bb); post_dom_check++; if (cd_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) || post_dom_check > MAX_POSTDOM_CHECK) break; } cur_cd_chain.pop (); gcc_assert (cur_cd_chain.length () == cur_chain_len); } gcc_assert (cur_cd_chain.length () == cur_chain_len); return found_cd_chain; } /* Return true if PRED can be invalidated by any predicate in GUARD. */ static bool can_be_invalidated_p (const pred_info &pred, const pred_chain &guard) { if (dump_file && dump_flags & TDF_DETAILS) { fprintf (dump_file, "Testing if predicate: "); dump_pred_info (pred); fprintf (dump_file, "\n...can be invalidated by a USE guard of: "); dump_pred_chain (guard); fputc ('\n', dump_file); } unsigned n = guard.length (); for (unsigned i = 0; i < n; ++i) { if (pred_neg_p (pred, guard[i])) { if (dump_file && dump_flags & TDF_DETAILS) { fprintf (dump_file, " Predicate invalidated by: "); dump_pred_info (guard[i]); fputc ('\n', dump_file); } return true; } } return false; } /* Return true if all predicates in PREDS are invalidated by GUARD being true. */ static bool can_be_invalidated_p (const pred_chain_union &preds, const pred_chain &guard) { if (preds.is_empty ()) return false; if (dump_file && dump_flags & TDF_DETAILS) dump_predicates (NULL, preds, "Testing if anything here can be invalidated: "); for (unsigned i = 0; i < preds.length (); ++i) { const pred_chain &chain = preds[i]; unsigned j; for (j = 0; j < chain.length (); ++j) if (can_be_invalidated_p (chain[j], guard)) break; /* If we were unable to invalidate any predicate in C, then there is a viable path from entry to the PHI where the PHI takes an interesting value and continues to a use of the PHI. */ if (j == chain.length ()) return false; } return true; } /* Return true if none of the PHI arguments in OPNDS is used given the use guards in *THIS that guard the PHI's use. */ bool predicate::use_cannot_happen (gphi *phi, unsigned opnds) { if (!m_eval.phi_arg_set (phi)) return false; /* PHI_USE_GUARDS are OR'ed together. If we have more than one possible guard, there's no way of knowing which guard was true. Since we need to be absolutely sure that the uninitialized operands will be invalidated, bail. */ const pred_chain_union &phi_use_guards = m_preds; if (phi_use_guards.length () != 1) return false; const pred_chain &use_guard = phi_use_guards[0]; /* Look for the control dependencies of all the interesting operands and build guard predicates describing them. */ unsigned n = gimple_phi_num_args (phi); for (unsigned i = 0; i < n; ++i) { if (!MASK_TEST_BIT (opnds, i)) continue; edge e = gimple_phi_arg_edge (phi, i); auto_vec dep_chains[MAX_NUM_CHAINS]; auto_vec cur_chain; unsigned num_chains = 0; unsigned num_calls = 0; /* Build the control dependency chain for the PHI argument... */ if (!compute_control_dep_chain (ENTRY_BLOCK_PTR_FOR_FN (cfun), e->src, dep_chains, &num_chains, cur_chain, &num_calls)) return false; if (DEBUG_PREDICATE_ANALYZER && dump_file) { fprintf (dump_file, "predicate::use_cannot_happen (...) " "dep_chains for arg %u:\n\t", i); dump_dep_chains (dep_chains, num_chains); } /* ...and convert it into a set of predicates guarding its definition. */ predicate def_preds (m_eval); def_preds.init_from_control_deps (dep_chains, num_chains); if (def_preds.is_empty ()) /* If there's no predicate there's no basis to rule the use out. */ return false; def_preds.simplify (); def_preds.normalize (); /* Can the guard for this PHI argument be negated by the one guarding the PHI use? */ if (!can_be_invalidated_p (def_preds.chain (), use_guard)) return false; } return true; } /* Implemented simplifications: 1) ((x IOR y) != 0) AND (x != 0) is equivalent to (x != 0); 2) (X AND Y) OR (!X AND Y) is equivalent to Y; 3) X OR (!X AND Y) is equivalent to (X OR Y); 4) ((x IAND y) != 0) || (x != 0 AND y != 0)) is equivalent to (x != 0 AND y != 0) 5) (X AND Y) OR (!X AND Z) OR (!Y AND Z) is equivalent to (X AND Y) OR Z PREDS is the predicate chains, and N is the number of chains. */ /* Implement rule 1 above. PREDS is the AND predicate to simplify in place. */ static void simplify_1 (pred_chain &chain) { bool simplified = false; pred_chain s_chain = vNULL; unsigned n = chain.length (); for (unsigned i = 0; i < n; i++) { pred_info &a_pred = chain[i]; if (!a_pred.pred_lhs || !is_neq_zero_form_p (a_pred)) continue; gimple *def_stmt = SSA_NAME_DEF_STMT (a_pred.pred_lhs); if (gimple_code (def_stmt) != GIMPLE_ASSIGN) continue; if (gimple_assign_rhs_code (def_stmt) != BIT_IOR_EXPR) continue; for (unsigned j = 0; j < n; j++) { const pred_info &b_pred = chain[j]; if (!b_pred.pred_lhs || !is_neq_zero_form_p (b_pred)) continue; if (pred_expr_equal_p (b_pred, gimple_assign_rhs1 (def_stmt)) || pred_expr_equal_p (b_pred, gimple_assign_rhs2 (def_stmt))) { /* Mark A_PRED for removal from PREDS. */ a_pred.pred_lhs = NULL; a_pred.pred_rhs = NULL; simplified = true; break; } } } if (!simplified) return; /* Remove predicates marked above. */ for (unsigned i = 0; i < n; i++) { pred_info &a_pred = chain[i]; if (!a_pred.pred_lhs) continue; s_chain.safe_push (a_pred); } chain.release (); chain = s_chain; } /* Implements rule 2 for the OR predicate PREDS: 2) (X AND Y) OR (!X AND Y) is equivalent to Y. */ bool predicate::simplify_2 () { bool simplified = false; /* (X AND Y) OR (!X AND Y) is equivalent to Y. (X AND Y) OR (X AND !Y) is equivalent to X. */ unsigned n = m_preds.length (); for (unsigned i = 0; i < n; i++) { pred_chain &a_chain = m_preds[i]; if (a_chain.length () != 2) continue; /* Create copies since the chain may be released below before the copy is added to the other chain. */ const pred_info x = a_chain[0]; const pred_info y = a_chain[1]; for (unsigned j = 0; j < n; j++) { if (j == i) continue; pred_chain &b_chain = m_preds[j]; if (b_chain.length () != 2) continue; const pred_info &x2 = b_chain[0]; const pred_info &y2 = b_chain[1]; if (pred_equal_p (x, x2) && pred_neg_p (y, y2)) { /* Kill a_chain. */ b_chain.release (); a_chain.release (); b_chain.safe_push (x); simplified = true; break; } if (pred_neg_p (x, x2) && pred_equal_p (y, y2)) { /* Kill a_chain. */ a_chain.release (); b_chain.release (); b_chain.safe_push (y); simplified = true; break; } } } /* Now clean up the chain. */ if (simplified) { pred_chain_union s_preds = vNULL; for (unsigned i = 0; i < n; i++) { if (m_preds[i].is_empty ()) continue; s_preds.safe_push (m_preds[i]); } m_preds.release (); m_preds = s_preds; s_preds = vNULL; } return simplified; } /* Implement rule 3 for the OR predicate PREDS: 3) x OR (!x AND y) is equivalent to x OR y. */ bool predicate::simplify_3 () { /* Now iteratively simplify X OR (!X AND Z ..) into X OR (Z ...). */ unsigned n = m_preds.length (); if (n < 2) return false; bool simplified = false; for (unsigned i = 0; i < n; i++) { const pred_chain &a_chain = m_preds[i]; if (a_chain.length () != 1) continue; const pred_info &x = a_chain[0]; for (unsigned j = 0; j < n; j++) { if (j == i) continue; pred_chain b_chain = m_preds[j]; if (b_chain.length () < 2) continue; for (unsigned k = 0; k < b_chain.length (); k++) { const pred_info &x2 = b_chain[k]; if (pred_neg_p (x, x2)) { b_chain.unordered_remove (k); simplified = true; break; } } } } return simplified; } /* Implement rule 4 for the OR predicate PREDS: 2) ((x AND y) != 0) OR (x != 0 AND y != 0) is equivalent to (x != 0 ANd y != 0). */ bool predicate::simplify_4 () { bool simplified = false; pred_chain_union s_preds = vNULL; unsigned n = m_preds.length (); for (unsigned i = 0; i < n; i++) { pred_chain a_chain = m_preds[i]; if (a_chain.length () != 1) continue; const pred_info &z = a_chain[0]; if (!is_neq_zero_form_p (z)) continue; gimple *def_stmt = SSA_NAME_DEF_STMT (z.pred_lhs); if (gimple_code (def_stmt) != GIMPLE_ASSIGN) continue; if (gimple_assign_rhs_code (def_stmt) != BIT_AND_EXPR) continue; for (unsigned j = 0; j < n; j++) { if (j == i) continue; pred_chain b_chain = m_preds[j]; if (b_chain.length () != 2) continue; const pred_info &x2 = b_chain[0]; const pred_info &y2 = b_chain[1]; if (!is_neq_zero_form_p (x2) || !is_neq_zero_form_p (y2)) continue; if ((pred_expr_equal_p (x2, gimple_assign_rhs1 (def_stmt)) && pred_expr_equal_p (y2, gimple_assign_rhs2 (def_stmt))) || (pred_expr_equal_p (x2, gimple_assign_rhs2 (def_stmt)) && pred_expr_equal_p (y2, gimple_assign_rhs1 (def_stmt)))) { /* Kill a_chain. */ a_chain.release (); simplified = true; break; } } } /* Now clean up the chain. */ if (simplified) { for (unsigned i = 0; i < n; i++) { if (m_preds[i].is_empty ()) continue; s_preds.safe_push (m_preds[i]); } m_preds.release (); m_preds = s_preds; s_preds = vNULL; } return simplified; } /* Simplify predicates in *THIS. */ void predicate::simplify (gimple *use_or_def, bool is_use) { if (dump_file && dump_flags & TDF_DETAILS) { fprintf (dump_file, "Before simplication "); dump (use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n"); } unsigned n = m_preds.length (); for (unsigned i = 0; i < n; i++) ::simplify_1 (m_preds[i]); if (n < 2) return; bool changed; do { changed = false; if (simplify_2 ()) changed = true; if (simplify_3 ()) changed = true; if (simplify_4 ()) changed = true; } while (changed); } /* Attempt to normalize predicate chains by following UD chains by building up a big tree of either IOR operations or AND operations, and converting the IOR tree into a pred_chain_union or the BIT_AND tree into a pred_chain. Example: _3 = _2 RELOP1 _1; _6 = _5 RELOP2 _4; _9 = _8 RELOP3 _7; _10 = _3 | _6; _12 = _9 | _0; _t = _10 | _12; then _t != 0 will be normalized into a pred_chain_union (_2 RELOP1 _1) OR (_5 RELOP2 _4) OR (_8 RELOP3 _7) OR (_0 != 0) Similarly given: _3 = _2 RELOP1 _1; _6 = _5 RELOP2 _4; _9 = _8 RELOP3 _7; _10 = _3 & _6; _12 = _9 & _0; then _t != 0 will be normalized into a pred_chain: (_2 RELOP1 _1) AND (_5 RELOP2 _4) AND (_8 RELOP3 _7) AND (_0 != 0) */ /* Store a PRED in *THIS. */ void predicate::push_pred (const pred_info &pred) { pred_chain chain = vNULL; chain.safe_push (pred); m_preds.safe_push (chain); } /* Dump predicates in *THIS for STMT prepended by MSG. */ void predicate::dump (gimple *stmt, const char *msg) const { fprintf (dump_file, "%s", msg); if (stmt) { fputc ('\t', dump_file); print_gimple_stmt (dump_file, stmt, 0); fprintf (dump_file, " is conditional on:\n"); } unsigned np = m_preds.length (); if (np == 0) { fprintf (dump_file, "\t(empty)\n"); return; } { tree expr = build_pred_expr (m_preds); char *str = print_generic_expr_to_str (expr); fprintf (dump_file, "\t%s (expanded)\n", str); free (str); } if (np > 1) fprintf (dump_file, "\tOR ("); else fputc ('\t', dump_file); for (unsigned i = 0; i < np; i++) { dump_pred_chain (m_preds[i]); if (i < np - 1) fprintf (dump_file, ", "); else if (i > 0) fputc (')', dump_file); } fputc ('\n', dump_file); } /* Initialize *THIS with the predicates of the control dependence chains between the basic block DEF_BB that defines a variable of interst and USE_BB that uses the variable, respectively. */ predicate::predicate (basic_block def_bb, basic_block use_bb, func_t &eval) : m_preds (vNULL), m_eval (eval) { /* Set CD_ROOT to the basic block closest to USE_BB that is the control equivalent of (is guarded by the same predicate as) DEF_BB that also dominates USE_BB. */ basic_block cd_root = def_bb; while (dominated_by_p (CDI_DOMINATORS, use_bb, cd_root)) { /* Find CD_ROOT's closest postdominator that's its control equivalent. */ if (basic_block bb = find_control_equiv_block (cd_root)) if (dominated_by_p (CDI_DOMINATORS, use_bb, bb)) { cd_root = bb; continue; } break; } /* Set DEP_CHAINS to the set of edges between CD_ROOT and USE_BB. Each DEP_CHAINS element is a series of edges whose conditions are logical conjunctions. Together, the DEP_CHAINS vector is used below to initialize an OR expression of the conjunctions. */ unsigned num_calls = 0; unsigned num_chains = 0; auto_vec dep_chains[MAX_NUM_CHAINS]; auto_vec cur_chain; compute_control_dep_chain (cd_root, use_bb, dep_chains, &num_chains, cur_chain, &num_calls); if (DEBUG_PREDICATE_ANALYZER && dump_file) { fprintf (dump_file, "predicate::predicate (def_bb = %u, use_bb = %u, func_t) " "initialized from %u dep_chains:\n\t", def_bb->index, use_bb->index, num_chains); dump_dep_chains (dep_chains, num_chains); } /* From the set of edges computed above initialize *THIS as the OR condition under which the definition in DEF_BB is used in USE_BB. Each OR subexpression is represented by one element of DEP_CHAINS, where each element consists of a series of AND subexpressions. */ init_from_control_deps (dep_chains, num_chains); } /* Release resources in *THIS. */ predicate::~predicate () { unsigned n = m_preds.length (); for (unsigned i = 0; i != n; ++i) m_preds[i].release (); m_preds.release (); } /* Copy-assign RHS to *THIS. */ predicate& predicate::operator= (const predicate &rhs) { if (this == &rhs) return *this; /* FIXME: Make this a compile-time constraint? */ gcc_assert (&m_eval == &rhs.m_eval); unsigned n = m_preds.length (); for (unsigned i = 0; i != n; ++i) m_preds[i].release (); m_preds.release (); n = rhs.m_preds.length (); for (unsigned i = 0; i != n; ++i) { const pred_chain &chain = rhs.m_preds[i]; m_preds.safe_push (chain.copy ()); } return *this; } /* For each use edge of PHI, compute all control dependence chains and convert those to the composite predicates in M_PREDS. Return true if a nonempty predicate has been obtained. */ bool predicate::init_from_phi_def (gphi *phi) { gcc_assert (is_empty ()); basic_block phi_bb = gimple_bb (phi); /* Find the closest dominating bb to be the control dependence root. */ basic_block cd_root = get_immediate_dominator (CDI_DOMINATORS, phi_bb); if (!cd_root) return false; /* Set DEF_EDGES to the edges to the PHI from the bb's that provide definitions of each of the PHI operands for which M_EVAL is false. */ auto_vec def_edges; hash_set visited_phis; collect_phi_def_edges (phi, cd_root, &def_edges, m_eval, &visited_phis); unsigned nedges = def_edges.length (); if (nedges == 0) return false; unsigned num_chains = 0; auto_vec dep_chains[MAX_NUM_CHAINS]; auto_vec cur_chain; for (unsigned i = 0; i < nedges; i++) { edge e = def_edges[i]; unsigned num_calls = 0; unsigned prev_nc = num_chains; compute_control_dep_chain (cd_root, e->src, dep_chains, &num_chains, cur_chain, &num_calls); /* Update the newly added chains with the phi operand edge. */ if (EDGE_COUNT (e->src->succs) > 1) { if (prev_nc == num_chains && num_chains < MAX_NUM_CHAINS) dep_chains[num_chains++] = vNULL; for (unsigned j = prev_nc; j < num_chains; j++) dep_chains[j].safe_push (e); } } /* Convert control dependence chains to the predicate in *THIS under which the PHI operands are defined to values for which M_EVAL is false. */ init_from_control_deps (dep_chains, num_chains); return !is_empty (); } /* Compute the predicates that guard the use USE_STMT and check if the incoming paths that have an empty (or possibly empty) definition can be pruned. Return true if it can be determined that the use of PHI's def in USE_STMT is guarded by a predicate set that does not overlap with the predicate sets of all runtime paths that do not have a definition. Return false if the use is not guarded or if it cannot be determined. USE_BB is the bb of the use (for phi operand use, the bb is not the bb of the phi stmt, but the source bb of the operand edge). OPNDS is a bitmap with a bit set for each PHI operand of interest. THIS->M_PREDS contains the (memoized) defining predicate chains of a PHI. If THIS->M_PREDS is empty, the PHI's defining predicate chains are computed and stored into THIS->M_PREDS as needed. VISITED_PHIS is a pointer set of phis being visited. */ bool predicate::is_use_guarded (gimple *use_stmt, basic_block use_bb, gphi *phi, unsigned opnds, hash_set *visited) { if (visited->add (phi)) return false; /* The basic block where the PHI is defined. */ basic_block def_bb = gimple_bb (phi); /* Try to build the predicate expression under which the PHI flows into its use. This will be empty if the PHI is defined and used in the same bb. */ predicate use_preds (def_bb, use_bb, m_eval); if (is_non_loop_exit_postdominating (use_bb, def_bb)) { if (is_empty ()) { /* Lazily initialize *THIS from the PHI and build its use expression. */ init_from_phi_def (phi); m_use_expr = build_pred_expr (use_preds.m_preds); } /* The use is not guarded. */ return false; } if (use_preds.is_empty ()) return false; /* Try to prune the dead incoming phi edges. */ if (!use_preds.overlap (phi, opnds, visited)) { if (DEBUG_PREDICATE_ANALYZER && dump_file) fputs ("found predicate overlap\n", dump_file); return true; } /* We might be able to prove that if the control dependencies for OPNDS are true, the control dependencies for USE_STMT can never be true. */ if (use_preds.use_cannot_happen (phi, opnds)) return true; if (is_empty ()) { /* Lazily initialize *THIS from PHI. */ if (!init_from_phi_def (phi)) { m_use_expr = build_pred_expr (use_preds.m_preds); return false; } simplify (phi); normalize (phi); } use_preds.simplify (use_stmt, /*is_use=*/true); use_preds.normalize (use_stmt, /*is_use=*/true); /* Return true if the predicate guarding the valid definition (i.e., *THIS) is a superset of the predicate guarding the use (i.e., USE_PREDS). */ if (superset_of (use_preds)) return true; m_use_expr = build_pred_expr (use_preds.m_preds); return false; } /* Public interface to the above. */ bool predicate::is_use_guarded (gimple *stmt, basic_block use_bb, gphi *phi, unsigned opnds) { hash_set visited; return is_use_guarded (stmt, use_bb, phi, opnds, &visited); } /* Normalize predicate PRED: 1) if PRED can no longer be normalized, append it to *THIS. 2) otherwise if PRED is of the form x != 0, follow x's definition and put normalized predicates into WORK_LIST. */ void predicate::normalize (pred_chain *norm_chain, pred_info pred, tree_code and_or_code, pred_chain *work_list, hash_set *mark_set) { if (!is_neq_zero_form_p (pred)) { if (and_or_code == BIT_IOR_EXPR) push_pred (pred); else norm_chain->safe_push (pred); return; } gimple *def_stmt = SSA_NAME_DEF_STMT (pred.pred_lhs); if (gimple_code (def_stmt) == GIMPLE_PHI && is_degenerate_phi (def_stmt, &pred)) /* PRED has been modified above. */ work_list->safe_push (pred); else if (gimple_code (def_stmt) == GIMPLE_PHI && and_or_code == BIT_IOR_EXPR) { unsigned n = gimple_phi_num_args (def_stmt); /* Punt for a nonzero constant. The predicate should be one guarding the phi edge. */ for (unsigned i = 0; i < n; ++i) { tree op = gimple_phi_arg_def (def_stmt, i); if (TREE_CODE (op) == INTEGER_CST && !integer_zerop (op)) { push_pred (pred); return; } } for (unsigned i = 0; i < n; ++i) { tree op = gimple_phi_arg_def (def_stmt, i); if (integer_zerop (op)) continue; push_to_worklist (op, work_list, mark_set); } } else if (gimple_code (def_stmt) != GIMPLE_ASSIGN) { if (and_or_code == BIT_IOR_EXPR) push_pred (pred); else norm_chain->safe_push (pred); } else if (gimple_assign_rhs_code (def_stmt) == and_or_code) { /* Avoid splitting up bit manipulations like x & 3 or y | 1. */ if (is_gimple_min_invariant (gimple_assign_rhs2 (def_stmt))) { /* But treat x & 3 as a condition. */ if (and_or_code == BIT_AND_EXPR) { pred_info n_pred; n_pred.pred_lhs = gimple_assign_rhs1 (def_stmt); n_pred.pred_rhs = gimple_assign_rhs2 (def_stmt); n_pred.cond_code = and_or_code; n_pred.invert = false; norm_chain->safe_push (n_pred); } } else { push_to_worklist (gimple_assign_rhs1 (def_stmt), work_list, mark_set); push_to_worklist (gimple_assign_rhs2 (def_stmt), work_list, mark_set); } } else if (TREE_CODE_CLASS (gimple_assign_rhs_code (def_stmt)) == tcc_comparison) { pred_info n_pred = get_pred_info_from_cmp (def_stmt); if (and_or_code == BIT_IOR_EXPR) push_pred (n_pred); else norm_chain->safe_push (n_pred); } else { if (and_or_code == BIT_IOR_EXPR) push_pred (pred); else norm_chain->safe_push (pred); } } /* Normalize PRED and store the normalized predicates in THIS->M_PREDS. */ void predicate::normalize (const pred_info &pred) { if (!is_neq_zero_form_p (pred)) { push_pred (pred); return; } tree_code and_or_code = ERROR_MARK; gimple *def_stmt = SSA_NAME_DEF_STMT (pred.pred_lhs); if (gimple_code (def_stmt) == GIMPLE_ASSIGN) and_or_code = gimple_assign_rhs_code (def_stmt); if (and_or_code != BIT_IOR_EXPR && and_or_code != BIT_AND_EXPR) { if (TREE_CODE_CLASS (and_or_code) == tcc_comparison) { pred_info n_pred = get_pred_info_from_cmp (def_stmt); push_pred (n_pred); } else push_pred (pred); return; } pred_chain norm_chain = vNULL; pred_chain work_list = vNULL; work_list.safe_push (pred); hash_set mark_set; while (!work_list.is_empty ()) { pred_info a_pred = work_list.pop (); normalize (&norm_chain, a_pred, and_or_code, &work_list, &mark_set); } if (and_or_code == BIT_AND_EXPR) m_preds.safe_push (norm_chain); work_list.release (); } /* Normalize a single predicate PRED_CHAIN and append it to *THIS. */ void predicate::normalize (const pred_chain &chain) { pred_chain work_list = vNULL; hash_set mark_set; for (unsigned i = 0; i < chain.length (); i++) { work_list.safe_push (chain[i]); mark_set.add (chain[i].pred_lhs); } /* Normalized chain of predicates built up below. */ pred_chain norm_chain = vNULL; while (!work_list.is_empty ()) { pred_info pi = work_list.pop (); predicate pred (m_eval); /* The predicate object is not modified here, only NORM_CHAIN and WORK_LIST are appended to. */ pred.normalize (&norm_chain, pi, BIT_AND_EXPR, &work_list, &mark_set); } m_preds.safe_push (norm_chain); work_list.release (); } /* Normalize predicate chains in THIS. */ void predicate::normalize (gimple *use_or_def, bool is_use) { if (dump_file && dump_flags & TDF_DETAILS) { fprintf (dump_file, "Before normalization "); dump (use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n"); } predicate norm_preds (m_eval); for (unsigned i = 0; i < m_preds.length (); i++) { if (m_preds[i].length () != 1) norm_preds.normalize (m_preds[i]); else norm_preds.normalize (m_preds[i][0]); } *this = norm_preds; if (dump_file) { fprintf (dump_file, "After normalization "); dump (use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n"); } } /* Convert the chains of control dependence edges into a set of predicates. A control dependence chain is represented by a vector edges. DEP_CHAINS points to an array of NUM_CHAINS dependence chains. One edge in a dependence chain is mapped to predicate expression represented by pred_info type. One dependence chain is converted to a composite predicate that is the result of AND operation of pred_info mapped to each edge. A composite predicate is represented by a vector of pred_info. Sets M_PREDS to the resulting composite predicates. */ void predicate::init_from_control_deps (const vec *dep_chains, unsigned num_chains) { gcc_assert (is_empty ()); bool has_valid_pred = false; if (num_chains == 0) return; if (num_chains >= MAX_NUM_CHAINS) { if (dump_file) fprintf (dump_file, "MAX_NUM_CHAINS exceeded: %u\n", num_chains); return; } /* Convert the control dependency chain into a set of predicates. */ m_preds.reserve (num_chains); for (unsigned i = 0; i < num_chains; i++) { /* One path through the CFG represents a logical conjunction of the predicates. */ const vec &path = dep_chains[i]; has_valid_pred = false; /* The chain of predicates guarding the definition along this path. */ pred_chain t_chain{ }; for (unsigned j = 0; j < path.length (); j++) { edge e = path[j]; basic_block guard_bb = e->src; /* Ignore empty forwarder blocks. */ if (empty_block_p (guard_bb) && single_succ_p (guard_bb)) continue; /* An empty basic block here is likely a PHI, and is not one of the cases we handle below. */ gimple_stmt_iterator gsi = gsi_last_bb (guard_bb); if (gsi_end_p (gsi)) { has_valid_pred = false; break; } /* Get the conditional controlling the bb exit edge. */ gimple *cond_stmt = gsi_stmt (gsi); if (is_gimple_call (cond_stmt) && EDGE_COUNT (e->src->succs) >= 2) /* Ignore EH edge. Can add assertion on the other edge's flag. */ continue; /* Skip if there is essentially one succesor. */ if (EDGE_COUNT (e->src->succs) == 2) { edge e1; edge_iterator ei1; bool skip = false; FOR_EACH_EDGE (e1, ei1, e->src->succs) { if (EDGE_COUNT (e1->dest->succs) == 0) { skip = true; break; } } if (skip) continue; } if (gimple_code (cond_stmt) == GIMPLE_COND) { /* The true edge corresponds to the uninteresting condition. Add the negated predicate(s) for the edge to record the interesting condition. */ pred_info one_pred; one_pred.pred_lhs = gimple_cond_lhs (cond_stmt); one_pred.pred_rhs = gimple_cond_rhs (cond_stmt); one_pred.cond_code = gimple_cond_code (cond_stmt); one_pred.invert = !!(e->flags & EDGE_FALSE_VALUE); t_chain.safe_push (one_pred); if (DEBUG_PREDICATE_ANALYZER && dump_file) { fprintf (dump_file, "one_pred = "); dump_pred_info (one_pred); fputc ('\n', dump_file); } has_valid_pred = true; } else if (gswitch *gs = dyn_cast (cond_stmt)) { /* Avoid quadratic behavior. */ if (gimple_switch_num_labels (gs) > MAX_SWITCH_CASES) { has_valid_pred = false; break; } /* Find the case label. */ tree l = NULL_TREE; unsigned idx; for (idx = 0; idx < gimple_switch_num_labels (gs); ++idx) { tree tl = gimple_switch_label (gs, idx); if (e->dest == label_to_block (cfun, CASE_LABEL (tl))) { if (!l) l = tl; else { l = NULL_TREE; break; } } } /* If more than one label reaches this block or the case label doesn't have a single value (like the default one) fail. */ if (!l || !CASE_LOW (l) || (CASE_HIGH (l) && !operand_equal_p (CASE_LOW (l), CASE_HIGH (l), 0))) { has_valid_pred = false; break; } pred_info one_pred; one_pred.pred_lhs = gimple_switch_index (gs); one_pred.pred_rhs = CASE_LOW (l); one_pred.cond_code = EQ_EXPR; one_pred.invert = false; t_chain.safe_push (one_pred); has_valid_pred = true; } else { /* Disabled. See PR 90994. has_valid_pred = false; */ break; } } if (!has_valid_pred) break; else m_preds.safe_push (t_chain); } if (DEBUG_PREDICATE_ANALYZER && dump_file) { fprintf (dump_file, "init_from_control_deps {%s}:\n", format_edge_vecs (dep_chains, num_chains).c_str ()); dump (NULL, ""); } if (has_valid_pred) gcc_assert (m_preds.length () != 0); else /* Clear M_PREDS to indicate failure. */ m_preds.release (); } /* Return the predicate expression guarding the definition of the interesting variable. When INVERT is set, return the logical NOT of the predicate. */ tree predicate::def_expr (bool invert /* = false */) const { /* The predicate is stored in an inverted form. */ return build_pred_expr (m_preds, !invert); } /* Return the predicate expression guarding the use of the interesting variable or null if the use predicate hasn't been determined yet. */ tree predicate::use_expr () const { return m_use_expr; } /* Return a logical AND expression with the (optionally inverted) predicate expression guarding the definition of the interesting variable and one guarding its use. Return null if the use predicate hasn't yet been determined. */ tree predicate::expr (bool invert /* = false */) const { if (!m_use_expr) return NULL_TREE; tree expr = build_pred_expr (m_preds, !invert); return build2 (TRUTH_AND_EXPR, boolean_type_node, expr, m_use_expr); }