This is a first step towards re-gimplifying statements with propagated addresses in the core propagation routines that already deal (well, some of them...) with computing invariantness.
The patch moves all such re-gimplification before any simplifications are performed (which means once "fixed" this can turn into asserts only). This makes it possible for match-and-simplify to wire in before all existing manual foldings (well, more easily at least). Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. I couldn't resist fixing a possible wrong-code issue wrt dropping alignment. I will explore pushing this re-gimplification into the propagation places / helpers instead as followups. Not sure if it is worth retaining other folding for GIMPLE_DEBUGs or GIMPLE_ASMs (what remains is folding from constant initializers). Richard. 2014-08-18 Richard Biener <rguent...@suse.de> * gimple-fold.c (maybe_fold_reference): Move re-gimplification code to ... (maybe_canonicalize_mem_ref_addr): ... this function. (fold_stmt_1): Apply it here before all simplification. Index: gcc/gimple-fold.c =================================================================== --- gcc/gimple-fold.c (revision 214085) +++ gcc/gimple-fold.c (working copy) @@ -256,7 +256,6 @@ get_symbol_constant_value (tree sym) static tree maybe_fold_reference (tree expr, bool is_lhs) { - tree *t = &expr; tree result; if ((TREE_CODE (expr) == VIEW_CONVERT_EXPR @@ -276,71 +275,11 @@ maybe_fold_reference (tree expr, bool is TREE_OPERAND (expr, 1), TREE_OPERAND (expr, 2)); - while (handled_component_p (*t)) - t = &TREE_OPERAND (*t, 0); - - /* Canonicalize MEM_REFs invariant address operand. Do this first - to avoid feeding non-canonical MEM_REFs elsewhere. */ - if (TREE_CODE (*t) == MEM_REF - && !is_gimple_mem_ref_addr (TREE_OPERAND (*t, 0))) - { - bool volatile_p = TREE_THIS_VOLATILE (*t); - tree tem = fold_binary (MEM_REF, TREE_TYPE (*t), - TREE_OPERAND (*t, 0), - TREE_OPERAND (*t, 1)); - if (tem) - { - TREE_THIS_VOLATILE (tem) = volatile_p; - *t = tem; - tem = maybe_fold_reference (expr, is_lhs); - if (tem) - return tem; - return expr; - } - } - if (!is_lhs && (result = fold_const_aggregate_ref (expr)) && is_gimple_min_invariant (result)) return result; - /* Fold back MEM_REFs to reference trees. */ - if (TREE_CODE (*t) == MEM_REF - && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR - && integer_zerop (TREE_OPERAND (*t, 1)) - && (TREE_THIS_VOLATILE (*t) - == TREE_THIS_VOLATILE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0))) - && !TYPE_REF_CAN_ALIAS_ALL (TREE_TYPE (TREE_OPERAND (*t, 1))) - && (TYPE_MAIN_VARIANT (TREE_TYPE (*t)) - == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_OPERAND (*t, 1))))) - /* We have to look out here to not drop a required conversion - from the rhs to the lhs if is_lhs, but we don't have the - rhs here to verify that. Thus require strict type - compatibility. */ - && types_compatible_p (TREE_TYPE (*t), - TREE_TYPE (TREE_OPERAND - (TREE_OPERAND (*t, 0), 0)))) - { - tree tem; - *t = TREE_OPERAND (TREE_OPERAND (*t, 0), 0); - tem = maybe_fold_reference (expr, is_lhs); - if (tem) - return tem; - return expr; - } - else if (TREE_CODE (*t) == TARGET_MEM_REF) - { - tree tem = maybe_fold_tmr (*t); - if (tem) - { - *t = tem; - tem = maybe_fold_reference (expr, is_lhs); - if (tem) - return tem; - return expr; - } - } - return NULL_TREE; } @@ -2678,6 +2755,86 @@ gimple_fold_call (gimple_stmt_iterator * return changed; } +/* Canonicalize MEM_REFs invariant address operand after propagation. */ + +static bool +maybe_canonicalize_mem_ref_addr (tree *t) +{ + bool res = false; + + if (TREE_CODE (*t) == ADDR_EXPR) + t = &TREE_OPERAND (*t, 0); + + while (handled_component_p (*t)) + t = &TREE_OPERAND (*t, 0); + + /* Canonicalize MEM [&foo.bar, 0] which appears after propagating + of invariant addresses into a SSA name MEM_REF address. */ + if (TREE_CODE (*t) == MEM_REF + || TREE_CODE (*t) == TARGET_MEM_REF) + { + tree addr = TREE_OPERAND (*t, 0); + if (TREE_CODE (addr) == ADDR_EXPR + && handled_component_p (TREE_OPERAND (addr, 0))) + { + tree base; + HOST_WIDE_INT coffset; + base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0), + &coffset); + if (!base) + gcc_unreachable (); + + TREE_OPERAND (*t, 0) = build_fold_addr_expr (base); + TREE_OPERAND (*t, 1) = int_const_binop (PLUS_EXPR, + TREE_OPERAND (*t, 1), + size_int (coffset)); + res = true; + } + gcc_checking_assert (is_gimple_mem_ref_addr (TREE_OPERAND (*t, 0))); + } + + /* Canonicalize back MEM_REFs to plain reference trees if the object + accessed is a decl that has the same access semantics as the MEM_REF. */ + if (TREE_CODE (*t) == MEM_REF + && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR + && integer_zerop (TREE_OPERAND (*t, 1))) + { + tree decl = TREE_OPERAND (TREE_OPERAND (*t, 0), 0); + tree alias_type = TREE_TYPE (TREE_OPERAND (*t, 1)); + if (/* Same volatile qualification. */ + TREE_THIS_VOLATILE (*t) == TREE_THIS_VOLATILE (decl) + /* Same TBAA behavior with -fstrict-aliasing. */ + && !TYPE_REF_CAN_ALIAS_ALL (alias_type) + && (TYPE_MAIN_VARIANT (TREE_TYPE (decl)) + == TYPE_MAIN_VARIANT (TREE_TYPE (alias_type))) + /* Same alignment. */ + && TYPE_ALIGN (TREE_TYPE (decl)) == TYPE_ALIGN (TREE_TYPE (*t)) + /* We have to look out here to not drop a required conversion + from the rhs to the lhs if *t appears on the lhs or vice-versa + if it appears on the rhs. Thus require strict type + compatibility. */ + && types_compatible_p (TREE_TYPE (*t), TREE_TYPE (decl))) + { + *t = TREE_OPERAND (TREE_OPERAND (*t, 0), 0); + res = true; + } + } + + /* Canonicalize TARGET_MEM_REF in particular with respect to + the indexes becoming constant. */ + else if (TREE_CODE (*t) == TARGET_MEM_REF) + { + tree tem = maybe_fold_tmr (*t); + if (tem) + { + *t = tem; + res = true; + } + } + + return res; +} + /* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument distinguishes both cases. */ @@ -2688,6 +2845,78 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, gimple stmt = gsi_stmt (*gsi); unsigned i; + /* First do required canonicalization of [TARGET_]MEM_REF addresses + after propagation. + ??? This shouldn't be done in generic folding but in the + propagation helpers which also know whether an address was + propagated. */ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) + { + tree *rhs = gimple_assign_rhs1_ptr (stmt); + if ((REFERENCE_CLASS_P (*rhs) + || TREE_CODE (*rhs) == ADDR_EXPR) + && maybe_canonicalize_mem_ref_addr (rhs)) + changed = true; + tree *lhs = gimple_assign_lhs_ptr (stmt); + if (REFERENCE_CLASS_P (*lhs) + && maybe_canonicalize_mem_ref_addr (lhs)) + changed = true; + } + break; + case GIMPLE_CALL: + { + for (i = 0; i < gimple_call_num_args (stmt); ++i) + { + tree *arg = gimple_call_arg_ptr (stmt, i); + if (REFERENCE_CLASS_P (*arg) + && maybe_canonicalize_mem_ref_addr (arg)) + changed = true; + } + tree *lhs = gimple_call_lhs_ptr (stmt); + if (*lhs + && REFERENCE_CLASS_P (*lhs) + && maybe_canonicalize_mem_ref_addr (lhs)) + changed = true; + break; + } + case GIMPLE_ASM: + { + 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) + && maybe_canonicalize_mem_ref_addr (&TREE_VALUE (link))) + 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) + || TREE_CODE (op) == ADDR_EXPR) + && maybe_canonicalize_mem_ref_addr (&TREE_VALUE (link))) + changed = true; + } + } + break; + case GIMPLE_DEBUG: + if (gimple_debug_bind_p (stmt)) + { + tree *val = gimple_debug_bind_get_value_ptr (stmt); + if (*val + && (REFERENCE_CLASS_P (*val) + || TREE_CODE (*val) == ADDR_EXPR) + && maybe_canonicalize_mem_ref_addr (val)) + changed = true; + } + break; + default:; + } + /* Fold the main computation performed by the statement. */ switch (gimple_code (stmt)) {