> On 10 Jun 2026, at 13:34, Kyrylo Tkachov <[email protected]> wrote: > > From: Kyrylo Tkachov <[email protected]> > > Add factor_out_conditional_load: it turns a diamond PHI <*P, *Q> whose two > arguments are single-use, non-volatile scalar MEM_REF loads reading the same > memory state into P' = PHI <P, Q> and a single load *P', reusing the PHI > result so its range info is preserved. No speculative load is introduced (the > load uses whichever pointer the taken edge selected). > > When the two arms' TBAA alias-ptr types differ, the merged load conservatively > falls back to ptr_type_node and drops the dependence clique/base, the way > get_alias_type_for_stmts does, rather than rejecting the transform. > > The helper is exported for the sink pass (next patch) to reuse. > > Bootstrapped and tested on aarch64-none-linux-gnu. >
I’ve found a bug with this patch (triggered by the follow-up patch). Will be sending out a fixed version soon. Thanks, Kyrill > Signed-off-by: Kyrylo Tkachov <[email protected]> > > PR tree-optimization/125557 > * tree-ssa-phiopt.h (factor_out_conditional_load): Declare. > * tree-ssa-phiopt.cc: Include "alias.h". > (remove_factored_arm_defs): New. > (factor_out_conditional_load): New. > --- > gcc/tree-ssa-phiopt.cc | 109 +++++++++++++++++++++++++++++++++++++++++ > gcc/tree-ssa-phiopt.h | 5 ++ > 2 files changed, 114 insertions(+) > > diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc > index dc6c33a930d..7100ff0e4f8 100644 > --- a/gcc/tree-ssa-phiopt.cc > +++ b/gcc/tree-ssa-phiopt.cc > @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see > #include "internal-fn.h" > #include "gimple-range.h" > #include "gimple-match.h" > +#include "alias.h" > #include "dbgcnt.h" > #include "tree-ssa-propagate.h" > #include "tree-ssa-dce.h" > @@ -417,6 +418,114 @@ commutative: > return -1; > } > > +/* Remove the two arm-defining statements D0 and D1 (D1 may be NULL, e.g. > when > + one PHI arm is a constant with no defining statement) once their results > have > + been factored into the merge. Used by factor_out_conditional_load below. > */ > + > +static void > +remove_factored_arm_defs (gimple *d0, gimple *d1) > +{ > + gimple_stmt_iterator gsi = gsi_for_stmt (d0); > + gsi_remove (&gsi, true); > + release_defs (d0); > + if (d1) > + { > + gsi = gsi_for_stmt (d1); > + gsi_remove (&gsi, true); > + release_defs (d1); > + } > +} > + > +/* If PHI at MERGE is a "load PHI" -- PHI <*P, *Q> whose two arguments are > + single-use, non-volatile scalar MEM_REF loads reading the same memory > state > + (same VUSE) -- factor the load out: introduce P' = PHI <P, Q> and a single > + load *P' replacing the PHI. No speculative load is introduced (the load > uses > + whichever pointer the taken edge selected). Together with operand > factoring > + this turns a conditional load into one reg-offset load. E0/E1 are the > + arm->MERGE edges. Returns true if the load was factored out. */ > + > +bool > +factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi) > +{ > + if (virtual_operand_p (gimple_phi_result (phi)) > + || gimple_phi_num_args (phi) != 2 > + || get_virtual_phi (merge)) > + return false; > + > + tree arg0 = gimple_phi_arg_def (phi, e0->dest_idx); > + tree arg1 = gimple_phi_arg_def (phi, e1->dest_idx); > + if (TREE_CODE (arg0) != SSA_NAME || TREE_CODE (arg1) != SSA_NAME > + || !has_single_use (arg0) || !has_single_use (arg1)) > + return false; > + > + gimple *d0 = SSA_NAME_DEF_STMT (arg0); > + gimple *d1 = SSA_NAME_DEF_STMT (arg1); > + if (!is_gimple_assign (d0) || !gimple_assign_load_p (d0) > + || !is_gimple_assign (d1) || !gimple_assign_load_p (d1) > + || gimple_has_volatile_ops (d0) || gimple_has_volatile_ops (d1) > + || gimple_vuse (d0) != gimple_vuse (d1)) > + return false; > + > + tree ref0 = gimple_assign_rhs1 (d0); > + tree ref1 = gimple_assign_rhs1 (d1); > + /* Both must be plain *P loads (zero offset) of a compatible value type. > The > + TBAA alias-ptr type carried by MEM_REF operand 1 need not match; it is > + merged below the way get_alias_type_for_stmts does. */ > + 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; > + > + tree p0 = TREE_OPERAND (ref0, 0); > + tree p1 = TREE_OPERAND (ref1, 0); > + if (!types_compatible_p (TREE_TYPE (p0), TREE_TYPE (p1))) > + return false; > + > + /* Build P' = PHI <P, Q> and the single load result = *P'. */ > + tree res = gimple_phi_result (phi); > + tree ptr = make_ssa_name (TREE_TYPE (p0)); > + gphi *pphi = create_phi_node (ptr, merge); > + add_phi_arg (pphi, p0, e0, gimple_phi_arg_location (phi, e0->dest_idx)); > + add_phi_arg (pphi, p1, e1, gimple_phi_arg_location (phi, e1->dest_idx)); > + > + /* 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), ptr, build_int_cst (atype, > 0)); > + MR_DEPENDENCE_CLIQUE (nref) = clique; > + MR_DEPENDENCE_BASE (nref) = base; > + gassign *load = gimple_build_assign (res, nref); > + gimple_set_vuse (load, gimple_vuse (d0)); > + 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. */ > + gsi = gsi_for_stmt (phi); > + gsi_remove (&gsi, true); > + > + /* The two arm loads are now dead. */ > + remove_factored_arm_defs (d0, d1); > + > + statistics_counter_event (cfun, "factored load out of COND_EXPR", 1); > + return true; > +} > + > /* PR66726: Factor operations out of COND_EXPR. If the arguments of the PHI > stmt are Unary operator, factor out the operation and perform the operation > to the result of PHI stmt. COND_STMT is the controlling predicate. > diff --git a/gcc/tree-ssa-phiopt.h b/gcc/tree-ssa-phiopt.h > index bba3136965d..fb585c81b26 100644 > --- a/gcc/tree-ssa-phiopt.h > +++ b/gcc/tree-ssa-phiopt.h > @@ -21,4 +21,9 @@ along with GCC; see the file COPYING3. If not see > extern int find_different_opnum (const gimple_match_op &arg0_op, > const gimple_match_op &arg1_op, > tree *new_arg0, tree *new_arg1); > + > +/* Factor a load out of a "load PHI" PHI <*P, *Q> into P' = PHI <P, Q>; > + result = *P'. */ > +extern bool factor_out_conditional_load (edge e0, edge e1, basic_block merge, > + gphi *phi); > #endif > -- > 2.50.1 (Apple Git-155) >
