diff -Nur gcc-4.8.2.orig/gcc/tree-ssa-forwprop.c gcc-4.8.2/gcc/tree-ssa-forwprop.c --- gcc-4.8.2.orig/gcc/tree-ssa-forwprop.c 2013-02-25 16:31:31.000000000 +0100 +++ gcc-4.8.2/gcc/tree-ssa-forwprop.c 2014-03-22 19:37:04.797991879 +0100 @@ -688,6 +688,130 @@ recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt)); } + /* DEF_RHS contains the address of the 0th element in an array. + USE_STMT uses type of DEF_RHS to compute the address of an + arbitrary element within the array. The (variable) byte offset + of the element is contained in OFFSET. + + We walk back through the use-def chains of OFFSET to verify that + it is indeed computing the offset of an element within the array + and extract the index corresponding to the given byte offset. + + We then try to fold the entire address expression into a form + &array[index]. + + If we are successful, we replace the right hand side of USE_STMT + with the new address computation. */ + + static bool + forward_propagate_addr_into_variable_array_index (tree offset, + tree def_rhs, + gimple_stmt_iterator *use_stmt_gsi) + { + tree index, tunit; + gimple offset_def, use_stmt = gsi_stmt (*use_stmt_gsi); + tree new_rhs, tmp; + + if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF) + tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (def_rhs))); + else if (TREE_CODE (TREE_TYPE (TREE_OPERAND (def_rhs, 0))) == ARRAY_TYPE) + tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs)))); + else + return false; + if (!host_integerp (tunit, 1)) + return false; + + /* Get the offset's defining statement. */ + offset_def = SSA_NAME_DEF_STMT (offset); + + /* Try to find an expression for a proper index. This is either a + multiplication expression by the element size or just the ssa name we came + along in case the element size is one. In that case, however, we do not + allow multiplications because they can be computing index to a higher + level dimension (PR 37861). */ + if (integer_onep (tunit)) + { + if (is_gimple_assign (offset_def) + && gimple_assign_rhs_code (offset_def) == MULT_EXPR) + return false; + + index = offset; + } + else + { + /* The statement which defines OFFSET before type conversion + must be a simple GIMPLE_ASSIGN. */ + if (!is_gimple_assign (offset_def)) + return false; + + /* The RHS of the statement which defines OFFSET must be a + multiplication of an object by the size of the array elements. + This implicitly verifies that the size of the array elements + is constant. */ + 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), tunit)) + { + /* The first operand to the MULT_EXPR is the desired index. */ + index = gimple_assign_rhs1 (offset_def); + } + /* If we have idx * tunit + CST * tunit re-associate that. */ + else if ((gimple_assign_rhs_code (offset_def) == PLUS_EXPR + || gimple_assign_rhs_code (offset_def) == MINUS_EXPR) + && TREE_CODE (gimple_assign_rhs1 (offset_def)) == SSA_NAME + && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST + && (tmp = div_if_zero_remainder (EXACT_DIV_EXPR, + gimple_assign_rhs2 (offset_def), + tunit)) != NULL_TREE) + { + gimple offset_def2 = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (offset_def)); + if (is_gimple_assign (offset_def2) + && gimple_assign_rhs_code (offset_def2) == MULT_EXPR + && TREE_CODE (gimple_assign_rhs2 (offset_def2)) == INTEGER_CST + && tree_int_cst_equal (gimple_assign_rhs2 (offset_def2), tunit)) + { + index = fold_build2 (gimple_assign_rhs_code (offset_def), + TREE_TYPE (offset), + gimple_assign_rhs1 (offset_def2), tmp); + } + else + return false; + } + else + return false; + } + + /* Replace the pointer addition with array indexing. */ + index = force_gimple_operand_gsi (use_stmt_gsi, index, true, NULL_TREE, + true, GSI_SAME_STMT); + if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF) + { + new_rhs = unshare_expr (def_rhs); + TREE_OPERAND (TREE_OPERAND (new_rhs, 0), 1) = index; + } + else + { + new_rhs = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs))), + unshare_expr (TREE_OPERAND (def_rhs, 0)), + index, integer_zero_node, NULL_TREE); + new_rhs = build_fold_addr_expr (new_rhs); + if (!useless_type_conversion_p (TREE_TYPE (gimple_assign_lhs (use_stmt)), + TREE_TYPE (new_rhs))) + { + new_rhs = force_gimple_operand_gsi (use_stmt_gsi, new_rhs, true, + NULL_TREE, true, GSI_SAME_STMT); + new_rhs = fold_convert (TREE_TYPE (gimple_assign_lhs (use_stmt)), + new_rhs); + } + } + gimple_assign_set_rhs_from_tree (use_stmt_gsi, new_rhs); + fold_stmt (use_stmt_gsi); + tidy_after_forward_propagate_addr (gsi_stmt (*use_stmt_gsi)); + return true; + } + + + /* NAME is a SSA_NAME representing DEF_RHS which is of the form ADDR_EXPR . @@ -977,6 +1101,19 @@ tidy_after_forward_propagate_addr (use_stmt); return true; } + /* Try to optimize &x[0] p+ OFFSET where OFFSET is defined by + converting a multiplication of an index by the size of the + array elements, then the result is converted into the proper + type for the arithmetic. */ + if (TREE_CODE (rhs2) == SSA_NAME + && (TREE_CODE (array_ref) != ARRAY_REF + || integer_zerop (TREE_OPERAND (array_ref, 1))) + && useless_type_conversion_p (TREE_TYPE (name), TREE_TYPE (def_rhs)) + /* Avoid problems with IVopts creating PLUS_EXPRs with a + different type than their operands. */ + && useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (def_rhs))) + return forward_propagate_addr_into_variable_array_index (rhs2, def_rhs, + use_stmt_gsi); return false; }