https://gcc.gnu.org/g:cf64e6b0e07355043d26a9d17ad749aa42b1aa9b
commit r17-1705-gcf64e6b0e07355043d26a9d17ad749aa42b1aa9b Author: Andrew Pinski <[email protected]> Date: Wed Jun 17 16:09:40 2026 -0700 phiopt: Add support for non-zero offsets for load factoring [PR122193] This extends the factor_out_conditional_load to support non-zero offsets for the MEM_REF. And fixes PR 122193. It tries not to create gimple statements that don't need to be created. E.g. If the base is the same then it creates the pointer plus in the join bb rather than the in the middle bbs. Also uses gimple_build for the pointer plus so that the if one of the offsets was 0, a new statement is not created. Bootstrapped and tested on x86_64-linux-gnu. PR tree-optimization/122193 gcc/ChangeLog: * tree-ssa-phiopt.cc (factor_out_conditional_load): Support non-zero offsets. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/factor_op_phi-load-offsets-1.c: New test. * gcc.dg/tree-ssa/factor_op_phi-load-offsets-2.c: New test. * gcc.dg/tree-ssa/factor_op_phi-load-offsets-3.c: New test. Signed-off-by: Andrew Pinski <[email protected]> Diff: --- .../gcc.dg/tree-ssa/factor_op_phi-load-offsets-1.c | 11 +++ .../gcc.dg/tree-ssa/factor_op_phi-load-offsets-2.c | 13 +++ .../gcc.dg/tree-ssa/factor_op_phi-load-offsets-3.c | 13 +++ gcc/tree-ssa-phiopt.cc | 106 +++++++++++++++++---- 4 files changed, 122 insertions(+), 21 deletions(-) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-1.c b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-1.c new file mode 100644 index 000000000000..95b36d192f5e --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-phiopt-details" } */ + +/* Support offsets that are different but the same base. */ +int test2(const int* data, bool b) { + if (b) + return data[1]; + return data[0]; +} +/* { dg-final { scan-tree-dump-times "was created for the index." 1 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times "changed to factor out load from" 1 "phiopt2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-2.c b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-2.c new file mode 100644 index 000000000000..79265ea044e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-2.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-phiopt-details" } */ + +/* Support different offsets and different bases. */ + +int test2(const int* data, const int *data1, bool b) { + if (b) + return data1[1]; + return data[2]; +} +/* { dg-final { scan-tree-dump-times "was created due to different offsets" 1 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times "changed to factor out load from" 1 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times "was created for the pointers" 1 "phiopt2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-3.c b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-3.c new file mode 100644 index 000000000000..ca652c2a30be --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/factor_op_phi-load-offsets-3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-phiopt-details" } */ + +/* Support of non-0 offsets. */ + +int test2(const int* data, const int *data1, bool b) { + if (b) + return data1[1]; + return data[1]; +} +/* { dg-final { scan-tree-dump-not "was created due to different offsets" "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times "changed to factor out load from" 1 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times "was created for the pointers" 1 "phiopt2" } } */ diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc index c6289c037588..62e4765c392c 100644 --- a/gcc/tree-ssa-phiopt.cc +++ b/gcc/tree-ssa-phiopt.cc @@ -3718,12 +3718,10 @@ factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi, tree ref0 = gimple_assign_rhs1 (load0); tree ref1 = gimple_assign_rhs1 (load1); - /* Both must be plain *P loads (zero offset) of a compatible value type. The + /* Both must be *P loads of a compatible value type. The TBAA alias-ptr type carried by MEM_REF operand 1 need not match; it is merged the way get_alias_type_for_stmts does when the load is built. */ if (TREE_CODE (ref0) != MEM_REF || TREE_CODE (ref1) != MEM_REF - || !integer_zerop (TREE_OPERAND (ref0, 1)) - || !integer_zerop (TREE_OPERAND (ref1, 1)) || !types_compatible_p (TREE_TYPE (ref0), TREE_TYPE (ref1))) return false; @@ -3738,6 +3736,89 @@ factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi, if (!is_factor_profitable (load1, merge, &p1, 1)) return false; + /* Merge the two arms' TBAA info as get_alias_type_for_stmts does: keep the + common alias-ptr type and dependence clique/base when the arms agree, + otherwise fall back to ptr_type_node (alias-everything) and drop the + clique/base, so the combined load conservatively conflicts with any store + either original arm could. */ + unsigned short clique = MR_DEPENDENCE_CLIQUE (ref0); + unsigned short base = MR_DEPENDENCE_BASE (ref0); + if (clique != MR_DEPENDENCE_CLIQUE (ref1) || base != MR_DEPENDENCE_BASE (ref1)) + clique = base = 0; + tree atype = TREE_TYPE (TREE_OPERAND (ref0, 1)); + if (!alias_ptr_types_compatible_p (atype, TREE_TYPE (TREE_OPERAND (ref1, 1)))) + { + atype = ptr_type_node; + clique = base = 0; + } + + tree index0 = TREE_OPERAND (ref0, 1); + tree index1 = TREE_OPERAND (ref1, 1); + tree newindex; + gimple_stmt_iterator gsi; + gsi = gsi_after_labels (merge); + + /* Try to handle different indices. */ + if (operand_equal_p (index0, index1)) + newindex = fold_convert (atype, index0); + /* FIXME: right now non ssa names with different indices are not handled. */ + else if (TREE_CODE (p0) != SSA_NAME || TREE_CODE (p1) != SSA_NAME) + return false; + /* If we have the same base already, just create a phi for the index + and the pointer plus will be done in the merge. */ + else if (p0 == p1) + { + index0 = fold_convert (sizetype, index0); + index1 = fold_convert (sizetype, index1); + tree index = make_ssa_name (sizetype); + gphi *pphi = create_phi_node (index, merge); + add_phi_arg (pphi, index0, e0, gimple_phi_arg_location (phi, e0->dest_idx)); + add_phi_arg (pphi, index1, e1, gimple_phi_arg_location (phi, e1->dest_idx)); + p0 = gimple_build (&gsi, true, GSI_SAME_STMT, + UNKNOWN_LOCATION, + POINTER_PLUS_EXPR, atype, p0, index); + /* Since we already have the same pointer for both, just set that way. + Also the index offset is already 0 because we just did the add. */ + p1 = p0; + newindex = build_zero_cst (atype); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "new PHI "); + print_generic_expr (dump_file, index); + fprintf (dump_file, + " was created for the index.\n"); + } + } + else + { + gimple_stmt_iterator gsi_index; + /* When the indices are different create 2 new pointers on each + of the middle bb right after the original load. + Note in the case of 0 index, gimple_build just returns + the original pointer. */ + gsi_index = gsi_for_stmt (load0); + index0 = fold_convert (sizetype, index0); + p0 = gimple_build (&gsi_index, false, GSI_SAME_STMT, + gimple_location (load0), + POINTER_PLUS_EXPR, atype, p0, index0); + + gsi_index = gsi_for_stmt (load1); + index1 = fold_convert (sizetype, index1); + p1 = gimple_build (&gsi_index, false, GSI_SAME_STMT, + gimple_location (load1), + POINTER_PLUS_EXPR, atype, p1, index1); + newindex = build_zero_cst (atype); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "new ptrs "); + print_generic_expr (dump_file, p0); + fprintf (dump_file, " and "); + print_generic_expr (dump_file, p1); + fprintf (dump_file, + " was created due to different offsets.\n"); + } + } + tree newptr; if (p0 != p1) { @@ -3760,25 +3841,9 @@ factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi, else newptr = p0; - /* Merge the two arms' TBAA info as get_alias_type_for_stmts does: keep the - common alias-ptr type and dependence clique/base when the arms agree, - otherwise fall back to ptr_type_node (alias-everything) and drop the - clique/base, so the combined load conservatively conflicts with any store - either original arm could. */ - unsigned short clique = MR_DEPENDENCE_CLIQUE (ref0); - unsigned short base = MR_DEPENDENCE_BASE (ref0); - if (clique != MR_DEPENDENCE_CLIQUE (ref1) || base != MR_DEPENDENCE_BASE (ref1)) - clique = base = 0; - tree atype = TREE_TYPE (TREE_OPERAND (ref0, 1)); - if (!alias_ptr_types_compatible_p (atype, TREE_TYPE (TREE_OPERAND (ref1, 1)))) - { - atype = ptr_type_node; - clique = base = 0; - } - /* Build the combined load RES = *PTR, reusing the PHI result so any range info on it is preserved (as factor_out_conditional_operation does). */ - tree nref = build2 (MEM_REF, TREE_TYPE (ref0), newptr, build_int_cst (atype, 0)); + tree nref = build2 (MEM_REF, TREE_TYPE (ref0), newptr, newindex); MR_DEPENDENCE_CLIQUE (nref) = clique; MR_DEPENDENCE_BASE (nref) = base; tree res = gimple_phi_result (phi); @@ -3787,7 +3852,6 @@ factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi, gimple_set_vuse (load, gimple_phi_result (vphi)); else gimple_set_vuse (load, gimple_vuse (load0)); - gimple_stmt_iterator gsi = gsi_after_labels (merge); gsi_insert_before (&gsi, load, GSI_SAME_STMT); /* RES is now defined by the load; drop the original PHI. */
