diff options
Diffstat (limited to 'gcc/tree-ssa-ccp.c')
-rw-r--r-- | gcc/tree-ssa-ccp.c | 2393 |
1 files changed, 2393 insertions, 0 deletions
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c new file mode 100644 index 00000000000..ef655629abd --- /dev/null +++ b/gcc/tree-ssa-ccp.c @@ -0,0 +1,2393 @@ +/* Conditional constant propagation pass for the GNU compiler. + Copyright (C) 2000, 2001, 2002, 2003 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 2, 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 COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +/* Conditional constant propagation. + + 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 "errors.h" +#include "ggc.h" +#include "tree.h" +#include "langhooks.h" + +/* These RTL headers are needed for basic-block.h. */ +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" + +#include "diagnostic.h" +#include "tree-inline.h" +#include "tree-flow.h" +#include "tree-simple.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "expr.h" +#include "flags.h" + + +/* Possible lattice values. */ +typedef enum +{ + UNINITIALIZED = 0, + UNDEFINED, + CONSTANT, + VARYING +} latticevalue; + +/* Use the TREE_VISITED bitflag to mark statements and PHI nodes that have + been deemed VARYING and shouldn't be simulated again. */ +#define DONT_SIMULATE_AGAIN(T) TREE_VISITED (T) + +/* Main structure for CCP. Contains the lattice value and, if it's a + constant, the constant value. */ +typedef struct +{ + latticevalue lattice_val; + tree const_val; +} value; + +/* A bitmap to keep track of executable blocks in the CFG. */ +static sbitmap executable_blocks; + +/* Array of control flow edges on the worklist. */ +static GTY(()) varray_type cfg_blocks = NULL; + +static unsigned int cfg_blocks_num = 0; +static int cfg_blocks_tail; +static int cfg_blocks_head; + +static sbitmap bb_in_list; + +/* This is used to track the current value of each variable. */ +static value *value_vector; + +/* Worklist of SSA edges which will need reexamination as their definition + has changed. SSA edges are def-use edges in the SSA web. For each + edge, we store the definition statement or PHI node D. The destination + nodes that need to be visited are accessed using immediate_uses (D). */ +static GTY(()) varray_type ssa_edges; + +static void initialize (void); +static void finalize (void); +static void visit_phi_node (tree); +static tree ccp_fold (tree); +static value cp_lattice_meet (value, value); +static void visit_stmt (tree); +static void visit_cond_stmt (tree); +static void visit_assignment (tree); +static void add_var_to_ssa_edges_worklist (tree); +static void add_outgoing_control_edges (basic_block); +static void add_control_edge (edge); +static void def_to_varying (tree); +static void set_lattice_value (tree, value); +static void simulate_block (basic_block); +static void simulate_stmt (tree); +static void substitute_and_fold (void); +static value evaluate_stmt (tree); +static void dump_lattice_value (FILE *, const char *, value); +static bool replace_uses_in (tree, bool *); +static latticevalue likely_value (tree); +static tree get_rhs (tree); +static void set_rhs (tree *, tree); +static value *get_value (tree); +static value get_default_value (tree); +static tree ccp_fold_builtin (tree, tree); +static bool get_strlen (tree, tree *, bitmap); +static inline bool cfg_blocks_empty_p (void); +static void cfg_blocks_add (basic_block); +static basic_block cfg_blocks_get (void); +static bool need_imm_uses_for (tree var); + +/* Main entry point for SSA Conditional Constant Propagation. FNDECL is + the declaration for the function to optimize. + + On exit, VARS_TO_RENAME will contain the symbols that have been exposed by + the propagation of ADDR_EXPR expressions into pointer dereferences and need + to be renamed into SSA. + + PHASE indicates which dump file from the DUMP_FILES array to use when + dumping debugging information. */ + +static void +tree_ssa_ccp (void) +{ + initialize (); + + /* Iterate until the worklists are empty. */ + while (!cfg_blocks_empty_p () || VARRAY_ACTIVE_SIZE (ssa_edges) > 0) + { + if (!cfg_blocks_empty_p ()) + { + /* Pull the next block to simulate off the worklist. */ + basic_block dest_block = cfg_blocks_get (); + simulate_block (dest_block); + } + + /* The SSA_EDGES worklist can get rather large. Go ahead and + drain the entire worklist each iteration through this loop. */ + while (VARRAY_ACTIVE_SIZE (ssa_edges) > 0) + { + /* Pull the statement to simulate off the worklist. */ + tree stmt = VARRAY_TOP_TREE (ssa_edges); + stmt_ann_t ann = stmt_ann (stmt); + VARRAY_POP (ssa_edges); + + /* visit_stmt can "cancel" reevaluation of some statements. + If it does, then in_ccp_worklist will be zero. */ + if (ann->in_ccp_worklist) + { + ann->in_ccp_worklist = 0; + simulate_stmt (stmt); + } + } + } + + /* Now perform substitutions based on the known constant values. */ + substitute_and_fold (); + + /* Now cleanup any unreachable code. */ + cleanup_tree_cfg (); + + /* Free allocated memory. */ + finalize (); + + /* Debugging dumps. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_referenced_vars (dump_file); + fprintf (dump_file, "\n\n"); + } +} + +static bool +gate_ccp (void) +{ + return flag_tree_ccp != 0; +} + +struct tree_opt_pass pass_ccp = +{ + "ccp", /* name */ + gate_ccp, /* gate */ + tree_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_dump_func | TODO_rename_vars + | TODO_ggc_collect | TODO_verify_ssa /* todo_flags_finish */ +}; + + +/* Get the constant value associated with variable VAR. */ + +static value * +get_value (tree var) +{ + value *val; + +#if defined ENABLE_CHECKING + if (TREE_CODE (var) != SSA_NAME) + abort (); +#endif + + val = &value_vector[SSA_NAME_VERSION (var)]; + if (val->lattice_val == UNINITIALIZED) + *val = get_default_value (var); + + return val; +} + + +/* Simulate the execution of BLOCK. Evaluate the statement associated + with each variable reference inside the block. */ + +static void +simulate_block (basic_block block) +{ + tree phi; + + /* There is nothing to do for the exit block. */ + if (block == EXIT_BLOCK_PTR) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nSimulating block %d\n", block->index); + + /* Always simulate PHI nodes, even if we have simulated this block + before. */ + for (phi = phi_nodes (block); phi; phi = TREE_CHAIN (phi)) + visit_phi_node (phi); + + /* If this is the first time we've simulated this block, then we + must simulate each of its statements. */ + if (!TEST_BIT (executable_blocks, block->index)) + { + block_stmt_iterator j; + unsigned int normal_edge_count; + edge e, normal_edge; + + /* Note that we have simulated this block. */ + SET_BIT (executable_blocks, block->index); + + for (j = bsi_start (block); !bsi_end_p (j); bsi_next (&j)) + visit_stmt (bsi_stmt (j)); + + /* We can not predict when abnormal edges will be executed, so + once a block is considered executable, we consider any + outgoing abnormal edges as executable. + + At the same time, if this block has only one successor that is + reached by non-abnormal edges, then add that successor to the + worklist. */ + normal_edge_count = 0; + normal_edge = NULL; + for (e = block->succ; e; e = e->succ_next) + { + if (e->flags & EDGE_ABNORMAL) + { + add_control_edge (e); + } + else + { + normal_edge_count++; + normal_edge = e; + } + } + + if (normal_edge_count == 1) + add_control_edge (normal_edge); + } +} + + +/* Follow the def-use edges for statement DEF_STMT and simulate all the + statements reached by it. */ + +static void +simulate_stmt (tree use_stmt) +{ + basic_block use_bb = bb_for_stmt (use_stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nSimulating statement (from ssa_edges): "); + print_generic_stmt (dump_file, use_stmt, dump_flags); + } + + if (TREE_CODE (use_stmt) == PHI_NODE) + { + /* PHI nodes are always visited, regardless of whether or not the + destination block is executable. */ + visit_phi_node (use_stmt); + } + else if (TEST_BIT (executable_blocks, use_bb->index)) + { + /* Otherwise, visit the statement containing the use reached by + DEF, only if the destination block is marked executable. */ + visit_stmt (use_stmt); + } +} + + +/* Perform final substitution and folding. After this pass the program + should still be in SSA form. */ + +static void +substitute_and_fold (void) +{ + basic_block bb; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "\nSubstituing constants and folding statements\n\n"); + + /* Substitute constants in every statement of every basic block. */ + FOR_EACH_BB (bb) + { + block_stmt_iterator i; + tree phi; + + /* Propagate our known constants into PHI nodes. */ + for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + int i; + + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + value *new_val; + tree *orig_p = &PHI_ARG_DEF (phi, i); + + if (! SSA_VAR_P (*orig_p)) + break; + + new_val = get_value (*orig_p); + if (new_val->lattice_val == CONSTANT + && may_propagate_copy (*orig_p, new_val->const_val)) + *orig_p = new_val->const_val; + } + } + + for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + { + bool replaced_address; + tree stmt = bsi_stmt (i); + + /* Skip statements that have been folded already. */ + if (stmt_modified_p (stmt) || !is_exec_stmt (stmt)) + continue; + + /* Replace the statement with its folded version and mark it + folded. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Line %d: replaced ", get_lineno (stmt)); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + } + + if (replace_uses_in (stmt, &replaced_address)) + { + bool changed = fold_stmt (bsi_stmt_ptr (i)); + stmt = bsi_stmt(i); + modify_stmt (stmt); + /* If we folded a builtin function, we'll likely + need to rename VDEFs. */ + if (replaced_address || changed) + mark_new_vars_to_rename (stmt, vars_to_rename); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " with "); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + fprintf (dump_file, "\n"); + } + } + } +} + + +/* 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 cp_lattice_meet() with all the arguments + of the PHI node that are incoming via executable edges. */ + +static void +visit_phi_node (tree phi) +{ + bool short_circuit = 0; + value phi_val, *curr_val; + int i; + + /* If the PHI node has already been deemed to be VARYING, don't simulate + it again. */ + if (DONT_SIMULATE_AGAIN (phi)) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nVisiting PHI node: "); + print_generic_expr (dump_file, phi, dump_flags); + } + + curr_val = get_value (PHI_RESULT (phi)); + switch (curr_val->lattice_val) + { + case VARYING: + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n Shortcircuit. Default of VARYING."); + short_circuit = 1; + break; + + case CONSTANT: + phi_val = *curr_val; + break; + + case UNDEFINED: + case UNINITIALIZED: + phi_val.lattice_val = UNDEFINED; + phi_val.const_val = NULL_TREE; + break; + + default: + abort (); + } + + /* If the variable is volatile or the variable is never referenced in a + real operand, then consider the PHI node VARYING. */ + if (short_circuit || TREE_THIS_VOLATILE (SSA_NAME_VAR (PHI_RESULT (phi)))) + { + phi_val.lattice_val = VARYING; + phi_val.const_val = NULL; + } + else + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + /* Compute the meet operator over all the PHI arguments. */ + edge e = 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 rdef = PHI_ARG_DEF (phi, i); + value *rdef_val, val; + + if (is_gimple_min_invariant (rdef)) + { + val.lattice_val = CONSTANT; + val.const_val = rdef; + rdef_val = &val; + } + else + rdef_val = get_value (rdef); + + phi_val = cp_lattice_meet (phi_val, *rdef_val); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\t"); + print_generic_expr (dump_file, rdef, dump_flags); + dump_lattice_value (dump_file, "\tValue: ", *rdef_val); + fprintf (dump_file, "\n"); + } + + if (phi_val.lattice_val == VARYING) + break; + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_lattice_value (dump_file, "\n PHI node value: ", phi_val); + fprintf (dump_file, "\n\n"); + } + + set_lattice_value (PHI_RESULT (phi), phi_val); + if (phi_val.lattice_val == VARYING) + DONT_SIMULATE_AGAIN (phi) = 1; +} + + +/* Compute the meet operator between VAL1 and VAL2: + + any M UNDEFINED = any + any M VARYING = VARYING + Ci M Cj = Ci if (i == j) + Ci M Cj = VARYING if (i != j) */ +static value +cp_lattice_meet (value val1, value val2) +{ + value result; + + /* any M UNDEFINED = any. */ + if (val1.lattice_val == UNDEFINED) + return val2; + else if (val2.lattice_val == UNDEFINED) + return val1; + + /* any M VARYING = VARYING. */ + if (val1.lattice_val == VARYING || val2.lattice_val == VARYING) + { + result.lattice_val = VARYING; + result.const_val = NULL_TREE; + return result; + } + + /* Ci M Cj = Ci if (i == j) + Ci M Cj = VARYING if (i != j) */ + if (simple_cst_equal (val1.const_val, val2.const_val) == 1) + { + result.lattice_val = CONSTANT; + result.const_val = val1.const_val; + } + else + { + result.lattice_val = VARYING; + result.const_val = NULL_TREE; + } + + return result; +} + + +/* Evaluate statement STMT. If the statement produces an output value and + its evaluation changes the lattice value of its output, do the following: + + - If the statement is an assignment, add all the SSA edges starting at + this definition. + + - If the statement is a conditional branch: + . If the statement evaluates to non-constant, add all edges to + worklist. + . If the statement is constant, add the edge executed as the + result of the branch. */ + +static void +visit_stmt (tree stmt) +{ + size_t i; + stmt_ann_t ann; + def_optype defs; + vdef_optype vdefs; + + /* If the statement has already been deemed to be VARYING, don't simulate + it again. */ + if (DONT_SIMULATE_AGAIN (stmt)) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nVisiting statement: "); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + fprintf (dump_file, "\n"); + } + + ann = stmt_ann (stmt); + + /* If this statement is already in the worklist then "cancel" it. The + reevaluation implied by the worklist entry will produce the same + value we generate here and thus reevaluating it again from the + worklist is pointless. */ + if (ann->in_ccp_worklist) + ann->in_ccp_worklist = 0; + + /* Now examine the statement. 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. */ + if (TREE_CODE (stmt) == MODIFY_EXPR + && TREE_CODE (TREE_OPERAND (stmt, 0)) == SSA_NAME) + visit_assignment (stmt); + + /* Definitions made by statements other than assignments to SSA_NAMEs + represent unknown modifications to their outputs. Mark them VARYING. */ + else if (NUM_DEFS (defs = DEF_OPS (ann)) != 0) + { + DONT_SIMULATE_AGAIN (stmt) = 1; + for (i = 0; i < NUM_DEFS (defs); i++) + { + tree def = DEF_OP (defs, i); + def_to_varying (def); + } + } + + /* If STMT is a conditional branch, see if we can determine which branch + will be taken. */ + else if (TREE_CODE (stmt) == COND_EXPR || TREE_CODE (stmt) == SWITCH_EXPR) + visit_cond_stmt (stmt); + + /* Any other kind of statement is not interesting for constant + propagation and, therefore, not worth simulating. */ + else + { + DONT_SIMULATE_AGAIN (stmt) = 1; + + /* If STMT is a computed goto, then mark all the output edges + executable. */ + if (computed_goto_p (stmt)) + add_outgoing_control_edges (bb_for_stmt (stmt)); + } + + /* Mark all VDEF operands VARYING. */ + vdefs = VDEF_OPS (ann); + for (i = 0; i < NUM_VDEFS (vdefs); i++) + def_to_varying (VDEF_RESULT (vdefs, i)); +} + + +/* Visit the assignment statement STMT. Set the value of its LHS to the + value computed by the RHS. */ + +static void +visit_assignment (tree stmt) +{ + value val; + tree lhs, rhs; + + lhs = TREE_OPERAND (stmt, 0); + rhs = TREE_OPERAND (stmt, 1); + + if (TREE_THIS_VOLATILE (SSA_NAME_VAR (lhs))) + { + /* Volatile variables are always VARYING. */ + val.lattice_val = VARYING; + val.const_val = NULL_TREE; + } + else if (TREE_CODE (rhs) == SSA_NAME) + { + /* For a simple copy operation, we copy the lattice values. */ + value *nval = get_value (rhs); + val = *nval; + } + else + { + /* Evaluate the statement. */ + val = evaluate_stmt (stmt); + } + + /* FIXME: Hack. If this was a definition of a bitfield, we need to widen + the constant value into the type of the destination variable. This + should not be necessary if GCC represented bitfields properly. */ + { + tree lhs = TREE_OPERAND (stmt, 0); + if (val.lattice_val == CONSTANT + && TREE_CODE (lhs) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (lhs, 1))) + { + tree w = widen_bitfield (val.const_val, TREE_OPERAND (lhs, 1), lhs); + + if (w && is_gimple_min_invariant (w)) + val.const_val = w; + else + { + val.lattice_val = VARYING; + val.const_val = NULL; + } + } + } + + /* Set the lattice value of the statement's output. */ + set_lattice_value (lhs, val); + if (val.lattice_val == VARYING) + DONT_SIMULATE_AGAIN (stmt) = 1; +} + + +/* Visit the conditional statement STMT. If it evaluates to a constant value, + mark outgoing edges appropriately. */ + +static void +visit_cond_stmt (tree stmt) +{ + edge e; + value val; + basic_block block; + + block = bb_for_stmt (stmt); + val = evaluate_stmt (stmt); + + /* 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, add + all outgoing edges from BLOCK. */ + e = find_taken_edge (block, val.const_val); + if (e) + add_control_edge (e); + else + { + DONT_SIMULATE_AGAIN (stmt) = 1; + add_outgoing_control_edges (block); + } +} + + +/* Add all the edges coming out of BB to the control flow worklist. */ + +static void +add_outgoing_control_edges (basic_block bb) +{ + edge e; + + for (e = bb->succ; e; e = e->succ_next) + add_control_edge (e); +} + + +/* Add edge E to the control flow worklist. */ + +static void +add_control_edge (edge e) +{ + basic_block bb = e->dest; + if (bb == EXIT_BLOCK_PTR) + return; + + /* If the edge had already been executed, skip it. */ + if (e->flags & EDGE_EXECUTABLE) + return; + + e->flags |= EDGE_EXECUTABLE; + + /* If the block is already in the list, we're done. */ + if (TEST_BIT (bb_in_list, bb->index)) + return; + + cfg_blocks_add (bb); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Adding Destination of edge (%d -> %d) to worklist\n\n", + e->src->index, e->dest->index); +} + + +/* 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. */ + +static tree +ccp_fold (tree stmt) +{ + tree rhs = get_rhs (stmt); + enum tree_code code = TREE_CODE (rhs); + int kind = TREE_CODE_CLASS (code); + tree retval = NULL_TREE; + + /* If the RHS is just a variable, then that variable must now have + a constant value that we can return directly. */ + if (TREE_CODE (rhs) == SSA_NAME) + return get_value (rhs)->const_val; + + /* Unary operators. Note that we know the single operand must + be a constant. So this should almost always return a + simplified RHS. */ + if (kind == '1') + { + /* Handle unary operators which can appear in GIMPLE form. */ + tree op0 = TREE_OPERAND (rhs, 0); + + /* Simplify the operand down to a constant. */ + if (TREE_CODE (op0) == SSA_NAME) + { + value *val = get_value (op0); + if (val->lattice_val == CONSTANT) + op0 = get_value (op0)->const_val; + } + + retval = nondestructive_fold_unary_to_constant (code, + TREE_TYPE (rhs), + op0); + + /* If we folded, but did not create an invariant, then we can not + use this expression. */ + if (retval && ! is_gimple_min_invariant (retval)) + return NULL; + + /* If we could not fold the expression, but the arguments are all + constants and gimple values, then build and return the new + expression. + + In some cases the new expression is still something we can + use as a replacement for an argument. This happens with + NOP conversions of types for example. + + In other cases the new expression can not be used as a + replacement for an argument (as it would create non-gimple + code). But the new expression can still be used to derive + other constants. */ + if (! retval && is_gimple_min_invariant (op0)) + return build1 (code, TREE_TYPE (rhs), op0); + } + + /* Binary and comparison operators. We know one or both of the + operands are constants. */ + else if (kind == '2' + || kind == '<' + || code == TRUTH_AND_EXPR + || code == TRUTH_OR_EXPR + || code == TRUTH_XOR_EXPR) + { + /* Handle binary and comparison operators that can appear in + GIMPLE form. */ + tree op0 = TREE_OPERAND (rhs, 0); + tree op1 = TREE_OPERAND (rhs, 1); + + /* Simplify the operands down to constants when appropriate. */ + if (TREE_CODE (op0) == SSA_NAME) + { + value *val = get_value (op0); + if (val->lattice_val == CONSTANT) + op0 = val->const_val; + } + + if (TREE_CODE (op1) == SSA_NAME) + { + value *val = get_value (op1); + if (val->lattice_val == CONSTANT) + op1 = val->const_val; + } + + retval = nondestructive_fold_binary_to_constant (code, + TREE_TYPE (rhs), + op0, op1); + + /* If we folded, but did not create an invariant, then we can not + use this expression. */ + if (retval && ! is_gimple_min_invariant (retval)) + return NULL; + + /* If we could not fold the expression, but the arguments are all + constants and gimple values, then build and return the new + expression. + + In some cases the new expression is still something we can + use as a replacement for an argument. This happens with + NOP conversions of types for example. + + In other cases the new expression can not be used as a + replacement for an argument (as it would create non-gimple + code). But the new expression can still be used to derive + other constants. */ + if (! retval + && is_gimple_min_invariant (op0) + && is_gimple_min_invariant (op1)) + return build (code, TREE_TYPE (rhs), op0, op1); + } + + /* We may be able to fold away calls to builtin functions if their + arguments are constants. */ + else if (code == CALL_EXPR + && TREE_CODE (TREE_OPERAND (rhs, 0)) == ADDR_EXPR + && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0)) + == FUNCTION_DECL) + && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0))) + { + use_optype uses = STMT_USE_OPS (stmt); + if (NUM_USES (uses) != 0) + { + tree *orig; + size_t i; + + /* Preserve the original values of every operand. */ + orig = xmalloc (sizeof (tree) * NUM_USES (uses)); + for (i = 0; i < NUM_USES (uses); i++) + orig[i] = USE_OP (uses, i); + + /* Substitute operands with their values and try to fold. */ + replace_uses_in (stmt, NULL); + retval = fold_builtin (rhs); + + /* Restore operands to their original form. */ + for (i = 0; i < NUM_USES (uses); i++) + *(USE_OP_PTR (uses, i)) = orig[i]; + free (orig); + } + } + else + return rhs; + + /* If we got a simplified form, see if we need to convert its type. */ + if (retval) + { + if (TREE_TYPE (retval) != TREE_TYPE (rhs)) + retval = convert (TREE_TYPE (rhs), retval); + + if (TREE_TYPE (retval) == TREE_TYPE (rhs)) + return retval; + } + + /* No simplification was possible. */ + return rhs; +} + + +/* Evaluate statement STMT. */ + +static value +evaluate_stmt (tree stmt) +{ + value val; + tree simplified; + latticevalue likelyvalue = likely_value (stmt); + + /* If the statement is likely to have a CONSTANT result, then try + to fold the statement to determine the constant value. */ + if (likelyvalue == CONSTANT) + simplified = ccp_fold (stmt); + /* If the statement is likely to have a VARYING result, then do not + bother folding the statement. */ + else if (likelyvalue == VARYING) + simplified = get_rhs (stmt); + /* Otherwise the statement is likely to have an UNDEFINED value and + there will be nothing to do. */ + else + simplified = NULL_TREE; + + if (simplified && is_gimple_min_invariant (simplified)) + { + /* The statement produced a constant value. */ + val.lattice_val = CONSTANT; + val.const_val = simplified; + } + else + { + /* The statement produced a nonconstant value. If the statement + had undefined operands, then the result of the statement should + be undefined. Else the result of the statement is VARYING. */ + val.lattice_val = (likelyvalue == UNDEFINED ? UNDEFINED : VARYING); + val.const_val = NULL_TREE; + } + + return val; +} + + +/* Debugging dumps. */ + +static void +dump_lattice_value (FILE *outf, const char *prefix, value val) +{ + switch (val.lattice_val) + { + case UNDEFINED: + fprintf (outf, "%sUNDEFINED", prefix); + break; + case VARYING: + fprintf (outf, "%sVARYING", prefix); + break; + case CONSTANT: + fprintf (outf, "%sCONSTANT ", prefix); + print_generic_expr (outf, val.const_val, dump_flags); + break; + default: + abort (); + } +} + +/* Given a constant value VAL for bitfield FIELD, and a destination + variable VAR, return VAL appropriately widened to fit into VAR. If + FIELD is wider than HOST_WIDE_INT, NULL is returned. */ + +tree +widen_bitfield (tree val, tree field, tree var) +{ + unsigned var_size, field_size; + tree wide_val; + unsigned HOST_WIDE_INT mask; + unsigned i; + + var_size = TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE ((var)))); + field_size = TREE_INT_CST_LOW (DECL_SIZE (field)); + + /* Give up if either the bitfield or the variable are too wide. */ + if (field_size > HOST_BITS_PER_WIDE_INT || var_size > HOST_BITS_PER_WIDE_INT) + return NULL; + +#if defined ENABLE_CHECKING + if (var_size < field_size) + abort (); +#endif + + /* If VAL is not an integer constant, then give up. */ + if (TREE_CODE (val) != INTEGER_CST) + return NULL; + + /* If the sign bit of the value is not set, or the field's type is + unsigned, then just mask off the high order bits of the value. */ + if ((TREE_INT_CST_LOW (val) & (1 << (field_size - 1))) == 0 + || DECL_UNSIGNED (field)) + { + /* Zero extension. Build a mask with the lower 'field_size' bits + set and a BIT_AND_EXPR node to clear the high order bits of + the value. */ + for (i = 0, mask = 0; i < field_size; i++) + mask |= 1 << i; + + wide_val = build (BIT_AND_EXPR, TREE_TYPE (var), val, + build_int_2 (mask, 0)); + } + else + { + /* Sign extension. Create a mask with the upper 'field_size' + bits set and a BIT_IOR_EXPR to set the high order bits of the + value. */ + for (i = 0, mask = 0; i < (var_size - field_size); i++) + mask |= 1 << (var_size - i - 1); + + wide_val = build (BIT_IOR_EXPR, TREE_TYPE (var), val, + build_int_2 (mask, 0)); + } + + return fold (wide_val); +} + + +/* Function indicating whether we ought to include information for 'var' + when calculating immediate uses. */ + +static bool +need_imm_uses_for (tree var) +{ + return get_value (var)->lattice_val != VARYING; +} + + +/* Initialize local data structures and worklists for CCP. */ + +static void +initialize (void) +{ + edge e; + basic_block bb; + sbitmap virtual_var; + + /* Worklist of SSA edges. */ + VARRAY_TREE_INIT (ssa_edges, 20, "ssa_edges"); + + executable_blocks = sbitmap_alloc (last_basic_block); + sbitmap_zero (executable_blocks); + + bb_in_list = sbitmap_alloc (last_basic_block); + sbitmap_zero (bb_in_list); + + value_vector = (value *) xmalloc (highest_ssa_version * sizeof (value)); + memset (value_vector, 0, highest_ssa_version * sizeof (value)); + + /* 1 if ssa variable is used in a virtual variable context. */ + virtual_var = sbitmap_alloc (highest_ssa_version); + sbitmap_zero (virtual_var); + + /* Initialize default values and simulation flags for PHI nodes, statements + and edges. */ + FOR_EACH_BB (bb) + { + block_stmt_iterator i; + tree stmt; + stmt_ann_t ann; + def_optype defs; + vdef_optype vdefs; + size_t x; + int vary; + + /* Get the default value for each definition. */ + for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + { + vary = 0; + stmt = bsi_stmt (i); + get_stmt_operands (stmt); + ann = stmt_ann (stmt); + defs = DEF_OPS (ann); + for (x = 0; x < NUM_DEFS (defs); x++) + { + tree def = DEF_OP (defs, x); + if (get_value (def)->lattice_val == VARYING) + vary = 1; + } + DONT_SIMULATE_AGAIN (stmt) = vary; + + /* Mark all VDEF operands VARYING. */ + vdefs = VDEF_OPS (ann); + for (x = 0; x < NUM_VDEFS (vdefs); x++) + { + tree res = VDEF_RESULT (vdefs, x); + get_value (res)->lattice_val = VARYING; + SET_BIT (virtual_var, SSA_NAME_VERSION (res)); + } + } + + for (e = bb->succ; e; e = e->succ_next) + e->flags &= ~EDGE_EXECUTABLE; + } + + /* Now process PHI nodes. */ + FOR_EACH_BB (bb) + { + tree phi, var; + int x; + for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + value *val; + val = get_value (PHI_RESULT (phi)); + if (val->lattice_val != VARYING) + { + for (x = 0; x < PHI_NUM_ARGS (phi); x++) + { + var = PHI_ARG_DEF (phi, x); + /* If one argument is virtual, the result is virtual, and + therefore varying. */ + if (TREE_CODE (var) == SSA_NAME) + { + if (TEST_BIT (virtual_var, SSA_NAME_VERSION (var))) + { + val->lattice_val = VARYING; + SET_BIT (virtual_var, + SSA_NAME_VERSION (PHI_RESULT (phi))); + break; + } + } + } + } + DONT_SIMULATE_AGAIN (phi) = ((val->lattice_val == VARYING) ? 1 : 0); + } + } + + sbitmap_free (virtual_var); + /* Compute immediate uses for variables we care about. */ + compute_immediate_uses (TDFA_USE_OPS, need_imm_uses_for); + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_immediate_uses (dump_file); + + VARRAY_BB_INIT (cfg_blocks, 20, "cfg_blocks"); + + /* Seed the algorithm by adding the successors of the entry block to the + edge worklist. */ + for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next) + { + if (e->dest != EXIT_BLOCK_PTR) + { + e->flags |= EDGE_EXECUTABLE; + cfg_blocks_add (e->dest); + } + } +} + + +/* Free allocated storage. */ + +static void +finalize (void) +{ + ssa_edges = NULL; + cfg_blocks = NULL; + free (value_vector); + sbitmap_free (bb_in_list); + sbitmap_free (executable_blocks); + free_df (); +} + +/* Is the block worklist empty. */ + +static inline bool +cfg_blocks_empty_p (void) +{ + return (cfg_blocks_num == 0); +} + +/* Add a basic block to the worklist. */ + +static void +cfg_blocks_add (basic_block bb) +{ + if (bb == ENTRY_BLOCK_PTR || bb == EXIT_BLOCK_PTR) + return; + + if (TEST_BIT (bb_in_list, bb->index)) + return; + + if (cfg_blocks_empty_p ()) + { + cfg_blocks_tail = cfg_blocks_head = 0; + cfg_blocks_num = 1; + } + else + { + cfg_blocks_num++; + if (cfg_blocks_num > VARRAY_SIZE (cfg_blocks)) + { + /* We have to grow the array now. Adjust to queue to occupy the + full space of the original array. */ + cfg_blocks_tail = VARRAY_SIZE (cfg_blocks); + cfg_blocks_head = 0; + VARRAY_GROW (cfg_blocks, 2 * VARRAY_SIZE (cfg_blocks)); + } + else + cfg_blocks_tail = (cfg_blocks_tail + 1) % VARRAY_SIZE (cfg_blocks); + } + VARRAY_BB (cfg_blocks, cfg_blocks_tail) = bb; + SET_BIT (bb_in_list, bb->index); +} + +/* Remove a block from the worklist. */ + +static basic_block +cfg_blocks_get (void) +{ + basic_block bb; + + bb = VARRAY_BB (cfg_blocks, cfg_blocks_head); + +#ifdef ENABLE_CHECKING + if (cfg_blocks_empty_p () || !bb) + abort (); +#endif + + cfg_blocks_head = (cfg_blocks_head + 1) % VARRAY_SIZE (cfg_blocks); + --cfg_blocks_num; + RESET_BIT (bb_in_list, bb->index); + + return bb; +} + +/* We have just defined a new value for VAR. Add all immediate uses + of VAR to the ssa_edges worklist. */ +static void +add_var_to_ssa_edges_worklist (tree var) +{ + tree stmt = SSA_NAME_DEF_STMT (var); + dataflow_t df = get_immediate_uses (stmt); + int num_uses = num_immediate_uses (df); + int i; + + for (i = 0; i < num_uses; i++) + { + tree use = immediate_use (df, i); + + if (!DONT_SIMULATE_AGAIN (use)) + { + stmt_ann_t ann = stmt_ann (use); + if (ann->in_ccp_worklist == 0) + { + ann->in_ccp_worklist = 1; + VARRAY_PUSH_TREE (ssa_edges, use); + } + } + } +} + +/* Set the lattice value for the variable VAR to VARYING. */ + +static void +def_to_varying (tree var) +{ + value val; + val.lattice_val = VARYING; + val.const_val = NULL_TREE; + set_lattice_value (var, val); +} + +/* Set the lattice value for variable VAR to VAL. */ + +static void +set_lattice_value (tree var, value val) +{ + value *old = get_value (var); + +#ifdef ENABLE_CHECKING + if (val.lattice_val == UNDEFINED) + { + /* CONSTANT->UNDEFINED is never a valid state transition. */ + if (old->lattice_val == CONSTANT) + abort (); + + /* VARYING->UNDEFINED is generally not a valid state transition, + except for values which are initialized to VARYING. */ + if (old->lattice_val == VARYING + && get_default_value (var).lattice_val != VARYING) + abort (); + } + else if (val.lattice_val == CONSTANT) + { + /* VARYING -> CONSTANT is an invalid state transition, except + for objects which start off in a VARYING state. */ + if (old->lattice_val == VARYING + && get_default_value (var).lattice_val != VARYING) + abort (); + } +#endif + + /* If the constant for VAR has changed, then this VAR is really varying. */ + if (old->lattice_val == CONSTANT && val.lattice_val == CONSTANT + && !simple_cst_equal (old->const_val, val.const_val)) + { + val.lattice_val = VARYING; + val.const_val = NULL_TREE; + } + + if (old->lattice_val != val.lattice_val) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_lattice_value (dump_file, + "Lattice value changed to ", val); + fprintf (dump_file, ". Adding definition to SSA edges.\n"); + } + + add_var_to_ssa_edges_worklist (var); + *old = val; + } +} + +/* Replace USE references in statement STMT with their immediate reaching + definition. Return true if at least one reference was replaced. If + REPLACED_ADDRESSES_P is given, it will be set to true if an address + constant was replaced. */ + +static bool +replace_uses_in (tree stmt, bool *replaced_addresses_p) +{ + bool replaced = false; + use_optype uses; + size_t i; + + if (replaced_addresses_p) + *replaced_addresses_p = false; + + get_stmt_operands (stmt); + + uses = STMT_USE_OPS (stmt); + for (i = 0; i < NUM_USES (uses); i++) + { + tree *use = USE_OP_PTR (uses, i); + value *val = get_value (*use); + + if (val->lattice_val == CONSTANT) + { + *use = val->const_val; + replaced = true; + if (POINTER_TYPE_P (TREE_TYPE (*use)) && replaced_addresses_p) + *replaced_addresses_p = true; + } + } + + return replaced; +} + +/* Return the likely latticevalue for STMT. + + If STMT has no operands, then return CONSTANT. + + Else if any operands of STMT are undefined, then return UNDEFINED. + + Else if any operands of STMT are constants, then return CONSTANT. + + Else return VARYING. */ + +static latticevalue +likely_value (tree stmt) +{ + use_optype uses; + size_t i; + int found_constant = 0; + stmt_ann_t ann; + + /* If the statement makes aliased loads or has volatile operands, it + won't fold to a constant value. */ + ann = stmt_ann (stmt); + if (ann->makes_aliased_loads || ann->has_volatile_ops) + return VARYING; + + /* A CALL_EXPR is assumed to be varying. This may be overly conservative, + in the presence of const and pure calls. */ + if (get_call_expr_in (stmt) != NULL_TREE) + return VARYING; + + get_stmt_operands (stmt); + + uses = USE_OPS (ann); + for (i = 0; i < NUM_USES (uses); i++) + { + tree use = USE_OP (uses, i); + value *val = get_value (use); + + if (val->lattice_val == UNDEFINED) + return UNDEFINED; + + if (val->lattice_val == CONSTANT) + found_constant = 1; + } + + return ((found_constant || !uses) ? CONSTANT : VARYING); +} + +/* A subroutine of fold_stmt_r. Attempts to fold *(A+O) to A[X]. + BASE is an array type. OFFSET is a byte displacement. ORIG_TYPE + is the desired result type. */ + +static tree +maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type) +{ + unsigned HOST_WIDE_INT lquo, lrem; + HOST_WIDE_INT hquo, hrem; + tree elt_size, min_idx, idx; + tree array_type, elt_type; + + /* Ignore stupid user tricks of indexing non-array variables. */ + array_type = TREE_TYPE (base); + if (TREE_CODE (array_type) != ARRAY_TYPE) + return NULL_TREE; + elt_type = TREE_TYPE (array_type); + if (!lang_hooks.types_compatible_p (orig_type, elt_type)) + return NULL_TREE; + + /* Whee. Ignore indexing of variable sized types. */ + elt_size = TYPE_SIZE_UNIT (elt_type); + if (TREE_CODE (elt_size) != INTEGER_CST) + return NULL_TREE; + + /* If the division isn't exact, then don't do anything. Equally + invalid as the above indexing of non-array variables. */ + if (div_and_round_double (TRUNC_DIV_EXPR, 1, + TREE_INT_CST_LOW (offset), + TREE_INT_CST_HIGH (offset), + TREE_INT_CST_LOW (elt_size), + TREE_INT_CST_HIGH (elt_size), + &lquo, &hquo, &lrem, &hrem) + || lrem || hrem) + return NULL_TREE; + idx = build_int_2_wide (lquo, hquo); + + /* Re-bias the index by the min index of the array type. */ + min_idx = TYPE_DOMAIN (TREE_TYPE (base)); + if (min_idx) + { + min_idx = TYPE_MIN_VALUE (min_idx); + if (min_idx) + { + idx = convert (TREE_TYPE (min_idx), idx); + if (!integer_zerop (min_idx)) + idx = int_const_binop (PLUS_EXPR, idx, min_idx, 1); + } + } + + return build (ARRAY_REF, orig_type, base, idx); +} + +/* A subroutine of fold_stmt_r. Attempts to fold *(S+O) to S.X. + BASE is a record type. OFFSET is a byte displacement. ORIG_TYPE + is the desired result type. */ +/* ??? This doesn't handle class inheritance. */ + +static tree +maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset, + tree orig_type, bool base_is_ptr) +{ + tree f, t, field_type, tail_array_field; + + if (TREE_CODE (record_type) != RECORD_TYPE + && TREE_CODE (record_type) != UNION_TYPE + && TREE_CODE (record_type) != QUAL_UNION_TYPE) + return NULL_TREE; + + /* Short-circuit silly cases. */ + if (lang_hooks.types_compatible_p (record_type, orig_type)) + return NULL_TREE; + + tail_array_field = NULL_TREE; + for (f = TYPE_FIELDS (record_type); f ; f = TREE_CHAIN (f)) + { + int cmp; + + if (TREE_CODE (f) != FIELD_DECL) + continue; + if (DECL_BIT_FIELD (f)) + continue; + if (TREE_CODE (DECL_FIELD_OFFSET (f)) != INTEGER_CST) + continue; + + /* ??? Java creates "interesting" fields for representing base classes. + They have no name, and have no context. With no context, we get into + trouble with nonoverlapping_component_refs_p. Skip them. */ + if (!DECL_FIELD_CONTEXT (f)) + continue; + + /* The previous array field isn't at the end. */ + tail_array_field = NULL_TREE; + + /* Check to see if this offset overlaps with the field. */ + cmp = tree_int_cst_compare (DECL_FIELD_OFFSET (f), offset); + if (cmp > 0) + continue; + + field_type = TREE_TYPE (f); + if (cmp < 0) + { + /* Don't care about offsets into the middle of scalars. */ + if (!AGGREGATE_TYPE_P (field_type)) + continue; + + /* Check for array at the end of the struct. This is often + used as for flexible array members. We should be able to + turn this into an array access anyway. */ + if (TREE_CODE (field_type) == ARRAY_TYPE) + tail_array_field = f; + + /* Check the end of the field against the offset. */ + if (!DECL_SIZE_UNIT (f) + || TREE_CODE (DECL_SIZE_UNIT (f)) != INTEGER_CST) + continue; + t = int_const_binop (MINUS_EXPR, offset, DECL_FIELD_OFFSET (f), 1); + if (!tree_int_cst_lt (t, DECL_SIZE_UNIT (f))) + continue; + + /* If we matched, then set offset to the displacement into + this field. */ + offset = t; + } + + /* Here we exactly match the offset being checked. If the types match, + then we can return that field. */ + else if (lang_hooks.types_compatible_p (orig_type, field_type)) + { + if (base_is_ptr) + base = build1 (INDIRECT_REF, record_type, base); + t = build (COMPONENT_REF, field_type, base, f); + return t; + } + + /* Don't care about type-punning of scalars. */ + else if (!AGGREGATE_TYPE_P (field_type)) + return NULL_TREE; + + goto found; + } + + if (!tail_array_field) + return NULL_TREE; + + f = tail_array_field; + field_type = TREE_TYPE (f); + + found: + /* If we get here, we've got an aggregate field, and a possibly + non-zero offset into them. Recurse and hope for a valid match. */ + if (base_is_ptr) + base = build1 (INDIRECT_REF, record_type, base); + base = build (COMPONENT_REF, field_type, base, f); + + t = maybe_fold_offset_to_array_ref (base, offset, orig_type); + if (t) + return t; + return maybe_fold_offset_to_component_ref (field_type, base, offset, + orig_type, false); +} + +/* A subroutine of fold_stmt_r. Attempt to simplify *(BASE+OFFSET). + Return the simplified expression, or NULL if nothing could be done. */ + +static tree +maybe_fold_stmt_indirect (tree expr, tree base, tree offset) +{ + tree t; + + /* We may well have constructed a double-nested PLUS_EXPR via multiple + substitutions. Fold that down to one. Remove NON_LVALUE_EXPRs that + are sometimes added. */ + base = fold (base); + STRIP_NOPS (base); + TREE_OPERAND (expr, 0) = base; + + /* One possibility is that the address reduces to a string constant. */ + t = fold_read_from_constant_string (expr); + if (t) + return t; + + /* Add in any offset from a PLUS_EXPR. */ + if (TREE_CODE (base) == PLUS_EXPR) + { + tree offset2; + + offset2 = TREE_OPERAND (base, 1); + if (TREE_CODE (offset2) != INTEGER_CST) + return NULL_TREE; + base = TREE_OPERAND (base, 0); + + offset = int_const_binop (PLUS_EXPR, offset, offset2, 1); + } + + if (TREE_CODE (base) == ADDR_EXPR) + { + /* Strip the ADDR_EXPR. */ + base = TREE_OPERAND (base, 0); + + /* Try folding *(&B+O) to B[X]. */ + t = maybe_fold_offset_to_array_ref (base, offset, TREE_TYPE (expr)); + if (t) + return t; + + /* Try folding *(&B+O) to B.X. */ + t = maybe_fold_offset_to_component_ref (TREE_TYPE (base), base, offset, + TREE_TYPE (expr), false); + if (t) + return t; + + /* Fold *&B to B. */ + if (integer_zerop (offset)) + return base; + } + else + { + /* We can get here for out-of-range string constant accesses, + such as "_"[3]. Bail out of the entire substitution search + and arrange for the entire statement to be replaced by a + call to __builtin_trap. In all likelyhood this will all be + constant-folded away, but in the meantime we can't leave with + something that get_expr_operands can't understand. */ + + t = base; + STRIP_NOPS (t); + if (TREE_CODE (t) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST) + { + /* FIXME: Except that this causes problems elsewhere with dead + code not being deleted, and we abort in the rtl expanders + because we failed to remove some ssa_name. In the meantime, + just return zero. */ + /* FIXME2: This condition should be signaled by + fold_read_from_constant_string directly, rather than + re-checking for it here. */ + return integer_zero_node; + } + + /* Try folding *(B+O) to B->X. Still an improvement. */ + if (POINTER_TYPE_P (TREE_TYPE (base))) + { + t = maybe_fold_offset_to_component_ref (TREE_TYPE (TREE_TYPE (base)), + base, offset, + TREE_TYPE (expr), true); + if (t) + return t; + } + } + + /* Otherwise we had an offset that we could not simplify. */ + return NULL_TREE; +} + +/* A subroutine of fold_stmt_r. EXPR is a PLUS_EXPR. + + A quaint feature extant in our address arithmetic is that there + can be hidden type changes here. The type of the result need + not be the same as the type of the input pointer. + + What we're after here is an expression of the form + (T *)(&array + const) + where the cast doesn't actually exist, but is implicit in the + type of the PLUS_EXPR. We'd like to turn this into + &array[x] + which may be able to propagate further. */ + +static tree +maybe_fold_stmt_addition (tree expr) +{ + tree op0 = TREE_OPERAND (expr, 0); + tree op1 = TREE_OPERAND (expr, 1); + tree ptr_type = TREE_TYPE (expr); + tree ptd_type; + tree t; + bool subtract = (TREE_CODE (expr) == MINUS_EXPR); + + /* We're only interested in pointer arithmetic. */ + if (!POINTER_TYPE_P (ptr_type)) + return NULL_TREE; + /* Canonicalize the integral operand to op1. */ + if (INTEGRAL_TYPE_P (TREE_TYPE (op0))) + { + if (subtract) + return NULL_TREE; + t = op0, op0 = op1, op1 = t; + } + /* It had better be a constant. */ + if (TREE_CODE (op1) != INTEGER_CST) + return NULL_TREE; + /* The first operand should be an ADDR_EXPR. */ + if (TREE_CODE (op0) != ADDR_EXPR) + return NULL_TREE; + op0 = TREE_OPERAND (op0, 0); + + /* If the first operand is an ARRAY_REF, expand it so that we can fold + the offset into it. */ + while (TREE_CODE (op0) == ARRAY_REF) + { + tree array_obj = TREE_OPERAND (op0, 0); + tree array_idx = TREE_OPERAND (op0, 1); + tree elt_type = TREE_TYPE (op0); + tree elt_size = TYPE_SIZE_UNIT (elt_type); + tree min_idx; + + if (TREE_CODE (array_idx) != INTEGER_CST) + break; + if (TREE_CODE (elt_size) != INTEGER_CST) + break; + + /* Un-bias the index by the min index of the array type. */ + min_idx = TYPE_DOMAIN (TREE_TYPE (array_obj)); + if (min_idx) + { + min_idx = TYPE_MIN_VALUE (min_idx); + if (min_idx) + { + array_idx = convert (TREE_TYPE (min_idx), array_idx); + if (!integer_zerop (min_idx)) + array_idx = int_const_binop (MINUS_EXPR, array_idx, + min_idx, 0); + } + } + + /* Convert the index to a byte offset. */ + array_idx = convert (sizetype, array_idx); + array_idx = int_const_binop (MULT_EXPR, array_idx, elt_size, 0); + + /* Update the operands for the next round, or for folding. */ + /* If we're manipulating unsigned types, then folding into negative + values can produce incorrect results. Particularly if the type + is smaller than the width of the pointer. */ + if (subtract + && TYPE_UNSIGNED (TREE_TYPE (op1)) + && tree_int_cst_lt (array_idx, op1)) + return NULL; + op1 = int_const_binop (subtract ? MINUS_EXPR : PLUS_EXPR, + array_idx, op1, 0); + subtract = false; + op0 = array_obj; + } + + /* If we weren't able to fold the subtraction into another array reference, + canonicalize the integer for passing to the array and component ref + simplification functions. */ + if (subtract) + { + if (TYPE_UNSIGNED (TREE_TYPE (op1))) + return NULL; + op1 = fold (build1 (NEGATE_EXPR, TREE_TYPE (op1), op1)); + /* ??? In theory fold should always produce another integer. */ + if (TREE_CODE (op1) != INTEGER_CST) + return NULL; + } + + ptd_type = TREE_TYPE (ptr_type); + + /* At which point we can try some of the same things as for indirects. */ + t = maybe_fold_offset_to_array_ref (op0, op1, ptd_type); + if (!t) + t = maybe_fold_offset_to_component_ref (TREE_TYPE (op0), op0, op1, + ptd_type, false); + if (t) + t = build1 (ADDR_EXPR, ptr_type, t); + + return t; +} + +/* Subroutine of fold_stmt called via walk_tree. We perform several + simplifications of EXPR_P, mostly having to do with pointer arithmetic. */ + +static tree +fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data) +{ + bool *changed_p = data; + tree expr = *expr_p, t; + + /* ??? It'd be nice if walk_tree had a pre-order option. */ + switch (TREE_CODE (expr)) + { + case INDIRECT_REF: + t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); + if (t) + return t; + *walk_subtrees = 0; + + t = maybe_fold_stmt_indirect (expr, TREE_OPERAND (expr, 0), + integer_zero_node); + break; + + /* ??? Could handle ARRAY_REF here, as a variant of INDIRECT_REF. + We'd only want to bother decomposing an existing ARRAY_REF if + the base array is found to have another offset contained within. + Otherwise we'd be wasting time. */ + + case ADDR_EXPR: + t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); + if (t) + return t; + *walk_subtrees = 0; + + /* Set TREE_INVARIANT properly so that the value is properly + considered constant, and so gets propagated as expected. */ + if (*changed_p) + recompute_tree_invarant_for_addr_expr (expr); + return NULL_TREE; + + case PLUS_EXPR: + case MINUS_EXPR: + t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); + if (t) + return t; + t = walk_tree (&TREE_OPERAND (expr, 1), fold_stmt_r, data, NULL); + if (t) + return t; + *walk_subtrees = 0; + + t = maybe_fold_stmt_addition (expr); + break; + + case COMPONENT_REF: + t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); + if (t) + return t; + *walk_subtrees = 0; + + /* Make sure the FIELD_DECL is actually a field in the type on + the lhs. In cases with IMA it is possible that it came + from another, equivalent type at this point. We have + already checked the equivalence in this case. + Match on type plus offset, to allow for unnamed fields. + We won't necessarily get the corresponding field for + unions; this is believed to be harmless. */ + + if ((current_file_decl && TREE_CHAIN (current_file_decl)) + && (DECL_FIELD_CONTEXT (TREE_OPERAND (expr, 1)) != + TREE_TYPE (TREE_OPERAND (expr, 0)))) + { + tree f; + tree orig_field = TREE_OPERAND (expr, 1); + tree orig_type = TREE_TYPE (orig_field); + for (f = TYPE_FIELDS (TREE_TYPE (TREE_OPERAND (expr, 0))); + f; f = TREE_CHAIN (f)) + { + if (lang_hooks.types_compatible_p (TREE_TYPE (f), orig_type) + && tree_int_cst_compare (DECL_FIELD_BIT_OFFSET (f), + DECL_FIELD_BIT_OFFSET (orig_field)) + == 0 + && tree_int_cst_compare (DECL_FIELD_OFFSET (f), + DECL_FIELD_OFFSET (orig_field)) + == 0) + { + TREE_OPERAND (expr, 1) = f; + break; + } + } + /* Fall through is an error; it will be detected in tree-sra. */ + } + break; + + default: + return NULL_TREE; + } + + if (t) + { + *expr_p = t; + *changed_p = true; + } + + return NULL_TREE; +} + +/* Fold the statement pointed by STMT_P. In some cases, this function may + replace the whole statement with a new one. Returns true iff folding + makes any changes. */ + +bool +fold_stmt (tree *stmt_p) +{ + tree rhs, result, stmt; + bool changed = false; + + stmt = *stmt_p; + + /* If we replaced constants and the statement makes pointer dereferences, + then we may need to fold instances of *&VAR into VAR, etc. */ + if (walk_tree (stmt_p, fold_stmt_r, &changed, NULL)) + { + *stmt_p + = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], + NULL); + return true; + } + + rhs = get_rhs (stmt); + if (!rhs) + return changed; + result = NULL_TREE; + + /* Check for builtins that CCP can handle using information not + available in the generic fold routines. */ + if (TREE_CODE (rhs) == CALL_EXPR) + { + tree callee = get_callee_fndecl (rhs); + if (callee && DECL_BUILT_IN (callee)) + result = ccp_fold_builtin (stmt, rhs); + } + + /* If we couldn't fold the RHS, hand over to the generic fold routines. */ + if (result == NULL_TREE) + result = fold (rhs); + + /* Strip away useless type conversions. Both the NON_LVALUE_EXPR that + may have been added by fold, and "useless" type conversions that might + now be apparent due to propagation. */ + STRIP_MAIN_TYPE_NOPS (result); + STRIP_USELESS_TYPE_CONVERSION (result); + + if (result != rhs) + { + changed = true; + set_rhs (stmt_p, result); + } + + return changed; +} + +/* Get the main expression from statement STMT. */ + +static tree +get_rhs (tree stmt) +{ + enum tree_code code = TREE_CODE (stmt); + + if (code == MODIFY_EXPR) + return TREE_OPERAND (stmt, 1); + if (code == COND_EXPR) + return COND_EXPR_COND (stmt); + else if (code == SWITCH_EXPR) + return SWITCH_COND (stmt); + else if (code == RETURN_EXPR) + { + if (!TREE_OPERAND (stmt, 0)) + return NULL_TREE; + if (TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR) + return TREE_OPERAND (TREE_OPERAND (stmt, 0), 1); + else + return TREE_OPERAND (stmt, 0); + } + else if (code == GOTO_EXPR) + return GOTO_DESTINATION (stmt); + else if (code == LABEL_EXPR) + return LABEL_EXPR_LABEL (stmt); + else + return stmt; +} + + +/* Set the main expression of *STMT_P to EXPR. */ + +static void +set_rhs (tree *stmt_p, tree expr) +{ + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + if (code == MODIFY_EXPR) + TREE_OPERAND (stmt, 1) = expr; + else if (code == COND_EXPR) + COND_EXPR_COND (stmt) = expr; + else if (code == SWITCH_EXPR) + SWITCH_COND (stmt) = expr; + else if (code == RETURN_EXPR) + { + if (TREE_OPERAND (stmt, 0) + && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR) + TREE_OPERAND (TREE_OPERAND (stmt, 0), 1) = expr; + else + TREE_OPERAND (stmt, 0) = expr; + } + else if (code == GOTO_EXPR) + GOTO_DESTINATION (stmt) = expr; + else if (code == LABEL_EXPR) + LABEL_EXPR_LABEL (stmt) = expr; + else + { + /* Replace the whole statement with EXPR. If EXPR has no side + effects, then replace *STMT_P with an empty statement. */ + stmt_ann_t ann = stmt_ann (stmt); + *stmt_p = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt (); + (*stmt_p)->common.ann = (tree_ann) ann; + + if (TREE_SIDE_EFFECTS (expr)) + { + def_optype defs; + vdef_optype vdefs; + size_t i; + + /* Fix all the SSA_NAMEs created by *STMT_P to point to its new + replacement. */ + defs = DEF_OPS (ann); + for (i = 0; i < NUM_DEFS (defs); i++) + { + tree var = DEF_OP (defs, i); + if (TREE_CODE (var) == SSA_NAME) + SSA_NAME_DEF_STMT (var) = *stmt_p; + } + + vdefs = VDEF_OPS (ann); + for (i = 0; i < NUM_VDEFS (vdefs); i++) + { + tree var = VDEF_RESULT (vdefs, i); + if (TREE_CODE (var) == SSA_NAME) + SSA_NAME_DEF_STMT (var) = *stmt_p; + } + } + } +} + + +/* Return a default value for variable VAR using the following rules: + + 1- Global and static variables are considered VARYING, unless they are + declared const. + + 2- Function arguments are considered VARYING. + + 3- 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. */ + +static value +get_default_value (tree var) +{ + value val; + tree sym; + + if (TREE_CODE (var) == SSA_NAME) + sym = SSA_NAME_VAR (var); + else + { +#ifdef ENABLE_CHECKING + if (!DECL_P (var)) + abort (); +#endif + sym = var; + } + + val.lattice_val = UNDEFINED; + val.const_val = NULL_TREE; + + if (TREE_CODE (sym) == PARM_DECL || TREE_THIS_VOLATILE (sym)) + { + /* Function arguments and volatile variables are considered VARYING. */ + val.lattice_val = VARYING; + } + else if (decl_function_context (sym) != current_function_decl + || TREE_STATIC (sym)) + { + /* Globals and static variables are considered VARYING, unless they + are declared 'const'. */ + val.lattice_val = VARYING; + + if (TREE_READONLY (sym) + && DECL_INITIAL (sym) + && is_gimple_min_invariant (DECL_INITIAL (sym))) + { + val.lattice_val = CONSTANT; + val.const_val = DECL_INITIAL (sym); + } + } + else + { + enum tree_code code; + tree stmt = SSA_NAME_DEF_STMT (var); + + if (!IS_EMPTY_STMT (stmt)) + { + code = TREE_CODE (stmt); + if (code != MODIFY_EXPR && code != PHI_NODE) + val.lattice_val = VARYING; + } + } + + return val; +} + + +/* Fold builtin call FN in statement STMT. If it cannot be folded into a + constant, return NULL_TREE. Otherwise, return its constant value. */ + +static tree +ccp_fold_builtin (tree stmt, tree fn) +{ + tree result, strlen_val[2]; + tree arglist = TREE_OPERAND (fn, 1), a; + tree callee = get_callee_fndecl (fn); + bitmap visited; + int strlen_arg, i; + + /* Ignore MD builtins. */ + if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD) + return NULL_TREE; + + /* First try the generic builtin folder. If that succeeds, return the + result directly. */ + result = fold_builtin (fn); + if (result) + return result; + + /* If the builtin could not be folded, and it has no argument list, + we're done. */ + if (!arglist) + return NULL_TREE; + + /* Limit the work only for builtins we know how to simplify. */ + switch (DECL_FUNCTION_CODE (callee)) + { + case BUILT_IN_STRLEN: + case BUILT_IN_FPUTS: + case BUILT_IN_FPUTS_UNLOCKED: + strlen_arg = 1; + break; + case BUILT_IN_STRCPY: + case BUILT_IN_STRNCPY: + strlen_arg = 2; + break; + default: + return NULL_TREE; + } + + /* Try to use the dataflow information gathered by the CCP process. */ + visited = BITMAP_XMALLOC (); + + memset (strlen_val, 0, sizeof (strlen_val)); + for (i = 0, a = arglist; + strlen_arg; + i++, strlen_arg >>= 1, a = TREE_CHAIN (a)) + if (strlen_arg & 1) + { + bitmap_clear (visited); + if (!get_strlen (TREE_VALUE (a), &strlen_val[i], visited)) + strlen_val[i] = NULL_TREE; + } + + BITMAP_XFREE (visited); + + /* FIXME. All this code looks dangerous in the sense that it might + create non-gimple expressions. */ + switch (DECL_FUNCTION_CODE (callee)) + { + case BUILT_IN_STRLEN: + /* Convert from the internal "sizetype" type to "size_t". */ + if (strlen_val[0] + && size_type_node) + { + tree new = convert (size_type_node, strlen_val[0]); + + /* If the result is not a valid gimple value, or not a cast + of a valid gimple value, then we can not use the result. */ + if (is_gimple_val (new) + || (is_gimple_cast (new) + && is_gimple_val (TREE_OPERAND (new, 0)))) + return new; + else + return NULL_TREE; + } + return strlen_val[0]; + case BUILT_IN_STRCPY: + if (strlen_val[1] + && is_gimple_val (strlen_val[1])) + return simplify_builtin_strcpy (arglist, strlen_val[1]); + case BUILT_IN_STRNCPY: + if (strlen_val[1] + && is_gimple_val (strlen_val[1])) + return simplify_builtin_strncpy (arglist, strlen_val[1]); + case BUILT_IN_FPUTS: + return simplify_builtin_fputs (arglist, + TREE_CODE (stmt) != MODIFY_EXPR, 0, + strlen_val[0]); + case BUILT_IN_FPUTS_UNLOCKED: + return simplify_builtin_fputs (arglist, + TREE_CODE (stmt) != MODIFY_EXPR, 1, + strlen_val[0]); + + default: + abort (); + } + + return NULL_TREE; +} + + +/* Return the string length of ARG in LENGTH. If ARG is an SSA name variable, + follow its use-def chains. If LENGTH is not NULL and its value is not + equal to the length we determine, or if we are unable to determine the + length, return false. VISITED is a bitmap of visited variables. */ + +static bool +get_strlen (tree arg, tree *length, bitmap visited) +{ + tree var, def_stmt, val; + + if (TREE_CODE (arg) != SSA_NAME) + { + val = c_strlen (arg, 1); + if (!val) + return false; + + if (*length && simple_cst_equal (val, *length) != 1) + return false; + + *length = val; + return true; + } + + /* If we were already here, break the infinite cycle. */ + if (bitmap_bit_p (visited, SSA_NAME_VERSION (arg))) + return true; + bitmap_set_bit (visited, SSA_NAME_VERSION (arg)); + + var = arg; + def_stmt = SSA_NAME_DEF_STMT (var); + + switch (TREE_CODE (def_stmt)) + { + case MODIFY_EXPR: + { + tree len, rhs; + + /* The RHS of the statement defining VAR must either have a + constant length or come from another SSA_NAME with a constant + length. */ + rhs = TREE_OPERAND (def_stmt, 1); + STRIP_NOPS (rhs); + if (TREE_CODE (rhs) == SSA_NAME) + return get_strlen (rhs, length, visited); + + /* See if the RHS is a constant length. */ + len = c_strlen (rhs, 1); + if (len) + { + if (*length && simple_cst_equal (len, *length) != 1) + return false; + + *length = len; + return true; + } + + break; + } + + case PHI_NODE: + { + /* All the arguments of the PHI node must have the same constant + length. */ + int i; + + for (i = 0; i < PHI_NUM_ARGS (def_stmt); i++) + { + tree arg = PHI_ARG_DEF (def_stmt, i); + + /* If this PHI has itself as an argument, we cannot + determine the string length of this argument. However, + if we can find a constant string length for the other + PHI args then we can still be sure that this is a + constant string length. So be optimistic and just + continue with the next argument. */ + if (arg == PHI_RESULT (def_stmt)) + continue; + + if (!get_strlen (arg, length, visited)) + return false; + } + + return true; + } + + default: + break; + } + + + return false; +} + + +/* 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 void +execute_fold_all_builtins (void) +{ + basic_block bb; + FOR_EACH_BB (bb) + { + block_stmt_iterator i; + for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + { + tree *stmtp = bsi_stmt_ptr (i); + tree call = get_rhs (*stmtp); + tree callee, result; + + if (!call || TREE_CODE (call) != CALL_EXPR) + continue; + callee = get_callee_fndecl (call); + if (!callee || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL) + continue; + + result = ccp_fold_builtin (*stmtp, call); + 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; + + default: + continue; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Simplified\n "); + print_generic_stmt (dump_file, *stmtp, dump_flags); + } + + set_rhs (stmtp, result); + modify_stmt (*stmtp); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "to\n "); + print_generic_stmt (dump_file, *stmtp, dump_flags); + fprintf (dump_file, "\n"); + } + } + } +} + +struct tree_opt_pass pass_fold_builtins = +{ + "fab", /* name */ + NULL, /* gate */ + execute_fold_all_builtins, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_ssa /* todo_flags_finish */ +}; + + +#include "gt-tree-ssa-ccp.h" |