diff options
Diffstat (limited to 'gcc-4.8.1/gcc/tree-ssa-ccp.c')
-rw-r--r-- | gcc-4.8.1/gcc/tree-ssa-ccp.c | 2528 |
1 files changed, 0 insertions, 2528 deletions
diff --git a/gcc-4.8.1/gcc/tree-ssa-ccp.c b/gcc-4.8.1/gcc/tree-ssa-ccp.c deleted file mode 100644 index a64bffcae..000000000 --- a/gcc-4.8.1/gcc/tree-ssa-ccp.c +++ /dev/null @@ -1,2528 +0,0 @@ -/* Conditional constant propagation pass for the GNU compiler. - Copyright (C) 2000-2013 Free Software Foundation, Inc. - Adapted from original RTL SSA-CCP by Daniel Berlin <dberlin@dberlin.org> - Adapted to GIMPLE trees by Diego Novillo <dnovillo@redhat.com> - -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 -<http://www.gnu.org/licenses/>. */ - -/* Conditional constant propagation (CCP) is based on the SSA - propagation engine (tree-ssa-propagate.c). Constant assignments of - the form VAR = CST are propagated from the assignments into uses of - VAR, which in turn may generate new constants. The simulation uses - a four level lattice to keep track of constant values associated - with SSA names. Given an SSA name V_i, it may take one of the - following values: - - UNINITIALIZED -> the initial state of the value. This value - is replaced with a correct initial value - the first time the value is used, so the - rest of the pass does not need to care about - it. Using this value simplifies initialization - of the pass, and prevents us from needlessly - scanning statements that are never reached. - - UNDEFINED -> V_i is a local variable whose definition - has not been processed yet. Therefore we - don't yet know if its value is a constant - or not. - - CONSTANT -> V_i has been found to hold a constant - value C. - - VARYING -> V_i cannot take a constant value, or if it - does, it is not possible to determine it - at compile time. - - The core of SSA-CCP is in ccp_visit_stmt and ccp_visit_phi_node: - - 1- In ccp_visit_stmt, we are interested in assignments whose RHS - evaluates into a constant and conditional jumps whose predicate - evaluates into a boolean true or false. When an assignment of - the form V_i = CONST is found, V_i's lattice value is set to - CONSTANT and CONST is associated with it. This causes the - propagation engine to add all the SSA edges coming out the - assignment into the worklists, so that statements that use V_i - can be visited. - - If the statement is a conditional with a constant predicate, we - mark the outgoing edges as executable or not executable - depending on the predicate's value. This is then used when - visiting PHI nodes to know when a PHI argument can be ignored. - - - 2- In ccp_visit_phi_node, if all the PHI arguments evaluate to the - same constant C, then the LHS of the PHI is set to C. This - evaluation is known as the "meet operation". Since one of the - goals of this evaluation is to optimistically return constant - values as often as possible, it uses two main short cuts: - - - If an argument is flowing in through a non-executable edge, it - is ignored. This is useful in cases like this: - - if (PRED) - a_9 = 3; - else - a_10 = 100; - a_11 = PHI (a_9, a_10) - - If PRED is known to always evaluate to false, then we can - assume that a_11 will always take its value from a_10, meaning - that instead of consider it VARYING (a_9 and a_10 have - different values), we can consider it CONSTANT 100. - - - If an argument has an UNDEFINED value, then it does not affect - the outcome of the meet operation. If a variable V_i has an - UNDEFINED value, it means that either its defining statement - hasn't been visited yet or V_i has no defining statement, in - which case the original symbol 'V' is being used - uninitialized. Since 'V' is a local variable, the compiler - may assume any initial value for it. - - - After propagation, every variable V_i that ends up with a lattice - value of CONSTANT will have the associated constant value in the - array CONST_VAL[i].VALUE. That is fed into substitute_and_fold for - final substitution and folding. - - References: - - Constant propagation with conditional branches, - Wegman and Zadeck, ACM TOPLAS 13(2):181-210. - - Building an Optimizing Compiler, - Robert Morgan, Butterworth-Heinemann, 1998, Section 8.9. - - Advanced Compiler Design and Implementation, - Steven Muchnick, Morgan Kaufmann, 1997, Section 12.6 */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "tree.h" -#include "flags.h" -#include "tm_p.h" -#include "basic-block.h" -#include "function.h" -#include "gimple-pretty-print.h" -#include "tree-flow.h" -#include "tree-pass.h" -#include "tree-ssa-propagate.h" -#include "value-prof.h" -#include "langhooks.h" -#include "target.h" -#include "diagnostic-core.h" -#include "dbgcnt.h" -#include "gimple-fold.h" -#include "params.h" -#include "hash-table.h" - - -/* Possible lattice values. */ -typedef enum -{ - UNINITIALIZED, - UNDEFINED, - CONSTANT, - VARYING -} ccp_lattice_t; - -struct prop_value_d { - /* Lattice value. */ - ccp_lattice_t lattice_val; - - /* Propagated value. */ - tree value; - - /* Mask that applies to the propagated value during CCP. For - X with a CONSTANT lattice value X & ~mask == value & ~mask. */ - double_int mask; -}; - -typedef struct prop_value_d prop_value_t; - -/* Array of propagated constant values. After propagation, - CONST_VAL[I].VALUE holds the constant value for SSA_NAME(I). If - the constant is held in an SSA name representing a memory store - (i.e., a VDEF), CONST_VAL[I].MEM_REF will contain the actual - memory reference used to store (i.e., the LHS of the assignment - doing the store). */ -static prop_value_t *const_val; -static unsigned n_const_val; - -static void canonicalize_float_value (prop_value_t *); -static bool ccp_fold_stmt (gimple_stmt_iterator *); - -/* Dump constant propagation value VAL to file OUTF prefixed by PREFIX. */ - -static void -dump_lattice_value (FILE *outf, const char *prefix, prop_value_t val) -{ - switch (val.lattice_val) - { - case UNINITIALIZED: - fprintf (outf, "%sUNINITIALIZED", prefix); - break; - case UNDEFINED: - fprintf (outf, "%sUNDEFINED", prefix); - break; - case VARYING: - fprintf (outf, "%sVARYING", prefix); - break; - case CONSTANT: - if (TREE_CODE (val.value) != INTEGER_CST - || val.mask.is_zero ()) - { - fprintf (outf, "%sCONSTANT ", prefix); - print_generic_expr (outf, val.value, dump_flags); - } - else - { - double_int cval = tree_to_double_int (val.value).and_not (val.mask); - fprintf (outf, "%sCONSTANT " HOST_WIDE_INT_PRINT_DOUBLE_HEX, - prefix, cval.high, cval.low); - fprintf (outf, " (" HOST_WIDE_INT_PRINT_DOUBLE_HEX ")", - val.mask.high, val.mask.low); - } - break; - default: - gcc_unreachable (); - } -} - - -/* Print lattice value VAL to stderr. */ - -void debug_lattice_value (prop_value_t val); - -DEBUG_FUNCTION void -debug_lattice_value (prop_value_t val) -{ - dump_lattice_value (stderr, "", val); - fprintf (stderr, "\n"); -} - - -/* Compute a default value for variable VAR and store it in the - CONST_VAL array. The following rules are used to get default - values: - - 1- Global and static variables that are declared constant are - considered CONSTANT. - - 2- Any other value is considered UNDEFINED. This is useful when - considering PHI nodes. PHI arguments that are undefined do not - change the constant value of the PHI node, which allows for more - constants to be propagated. - - 3- Variables defined by statements other than assignments and PHI - nodes are considered VARYING. - - 4- Initial values of variables that are not GIMPLE registers are - considered VARYING. */ - -static prop_value_t -get_default_value (tree var) -{ - prop_value_t val = { UNINITIALIZED, NULL_TREE, { 0, 0 } }; - gimple stmt; - - stmt = SSA_NAME_DEF_STMT (var); - - if (gimple_nop_p (stmt)) - { - /* Variables defined by an empty statement are those used - before being initialized. If VAR is a local variable, we - can assume initially that it is UNDEFINED, otherwise we must - consider it VARYING. */ - if (!virtual_operand_p (var) - && TREE_CODE (SSA_NAME_VAR (var)) == VAR_DECL) - val.lattice_val = UNDEFINED; - else - { - val.lattice_val = VARYING; - val.mask = double_int_minus_one; - } - } - else if (is_gimple_assign (stmt) - /* Value-returning GIMPLE_CALL statements assign to - a variable, and are treated similarly to GIMPLE_ASSIGN. */ - || (is_gimple_call (stmt) - && gimple_call_lhs (stmt) != NULL_TREE) - || gimple_code (stmt) == GIMPLE_PHI) - { - tree cst; - if (gimple_assign_single_p (stmt) - && DECL_P (gimple_assign_rhs1 (stmt)) - && (cst = get_symbol_constant_value (gimple_assign_rhs1 (stmt)))) - { - val.lattice_val = CONSTANT; - val.value = cst; - } - else - /* Any other variable defined by an assignment or a PHI node - is considered UNDEFINED. */ - val.lattice_val = UNDEFINED; - } - else - { - /* Otherwise, VAR will never take on a constant value. */ - val.lattice_val = VARYING; - val.mask = double_int_minus_one; - } - - return val; -} - - -/* Get the constant value associated with variable VAR. */ - -static inline prop_value_t * -get_value (tree var) -{ - prop_value_t *val; - - if (const_val == NULL - || SSA_NAME_VERSION (var) >= n_const_val) - return NULL; - - val = &const_val[SSA_NAME_VERSION (var)]; - if (val->lattice_val == UNINITIALIZED) - *val = get_default_value (var); - - canonicalize_float_value (val); - - return val; -} - -/* Return the constant tree value associated with VAR. */ - -static inline tree -get_constant_value (tree var) -{ - prop_value_t *val; - if (TREE_CODE (var) != SSA_NAME) - { - if (is_gimple_min_invariant (var)) - return var; - return NULL_TREE; - } - val = get_value (var); - if (val - && val->lattice_val == CONSTANT - && (TREE_CODE (val->value) != INTEGER_CST - || val->mask.is_zero ())) - return val->value; - return NULL_TREE; -} - -/* Sets the value associated with VAR to VARYING. */ - -static inline void -set_value_varying (tree var) -{ - prop_value_t *val = &const_val[SSA_NAME_VERSION (var)]; - - val->lattice_val = VARYING; - val->value = NULL_TREE; - val->mask = double_int_minus_one; -} - -/* For float types, modify the value of VAL to make ccp work correctly - for non-standard values (-0, NaN): - - If HONOR_SIGNED_ZEROS is false, and VAL = -0, we canonicalize it to 0. - If HONOR_NANS is false, and VAL is NaN, we canonicalize it to UNDEFINED. - This is to fix the following problem (see PR 29921): Suppose we have - - x = 0.0 * y - - and we set value of y to NaN. This causes value of x to be set to NaN. - When we later determine that y is in fact VARYING, fold uses the fact - that HONOR_NANS is false, and we try to change the value of x to 0, - causing an ICE. With HONOR_NANS being false, the real appearance of - NaN would cause undefined behavior, though, so claiming that y (and x) - are UNDEFINED initially is correct. */ - -static void -canonicalize_float_value (prop_value_t *val) -{ - enum machine_mode mode; - tree type; - REAL_VALUE_TYPE d; - - if (val->lattice_val != CONSTANT - || TREE_CODE (val->value) != REAL_CST) - return; - - d = TREE_REAL_CST (val->value); - type = TREE_TYPE (val->value); - mode = TYPE_MODE (type); - - if (!HONOR_SIGNED_ZEROS (mode) - && REAL_VALUE_MINUS_ZERO (d)) - { - val->value = build_real (type, dconst0); - return; - } - - if (!HONOR_NANS (mode) - && REAL_VALUE_ISNAN (d)) - { - val->lattice_val = UNDEFINED; - val->value = NULL; - return; - } -} - -/* Return whether the lattice transition is valid. */ - -static bool -valid_lattice_transition (prop_value_t old_val, prop_value_t new_val) -{ - /* Lattice transitions must always be monotonically increasing in - value. */ - if (old_val.lattice_val < new_val.lattice_val) - return true; - - if (old_val.lattice_val != new_val.lattice_val) - return false; - - if (!old_val.value && !new_val.value) - return true; - - /* Now both lattice values are CONSTANT. */ - - /* Allow transitioning from PHI <&x, not executable> == &x - to PHI <&x, &y> == common alignment. */ - if (TREE_CODE (old_val.value) != INTEGER_CST - && TREE_CODE (new_val.value) == INTEGER_CST) - return true; - - /* Bit-lattices have to agree in the still valid bits. */ - if (TREE_CODE (old_val.value) == INTEGER_CST - && TREE_CODE (new_val.value) == INTEGER_CST) - return tree_to_double_int (old_val.value).and_not (new_val.mask) - == tree_to_double_int (new_val.value).and_not (new_val.mask); - - /* Otherwise constant values have to agree. */ - return operand_equal_p (old_val.value, new_val.value, 0); -} - -/* Set the value for variable VAR to NEW_VAL. Return true if the new - value is different from VAR's previous value. */ - -static bool -set_lattice_value (tree var, prop_value_t new_val) -{ - /* We can deal with old UNINITIALIZED values just fine here. */ - prop_value_t *old_val = &const_val[SSA_NAME_VERSION (var)]; - - canonicalize_float_value (&new_val); - - /* We have to be careful to not go up the bitwise lattice - represented by the mask. - ??? This doesn't seem to be the best place to enforce this. */ - if (new_val.lattice_val == CONSTANT - && old_val->lattice_val == CONSTANT - && TREE_CODE (new_val.value) == INTEGER_CST - && TREE_CODE (old_val->value) == INTEGER_CST) - { - double_int diff; - diff = tree_to_double_int (new_val.value) - ^ tree_to_double_int (old_val->value); - new_val.mask = new_val.mask | old_val->mask | diff; - } - - gcc_assert (valid_lattice_transition (*old_val, new_val)); - - /* If *OLD_VAL and NEW_VAL are the same, return false to inform the - caller that this was a non-transition. */ - if (old_val->lattice_val != new_val.lattice_val - || (new_val.lattice_val == CONSTANT - && TREE_CODE (new_val.value) == INTEGER_CST - && (TREE_CODE (old_val->value) != INTEGER_CST - || new_val.mask != old_val->mask))) - { - /* ??? We would like to delay creation of INTEGER_CSTs from - partially constants here. */ - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - dump_lattice_value (dump_file, "Lattice value changed to ", new_val); - fprintf (dump_file, ". Adding SSA edges to worklist.\n"); - } - - *old_val = new_val; - - gcc_assert (new_val.lattice_val != UNINITIALIZED); - return true; - } - - return false; -} - -static prop_value_t get_value_for_expr (tree, bool); -static prop_value_t bit_value_binop (enum tree_code, tree, tree, tree); -static void bit_value_binop_1 (enum tree_code, tree, double_int *, double_int *, - tree, double_int, double_int, - tree, double_int, double_int); - -/* Return a double_int that can be used for bitwise simplifications - from VAL. */ - -static double_int -value_to_double_int (prop_value_t val) -{ - if (val.value - && TREE_CODE (val.value) == INTEGER_CST) - return tree_to_double_int (val.value); - else - return double_int_zero; -} - -/* Return the value for the address expression EXPR based on alignment - information. */ - -static prop_value_t -get_value_from_alignment (tree expr) -{ - tree type = TREE_TYPE (expr); - prop_value_t val; - unsigned HOST_WIDE_INT bitpos; - unsigned int align; - - gcc_assert (TREE_CODE (expr) == ADDR_EXPR); - - get_pointer_alignment_1 (expr, &align, &bitpos); - val.mask = (POINTER_TYPE_P (type) || TYPE_UNSIGNED (type) - ? double_int::mask (TYPE_PRECISION (type)) - : double_int_minus_one) - .and_not (double_int::from_uhwi (align / BITS_PER_UNIT - 1)); - val.lattice_val = val.mask.is_minus_one () ? VARYING : CONSTANT; - if (val.lattice_val == CONSTANT) - val.value - = double_int_to_tree (type, - double_int::from_uhwi (bitpos / BITS_PER_UNIT)); - else - val.value = NULL_TREE; - - return val; -} - -/* Return the value for the tree operand EXPR. If FOR_BITS_P is true - return constant bits extracted from alignment information for - invariant addresses. */ - -static prop_value_t -get_value_for_expr (tree expr, bool for_bits_p) -{ - prop_value_t val; - - if (TREE_CODE (expr) == SSA_NAME) - { - val = *get_value (expr); - if (for_bits_p - && val.lattice_val == CONSTANT - && TREE_CODE (val.value) == ADDR_EXPR) - val = get_value_from_alignment (val.value); - } - else if (is_gimple_min_invariant (expr) - && (!for_bits_p || TREE_CODE (expr) != ADDR_EXPR)) - { - val.lattice_val = CONSTANT; - val.value = expr; - val.mask = double_int_zero; - canonicalize_float_value (&val); - } - else if (TREE_CODE (expr) == ADDR_EXPR) - val = get_value_from_alignment (expr); - else - { - val.lattice_val = VARYING; - val.mask = double_int_minus_one; - val.value = NULL_TREE; - } - return val; -} - -/* Return the likely CCP lattice value for STMT. - - If STMT has no operands, then return CONSTANT. - - Else if undefinedness of operands of STMT cause its value to be - undefined, then return UNDEFINED. - - Else if any operands of STMT are constants, then return CONSTANT. - - Else return VARYING. */ - -static ccp_lattice_t -likely_value (gimple stmt) -{ - bool has_constant_operand, has_undefined_operand, all_undefined_operands; - tree use; - ssa_op_iter iter; - unsigned i; - - enum gimple_code code = gimple_code (stmt); - - /* This function appears to be called only for assignments, calls, - conditionals, and switches, due to the logic in visit_stmt. */ - gcc_assert (code == GIMPLE_ASSIGN - || code == GIMPLE_CALL - || code == GIMPLE_COND - || code == GIMPLE_SWITCH); - - /* If the statement has volatile operands, it won't fold to a - constant value. */ - if (gimple_has_volatile_ops (stmt)) - return VARYING; - - /* Arrive here for more complex cases. */ - has_constant_operand = false; - has_undefined_operand = false; - all_undefined_operands = true; - FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) - { - prop_value_t *val = get_value (use); - - if (val->lattice_val == UNDEFINED) - has_undefined_operand = true; - else - all_undefined_operands = false; - - if (val->lattice_val == CONSTANT) - has_constant_operand = true; - } - - /* There may be constants in regular rhs operands. For calls we - have to ignore lhs, fndecl and static chain, otherwise only - the lhs. */ - for (i = (is_gimple_call (stmt) ? 2 : 0) + gimple_has_lhs (stmt); - i < gimple_num_ops (stmt); ++i) - { - tree op = gimple_op (stmt, i); - if (!op || TREE_CODE (op) == SSA_NAME) - continue; - if (is_gimple_min_invariant (op)) - has_constant_operand = true; - } - - if (has_constant_operand) - all_undefined_operands = false; - - /* If the operation combines operands like COMPLEX_EXPR make sure to - not mark the result UNDEFINED if only one part of the result is - undefined. */ - if (has_undefined_operand && all_undefined_operands) - return UNDEFINED; - else if (code == GIMPLE_ASSIGN && has_undefined_operand) - { - switch (gimple_assign_rhs_code (stmt)) - { - /* Unary operators are handled with all_undefined_operands. */ - case PLUS_EXPR: - case MINUS_EXPR: - case POINTER_PLUS_EXPR: - /* Not MIN_EXPR, MAX_EXPR. One VARYING operand may be selected. - Not bitwise operators, one VARYING operand may specify the - result completely. Not logical operators for the same reason. - Not COMPLEX_EXPR as one VARYING operand makes the result partly - not UNDEFINED. Not *DIV_EXPR, comparisons and shifts because - the undefined operand may be promoted. */ - return UNDEFINED; - - case ADDR_EXPR: - /* If any part of an address is UNDEFINED, like the index - of an ARRAY_EXPR, then treat the result as UNDEFINED. */ - return UNDEFINED; - - default: - ; - } - } - /* If there was an UNDEFINED operand but the result may be not UNDEFINED - fall back to CONSTANT. During iteration UNDEFINED may still drop - to CONSTANT. */ - if (has_undefined_operand) - return CONSTANT; - - /* We do not consider virtual operands here -- load from read-only - memory may have only VARYING virtual operands, but still be - constant. */ - if (has_constant_operand - || gimple_references_memory_p (stmt)) - return CONSTANT; - - return VARYING; -} - -/* Returns true if STMT cannot be constant. */ - -static bool -surely_varying_stmt_p (gimple stmt) -{ - /* If the statement has operands that we cannot handle, it cannot be - constant. */ - if (gimple_has_volatile_ops (stmt)) - return true; - - /* If it is a call and does not return a value or is not a - builtin and not an indirect call, it is varying. */ - if (is_gimple_call (stmt)) - { - tree fndecl; - if (!gimple_call_lhs (stmt) - || ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE - && !DECL_BUILT_IN (fndecl))) - return true; - } - - /* Any other store operation is not interesting. */ - else if (gimple_vdef (stmt)) - return true; - - /* Anything other than assignments and conditional jumps are not - interesting for CCP. */ - if (gimple_code (stmt) != GIMPLE_ASSIGN - && gimple_code (stmt) != GIMPLE_COND - && gimple_code (stmt) != GIMPLE_SWITCH - && gimple_code (stmt) != GIMPLE_CALL) - return true; - - return false; -} - -/* Initialize local data structures for CCP. */ - -static void -ccp_initialize (void) -{ - basic_block bb; - - n_const_val = num_ssa_names; - const_val = XCNEWVEC (prop_value_t, n_const_val); - - /* Initialize simulation flags for PHI nodes and statements. */ - FOR_EACH_BB (bb) - { - gimple_stmt_iterator i; - - for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) - { - gimple stmt = gsi_stmt (i); - bool is_varying; - - /* If the statement is a control insn, then we do not - want to avoid simulating the statement once. Failure - to do so means that those edges will never get added. */ - if (stmt_ends_bb_p (stmt)) - is_varying = false; - else - is_varying = surely_varying_stmt_p (stmt); - - if (is_varying) - { - tree def; - ssa_op_iter iter; - - /* If the statement will not produce a constant, mark - all its outputs VARYING. */ - FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) - set_value_varying (def); - } - prop_set_simulate_again (stmt, !is_varying); - } - } - - /* Now process PHI nodes. We never clear the simulate_again flag on - phi nodes, since we do not know which edges are executable yet, - except for phi nodes for virtual operands when we do not do store ccp. */ - FOR_EACH_BB (bb) - { - gimple_stmt_iterator i; - - for (i = gsi_start_phis (bb); !gsi_end_p (i); gsi_next (&i)) - { - gimple phi = gsi_stmt (i); - - if (virtual_operand_p (gimple_phi_result (phi))) - prop_set_simulate_again (phi, false); - else - prop_set_simulate_again (phi, true); - } - } -} - -/* Debug count support. Reset the values of ssa names - VARYING when the total number ssa names analyzed is - beyond the debug count specified. */ - -static void -do_dbg_cnt (void) -{ - unsigned i; - for (i = 0; i < num_ssa_names; i++) - { - if (!dbg_cnt (ccp)) - { - const_val[i].lattice_val = VARYING; - const_val[i].mask = double_int_minus_one; - const_val[i].value = NULL_TREE; - } - } -} - - -/* Do final substitution of propagated values, cleanup the flowgraph and - free allocated storage. - - Return TRUE when something was optimized. */ - -static bool -ccp_finalize (void) -{ - bool something_changed; - unsigned i; - - do_dbg_cnt (); - - /* Derive alignment and misalignment information from partially - constant pointers in the lattice. */ - for (i = 1; i < num_ssa_names; ++i) - { - tree name = ssa_name (i); - prop_value_t *val; - unsigned int tem, align; - - if (!name - || !POINTER_TYPE_P (TREE_TYPE (name))) - continue; - - val = get_value (name); - if (val->lattice_val != CONSTANT - || TREE_CODE (val->value) != INTEGER_CST) - continue; - - /* Trailing constant bits specify the alignment, trailing value - bits the misalignment. */ - tem = val->mask.low; - align = (tem & -tem); - if (align > 1) - set_ptr_info_alignment (get_ptr_info (name), align, - TREE_INT_CST_LOW (val->value) & (align - 1)); - } - - /* Perform substitutions based on the known constant values. */ - something_changed = substitute_and_fold (get_constant_value, - ccp_fold_stmt, true); - - free (const_val); - const_val = NULL; - return something_changed;; -} - - -/* Compute the meet operator between *VAL1 and *VAL2. Store the result - in VAL1. - - any M UNDEFINED = any - any M VARYING = VARYING - Ci M Cj = Ci if (i == j) - Ci M Cj = VARYING if (i != j) - */ - -static void -ccp_lattice_meet (prop_value_t *val1, prop_value_t *val2) -{ - if (val1->lattice_val == UNDEFINED) - { - /* UNDEFINED M any = any */ - *val1 = *val2; - } - else if (val2->lattice_val == UNDEFINED) - { - /* any M UNDEFINED = any - Nothing to do. VAL1 already contains the value we want. */ - ; - } - else if (val1->lattice_val == VARYING - || val2->lattice_val == VARYING) - { - /* any M VARYING = VARYING. */ - val1->lattice_val = VARYING; - val1->mask = double_int_minus_one; - val1->value = NULL_TREE; - } - else if (val1->lattice_val == CONSTANT - && val2->lattice_val == CONSTANT - && TREE_CODE (val1->value) == INTEGER_CST - && TREE_CODE (val2->value) == INTEGER_CST) - { - /* Ci M Cj = Ci if (i == j) - Ci M Cj = VARYING if (i != j) - - For INTEGER_CSTs mask unequal bits. If no equal bits remain, - drop to varying. */ - val1->mask = val1->mask | val2->mask - | (tree_to_double_int (val1->value) - ^ tree_to_double_int (val2->value)); - if (val1->mask.is_minus_one ()) - { - val1->lattice_val = VARYING; - val1->value = NULL_TREE; - } - } - else if (val1->lattice_val == CONSTANT - && val2->lattice_val == CONSTANT - && simple_cst_equal (val1->value, val2->value) == 1) - { - /* Ci M Cj = Ci if (i == j) - Ci M Cj = VARYING if (i != j) - - VAL1 already contains the value we want for equivalent values. */ - } - else if (val1->lattice_val == CONSTANT - && val2->lattice_val == CONSTANT - && (TREE_CODE (val1->value) == ADDR_EXPR - || TREE_CODE (val2->value) == ADDR_EXPR)) - { - /* When not equal addresses are involved try meeting for - alignment. */ - prop_value_t tem = *val2; - if (TREE_CODE (val1->value) == ADDR_EXPR) - *val1 = get_value_for_expr (val1->value, true); - if (TREE_CODE (val2->value) == ADDR_EXPR) - tem = get_value_for_expr (val2->value, true); - ccp_lattice_meet (val1, &tem); - } - else - { - /* Any other combination is VARYING. */ - val1->lattice_val = VARYING; - val1->mask = double_int_minus_one; - val1->value = NULL_TREE; - } -} - - -/* Loop through the PHI_NODE's parameters for BLOCK and compare their - lattice values to determine PHI_NODE's lattice value. The value of a - PHI node is determined calling ccp_lattice_meet with all the arguments - of the PHI node that are incoming via executable edges. */ - -static enum ssa_prop_result -ccp_visit_phi_node (gimple phi) -{ - unsigned i; - prop_value_t *old_val, new_val; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\nVisiting PHI node: "); - print_gimple_stmt (dump_file, phi, 0, dump_flags); - } - - old_val = get_value (gimple_phi_result (phi)); - switch (old_val->lattice_val) - { - case VARYING: - return SSA_PROP_VARYING; - - case CONSTANT: - new_val = *old_val; - break; - - case UNDEFINED: - new_val.lattice_val = UNDEFINED; - new_val.value = NULL_TREE; - break; - - default: - gcc_unreachable (); - } - - for (i = 0; i < gimple_phi_num_args (phi); i++) - { - /* Compute the meet operator over all the PHI arguments flowing - through executable edges. */ - edge e = gimple_phi_arg_edge (phi, i); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, - "\n Argument #%d (%d -> %d %sexecutable)\n", - i, e->src->index, e->dest->index, - (e->flags & EDGE_EXECUTABLE) ? "" : "not "); - } - - /* If the incoming edge is executable, Compute the meet operator for - the existing value of the PHI node and the current PHI argument. */ - if (e->flags & EDGE_EXECUTABLE) - { - tree arg = gimple_phi_arg (phi, i)->def; - prop_value_t arg_val = get_value_for_expr (arg, false); - - ccp_lattice_meet (&new_val, &arg_val); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\t"); - print_generic_expr (dump_file, arg, dump_flags); - dump_lattice_value (dump_file, "\tValue: ", arg_val); - fprintf (dump_file, "\n"); - } - - if (new_val.lattice_val == VARYING) - break; - } - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - dump_lattice_value (dump_file, "\n PHI node value: ", new_val); - fprintf (dump_file, "\n\n"); - } - - /* Make the transition to the new value. */ - if (set_lattice_value (gimple_phi_result (phi), new_val)) - { - if (new_val.lattice_val == VARYING) - return SSA_PROP_VARYING; - else - return SSA_PROP_INTERESTING; - } - else - return SSA_PROP_NOT_INTERESTING; -} - -/* Return the constant value for OP or OP otherwise. */ - -static tree -valueize_op (tree op) -{ - if (TREE_CODE (op) == SSA_NAME) - { - tree tem = get_constant_value (op); - if (tem) - return tem; - } - return op; -} - -/* CCP specific front-end to the non-destructive constant folding - routines. - - Attempt to simplify the RHS of STMT knowing that one or more - operands are constants. - - If simplification is possible, return the simplified RHS, - otherwise return the original RHS or NULL_TREE. */ - -static tree -ccp_fold (gimple stmt) -{ - location_t loc = gimple_location (stmt); - switch (gimple_code (stmt)) - { - case GIMPLE_COND: - { - /* Handle comparison operators that can appear in GIMPLE form. */ - tree op0 = valueize_op (gimple_cond_lhs (stmt)); - tree op1 = valueize_op (gimple_cond_rhs (stmt)); - enum tree_code code = gimple_cond_code (stmt); - return fold_binary_loc (loc, code, boolean_type_node, op0, op1); - } - - case GIMPLE_SWITCH: - { - /* Return the constant switch index. */ - return valueize_op (gimple_switch_index (stmt)); - } - - case GIMPLE_ASSIGN: - case GIMPLE_CALL: - return gimple_fold_stmt_to_constant_1 (stmt, valueize_op); - - default: - gcc_unreachable (); - } -} - -/* Apply the operation CODE in type TYPE to the value, mask pair - RVAL and RMASK representing a value of type RTYPE and set - the value, mask pair *VAL and *MASK to the result. */ - -static void -bit_value_unop_1 (enum tree_code code, tree type, - double_int *val, double_int *mask, - tree rtype, double_int rval, double_int rmask) -{ - switch (code) - { - case BIT_NOT_EXPR: - *mask = rmask; - *val = ~rval; - break; - - case NEGATE_EXPR: - { - double_int temv, temm; - /* Return ~rval + 1. */ - bit_value_unop_1 (BIT_NOT_EXPR, type, &temv, &temm, type, rval, rmask); - bit_value_binop_1 (PLUS_EXPR, type, val, mask, - type, temv, temm, - type, double_int_one, double_int_zero); - break; - } - - CASE_CONVERT: - { - bool uns; - - /* First extend mask and value according to the original type. */ - uns = TYPE_UNSIGNED (rtype); - *mask = rmask.ext (TYPE_PRECISION (rtype), uns); - *val = rval.ext (TYPE_PRECISION (rtype), uns); - - /* Then extend mask and value according to the target type. */ - uns = TYPE_UNSIGNED (type); - *mask = (*mask).ext (TYPE_PRECISION (type), uns); - *val = (*val).ext (TYPE_PRECISION (type), uns); - break; - } - - default: - *mask = double_int_minus_one; - break; - } -} - -/* Apply the operation CODE in type TYPE to the value, mask pairs - R1VAL, R1MASK and R2VAL, R2MASK representing a values of type R1TYPE - and R2TYPE and set the value, mask pair *VAL and *MASK to the result. */ - -static void -bit_value_binop_1 (enum tree_code code, tree type, - double_int *val, double_int *mask, - tree r1type, double_int r1val, double_int r1mask, - tree r2type, double_int r2val, double_int r2mask) -{ - bool uns = TYPE_UNSIGNED (type); - /* Assume we'll get a constant result. Use an initial varying value, - we fall back to varying in the end if necessary. */ - *mask = double_int_minus_one; - switch (code) - { - case BIT_AND_EXPR: - /* The mask is constant where there is a known not - set bit, (m1 | m2) & ((v1 | m1) & (v2 | m2)) */ - *mask = (r1mask | r2mask) & (r1val | r1mask) & (r2val | r2mask); - *val = r1val & r2val; - break; - - case BIT_IOR_EXPR: - /* The mask is constant where there is a known - set bit, (m1 | m2) & ~((v1 & ~m1) | (v2 & ~m2)). */ - *mask = (r1mask | r2mask) - .and_not (r1val.and_not (r1mask) | r2val.and_not (r2mask)); - *val = r1val | r2val; - break; - - case BIT_XOR_EXPR: - /* m1 | m2 */ - *mask = r1mask | r2mask; - *val = r1val ^ r2val; - break; - - case LROTATE_EXPR: - case RROTATE_EXPR: - if (r2mask.is_zero ()) - { - HOST_WIDE_INT shift = r2val.low; - if (code == RROTATE_EXPR) - shift = -shift; - *mask = r1mask.lrotate (shift, TYPE_PRECISION (type)); - *val = r1val.lrotate (shift, TYPE_PRECISION (type)); - } - break; - - case LSHIFT_EXPR: - case RSHIFT_EXPR: - /* ??? We can handle partially known shift counts if we know - its sign. That way we can tell that (x << (y | 8)) & 255 - is zero. */ - if (r2mask.is_zero ()) - { - HOST_WIDE_INT shift = r2val.low; - if (code == RSHIFT_EXPR) - shift = -shift; - /* We need to know if we are doing a left or a right shift - to properly shift in zeros for left shift and unsigned - right shifts and the sign bit for signed right shifts. - For signed right shifts we shift in varying in case - the sign bit was varying. */ - if (shift > 0) - { - *mask = r1mask.llshift (shift, TYPE_PRECISION (type)); - *val = r1val.llshift (shift, TYPE_PRECISION (type)); - } - else if (shift < 0) - { - shift = -shift; - *mask = r1mask.rshift (shift, TYPE_PRECISION (type), !uns); - *val = r1val.rshift (shift, TYPE_PRECISION (type), !uns); - } - else - { - *mask = r1mask; - *val = r1val; - } - } - break; - - case PLUS_EXPR: - case POINTER_PLUS_EXPR: - { - double_int lo, hi; - /* Do the addition with unknown bits set to zero, to give carry-ins of - zero wherever possible. */ - lo = r1val.and_not (r1mask) + r2val.and_not (r2mask); - lo = lo.ext (TYPE_PRECISION (type), uns); - /* Do the addition with unknown bits set to one, to give carry-ins of - one wherever possible. */ - hi = (r1val | r1mask) + (r2val | r2mask); - hi = hi.ext (TYPE_PRECISION (type), uns); - /* Each bit in the result is known if (a) the corresponding bits in - both inputs are known, and (b) the carry-in to that bit position - is known. We can check condition (b) by seeing if we got the same - result with minimised carries as with maximised carries. */ - *mask = r1mask | r2mask | (lo ^ hi); - *mask = (*mask).ext (TYPE_PRECISION (type), uns); - /* It shouldn't matter whether we choose lo or hi here. */ - *val = lo; - break; - } - - case MINUS_EXPR: - { - double_int temv, temm; - bit_value_unop_1 (NEGATE_EXPR, r2type, &temv, &temm, - r2type, r2val, r2mask); - bit_value_binop_1 (PLUS_EXPR, type, val, mask, - r1type, r1val, r1mask, - r2type, temv, temm); - break; - } - - case MULT_EXPR: - { - /* Just track trailing zeros in both operands and transfer - them to the other. */ - int r1tz = (r1val | r1mask).trailing_zeros (); - int r2tz = (r2val | r2mask).trailing_zeros (); - if (r1tz + r2tz >= HOST_BITS_PER_DOUBLE_INT) - { - *mask = double_int_zero; - *val = double_int_zero; - } - else if (r1tz + r2tz > 0) - { - *mask = ~double_int::mask (r1tz + r2tz); - *mask = (*mask).ext (TYPE_PRECISION (type), uns); - *val = double_int_zero; - } - break; - } - - case EQ_EXPR: - case NE_EXPR: - { - double_int m = r1mask | r2mask; - if (r1val.and_not (m) != r2val.and_not (m)) - { - *mask = double_int_zero; - *val = ((code == EQ_EXPR) ? double_int_zero : double_int_one); - } - else - { - /* We know the result of a comparison is always one or zero. */ - *mask = double_int_one; - *val = double_int_zero; - } - break; - } - - case GE_EXPR: - case GT_EXPR: - { - double_int tem = r1val; - r1val = r2val; - r2val = tem; - tem = r1mask; - r1mask = r2mask; - r2mask = tem; - code = swap_tree_comparison (code); - } - /* Fallthru. */ - case LT_EXPR: - case LE_EXPR: - { - int minmax, maxmin; - /* If the most significant bits are not known we know nothing. */ - if (r1mask.is_negative () || r2mask.is_negative ()) - break; - - /* For comparisons the signedness is in the comparison operands. */ - uns = TYPE_UNSIGNED (r1type); - - /* If we know the most significant bits we know the values - value ranges by means of treating varying bits as zero - or one. Do a cross comparison of the max/min pairs. */ - maxmin = (r1val | r1mask).cmp (r2val.and_not (r2mask), uns); - minmax = r1val.and_not (r1mask).cmp (r2val | r2mask, uns); - if (maxmin < 0) /* r1 is less than r2. */ - { - *mask = double_int_zero; - *val = double_int_one; - } - else if (minmax > 0) /* r1 is not less or equal to r2. */ - { - *mask = double_int_zero; - *val = double_int_zero; - } - else if (maxmin == minmax) /* r1 and r2 are equal. */ - { - /* This probably should never happen as we'd have - folded the thing during fully constant value folding. */ - *mask = double_int_zero; - *val = (code == LE_EXPR ? double_int_one : double_int_zero); - } - else - { - /* We know the result of a comparison is always one or zero. */ - *mask = double_int_one; - *val = double_int_zero; - } - break; - } - - default:; - } -} - -/* Return the propagation value when applying the operation CODE to - the value RHS yielding type TYPE. */ - -static prop_value_t -bit_value_unop (enum tree_code code, tree type, tree rhs) -{ - prop_value_t rval = get_value_for_expr (rhs, true); - double_int value, mask; - prop_value_t val; - - if (rval.lattice_val == UNDEFINED) - return rval; - - gcc_assert ((rval.lattice_val == CONSTANT - && TREE_CODE (rval.value) == INTEGER_CST) - || rval.mask.is_minus_one ()); - bit_value_unop_1 (code, type, &value, &mask, - TREE_TYPE (rhs), value_to_double_int (rval), rval.mask); - if (!mask.is_minus_one ()) - { - val.lattice_val = CONSTANT; - val.mask = mask; - /* ??? Delay building trees here. */ - val.value = double_int_to_tree (type, value); - } - else - { - val.lattice_val = VARYING; - val.value = NULL_TREE; - val.mask = double_int_minus_one; - } - return val; -} - -/* Return the propagation value when applying the operation CODE to - the values RHS1 and RHS2 yielding type TYPE. */ - -static prop_value_t -bit_value_binop (enum tree_code code, tree type, tree rhs1, tree rhs2) -{ - prop_value_t r1val = get_value_for_expr (rhs1, true); - prop_value_t r2val = get_value_for_expr (rhs2, true); - double_int value, mask; - prop_value_t val; - - if (r1val.lattice_val == UNDEFINED - || r2val.lattice_val == UNDEFINED) - { - val.lattice_val = VARYING; - val.value = NULL_TREE; - val.mask = double_int_minus_one; - return val; - } - - gcc_assert ((r1val.lattice_val == CONSTANT - && TREE_CODE (r1val.value) == INTEGER_CST) - || r1val.mask.is_minus_one ()); - gcc_assert ((r2val.lattice_val == CONSTANT - && TREE_CODE (r2val.value) == INTEGER_CST) - || r2val.mask.is_minus_one ()); - bit_value_binop_1 (code, type, &value, &mask, - TREE_TYPE (rhs1), value_to_double_int (r1val), r1val.mask, - TREE_TYPE (rhs2), value_to_double_int (r2val), r2val.mask); - if (!mask.is_minus_one ()) - { - val.lattice_val = CONSTANT; - val.mask = mask; - /* ??? Delay building trees here. */ - val.value = double_int_to_tree (type, value); - } - else - { - val.lattice_val = VARYING; - val.value = NULL_TREE; - val.mask = double_int_minus_one; - } - return val; -} - -/* Return the propagation value when applying __builtin_assume_aligned to - its arguments. */ - -static prop_value_t -bit_value_assume_aligned (gimple stmt) -{ - tree ptr = gimple_call_arg (stmt, 0), align, misalign = NULL_TREE; - tree type = TREE_TYPE (ptr); - unsigned HOST_WIDE_INT aligni, misaligni = 0; - prop_value_t ptrval = get_value_for_expr (ptr, true); - prop_value_t alignval; - double_int value, mask; - prop_value_t val; - if (ptrval.lattice_val == UNDEFINED) - return ptrval; - gcc_assert ((ptrval.lattice_val == CONSTANT - && TREE_CODE (ptrval.value) == INTEGER_CST) - || ptrval.mask.is_minus_one ()); - align = gimple_call_arg (stmt, 1); - if (!host_integerp (align, 1)) - return ptrval; - aligni = tree_low_cst (align, 1); - if (aligni <= 1 - || (aligni & (aligni - 1)) != 0) - return ptrval; - if (gimple_call_num_args (stmt) > 2) - { - misalign = gimple_call_arg (stmt, 2); - if (!host_integerp (misalign, 1)) - return ptrval; - misaligni = tree_low_cst (misalign, 1); - if (misaligni >= aligni) - return ptrval; - } - align = build_int_cst_type (type, -aligni); - alignval = get_value_for_expr (align, true); - bit_value_binop_1 (BIT_AND_EXPR, type, &value, &mask, - type, value_to_double_int (ptrval), ptrval.mask, - type, value_to_double_int (alignval), alignval.mask); - if (!mask.is_minus_one ()) - { - val.lattice_val = CONSTANT; - val.mask = mask; - gcc_assert ((mask.low & (aligni - 1)) == 0); - gcc_assert ((value.low & (aligni - 1)) == 0); - value.low |= misaligni; - /* ??? Delay building trees here. */ - val.value = double_int_to_tree (type, value); - } - else - { - val.lattice_val = VARYING; - val.value = NULL_TREE; - val.mask = double_int_minus_one; - } - return val; -} - -/* Evaluate statement STMT. - Valid only for assignments, calls, conditionals, and switches. */ - -static prop_value_t -evaluate_stmt (gimple stmt) -{ - prop_value_t val; - tree simplified = NULL_TREE; - ccp_lattice_t likelyvalue = likely_value (stmt); - bool is_constant = false; - unsigned int align; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "which is likely "); - switch (likelyvalue) - { - case CONSTANT: - fprintf (dump_file, "CONSTANT"); - break; - case UNDEFINED: - fprintf (dump_file, "UNDEFINED"); - break; - case VARYING: - fprintf (dump_file, "VARYING"); - break; - default:; - } - fprintf (dump_file, "\n"); - } - - /* If the statement is likely to have a CONSTANT result, then try - to fold the statement to determine the constant value. */ - /* FIXME. This is the only place that we call ccp_fold. - Since likely_value never returns CONSTANT for calls, we will - not attempt to fold them, including builtins that may profit. */ - if (likelyvalue == CONSTANT) - { - fold_defer_overflow_warnings (); - simplified = ccp_fold (stmt); - is_constant = simplified && is_gimple_min_invariant (simplified); - fold_undefer_overflow_warnings (is_constant, stmt, 0); - if (is_constant) - { - /* The statement produced a constant value. */ - val.lattice_val = CONSTANT; - val.value = simplified; - val.mask = double_int_zero; - } - } - /* If the statement is likely to have a VARYING result, then do not - bother folding the statement. */ - else if (likelyvalue == VARYING) - { - enum gimple_code code = gimple_code (stmt); - if (code == GIMPLE_ASSIGN) - { - enum tree_code subcode = gimple_assign_rhs_code (stmt); - - /* Other cases cannot satisfy is_gimple_min_invariant - without folding. */ - if (get_gimple_rhs_class (subcode) == GIMPLE_SINGLE_RHS) - simplified = gimple_assign_rhs1 (stmt); - } - else if (code == GIMPLE_SWITCH) - simplified = gimple_switch_index (stmt); - else - /* These cannot satisfy is_gimple_min_invariant without folding. */ - gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND); - is_constant = simplified && is_gimple_min_invariant (simplified); - if (is_constant) - { - /* The statement produced a constant value. */ - val.lattice_val = CONSTANT; - val.value = simplified; - val.mask = double_int_zero; - } - } - - /* Resort to simplification for bitwise tracking. */ - if (flag_tree_bit_ccp - && (likelyvalue == CONSTANT || is_gimple_call (stmt)) - && !is_constant) - { - enum gimple_code code = gimple_code (stmt); - val.lattice_val = VARYING; - val.value = NULL_TREE; - val.mask = double_int_minus_one; - if (code == GIMPLE_ASSIGN) - { - enum tree_code subcode = gimple_assign_rhs_code (stmt); - tree rhs1 = gimple_assign_rhs1 (stmt); - switch (get_gimple_rhs_class (subcode)) - { - case GIMPLE_SINGLE_RHS: - if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1)) - || POINTER_TYPE_P (TREE_TYPE (rhs1))) - val = get_value_for_expr (rhs1, true); - break; - - case GIMPLE_UNARY_RHS: - if ((INTEGRAL_TYPE_P (TREE_TYPE (rhs1)) - || POINTER_TYPE_P (TREE_TYPE (rhs1))) - && (INTEGRAL_TYPE_P (gimple_expr_type (stmt)) - || POINTER_TYPE_P (gimple_expr_type (stmt)))) - val = bit_value_unop (subcode, gimple_expr_type (stmt), rhs1); - break; - - case GIMPLE_BINARY_RHS: - if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1)) - || POINTER_TYPE_P (TREE_TYPE (rhs1))) - { - tree lhs = gimple_assign_lhs (stmt); - tree rhs2 = gimple_assign_rhs2 (stmt); - val = bit_value_binop (subcode, - TREE_TYPE (lhs), rhs1, rhs2); - } - break; - - default:; - } - } - else if (code == GIMPLE_COND) - { - enum tree_code code = gimple_cond_code (stmt); - tree rhs1 = gimple_cond_lhs (stmt); - tree rhs2 = gimple_cond_rhs (stmt); - if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1)) - || POINTER_TYPE_P (TREE_TYPE (rhs1))) - val = bit_value_binop (code, TREE_TYPE (rhs1), rhs1, rhs2); - } - else if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) - { - tree fndecl = gimple_call_fndecl (stmt); - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_MALLOC: - case BUILT_IN_REALLOC: - case BUILT_IN_CALLOC: - case BUILT_IN_STRDUP: - case BUILT_IN_STRNDUP: - val.lattice_val = CONSTANT; - val.value = build_int_cst (TREE_TYPE (gimple_get_lhs (stmt)), 0); - val.mask = double_int::from_shwi - (~(((HOST_WIDE_INT) MALLOC_ABI_ALIGNMENT) - / BITS_PER_UNIT - 1)); - break; - - case BUILT_IN_ALLOCA: - case BUILT_IN_ALLOCA_WITH_ALIGN: - align = (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN - ? TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)) - : BIGGEST_ALIGNMENT); - val.lattice_val = CONSTANT; - val.value = build_int_cst (TREE_TYPE (gimple_get_lhs (stmt)), 0); - val.mask = double_int::from_shwi (~(((HOST_WIDE_INT) align) - / BITS_PER_UNIT - 1)); - break; - - /* These builtins return their first argument, unmodified. */ - case BUILT_IN_MEMCPY: - case BUILT_IN_MEMMOVE: - case BUILT_IN_MEMSET: - case BUILT_IN_STRCPY: - case BUILT_IN_STRNCPY: - case BUILT_IN_MEMCPY_CHK: - case BUILT_IN_MEMMOVE_CHK: - case BUILT_IN_MEMSET_CHK: - case BUILT_IN_STRCPY_CHK: - case BUILT_IN_STRNCPY_CHK: - val = get_value_for_expr (gimple_call_arg (stmt, 0), true); - break; - - case BUILT_IN_ASSUME_ALIGNED: - val = bit_value_assume_aligned (stmt); - break; - - default:; - } - } - is_constant = (val.lattice_val == CONSTANT); - } - - if (!is_constant) - { - /* The statement produced a nonconstant value. If the statement - had UNDEFINED operands, then the result of the statement - should be UNDEFINED. Otherwise, the statement is VARYING. */ - if (likelyvalue == UNDEFINED) - { - val.lattice_val = likelyvalue; - val.mask = double_int_zero; - } - else - { - val.lattice_val = VARYING; - val.mask = double_int_minus_one; - } - - val.value = NULL_TREE; - } - - return val; -} - -typedef hash_table <pointer_hash <gimple_statement_d> > gimple_htab; - -/* Given a BUILT_IN_STACK_SAVE value SAVED_VAL, insert a clobber of VAR before - each matching BUILT_IN_STACK_RESTORE. Mark visited phis in VISITED. */ - -static void -insert_clobber_before_stack_restore (tree saved_val, tree var, - gimple_htab *visited) -{ - gimple stmt, clobber_stmt; - tree clobber; - imm_use_iterator iter; - gimple_stmt_iterator i; - gimple *slot; - - FOR_EACH_IMM_USE_STMT (stmt, iter, saved_val) - if (gimple_call_builtin_p (stmt, BUILT_IN_STACK_RESTORE)) - { - clobber = build_constructor (TREE_TYPE (var), - NULL); - TREE_THIS_VOLATILE (clobber) = 1; - clobber_stmt = gimple_build_assign (var, clobber); - - i = gsi_for_stmt (stmt); - gsi_insert_before (&i, clobber_stmt, GSI_SAME_STMT); - } - else if (gimple_code (stmt) == GIMPLE_PHI) - { - if (!visited->is_created ()) - visited->create (10); - - slot = visited->find_slot (stmt, INSERT); - if (*slot != NULL) - continue; - - *slot = stmt; - insert_clobber_before_stack_restore (gimple_phi_result (stmt), var, - visited); - } - else - gcc_assert (is_gimple_debug (stmt)); -} - -/* Advance the iterator to the previous non-debug gimple statement in the same - or dominating basic block. */ - -static inline void -gsi_prev_dom_bb_nondebug (gimple_stmt_iterator *i) -{ - basic_block dom; - - gsi_prev_nondebug (i); - while (gsi_end_p (*i)) - { - dom = get_immediate_dominator (CDI_DOMINATORS, i->bb); - if (dom == NULL || dom == ENTRY_BLOCK_PTR) - return; - - *i = gsi_last_bb (dom); - } -} - -/* Find a BUILT_IN_STACK_SAVE dominating gsi_stmt (I), and insert - a clobber of VAR before each matching BUILT_IN_STACK_RESTORE. - - It is possible that BUILT_IN_STACK_SAVE cannot be find in a dominator when a - previous pass (such as DOM) duplicated it along multiple paths to a BB. In - that case the function gives up without inserting the clobbers. */ - -static void -insert_clobbers_for_var (gimple_stmt_iterator i, tree var) -{ - gimple stmt; - tree saved_val; - gimple_htab visited; - - for (; !gsi_end_p (i); gsi_prev_dom_bb_nondebug (&i)) - { - stmt = gsi_stmt (i); - - if (!gimple_call_builtin_p (stmt, BUILT_IN_STACK_SAVE)) - continue; - - saved_val = gimple_call_lhs (stmt); - if (saved_val == NULL_TREE) - continue; - - insert_clobber_before_stack_restore (saved_val, var, &visited); - break; - } - - if (visited.is_created ()) - visited.dispose (); -} - -/* Detects a __builtin_alloca_with_align with constant size argument. Declares - fixed-size array and returns the address, if found, otherwise returns - NULL_TREE. */ - -static tree -fold_builtin_alloca_with_align (gimple stmt) -{ - unsigned HOST_WIDE_INT size, threshold, n_elem; - tree lhs, arg, block, var, elem_type, array_type; - - /* Get lhs. */ - lhs = gimple_call_lhs (stmt); - if (lhs == NULL_TREE) - return NULL_TREE; - - /* Detect constant argument. */ - arg = get_constant_value (gimple_call_arg (stmt, 0)); - if (arg == NULL_TREE - || TREE_CODE (arg) != INTEGER_CST - || !host_integerp (arg, 1)) - return NULL_TREE; - - size = TREE_INT_CST_LOW (arg); - - /* Heuristic: don't fold large allocas. */ - threshold = (unsigned HOST_WIDE_INT)PARAM_VALUE (PARAM_LARGE_STACK_FRAME); - /* In case the alloca is located at function entry, it has the same lifetime - as a declared array, so we allow a larger size. */ - block = gimple_block (stmt); - if (!(cfun->after_inlining - && TREE_CODE (BLOCK_SUPERCONTEXT (block)) == FUNCTION_DECL)) - threshold /= 10; - if (size > threshold) - return NULL_TREE; - - /* Declare array. */ - elem_type = build_nonstandard_integer_type (BITS_PER_UNIT, 1); - n_elem = size * 8 / BITS_PER_UNIT; - array_type = build_array_type_nelts (elem_type, n_elem); - var = create_tmp_var (array_type, NULL); - DECL_ALIGN (var) = TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)); - { - struct ptr_info_def *pi = SSA_NAME_PTR_INFO (lhs); - if (pi != NULL && !pi->pt.anything) - { - bool singleton_p; - unsigned uid; - singleton_p = pt_solution_singleton_p (&pi->pt, &uid); - gcc_assert (singleton_p); - SET_DECL_PT_UID (var, uid); - } - } - - /* Fold alloca to the address of the array. */ - return fold_convert (TREE_TYPE (lhs), build_fold_addr_expr (var)); -} - -/* Fold the stmt at *GSI with CCP specific information that propagating - and regular folding does not catch. */ - -static bool -ccp_fold_stmt (gimple_stmt_iterator *gsi) -{ - gimple stmt = gsi_stmt (*gsi); - - switch (gimple_code (stmt)) - { - case GIMPLE_COND: - { - prop_value_t val; - /* Statement evaluation will handle type mismatches in constants - more gracefully than the final propagation. This allows us to - fold more conditionals here. */ - val = evaluate_stmt (stmt); - if (val.lattice_val != CONSTANT - || !val.mask.is_zero ()) - return false; - - if (dump_file) - { - fprintf (dump_file, "Folding predicate "); - print_gimple_expr (dump_file, stmt, 0, 0); - fprintf (dump_file, " to "); - print_generic_expr (dump_file, val.value, 0); - fprintf (dump_file, "\n"); - } - - if (integer_zerop (val.value)) - gimple_cond_make_false (stmt); - else - gimple_cond_make_true (stmt); - - return true; - } - - case GIMPLE_CALL: - { - tree lhs = gimple_call_lhs (stmt); - int flags = gimple_call_flags (stmt); - tree val; - tree argt; - bool changed = false; - unsigned i; - - /* If the call was folded into a constant make sure it goes - away even if we cannot propagate into all uses because of - type issues. */ - if (lhs - && TREE_CODE (lhs) == SSA_NAME - && (val = get_constant_value (lhs)) - /* Don't optimize away calls that have side-effects. */ - && (flags & (ECF_CONST|ECF_PURE)) != 0 - && (flags & ECF_LOOPING_CONST_OR_PURE) == 0) - { - tree new_rhs = unshare_expr (val); - bool res; - if (!useless_type_conversion_p (TREE_TYPE (lhs), - TREE_TYPE (new_rhs))) - new_rhs = fold_convert (TREE_TYPE (lhs), new_rhs); - res = update_call_from_tree (gsi, new_rhs); - gcc_assert (res); - return true; - } - - /* Internal calls provide no argument types, so the extra laxity - for normal calls does not apply. */ - if (gimple_call_internal_p (stmt)) - return false; - - /* The heuristic of fold_builtin_alloca_with_align differs before and - after inlining, so we don't require the arg to be changed into a - constant for folding, but just to be constant. */ - if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) - { - tree new_rhs = fold_builtin_alloca_with_align (stmt); - if (new_rhs) - { - bool res = update_call_from_tree (gsi, new_rhs); - tree var = TREE_OPERAND (TREE_OPERAND (new_rhs, 0),0); - gcc_assert (res); - insert_clobbers_for_var (*gsi, var); - return true; - } - } - - /* Propagate into the call arguments. Compared to replace_uses_in - this can use the argument slot types for type verification - instead of the current argument type. We also can safely - drop qualifiers here as we are dealing with constants anyway. */ - argt = TYPE_ARG_TYPES (gimple_call_fntype (stmt)); - for (i = 0; i < gimple_call_num_args (stmt) && argt; - ++i, argt = TREE_CHAIN (argt)) - { - tree arg = gimple_call_arg (stmt, i); - if (TREE_CODE (arg) == SSA_NAME - && (val = get_constant_value (arg)) - && useless_type_conversion_p - (TYPE_MAIN_VARIANT (TREE_VALUE (argt)), - TYPE_MAIN_VARIANT (TREE_TYPE (val)))) - { - gimple_call_set_arg (stmt, i, unshare_expr (val)); - changed = true; - } - } - - return changed; - } - - case GIMPLE_ASSIGN: - { - tree lhs = gimple_assign_lhs (stmt); - tree val; - - /* If we have a load that turned out to be constant replace it - as we cannot propagate into all uses in all cases. */ - if (gimple_assign_single_p (stmt) - && TREE_CODE (lhs) == SSA_NAME - && (val = get_constant_value (lhs))) - { - tree rhs = unshare_expr (val); - if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) - rhs = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (lhs), rhs); - gimple_assign_set_rhs_from_tree (gsi, rhs); - return true; - } - - return false; - } - - default: - return false; - } -} - -/* Visit the assignment statement STMT. Set the value of its LHS to the - value computed by the RHS and store LHS in *OUTPUT_P. If STMT - creates virtual definitions, set the value of each new name to that - of the RHS (if we can derive a constant out of the RHS). - Value-returning call statements also perform an assignment, and - are handled here. */ - -static enum ssa_prop_result -visit_assignment (gimple stmt, tree *output_p) -{ - prop_value_t val; - enum ssa_prop_result retval; - - tree lhs = gimple_get_lhs (stmt); - - gcc_assert (gimple_code (stmt) != GIMPLE_CALL - || gimple_call_lhs (stmt) != NULL_TREE); - - if (gimple_assign_single_p (stmt) - && gimple_assign_rhs_code (stmt) == SSA_NAME) - /* For a simple copy operation, we copy the lattice values. */ - val = *get_value (gimple_assign_rhs1 (stmt)); - else - /* Evaluate the statement, which could be - either a GIMPLE_ASSIGN or a GIMPLE_CALL. */ - val = evaluate_stmt (stmt); - - retval = SSA_PROP_NOT_INTERESTING; - - /* Set the lattice value of the statement's output. */ - if (TREE_CODE (lhs) == SSA_NAME) - { - /* If STMT is an assignment to an SSA_NAME, we only have one - value to set. */ - if (set_lattice_value (lhs, val)) - { - *output_p = lhs; - if (val.lattice_val == VARYING) - retval = SSA_PROP_VARYING; - else - retval = SSA_PROP_INTERESTING; - } - } - - return retval; -} - - -/* Visit the conditional statement STMT. Return SSA_PROP_INTERESTING - if it can determine which edge will be taken. Otherwise, return - SSA_PROP_VARYING. */ - -static enum ssa_prop_result -visit_cond_stmt (gimple stmt, edge *taken_edge_p) -{ - prop_value_t val; - basic_block block; - - block = gimple_bb (stmt); - val = evaluate_stmt (stmt); - if (val.lattice_val != CONSTANT - || !val.mask.is_zero ()) - return SSA_PROP_VARYING; - - /* Find which edge out of the conditional block will be taken and add it - to the worklist. If no single edge can be determined statically, - return SSA_PROP_VARYING to feed all the outgoing edges to the - propagation engine. */ - *taken_edge_p = find_taken_edge (block, val.value); - if (*taken_edge_p) - return SSA_PROP_INTERESTING; - else - return SSA_PROP_VARYING; -} - - -/* Evaluate statement STMT. If the statement produces an output value and - its evaluation changes the lattice value of its output, return - SSA_PROP_INTERESTING and set *OUTPUT_P to the SSA_NAME holding the - output value. - - If STMT is a conditional branch and we can determine its truth - value, set *TAKEN_EDGE_P accordingly. If STMT produces a varying - value, return SSA_PROP_VARYING. */ - -static enum ssa_prop_result -ccp_visit_stmt (gimple stmt, edge *taken_edge_p, tree *output_p) -{ - tree def; - ssa_op_iter iter; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\nVisiting statement:\n"); - print_gimple_stmt (dump_file, stmt, 0, dump_flags); - } - - switch (gimple_code (stmt)) - { - case GIMPLE_ASSIGN: - /* If the statement is an assignment that produces a single - output value, evaluate its RHS to see if the lattice value of - its output has changed. */ - return visit_assignment (stmt, output_p); - - case GIMPLE_CALL: - /* A value-returning call also performs an assignment. */ - if (gimple_call_lhs (stmt) != NULL_TREE) - return visit_assignment (stmt, output_p); - break; - - case GIMPLE_COND: - case GIMPLE_SWITCH: - /* If STMT is a conditional branch, see if we can determine - which branch will be taken. */ - /* FIXME. It appears that we should be able to optimize - computed GOTOs here as well. */ - return visit_cond_stmt (stmt, taken_edge_p); - - default: - break; - } - - /* Any other kind of statement is not interesting for constant - propagation and, therefore, not worth simulating. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "No interesting values produced. Marked VARYING.\n"); - - /* Definitions made by statements other than assignments to - SSA_NAMEs represent unknown modifications to their outputs. - Mark them VARYING. */ - FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) - { - prop_value_t v = { VARYING, NULL_TREE, { -1, (HOST_WIDE_INT) -1 } }; - set_lattice_value (def, v); - } - - return SSA_PROP_VARYING; -} - - -/* Main entry point for SSA Conditional Constant Propagation. */ - -static unsigned int -do_ssa_ccp (void) -{ - unsigned int todo = 0; - calculate_dominance_info (CDI_DOMINATORS); - ccp_initialize (); - ssa_propagate (ccp_visit_stmt, ccp_visit_phi_node); - if (ccp_finalize ()) - todo = (TODO_cleanup_cfg | TODO_update_ssa); - free_dominance_info (CDI_DOMINATORS); - return todo; -} - - -static bool -gate_ccp (void) -{ - return flag_tree_ccp != 0; -} - - -struct gimple_opt_pass pass_ccp = -{ - { - GIMPLE_PASS, - "ccp", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - gate_ccp, /* gate */ - do_ssa_ccp, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_TREE_CCP, /* tv_id */ - PROP_cfg | PROP_ssa, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_verify_ssa - | TODO_update_address_taken - | TODO_verify_stmts | TODO_ggc_collect/* todo_flags_finish */ - } -}; - - - -/* Try to optimize out __builtin_stack_restore. Optimize it out - if there is another __builtin_stack_restore in the same basic - block and no calls or ASM_EXPRs are in between, or if this block's - only outgoing edge is to EXIT_BLOCK and there are no calls or - ASM_EXPRs after this __builtin_stack_restore. */ - -static tree -optimize_stack_restore (gimple_stmt_iterator i) -{ - tree callee; - gimple stmt; - - basic_block bb = gsi_bb (i); - gimple call = gsi_stmt (i); - - if (gimple_code (call) != GIMPLE_CALL - || gimple_call_num_args (call) != 1 - || TREE_CODE (gimple_call_arg (call, 0)) != SSA_NAME - || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0)))) - return NULL_TREE; - - for (gsi_next (&i); !gsi_end_p (i); gsi_next (&i)) - { - stmt = gsi_stmt (i); - if (gimple_code (stmt) == GIMPLE_ASM) - return NULL_TREE; - if (gimple_code (stmt) != GIMPLE_CALL) - continue; - - callee = gimple_call_fndecl (stmt); - if (!callee - || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL - /* All regular builtins are ok, just obviously not alloca. */ - || DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA - || DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA_WITH_ALIGN) - return NULL_TREE; - - if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE) - goto second_stack_restore; - } - - if (!gsi_end_p (i)) - return NULL_TREE; - - /* Allow one successor of the exit block, or zero successors. */ - switch (EDGE_COUNT (bb->succs)) - { - case 0: - break; - case 1: - if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR) - return NULL_TREE; - break; - default: - return NULL_TREE; - } - second_stack_restore: - - /* If there's exactly one use, then zap the call to __builtin_stack_save. - If there are multiple uses, then the last one should remove the call. - In any case, whether the call to __builtin_stack_save can be removed - or not is irrelevant to removing the call to __builtin_stack_restore. */ - if (has_single_use (gimple_call_arg (call, 0))) - { - gimple stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0)); - if (is_gimple_call (stack_save)) - { - callee = gimple_call_fndecl (stack_save); - if (callee - && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL - && DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_SAVE) - { - gimple_stmt_iterator stack_save_gsi; - tree rhs; - - stack_save_gsi = gsi_for_stmt (stack_save); - rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0); - update_call_from_tree (&stack_save_gsi, rhs); - } - } - } - - /* No effect, so the statement will be deleted. */ - return integer_zero_node; -} - -/* If va_list type is a simple pointer and nothing special is needed, - optimize __builtin_va_start (&ap, 0) into ap = __builtin_next_arg (0), - __builtin_va_end (&ap) out as NOP and __builtin_va_copy into a simple - pointer assignment. */ - -static tree -optimize_stdarg_builtin (gimple call) -{ - tree callee, lhs, rhs, cfun_va_list; - bool va_list_simple_ptr; - location_t loc = gimple_location (call); - - if (gimple_code (call) != GIMPLE_CALL) - return NULL_TREE; - - callee = gimple_call_fndecl (call); - - cfun_va_list = targetm.fn_abi_va_list (callee); - va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list) - && (TREE_TYPE (cfun_va_list) == void_type_node - || TREE_TYPE (cfun_va_list) == char_type_node); - - switch (DECL_FUNCTION_CODE (callee)) - { - case BUILT_IN_VA_START: - if (!va_list_simple_ptr - || targetm.expand_builtin_va_start != NULL - || !builtin_decl_explicit_p (BUILT_IN_NEXT_ARG)) - return NULL_TREE; - - if (gimple_call_num_args (call) != 2) - return NULL_TREE; - - lhs = gimple_call_arg (call, 0); - if (!POINTER_TYPE_P (TREE_TYPE (lhs)) - || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs))) - != TYPE_MAIN_VARIANT (cfun_va_list)) - return NULL_TREE; - - lhs = build_fold_indirect_ref_loc (loc, lhs); - rhs = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_NEXT_ARG), - 1, integer_zero_node); - rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); - return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); - - case BUILT_IN_VA_COPY: - if (!va_list_simple_ptr) - return NULL_TREE; - - if (gimple_call_num_args (call) != 2) - return NULL_TREE; - - lhs = gimple_call_arg (call, 0); - if (!POINTER_TYPE_P (TREE_TYPE (lhs)) - || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs))) - != TYPE_MAIN_VARIANT (cfun_va_list)) - return NULL_TREE; - - lhs = build_fold_indirect_ref_loc (loc, lhs); - rhs = gimple_call_arg (call, 1); - if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs)) - != TYPE_MAIN_VARIANT (cfun_va_list)) - return NULL_TREE; - - rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); - return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); - - case BUILT_IN_VA_END: - /* No effect, so the statement will be deleted. */ - return integer_zero_node; - - default: - gcc_unreachable (); - } -} - -/* Attemp to make the block of __builtin_unreachable I unreachable by changing - the incoming jumps. Return true if at least one jump was changed. */ - -static bool -optimize_unreachable (gimple_stmt_iterator i) -{ - basic_block bb = gsi_bb (i); - gimple_stmt_iterator gsi; - gimple stmt; - edge_iterator ei; - edge e; - bool ret; - - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) - { - stmt = gsi_stmt (gsi); - - if (is_gimple_debug (stmt)) - continue; - - if (gimple_code (stmt) == GIMPLE_LABEL) - { - /* Verify we do not need to preserve the label. */ - if (FORCED_LABEL (gimple_label_label (stmt))) - return false; - - continue; - } - - /* Only handle the case that __builtin_unreachable is the first statement - in the block. We rely on DCE to remove stmts without side-effects - before __builtin_unreachable. */ - if (gsi_stmt (gsi) != gsi_stmt (i)) - return false; - } - - ret = false; - FOR_EACH_EDGE (e, ei, bb->preds) - { - gsi = gsi_last_bb (e->src); - if (gsi_end_p (gsi)) - continue; - - stmt = gsi_stmt (gsi); - if (gimple_code (stmt) == GIMPLE_COND) - { - if (e->flags & EDGE_TRUE_VALUE) - gimple_cond_make_false (stmt); - else if (e->flags & EDGE_FALSE_VALUE) - gimple_cond_make_true (stmt); - else - gcc_unreachable (); - update_stmt (stmt); - } - else - { - /* Todo: handle other cases, f.i. switch statement. */ - continue; - } - - ret = true; - } - - return ret; -} - -/* A simple pass that attempts to fold all builtin functions. This pass - is run after we've propagated as many constants as we can. */ - -static unsigned int -execute_fold_all_builtins (void) -{ - bool cfg_changed = false; - basic_block bb; - unsigned int todoflags = 0; - - FOR_EACH_BB (bb) - { - gimple_stmt_iterator i; - for (i = gsi_start_bb (bb); !gsi_end_p (i); ) - { - gimple stmt, old_stmt; - tree callee, result; - enum built_in_function fcode; - - stmt = gsi_stmt (i); - - if (gimple_code (stmt) != GIMPLE_CALL) - { - gsi_next (&i); - continue; - } - callee = gimple_call_fndecl (stmt); - if (!callee || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL) - { - gsi_next (&i); - continue; - } - fcode = DECL_FUNCTION_CODE (callee); - - result = gimple_fold_builtin (stmt); - - if (result) - gimple_remove_stmt_histograms (cfun, stmt); - - if (!result) - switch (DECL_FUNCTION_CODE (callee)) - { - case BUILT_IN_CONSTANT_P: - /* Resolve __builtin_constant_p. If it hasn't been - folded to integer_one_node by now, it's fairly - certain that the value simply isn't constant. */ - result = integer_zero_node; - break; - - case BUILT_IN_ASSUME_ALIGNED: - /* Remove __builtin_assume_aligned. */ - result = gimple_call_arg (stmt, 0); - break; - - case BUILT_IN_STACK_RESTORE: - result = optimize_stack_restore (i); - if (result) - break; - gsi_next (&i); - continue; - - case BUILT_IN_UNREACHABLE: - if (optimize_unreachable (i)) - cfg_changed = true; - break; - - case BUILT_IN_VA_START: - case BUILT_IN_VA_END: - case BUILT_IN_VA_COPY: - /* These shouldn't be folded before pass_stdarg. */ - result = optimize_stdarg_builtin (stmt); - if (result) - break; - /* FALLTHRU */ - - default: - gsi_next (&i); - continue; - } - - if (result == NULL_TREE) - break; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Simplified\n "); - print_gimple_stmt (dump_file, stmt, 0, dump_flags); - } - - old_stmt = stmt; - if (!update_call_from_tree (&i, result)) - { - gimplify_and_update_call_from_tree (&i, result); - todoflags |= TODO_update_address_taken; - } - - stmt = gsi_stmt (i); - update_stmt (stmt); - - if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt) - && gimple_purge_dead_eh_edges (bb)) - cfg_changed = true; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "to\n "); - print_gimple_stmt (dump_file, stmt, 0, dump_flags); - fprintf (dump_file, "\n"); - } - - /* Retry the same statement if it changed into another - builtin, there might be new opportunities now. */ - if (gimple_code (stmt) != GIMPLE_CALL) - { - gsi_next (&i); - continue; - } - callee = gimple_call_fndecl (stmt); - if (!callee - || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL - || DECL_FUNCTION_CODE (callee) == fcode) - gsi_next (&i); - } - } - - /* Delete unreachable blocks. */ - if (cfg_changed) - todoflags |= TODO_cleanup_cfg; - - return todoflags; -} - - -struct gimple_opt_pass pass_fold_builtins = -{ - { - GIMPLE_PASS, - "fab", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - NULL, /* gate */ - execute_fold_all_builtins, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_NONE, /* tv_id */ - PROP_cfg | PROP_ssa, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_verify_ssa - | TODO_update_ssa /* todo_flags_finish */ - } -}; |