Mercurial > hg > CbC > CbC_gcc
diff gcc/tree-ssa-ccp.c @ 55:77e2b8dfacca gcc-4.4.5
update it from 4.4.3 to 4.5.0
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 12 Feb 2010 23:39:51 +0900 |
parents | 3bfb6c00c1e0 |
children | b7f97abdc517 |
line wrap: on
line diff
--- a/gcc/tree-ssa-ccp.c Sun Feb 07 18:28:00 2010 +0900 +++ b/gcc/tree-ssa-ccp.c Fri Feb 12 23:39:51 2010 +0900 @@ -5,17 +5,17 @@ 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/>. */ @@ -63,7 +63,7 @@ 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 @@ -208,6 +208,7 @@ #include "langhooks.h" #include "target.h" #include "toplev.h" +#include "dbgcnt.h" /* Possible lattice values. */ @@ -228,6 +229,7 @@ static prop_value_t *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. */ @@ -275,15 +277,27 @@ get_symbol_constant_value (tree sym) { if (TREE_STATIC (sym) - && TREE_READONLY (sym) - && !MTAG_P (sym)) + && (TREE_READONLY (sym) + || TREE_CODE (sym) == CONST_DECL)) { tree val = DECL_INITIAL (sym); if (val) { STRIP_USELESS_TYPE_CONVERSION (val); if (is_gimple_min_invariant (val)) - return val; + { + if (TREE_CODE (val) == ADDR_EXPR) + { + tree base = get_base_address (TREE_OPERAND (val, 0)); + if (base && TREE_CODE (base) == VAR_DECL) + { + TREE_ADDRESSABLE (base) = 1; + if (gimple_referenced_vars (cfun)) + add_referenced_var (base); + } + } + return val; + } } /* Variables declared 'const' without an initializer have zero as the initializer if they may not be @@ -322,52 +336,45 @@ { tree sym = SSA_NAME_VAR (var); prop_value_t val = { UNINITIALIZED, NULL_TREE }; - tree cst_val; - - if (!is_gimple_reg (var)) + gimple stmt; + + stmt = SSA_NAME_DEF_STMT (var); + + if (gimple_nop_p (stmt)) { - /* Short circuit for regular CCP. We are not interested in any - non-register when DO_STORE_CCP is false. */ - val.lattice_val = VARYING; + /* 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 (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL) + val.lattice_val = UNDEFINED; + else + val.lattice_val = VARYING; } - else if ((cst_val = get_symbol_constant_value (sym)) != NULL_TREE) + 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) { - /* Globals and static variables declared 'const' take their - initial value. */ - val.lattice_val = CONSTANT; - val.value = cst_val; + 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 { - gimple 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 (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL) - val.lattice_val = UNDEFINED; - else - val.lattice_val = VARYING; - } - 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) - { - /* 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; - } + /* Otherwise, VAR will never take on a constant value. */ + val.lattice_val = VARYING; } return val; @@ -505,6 +512,7 @@ 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); @@ -520,33 +528,11 @@ if (gimple_has_volatile_ops (stmt)) return VARYING; - /* If we are not doing store-ccp, statements with loads - and/or stores will never fold into a constant. */ - if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS)) - return VARYING; - - /* Note that only a GIMPLE_SINGLE_RHS assignment can satisfy - is_gimple_min_invariant, so we do not consider calls or - other forms of assignment. */ - if (gimple_assign_single_p (stmt) - && is_gimple_min_invariant (gimple_assign_rhs1 (stmt))) - return CONSTANT; - - if (code == GIMPLE_COND - && is_gimple_min_invariant (gimple_cond_lhs (stmt)) - && is_gimple_min_invariant (gimple_cond_rhs (stmt))) - return CONSTANT; - - if (code == GIMPLE_SWITCH - && is_gimple_min_invariant (gimple_switch_index (stmt))) - return CONSTANT; - /* 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 | SSA_OP_VUSE) + FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) { prop_value_t *val = get_value (use); @@ -559,6 +545,19 @@ 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 the operation combines operands like COMPLEX_EXPR make sure to not mark the result UNDEFINED if only one part of the result is undefined. */ @@ -589,11 +588,11 @@ if (has_undefined_operand) return VARYING; + /* 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 - /* We do not consider virtual operands here -- load from read-only - memory may have only VARYING virtual operands, but still be - constant. */ - || ZERO_SSA_OPERANDS (stmt, SSA_OP_USE)) + || gimple_references_memory_p (stmt)) return CONSTANT; return VARYING; @@ -609,9 +608,6 @@ if (gimple_has_volatile_ops (stmt)) return true; - if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS)) - 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)) @@ -623,6 +619,10 @@ 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 @@ -651,7 +651,15 @@ for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) { gimple stmt = gsi_stmt (i); - bool is_varying = surely_varying_stmt_p (stmt); + 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) { @@ -661,10 +669,7 @@ /* 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) - { - if (is_varying) - set_value_varying (def); - } + set_value_varying (def); } prop_set_simulate_again (stmt, !is_varying); } @@ -689,17 +694,38 @@ } } +/* 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].value = NULL_TREE; + } + } +} + /* Do final substitution of propagated values, cleanup the flowgraph and - free allocated storage. + free allocated storage. Return TRUE when something was optimized. */ static bool ccp_finalize (void) { + bool something_changed; + + do_dbg_cnt (); /* Perform substitutions based on the known constant values. */ - bool something_changed = substitute_and_fold (const_val, false); + something_changed = substitute_and_fold (const_val, ccp_fold_stmt); free (const_val); const_val = NULL; @@ -856,7 +882,7 @@ return SSA_PROP_NOT_INTERESTING; } -/* Return true if we may propagate the address expression ADDR into the +/* Return true if we may propagate the address expression ADDR into the dereference DEREF and cancel them. */ bool @@ -898,6 +924,7 @@ static tree ccp_fold (gimple stmt) { + location_t loc = gimple_location (stmt); switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: @@ -946,24 +973,61 @@ } } } + else if (TREE_CODE (rhs) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE + && (CONSTRUCTOR_NELTS (rhs) + == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)))) + { + unsigned i; + tree val, list; + + list = NULL_TREE; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val) + { + if (TREE_CODE (val) == SSA_NAME + && get_value (val)->lattice_val == CONSTANT) + val = get_value (val)->value; + if (TREE_CODE (val) == INTEGER_CST + || TREE_CODE (val) == REAL_CST + || TREE_CODE (val) == FIXED_CST) + list = tree_cons (NULL_TREE, val, list); + else + return NULL_TREE; + } + + return build_vector (TREE_TYPE (rhs), nreverse (list)); + } if (kind == tcc_reference) { - if (TREE_CODE (rhs) == VIEW_CONVERT_EXPR + if ((TREE_CODE (rhs) == VIEW_CONVERT_EXPR + || TREE_CODE (rhs) == REALPART_EXPR + || TREE_CODE (rhs) == IMAGPART_EXPR) && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME) { prop_value_t *val = get_value (TREE_OPERAND (rhs, 0)); if (val->lattice_val == CONSTANT) - return fold_unary (VIEW_CONVERT_EXPR, + return fold_unary_loc (EXPR_LOCATION (rhs), + TREE_CODE (rhs), TREE_TYPE (rhs), val->value); } + else if (TREE_CODE (rhs) == INDIRECT_REF + && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME) + { + prop_value_t *val = get_value (TREE_OPERAND (rhs, 0)); + if (val->lattice_val == CONSTANT + && TREE_CODE (val->value) == ADDR_EXPR + && useless_type_conversion_p (TREE_TYPE (rhs), + TREE_TYPE (TREE_TYPE (val->value)))) + rhs = TREE_OPERAND (val->value, 0); + } return fold_const_aggregate_ref (rhs); } else if (kind == tcc_declaration) return get_symbol_constant_value (rhs); return rhs; } - + case GIMPLE_UNARY_RHS: { /* Handle unary operators that can appear in GIMPLE form. @@ -1000,14 +1064,16 @@ if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (op0)) && ((tem = maybe_fold_offset_to_address - (op0, integer_zero_node, TREE_TYPE (lhs))) + (loc, + op0, integer_zero_node, TREE_TYPE (lhs))) != NULL_TREE)) return tem; return op0; } - return fold_unary_ignore_overflow (subcode, - gimple_expr_type (stmt), op0); + return + fold_unary_ignore_overflow_loc (loc, subcode, + gimple_expr_type (stmt), op0); } case GIMPLE_BINARY_RHS: @@ -1036,14 +1102,14 @@ && TREE_CODE (op0) == ADDR_EXPR && TREE_CODE (op1) == INTEGER_CST) { - tree lhs = gimple_assign_lhs (stmt); - tree tem = maybe_fold_offset_to_address (op0, op1, - TREE_TYPE (lhs)); + tree tem = maybe_fold_offset_to_address + (loc, op0, op1, TREE_TYPE (op0)); if (tem != NULL_TREE) return tem; } - return fold_binary (subcode, gimple_expr_type (stmt), op0, op1); + return fold_binary_loc (loc, subcode, + gimple_expr_type (stmt), op0, op1); } default: @@ -1080,9 +1146,10 @@ args[i] = val->value; } } - call = build_call_array (gimple_call_return_type (stmt), - fn, gimple_call_num_args (stmt), args); - retval = fold_call_expr (call, false); + call = build_call_array_loc (loc, + gimple_call_return_type (stmt), + fn, gimple_call_num_args (stmt), args); + retval = fold_call_expr (EXPR_LOCATION (call), call, false); if (retval) /* fold_call_expr wraps the result inside a NOP_EXPR. */ STRIP_NOPS (retval); @@ -1113,7 +1180,7 @@ op1 = val->value; } - return fold_binary (code, boolean_type_node, op0, op1); + return fold_binary_loc (loc, code, boolean_type_node, op0, op1); } case GIMPLE_SWITCH: @@ -1148,6 +1215,9 @@ unsigned HOST_WIDE_INT cnt; tree cfield, cval; + if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration) + return get_symbol_constant_value (t); + switch (TREE_CODE (t)) { case ARRAY_REF: @@ -1228,6 +1298,12 @@ if (tree_int_cst_equal (cfield, idx)) { STRIP_USELESS_TYPE_CONVERSION (cval); + if (TREE_CODE (cval) == ADDR_EXPR) + { + tree base = get_base_address (TREE_OPERAND (cval, 0)); + if (base && TREE_CODE (base) == VAR_DECL) + add_referenced_var (base); + } return cval; } break; @@ -1271,6 +1347,12 @@ && ! DECL_BIT_FIELD (cfield)) { STRIP_USELESS_TYPE_CONVERSION (cval); + if (TREE_CODE (cval) == ADDR_EXPR) + { + tree base = get_base_address (TREE_OPERAND (cval, 0)); + if (base && TREE_CODE (base) == VAR_DECL) + add_referenced_var (base); + } return cval; } break; @@ -1280,7 +1362,8 @@ { tree c = fold_const_aggregate_ref (TREE_OPERAND (t, 0)); if (c && TREE_CODE (c) == COMPLEX_CST) - return fold_build1 (TREE_CODE (t), TREE_TYPE (t), c); + return fold_build1_loc (EXPR_LOCATION (t), + TREE_CODE (t), TREE_TYPE (t), c); break; } @@ -1332,7 +1415,7 @@ 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) @@ -1341,8 +1424,8 @@ 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); + /* 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); @@ -1390,6 +1473,34 @@ return val; } +/* 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); + prop_value_t val; + + if (gimple_code (stmt) != GIMPLE_COND) + return false; + + /* 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 + || TREE_CODE (val.value) != INTEGER_CST) + return false; + + if (integer_zerop (val.value)) + gimple_cond_make_false (stmt); + else + gimple_cond_make_true (stmt); + + return true; +} + /* 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 @@ -1476,7 +1587,7 @@ 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. */ @@ -1558,7 +1669,7 @@ } -struct gimple_opt_pass pass_ccp = +struct gimple_opt_pass pass_ccp = { { GIMPLE_PASS, @@ -1579,12 +1690,15 @@ }; -/* A subroutine of fold_stmt_r. Attempts to fold *(A+O) to A[X]. +/* A subroutine of fold_stmt. 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. */ + is the desired result type. + + LOC is the location of the original expression. */ static tree -maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type, +maybe_fold_offset_to_array_ref (location_t loc, tree base, tree offset, + tree orig_type, bool allow_negative_idx) { tree min_idx, idx, idx_type, elt_offset = integer_zero_node; @@ -1693,7 +1807,7 @@ (char *)a - 4; which should be not folded to &a->d[-8]. */ if (domain_type - && TYPE_MAX_VALUE (domain_type) + && TYPE_MAX_VALUE (domain_type) && TREE_CODE (TYPE_MAX_VALUE (domain_type)) == INTEGER_CST) { tree up_bound = TYPE_MAX_VALUE (domain_type); @@ -1717,17 +1831,23 @@ && compare_tree_int (idx, 0) < 0) return NULL_TREE; - return build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE); + { + tree t = build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE); + SET_EXPR_LOCATION (t, loc); + return t; + } } /* Attempt to fold *(S+O) to S.X. BASE is a record type. OFFSET is a byte displacement. ORIG_TYPE - is the desired result type. */ + is the desired result type. + + LOC is the location of the original expression. */ static tree -maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset, - tree orig_type) +maybe_fold_offset_to_component_ref (location_t loc, tree record_type, + tree base, tree offset, tree orig_type) { tree f, t, field_type, tail_array_field, field_offset; tree ret; @@ -1782,7 +1902,7 @@ t = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE); return t; } - + /* Don't care about offsets into the middle of scalars. */ if (!AGGREGATE_TYPE_P (field_type)) continue; @@ -1804,13 +1924,14 @@ /* If we matched, then set offset to the displacement into this field. */ new_base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE); + SET_EXPR_LOCATION (new_base, loc); /* Recurse to possibly find the match. */ - ret = maybe_fold_offset_to_array_ref (new_base, t, orig_type, + ret = maybe_fold_offset_to_array_ref (loc, new_base, t, orig_type, f == TYPE_FIELDS (record_type)); if (ret) return ret; - ret = maybe_fold_offset_to_component_ref (field_type, new_base, t, + ret = maybe_fold_offset_to_component_ref (loc, field_type, new_base, t, orig_type); if (ret) return ret; @@ -1823,26 +1944,30 @@ field_type = TREE_TYPE (f); offset = int_const_binop (MINUS_EXPR, offset, byte_position (f), 1); - /* If we get here, we've got an aggregate field, and a possibly + /* If we get here, we've got an aggregate field, and a possibly nonzero offset into them. Recurse and hope for a valid match. */ base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE); - - t = maybe_fold_offset_to_array_ref (base, offset, orig_type, + SET_EXPR_LOCATION (base, loc); + + t = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type, f == TYPE_FIELDS (record_type)); if (t) return t; - return maybe_fold_offset_to_component_ref (field_type, base, offset, + return maybe_fold_offset_to_component_ref (loc, field_type, base, offset, orig_type); } /* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type - or BASE[index] or by combination of those. + or BASE[index] or by combination of those. + + LOC is the location of original expression. Before attempting the conversion strip off existing ADDR_EXPRs and handled component refs. */ tree -maybe_fold_offset_to_reference (tree base, tree offset, tree orig_type) +maybe_fold_offset_to_reference (location_t loc, tree base, tree offset, + tree orig_type) { tree ret; tree type; @@ -1872,7 +1997,7 @@ if (sub_offset) offset = int_const_binop (PLUS_EXPR, offset, build_int_cst (TREE_TYPE (offset), - sub_offset / BITS_PER_UNIT), 1); + sub_offset / BITS_PER_UNIT), 1); } } if (useless_type_conversion_p (orig_type, TREE_TYPE (base)) @@ -1880,9 +2005,9 @@ return base; type = TREE_TYPE (base); - ret = maybe_fold_offset_to_component_ref (type, base, offset, orig_type); + ret = maybe_fold_offset_to_component_ref (loc, type, base, offset, orig_type); if (!ret) - ret = maybe_fold_offset_to_array_ref (base, offset, orig_type, true); + ret = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type, true); return ret; } @@ -1890,17 +2015,21 @@ /* Attempt to express (ORIG_TYPE)&BASE+OFFSET as &BASE->field_of_orig_type or &BASE[index] or by combination of those. + LOC is the location of the original expression. + Before attempting the conversion strip off existing component refs. */ tree -maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type) +maybe_fold_offset_to_address (location_t loc, tree addr, tree offset, + tree orig_type) { tree t; gcc_assert (POINTER_TYPE_P (TREE_TYPE (addr)) && POINTER_TYPE_P (orig_type)); - t = maybe_fold_offset_to_reference (addr, offset, TREE_TYPE (orig_type)); + t = maybe_fold_offset_to_reference (loc, addr, offset, + TREE_TYPE (orig_type)); if (t != NULL_TREE) { tree orig = addr; @@ -1937,13 +2066,13 @@ ptr_type = build_pointer_type (TREE_TYPE (t)); if (!useless_type_conversion_p (orig_type, ptr_type)) return NULL_TREE; - return build_fold_addr_expr_with_type (t, ptr_type); + return build_fold_addr_expr_with_type_loc (loc, t, ptr_type); } return NULL_TREE; } -/* A subroutine of fold_stmt_r. Attempt to simplify *(BASE+OFFSET). +/* A subroutine of fold_stmt. Attempt to simplify *(BASE+OFFSET). Return the simplified expression, or NULL if nothing could be done. */ static tree @@ -1951,6 +2080,7 @@ { tree t; bool volatile_p = TREE_THIS_VOLATILE (expr); + location_t loc = EXPR_LOCATION (expr); /* We may well have constructed a double-nested PLUS_EXPR via multiple substitutions. Fold that down to one. Remove NON_LVALUE_EXPRs that @@ -1991,7 +2121,7 @@ return DECL_INITIAL (base); /* Try folding *(&B+O) to B.X. */ - t = maybe_fold_offset_to_reference (base_addr, offset, + t = maybe_fold_offset_to_reference (loc, base_addr, offset, TREE_TYPE (expr)); if (t) { @@ -2005,7 +2135,7 @@ } else { - /* We can get here for out-of-range string constant accesses, + /* 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 likelihood this will all be @@ -2018,11 +2148,11 @@ && TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST) { /* FIXME: Except that this causes problems elsewhere with dead - code not being deleted, and we die in the rtl expanders + code not being deleted, and we die 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 + fold_read_from_constant_string directly, rather than re-checking for it here. */ return integer_zero_node; } @@ -2030,7 +2160,7 @@ /* Try folding *(B+O) to B->X. Still an improvement. */ if (POINTER_TYPE_P (TREE_TYPE (base))) { - t = maybe_fold_offset_to_reference (base, offset, + t = maybe_fold_offset_to_reference (loc, base, offset, TREE_TYPE (expr)); if (t) return t; @@ -2055,19 +2185,52 @@ which may be able to propagate further. */ tree -maybe_fold_stmt_addition (tree res_type, tree op0, tree op1) +maybe_fold_stmt_addition (location_t loc, tree res_type, tree op0, tree op1) { tree ptd_type; tree 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); + /* It had better be a constant. */ + if (TREE_CODE (op1) != INTEGER_CST) + { + /* Or op0 should now be A[0] and the non-constant offset defined + via a multiplication by the array element size. */ + if (TREE_CODE (op0) == ARRAY_REF + && integer_zerop (TREE_OPERAND (op0, 1)) + && TREE_CODE (op1) == SSA_NAME + && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (op0)), 1)) + { + gimple offset_def = SSA_NAME_DEF_STMT (op1); + if (!is_gimple_assign (offset_def)) + return NULL_TREE; + + if (gimple_assign_rhs_code (offset_def) == MULT_EXPR + && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST + && tree_int_cst_equal (gimple_assign_rhs2 (offset_def), + TYPE_SIZE_UNIT (TREE_TYPE (op0)))) + return build_fold_addr_expr + (build4 (ARRAY_REF, TREE_TYPE (op0), + TREE_OPERAND (op0, 0), + gimple_assign_rhs1 (offset_def), + TREE_OPERAND (op0, 2), + TREE_OPERAND (op0, 3))); + else if (integer_onep (TYPE_SIZE_UNIT (TREE_TYPE (op0))) + && gimple_assign_rhs_code (offset_def) != MULT_EXPR) + return build_fold_addr_expr + (build4 (ARRAY_REF, TREE_TYPE (op0), + TREE_OPERAND (op0, 0), + op1, + TREE_OPERAND (op0, 2), + TREE_OPERAND (op0, 3))); + } + return NULL_TREE; + } + /* 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) @@ -2119,190 +2282,90 @@ ptd_type = TREE_TYPE (TREE_TYPE (op0)); /* 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, true); + t = maybe_fold_offset_to_array_ref (loc, op0, op1, ptd_type, true); if (!t) - t = maybe_fold_offset_to_component_ref (TREE_TYPE (op0), op0, op1, + t = maybe_fold_offset_to_component_ref (loc, TREE_TYPE (op0), op0, op1, ptd_type); if (t) - t = build1 (ADDR_EXPR, res_type, t); + { + t = build1 (ADDR_EXPR, res_type, t); + SET_EXPR_LOCATION (t, loc); + } return t; } -/* For passing state through walk_tree into fold_stmt_r and its - children. */ - -struct fold_stmt_r_data -{ - gimple stmt; - bool *changed_p; - bool *inside_addr_expr_p; -}; - -/* Subroutine of fold_stmt called via walk_tree. We perform several - simplifications of EXPR_P, mostly having to do with pointer arithmetic. */ +/* Subroutine of fold_stmt. We perform several simplifications of the + memory reference tree EXPR and make sure to re-gimplify them properly + after propagation of constant addresses. IS_LHS is true if the + reference is supposed to be an lvalue. */ static tree -fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data) +maybe_fold_reference (tree expr, bool is_lhs) { - struct walk_stmt_info *wi = (struct walk_stmt_info *) data; - struct fold_stmt_r_data *fold_stmt_r_data; - bool *inside_addr_expr_p; - bool *changed_p; - tree expr = *expr_p, t; - bool volatile_p = TREE_THIS_VOLATILE (expr); - - fold_stmt_r_data = (struct fold_stmt_r_data *) wi->info; - inside_addr_expr_p = fold_stmt_r_data->inside_addr_expr_p; - changed_p = fold_stmt_r_data->changed_p; - - /* ??? It'd be nice if walk_tree had a pre-order option. */ - switch (TREE_CODE (expr)) + tree *t = &expr; + + if (TREE_CODE (expr) == ARRAY_REF + && !is_lhs) + { + tree tem = fold_read_from_constant_string (expr); + if (tem) + return tem; + } + + /* ??? We might want to open-code the relevant remaining cases + to avoid using the generic fold. */ + if (handled_component_p (*t) + && CONSTANT_CLASS_P (TREE_OPERAND (*t, 0))) { - 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); + tree tem = fold (*t); + if (tem != *t) + return tem; + } + + while (handled_component_p (*t)) + t = &TREE_OPERAND (*t, 0); + + if (TREE_CODE (*t) == INDIRECT_REF) + { + tree tem = maybe_fold_stmt_indirect (*t, TREE_OPERAND (*t, 0), + integer_zero_node); /* Avoid folding *"abc" = 5 into 'a' = 5. */ - if (wi->is_lhs && t && TREE_CODE (t) == INTEGER_CST) - t = NULL_TREE; - if (!t - && TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR) + if (is_lhs && tem && CONSTANT_CLASS_P (tem)) + tem = NULL_TREE; + if (!tem + && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR) /* If we had a good reason for propagating the address here, make sure we end up with valid gimple. See PR34989. */ - t = TREE_OPERAND (TREE_OPERAND (expr, 0), 0); - break; - - case NOP_EXPR: - t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); - if (t) - return t; - *walk_subtrees = 0; - - if (POINTER_TYPE_P (TREE_TYPE (expr)) - && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (expr))) - && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))) - && (t = maybe_fold_offset_to_address (TREE_OPERAND (expr, 0), - integer_zero_node, - TREE_TYPE (TREE_TYPE (expr))))) - return t; - break; - - /* ??? Could handle more ARRAY_REFs 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 ARRAY_REF: - /* If we are not processing expressions found within an - ADDR_EXPR, then we can fold constant array references. - Don't fold on LHS either, to avoid folding "abc"[0] = 5 - into 'a' = 5. */ - if (!*inside_addr_expr_p && !wi->is_lhs) - t = fold_read_from_constant_string (expr); - else - t = NULL; - break; - - case ADDR_EXPR: - *inside_addr_expr_p = true; - t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); - *inside_addr_expr_p = false; - if (t) - return t; - *walk_subtrees = 0; - - /* Make sure the value is properly considered constant, and so gets - propagated as expected. */ - if (*changed_p) - recompute_tree_invariant_for_addr_expr (expr); - return NULL_TREE; - - 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. - We've already checked that the records are compatible, so we should - come up with a set of compatible fields. */ - { - tree expr_record = TREE_TYPE (TREE_OPERAND (expr, 0)); - tree expr_field = TREE_OPERAND (expr, 1); - - if (DECL_FIELD_CONTEXT (expr_field) != TYPE_MAIN_VARIANT (expr_record)) - { - expr_field = find_compatible_field (expr_record, expr_field); - TREE_OPERAND (expr, 1) = expr_field; - } - } - break; - - case TARGET_MEM_REF: - t = maybe_fold_tmr (expr); - break; - - case POINTER_PLUS_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 (TREE_TYPE (expr), - TREE_OPERAND (expr, 0), - TREE_OPERAND (expr, 1)); - break; - - case COND_EXPR: - if (COMPARISON_CLASS_P (TREE_OPERAND (expr, 0))) - { - tree op0 = TREE_OPERAND (expr, 0); - tree tem; - bool set; - - fold_defer_overflow_warnings (); - tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0), - TREE_OPERAND (op0, 0), - TREE_OPERAND (op0, 1)); - /* This is actually a conditional expression, not a GIMPLE - conditional statement, however, the valid_gimple_rhs_p - test still applies. */ - set = tem && is_gimple_condexpr (tem) && valid_gimple_rhs_p (tem); - fold_undefer_overflow_warnings (set, fold_stmt_r_data->stmt, 0); - if (set) - { - COND_EXPR_COND (expr) = tem; - t = expr; - break; - } - } - return NULL_TREE; - - default: - return NULL_TREE; + tem = TREE_OPERAND (TREE_OPERAND (*t, 0), 0); + + if (tem) + { + *t = tem; + tem = maybe_fold_reference (expr, is_lhs); + if (tem) + return tem; + return expr; + } } - - if (t) + else if (!is_lhs + && DECL_P (*t)) { - /* Preserve volatileness of the original expression. - We can end up with a plain decl here which is shared - and we shouldn't mess with its flags. */ - if (!SSA_VAR_P (t)) - TREE_THIS_VOLATILE (t) = volatile_p; - *expr_p = t; - *changed_p = true; + tree tem = get_symbol_constant_value (*t); + if (tem) + { + *t = tem; + tem = maybe_fold_reference (expr, is_lhs); + if (tem) + return tem; + return expr; + } } return NULL_TREE; } + /* Return the string length, maximum string length or maximum value of ARG in LENGTH. If ARG is an SSA name variable, follow its use-def chains. If LENGTH @@ -2317,7 +2380,7 @@ { tree var, val; gimple def_stmt; - + if (TREE_CODE (arg) != SSA_NAME) { if (TREE_CODE (arg) == COND_EXPR) @@ -2412,7 +2475,7 @@ return false; } } - return true; + return true; default: return false; @@ -2436,6 +2499,7 @@ bitmap visited; bool ignore; int nargs; + location_t loc = gimple_location (stmt); gcc_assert (is_gimple_call (stmt)); @@ -2532,7 +2596,7 @@ case BUILT_IN_STRCPY: if (val[1] && is_gimple_val (val[1]) && nargs == 2) - result = fold_builtin_strcpy (callee, + result = fold_builtin_strcpy (loc, callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), val[1]); @@ -2540,7 +2604,7 @@ case BUILT_IN_STRNCPY: if (val[1] && is_gimple_val (val[1]) && nargs == 3) - result = fold_builtin_strncpy (callee, + result = fold_builtin_strncpy (loc, callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2), @@ -2549,14 +2613,14 @@ case BUILT_IN_FPUTS: if (nargs == 2) - result = fold_builtin_fputs (gimple_call_arg (stmt, 0), + result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), ignore, false, val[0]); break; case BUILT_IN_FPUTS_UNLOCKED: if (nargs == 2) - result = fold_builtin_fputs (gimple_call_arg (stmt, 0), + result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), ignore, true, val[0]); break; @@ -2566,7 +2630,7 @@ case BUILT_IN_MEMMOVE_CHK: case BUILT_IN_MEMSET_CHK: if (val[2] && is_gimple_val (val[2]) && nargs == 4) - result = fold_builtin_memory_chk (callee, + result = fold_builtin_memory_chk (loc, callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2), @@ -2578,7 +2642,7 @@ case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: if (val[1] && is_gimple_val (val[1]) && nargs == 3) - result = fold_builtin_stxcpy_chk (callee, + result = fold_builtin_stxcpy_chk (loc, callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2), @@ -2588,7 +2652,7 @@ case BUILT_IN_STRNCPY_CHK: if (val[2] && is_gimple_val (val[2]) && nargs == 4) - result = fold_builtin_strncpy_chk (gimple_call_arg (stmt, 0), + result = fold_builtin_strncpy_chk (loc, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2), gimple_call_arg (stmt, 3), @@ -2621,41 +2685,101 @@ { gimple stmt = gsi_stmt (*si); enum tree_code subcode = gimple_assign_rhs_code (stmt); - - tree result = NULL; + location_t loc = gimple_location (stmt); + + tree result = NULL_TREE; switch (get_gimple_rhs_class (subcode)) { case GIMPLE_SINGLE_RHS: { tree rhs = gimple_assign_rhs1 (stmt); - + /* Try to fold a conditional expression. */ if (TREE_CODE (rhs) == COND_EXPR) { - tree temp = fold (COND_EXPR_COND (rhs)); - if (temp != COND_EXPR_COND (rhs)) - result = fold_build3 (COND_EXPR, TREE_TYPE (rhs), temp, - COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs)); + tree op0 = COND_EXPR_COND (rhs); + tree tem; + bool set = false; + location_t cond_loc = EXPR_LOCATION (rhs); + + if (COMPARISON_CLASS_P (op0)) + { + fold_defer_overflow_warnings (); + tem = fold_binary_loc (cond_loc, + TREE_CODE (op0), TREE_TYPE (op0), + TREE_OPERAND (op0, 0), + TREE_OPERAND (op0, 1)); + /* This is actually a conditional expression, not a GIMPLE + conditional statement, however, the valid_gimple_rhs_p + test still applies. */ + set = (tem && is_gimple_condexpr (tem) + && valid_gimple_rhs_p (tem)); + fold_undefer_overflow_warnings (set, stmt, 0); + } + else if (is_gimple_min_invariant (op0)) + { + tem = op0; + set = true; + } + else + return NULL_TREE; + + if (set) + result = fold_build3_loc (cond_loc, COND_EXPR, TREE_TYPE (rhs), tem, + COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs)); } + else if (TREE_CODE (rhs) == TARGET_MEM_REF) + return maybe_fold_tmr (rhs); + + else if (REFERENCE_CLASS_P (rhs)) + return maybe_fold_reference (rhs, false); + + else if (TREE_CODE (rhs) == ADDR_EXPR) + { + tree tem = maybe_fold_reference (TREE_OPERAND (rhs, 0), true); + if (tem) + result = fold_convert (TREE_TYPE (rhs), + build_fold_addr_expr_loc (loc, tem)); + } + + else if (TREE_CODE (rhs) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE + && (CONSTRUCTOR_NELTS (rhs) + == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)))) + { + /* Fold a constant vector CONSTRUCTOR to VECTOR_CST. */ + unsigned i; + tree val; + + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val) + if (TREE_CODE (val) != INTEGER_CST + && TREE_CODE (val) != REAL_CST + && TREE_CODE (val) != FIXED_CST) + return NULL_TREE; + + return build_vector_from_ctor (TREE_TYPE (rhs), + CONSTRUCTOR_ELTS (rhs)); + } + + else if (DECL_P (rhs)) + return get_symbol_constant_value (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 + that may have been added by fold, and "useless" type conversions that might now be apparent due to propagation. */ STRIP_USELESS_TYPE_CONVERSION (result); if (result != rhs && valid_gimple_rhs_p (result)) return result; - else - /* It is possible that fold_stmt_r simplified the RHS. - Make sure that the subcode of this statement still - reflects the principal operator of the rhs operand. */ - return rhs; + + return NULL_TREE; } break; @@ -2663,7 +2787,7 @@ { tree rhs = gimple_assign_rhs1 (stmt); - result = fold_unary (subcode, gimple_expr_type (stmt), rhs); + result = fold_unary_loc (loc, subcode, gimple_expr_type (stmt), rhs); if (result) { /* If the operation was a conversion do _not_ mark a @@ -2685,7 +2809,8 @@ && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt)))) { tree type = gimple_expr_type (stmt); - tree t = maybe_fold_offset_to_address (gimple_assign_rhs1 (stmt), + tree t = maybe_fold_offset_to_address (loc, + gimple_assign_rhs1 (stmt), integer_zero_node, type); if (t) return t; @@ -2705,13 +2830,14 @@ (TREE_TYPE (gimple_assign_lhs (stmt)), type)) type = TREE_TYPE (gimple_assign_rhs1 (stmt)); } - result = maybe_fold_stmt_addition (type, + result = maybe_fold_stmt_addition (gimple_location (stmt), + type, gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt)); } if (!result) - result = fold_binary (subcode, + result = fold_binary_loc (loc, subcode, TREE_TYPE (gimple_assign_lhs (stmt)), gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt)); @@ -2750,7 +2876,8 @@ static bool fold_gimple_cond (gimple stmt) { - tree result = fold_binary (gimple_cond_code (stmt), + tree result = fold_binary_loc (gimple_location (stmt), + gimple_cond_code (stmt), boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); @@ -2768,6 +2895,7 @@ return false; } +static void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); /* Attempt to fold a call statement referenced by the statement iterator GSI. The statement may be replaced by another statement, e.g., if the call @@ -2788,7 +2916,11 @@ tree result = ccp_fold_builtin (stmt); if (result) - return update_call_from_tree (gsi, result); + { + if (!update_call_from_tree (gsi, result)) + gimplify_and_update_call_from_tree (gsi, result); + return true; + } } else { @@ -2826,125 +2958,130 @@ return false; } -/* Fold the statement pointed to by GSI. In some cases, this function may - replace the whole statement with a new one. Returns true iff folding - makes any changes. */ - -bool -fold_stmt (gimple_stmt_iterator *gsi) +/* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument + distinguishes both cases. */ + +static bool +fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) { - tree res; - struct fold_stmt_r_data fold_stmt_r_data; - struct walk_stmt_info wi; - bool changed = false; - bool inside_addr_expr = false; - gimple stmt = gsi_stmt (*gsi); - - fold_stmt_r_data.stmt = stmt; - fold_stmt_r_data.changed_p = &changed; - fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr; - - memset (&wi, 0, sizeof (wi)); - wi.info = &fold_stmt_r_data; - - /* Fold the individual operands. - For example, fold instances of *&VAR into VAR, etc. */ - res = walk_gimple_op (stmt, fold_stmt_r, &wi); - gcc_assert (!res); + unsigned i; /* Fold the main computation performed by the statement. */ switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: { + unsigned old_num_ops = gimple_num_ops (stmt); tree new_rhs = fold_gimple_assign (gsi); - if (new_rhs != NULL_TREE) + if (new_rhs != NULL_TREE + && (!inplace + || get_gimple_rhs_num_ops (TREE_CODE (new_rhs)) < old_num_ops)) { gimple_assign_set_rhs_from_tree (gsi, new_rhs); changed = true; } - stmt = gsi_stmt (*gsi); break; } + case GIMPLE_COND: changed |= fold_gimple_cond (stmt); break; + case GIMPLE_CALL: + /* Fold *& in call arguments. */ + for (i = 0; i < gimple_call_num_args (stmt); ++i) + if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i))) + { + tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false); + if (tmp) + { + gimple_call_set_arg (stmt, i, tmp); + changed = true; + } + } /* The entire statement may be replaced in this case. */ - changed |= fold_gimple_call (gsi); + if (!inplace) + changed |= fold_gimple_call (gsi); break; - default: - return changed; + case GIMPLE_ASM: + /* Fold *& in asm operands. */ + for (i = 0; i < gimple_asm_noutputs (stmt); ++i) + { + tree link = gimple_asm_output_op (stmt, i); + tree op = TREE_VALUE (link); + if (REFERENCE_CLASS_P (op) + && (op = maybe_fold_reference (op, true)) != NULL_TREE) + { + TREE_VALUE (link) = op; + changed = true; + } + } + for (i = 0; i < gimple_asm_ninputs (stmt); ++i) + { + tree link = gimple_asm_input_op (stmt, i); + tree op = TREE_VALUE (link); + if (REFERENCE_CLASS_P (op) + && (op = maybe_fold_reference (op, false)) != NULL_TREE) + { + TREE_VALUE (link) = op; + changed = true; + } + } break; + + default:; + } + + stmt = gsi_stmt (*gsi); + + /* Fold *& on the lhs. */ + if (gimple_has_lhs (stmt)) + { + tree lhs = gimple_get_lhs (stmt); + if (lhs && REFERENCE_CLASS_P (lhs)) + { + tree new_lhs = maybe_fold_reference (lhs, true); + if (new_lhs) + { + gimple_set_lhs (stmt, new_lhs); + changed = true; + } + } } return changed; } +/* Fold the statement pointed to by GSI. In some cases, this function may + replace the whole statement with a new one. Returns true iff folding + makes any changes. + The statement pointed to by GSI should be in valid gimple form but may + be in unfolded state as resulting from for example constant propagation + which can produce *&x = 0. */ + +bool +fold_stmt (gimple_stmt_iterator *gsi) +{ + return fold_stmt_1 (gsi, false); +} + /* Perform the minimal folding on statement STMT. Only operations like *&x created by constant propagation are handled. The statement cannot be replaced with a new one. Return true if the statement was - changed, false otherwise. */ + changed, false otherwise. + The statement STMT should be in valid gimple form but may + be in unfolded state as resulting from for example constant propagation + which can produce *&x = 0. */ bool fold_stmt_inplace (gimple stmt) { - tree res; - struct fold_stmt_r_data fold_stmt_r_data; - struct walk_stmt_info wi; - gimple_stmt_iterator si; - - bool changed = false; - bool inside_addr_expr = false; - - fold_stmt_r_data.stmt = stmt; - fold_stmt_r_data.changed_p = &changed; - fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr; - - memset (&wi, 0, sizeof (wi)); - wi.info = &fold_stmt_r_data; - - /* Fold the individual operands. - For example, fold instances of *&VAR into VAR, etc. - - It appears that, at one time, maybe_fold_stmt_indirect - would cause the walk to return non-null in order to - signal that the entire statement should be replaced with - a call to _builtin_trap. This functionality is currently - disabled, as noted in a FIXME, and cannot be supported here. */ - res = walk_gimple_op (stmt, fold_stmt_r, &wi); - gcc_assert (!res); - - /* Fold the main computation performed by the statement. */ - switch (gimple_code (stmt)) - { - case GIMPLE_ASSIGN: - { - unsigned old_num_ops; - tree new_rhs; - old_num_ops = gimple_num_ops (stmt); - si = gsi_for_stmt (stmt); - new_rhs = fold_gimple_assign (&si); - if (new_rhs != NULL_TREE - && get_gimple_rhs_num_ops (TREE_CODE (new_rhs)) < old_num_ops) - { - gimple_assign_set_rhs_from_tree (&si, new_rhs); - changed = true; - } - gcc_assert (gsi_stmt (si) == stmt); - break; - } - case GIMPLE_COND: - changed |= fold_gimple_cond (stmt); - break; - - default: - break; - } - + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + bool changed = fold_stmt_1 (&gsi, true); + gcc_assert (gsi_stmt (gsi) == stmt); return changed; } @@ -2957,9 +3094,8 @@ static tree optimize_stack_restore (gimple_stmt_iterator i) { - tree callee, rhs; - gimple stmt, stack_save; - gimple_stmt_iterator stack_save_gsi; + tree callee; + gimple stmt; basic_block bb = gsi_bb (i); gimple call = gsi_stmt (i); @@ -2983,37 +3119,49 @@ return NULL_TREE; if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE) - break; + goto second_stack_restore; } - if (gsi_end_p (i) - && (! single_succ_p (bb) - || single_succ_edge (bb)->dest != EXIT_BLOCK_PTR)) - return NULL_TREE; - - stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0)); - if (gimple_code (stack_save) != GIMPLE_CALL - || gimple_call_lhs (stack_save) != gimple_call_arg (call, 0) - || stmt_could_throw_p (stack_save) - || !has_single_use (gimple_call_arg (call, 0))) + if (!gsi_end_p (i)) return NULL_TREE; - 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_call_num_args (stack_save) != 0) - return NULL_TREE; - - stack_save_gsi = gsi_for_stmt (stack_save); - push_stmt_changes (gsi_stmt_ptr (&stack_save_gsi)); - rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0); - if (!update_call_from_tree (&stack_save_gsi, rhs)) + /* Allow one successor of the exit block, or zero successors. */ + switch (EDGE_COUNT (bb->succs)) { - discard_stmt_changes (gsi_stmt_ptr (&stack_save_gsi)); + case 0: + break; + case 1: + if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR) + return NULL_TREE; + break; + default: return NULL_TREE; } - pop_stmt_changes (gsi_stmt_ptr (&stack_save_gsi)); + 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; @@ -3029,6 +3177,7 @@ { 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; @@ -3056,11 +3205,11 @@ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs))) != TYPE_MAIN_VARIANT (cfun_va_list)) return NULL_TREE; - - lhs = build_fold_indirect_ref (lhs); - rhs = build_call_expr (built_in_decls[BUILT_IN_NEXT_ARG], + + lhs = build_fold_indirect_ref_loc (loc, lhs); + rhs = build_call_expr_loc (loc, built_in_decls[BUILT_IN_NEXT_ARG], 1, integer_zero_node); - rhs = fold_convert (TREE_TYPE (lhs), rhs); + rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); case BUILT_IN_VA_COPY: @@ -3076,13 +3225,13 @@ != TYPE_MAIN_VARIANT (cfun_va_list)) return NULL_TREE; - lhs = build_fold_indirect_ref (lhs); + 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 (TREE_TYPE (lhs), rhs); + rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); case BUILT_IN_VA_END: @@ -3122,7 +3271,7 @@ if (lhs == NULL_TREE) gimplify_and_add (expr, &stmts); - else + else tmp = get_initialized_tmp_var (expr, &stmts, NULL); pop_gimplify_context (NULL); @@ -3141,11 +3290,16 @@ } if (lhs == NULL_TREE) - new_stmt = gimple_build_nop (); + { + new_stmt = gimple_build_nop (); + unlink_stmt_vdef (stmt); + release_defs (stmt); + } else { new_stmt = gimple_build_assign (lhs, tmp); - copy_virtual_operands (new_stmt, stmt); + gimple_set_vuse (new_stmt, gimple_vuse (stmt)); + gimple_set_vdef (new_stmt, gimple_vdef (stmt)); move_ssa_defining_stmt_for_defs (new_stmt, stmt); } @@ -3162,7 +3316,7 @@ bool cfg_changed = false; basic_block bb; unsigned int todoflags = 0; - + FOR_EACH_BB (bb) { gimple_stmt_iterator i; @@ -3230,16 +3384,14 @@ } old_stmt = stmt; - push_stmt_changes (gsi_stmt_ptr (&i)); - if (!update_call_from_tree (&i, result)) - { - gimplify_and_update_call_from_tree (&i, result); - todoflags |= TODO_rebuild_alias; - } + { + gimplify_and_update_call_from_tree (&i, result); + todoflags |= TODO_update_address_taken; + } stmt = gsi_stmt (i); - pop_stmt_changes (gsi_stmt_ptr (&i)); + update_stmt (stmt); if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt) && gimple_purge_dead_eh_edges (bb)) @@ -3266,16 +3418,16 @@ gsi_next (&i); } } - + /* Delete unreachable blocks. */ if (cfg_changed) todoflags |= TODO_cleanup_cfg; - + return todoflags; } -struct gimple_opt_pass pass_fold_builtins = +struct gimple_opt_pass pass_fold_builtins = { { GIMPLE_PASS, @@ -3285,7 +3437,7 @@ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - 0, /* tv_id */ + TV_NONE, /* tv_id */ PROP_cfg | PROP_ssa, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */