X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ftree-ssa-ccp.c;h=fb6eb4da209c375522de7ab905db90311634d395;hb=b437daf41754afff274e689c4eddeb291a0e679e;hp=0365697fc85e4d2dd596f5b65132666d91db7353;hpb=be9f921eb27de7f5f22e4755df15340ab8042a09;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c index 0365697fc85..fb6eb4da209 100644 --- a/gcc/tree-ssa-ccp.c +++ b/gcc/tree-ssa-ccp.c @@ -1,5 +1,5 @@ /* Conditional constant propagation pass for the GNU compiler. - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Adapted from original RTL SSA-CCP by Daniel Berlin Adapted to GIMPLE trees by Diego Novillo @@ -208,6 +208,7 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "target.h" #include "toplev.h" +#include "dbgcnt.h" /* Possible lattice values. */ @@ -227,6 +228,8 @@ typedef enum doing the store). */ static prop_value_t *const_val; +static void canonicalize_float_value (prop_value_t *); + /* Dump constant propagation value VAL to file OUTF prefixed by PREFIX. */ static void @@ -273,24 +276,32 @@ tree get_symbol_constant_value (tree sym) { if (TREE_STATIC (sym) - && TREE_READONLY (sym) - && !MTAG_P (sym)) + && TREE_READONLY (sym)) { 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) + add_referenced_var (base); + } + return val; + } } /* Variables declared 'const' without an initializer have zero as the initializer if they may not be overridden at link or run time. */ if (!val + && !DECL_EXTERNAL (sym) && targetm.binds_local_p (sym) && (INTEGRAL_TYPE_P (TREE_TYPE (sym)) || SCALAR_FLOAT_TYPE_P (TREE_TYPE (sym)))) - return fold_convert (TREE_TYPE (sym), integer_zero_node); + return fold_convert (TREE_TYPE (sym), integer_zero_node); } return NULL_TREE; @@ -319,52 +330,45 @@ get_default_value (tree var) { tree sym = SSA_NAME_VAR (var); prop_value_t val = { UNINITIALIZED, NULL_TREE }; - tree cst_val; - - if (!is_gimple_reg (var)) - { - /* Short circuit for regular CCP. We are not interested in any - non-register when DO_STORE_CCP is false. */ - val.lattice_val = VARYING; - } - else if ((cst_val = get_symbol_constant_value (sym)) != NULL_TREE) + gimple stmt; + + stmt = SSA_NAME_DEF_STMT (var); + + if (gimple_nop_p (stmt)) { - /* Globals and static variables declared 'const' take their - initial value. */ - val.lattice_val = CONSTANT; - val.value = cst_val; + /* 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 - { - gimple stmt = SSA_NAME_DEF_STMT (var); - - if (gimple_nop_p (stmt)) + else if (is_gimple_assign (stmt) + /* Value-returning GIMPLE_CALL statements assign to + a variable, and are treated similarly to GIMPLE_ASSIGN. */ + || (is_gimple_call (stmt) + && gimple_call_lhs (stmt) != NULL_TREE) + || gimple_code (stmt) == GIMPLE_PHI) + { + tree cst; + if (gimple_assign_single_p (stmt) + && DECL_P (gimple_assign_rhs1 (stmt)) + && (cst = get_symbol_constant_value (gimple_assign_rhs1 (stmt)))) { - /* 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; + val.lattice_val = CONSTANT; + val.value = cst; } else - { - /* Otherwise, VAR will never take on a constant value. */ - val.lattice_val = VARYING; - } + /* 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; } return val; @@ -385,6 +389,8 @@ get_value (tree var) if (val->lattice_val == UNINITIALIZED) *val = get_default_value (var); + canonicalize_float_value (val); + return val; } @@ -500,6 +506,7 @@ likely_value (gimple stmt) bool has_constant_operand, has_undefined_operand, all_undefined_operands; tree use; ssa_op_iter iter; + unsigned i; enum gimple_code code = gimple_code (stmt); @@ -515,33 +522,11 @@ likely_value (gimple stmt) 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); @@ -554,6 +539,19 @@ likely_value (gimple stmt) 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. */ @@ -584,11 +582,11 @@ likely_value (gimple stmt) 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; @@ -604,9 +602,6 @@ surely_varying_stmt_p (gimple stmt) 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)) @@ -618,6 +613,10 @@ surely_varying_stmt_p (gimple stmt) 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 @@ -656,10 +655,7 @@ ccp_initialize (void) /* 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); } @@ -684,6 +680,24 @@ ccp_initialize (void) } } +/* 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. @@ -693,8 +707,11 @@ ccp_initialize (void) 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, false); free (const_val); const_val = NULL; @@ -851,6 +868,35 @@ ccp_visit_phi_node (gimple phi) return SSA_PROP_NOT_INTERESTING; } +/* Return true if we may propagate the address expression ADDR into the + dereference DEREF and cancel them. */ + +bool +may_propagate_address_into_dereference (tree addr, tree deref) +{ + gcc_assert (INDIRECT_REF_P (deref) + && TREE_CODE (addr) == ADDR_EXPR); + + /* Don't propagate if ADDR's operand has incomplete type. */ + if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_OPERAND (addr, 0)))) + return false; + + /* If the address is invariant then we do not need to preserve restrict + qualifications. But we do need to preserve volatile qualifiers until + we can annotate the folded dereference itself properly. */ + if (is_gimple_min_invariant (addr) + && (!TREE_THIS_VOLATILE (deref) + || TYPE_VOLATILE (TREE_TYPE (addr)))) + return useless_type_conversion_p (TREE_TYPE (deref), + TREE_TYPE (TREE_OPERAND (addr, 0))); + + /* Else both the address substitution and the folding must result in + a valid useless type conversion sequence. */ + return (useless_type_conversion_p (TREE_TYPE (TREE_OPERAND (deref, 0)), + TREE_TYPE (addr)) + && useless_type_conversion_p (TREE_TYPE (deref), + TREE_TYPE (TREE_OPERAND (addr, 0)))); +} /* CCP specific front-end to the non-destructive constant folding routines. @@ -897,12 +943,8 @@ ccp_fold (gimple stmt) prop_value_t *val = get_value (TREE_OPERAND (*base, 0)); if (val->lattice_val == CONSTANT && TREE_CODE (val->value) == ADDR_EXPR - && useless_type_conversion_p - (TREE_TYPE (TREE_OPERAND (*base, 0)), - TREE_TYPE (val->value)) - && useless_type_conversion_p - (TREE_TYPE (*base), - TREE_TYPE (TREE_OPERAND (val->value, 0)))) + && may_propagate_address_into_dereference + (val->value, *base)) { /* We need to return a new tree, not modify the IL or share parts of it. So play some tricks to @@ -916,17 +958,53 @@ ccp_fold (gimple stmt) } } } + 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 (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) @@ -941,7 +1019,6 @@ ccp_fold (gimple stmt) so this should almost always return a simplified RHS. */ tree lhs = gimple_assign_lhs (stmt); tree op0 = gimple_assign_rhs1 (stmt); - tree res; /* Simplify the operand down to a constant. */ if (TREE_CODE (op0) == SSA_NAME) @@ -977,20 +1054,8 @@ ccp_fold (gimple stmt) return op0; } - res = fold_unary (subcode, gimple_expr_type (stmt), op0); - - /* If the operation was a conversion do _not_ mark a - resulting constant with TREE_OVERFLOW if the original - constant was not. These conversions have implementation - defined behavior and retaining the TREE_OVERFLOW flag - here would confuse later passes such as VRP. */ - if (res - && TREE_CODE (res) == INTEGER_CST - && TREE_CODE (op0) == INTEGER_CST - && CONVERT_EXPR_CODE_P (subcode)) - TREE_OVERFLOW (res) = TREE_OVERFLOW (op0); - - return res; + return fold_unary_ignore_overflow (subcode, + gimple_expr_type (stmt), op0); } case GIMPLE_BINARY_RHS: @@ -1131,6 +1196,9 @@ fold_const_aggregate_ref (tree t) 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: @@ -1211,6 +1279,12 @@ fold_const_aggregate_ref (tree t) 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; @@ -1254,6 +1328,12 @@ fold_const_aggregate_ref (tree t) && ! 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; @@ -1273,7 +1353,9 @@ fold_const_aggregate_ref (tree t) if (TREE_CODE (base) == SSA_NAME && (value = get_value (base)) && value->lattice_val == CONSTANT - && TREE_CODE (value->value) == ADDR_EXPR) + && TREE_CODE (value->value) == ADDR_EXPR + && useless_type_conversion_p (TREE_TYPE (t), + TREE_TYPE (TREE_TYPE (value->value)))) return fold_const_aggregate_ref (TREE_OPERAND (value->value, 0)); break; } @@ -1560,7 +1642,7 @@ struct gimple_opt_pass pass_ccp = }; -/* 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. */ @@ -1929,8 +2011,7 @@ maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type) || (TREE_CODE (orig) == COMPONENT_REF && TREE_CODE (TREE_TYPE (TREE_OPERAND (orig, 1))) == ARRAY_TYPE)) && (TREE_CODE (t) == ARRAY_REF - || (TREE_CODE (t) == COMPONENT_REF - && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 1))) == ARRAY_TYPE)) + || TREE_CODE (t) == COMPONENT_REF) && !operand_equal_p (TREE_CODE (orig) == ARRAY_REF ? TREE_OPERAND (orig, 0) : orig, TREE_CODE (t) == ARRAY_REF @@ -1946,7 +2027,7 @@ maybe_fold_offset_to_address (tree addr, tree offset, tree orig_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 @@ -2063,14 +2144,47 @@ maybe_fold_stmt_addition (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 build1 (ADDR_EXPR, res_type, + 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 build1 (ADDR_EXPR, res_type, + 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) @@ -2132,175 +2246,64 @@ maybe_fold_stmt_addition (tree res_type, tree op0, tree op1) 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); + tree *t = &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; + if (TREE_CODE (expr) == ARRAY_REF + && !is_lhs) + { + tree tem = fold_read_from_constant_string (expr); + if (tem) + return tem; + } - /* ??? It'd be nice if walk_tree had a pre-order option. */ - switch (TREE_CODE (expr)) + /* ??? 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; + tree tem = fold (*t); + if (tem != *t) + return tem; + } + + while (handled_component_p (*t)) + t = &TREE_OPERAND (*t, 0); - t = maybe_fold_stmt_indirect (expr, TREE_OPERAND (expr, 0), - integer_zero_node); - if (!t - && TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR) + 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 (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; + tem = TREE_OPERAND (TREE_OPERAND (*t, 0), 0); - /* ??? 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. */ - if (!*inside_addr_expr_p) - 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; - } - - if (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; + 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 @@ -2496,6 +2499,9 @@ ccp_fold_builtin (gimple stmt) return NULL_TREE; } + if (arg_idx >= nargs) + return NULL_TREE; + /* Try to use the dataflow information gathered by the CCP process. */ visited = BITMAP_ALLOC (NULL); bitmap_clear (visited); @@ -2511,7 +2517,7 @@ ccp_fold_builtin (gimple stmt) switch (DECL_FUNCTION_CODE (callee)) { case BUILT_IN_STRLEN: - if (val[0]) + if (val[0] && nargs == 1) { tree new_val = fold_convert (TREE_TYPE (gimple_call_lhs (stmt)), val[0]); @@ -2543,22 +2549,24 @@ ccp_fold_builtin (gimple stmt) break; case BUILT_IN_FPUTS: - result = fold_builtin_fputs (gimple_call_arg (stmt, 0), - gimple_call_arg (stmt, 1), - ignore, false, val[0]); + if (nargs == 2) + result = fold_builtin_fputs (gimple_call_arg (stmt, 0), + gimple_call_arg (stmt, 1), + ignore, false, val[0]); break; case BUILT_IN_FPUTS_UNLOCKED: - result = fold_builtin_fputs (gimple_call_arg (stmt, 0), - gimple_call_arg (stmt, 1), - ignore, true, val[0]); + if (nargs == 2) + result = fold_builtin_fputs (gimple_call_arg (stmt, 0), + gimple_call_arg (stmt, 1), + ignore, true, val[0]); break; case BUILT_IN_MEMCPY_CHK: case BUILT_IN_MEMPCPY_CHK: case BUILT_IN_MEMMOVE_CHK: case BUILT_IN_MEMSET_CHK: - if (val[2] && is_gimple_val (val[2])) + if (val[2] && is_gimple_val (val[2]) && nargs == 4) result = fold_builtin_memory_chk (callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), @@ -2570,7 +2578,7 @@ ccp_fold_builtin (gimple stmt) case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: - if (val[1] && is_gimple_val (val[1])) + if (val[1] && is_gimple_val (val[1]) && nargs == 3) result = fold_builtin_stxcpy_chk (callee, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), @@ -2580,7 +2588,7 @@ ccp_fold_builtin (gimple stmt) break; case BUILT_IN_STRNCPY_CHK: - if (val[2] && is_gimple_val (val[2])) + if (val[2] && is_gimple_val (val[2]) && nargs == 4) result = fold_builtin_strncpy_chk (gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2), @@ -2615,23 +2623,80 @@ fold_gimple_assign (gimple_stmt_iterator *si) gimple stmt = gsi_stmt (*si); enum tree_code subcode = gimple_assign_rhs_code (stmt); - tree result = NULL; + 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; + + if (COMPARISON_CLASS_P (op0)) + { + 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, stmt, 0); + } + else if (is_gimple_min_invariant (op0)) + { + tem = op0; + set = true; + } + else + return NULL_TREE; + + if (set) + result = fold_build3 (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 (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)); + } + /* If we couldn't fold the RHS, hand over to the generic fold routines. */ if (result == NULL_TREE) @@ -2644,11 +2709,8 @@ fold_gimple_assign (gimple_stmt_iterator *si) 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; @@ -2689,10 +2751,19 @@ fold_gimple_assign (gimple_stmt_iterator *si) case GIMPLE_BINARY_RHS: /* Try to fold pointer addition. */ if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) - result = maybe_fold_stmt_addition ( - TREE_TYPE (gimple_assign_lhs (stmt)), - gimple_assign_rhs1 (stmt), - gimple_assign_rhs2 (stmt)); + { + tree type = TREE_TYPE (gimple_assign_rhs1 (stmt)); + if (TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE) + { + type = build_pointer_type (TREE_TYPE (TREE_TYPE (type))); + if (!useless_type_conversion_p + (TREE_TYPE (gimple_assign_lhs (stmt)), type)) + type = TREE_TYPE (gimple_assign_rhs1 (stmt)); + } + result = maybe_fold_stmt_addition (type, + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt)); + } if (!result) result = fold_binary (subcode, @@ -2810,125 +2881,130 @@ fold_gimple_call (gimple_stmt_iterator *gsi) 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. */ +/* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument + distinguishes both cases. */ -bool -fold_stmt (gimple_stmt_iterator *gsi) +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; } @@ -2990,14 +3066,9 @@ optimize_stack_restore (gimple_stmt_iterator i) 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)) - { - discard_stmt_changes (gsi_stmt_ptr (&stack_save_gsi)); - return NULL_TREE; - } - pop_stmt_changes (gsi_stmt_ptr (&stack_save_gsi)); + return NULL_TREE; /* No effect, so the statement will be deleted. */ return integer_zero_node; @@ -3125,11 +3196,16 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr) } 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); } @@ -3214,16 +3290,14 @@ execute_fold_all_builtins (void) } 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)) @@ -3269,7 +3343,7 @@ struct gimple_opt_pass pass_fold_builtins = 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 */