Re: [RFC] aarch64: Split selected LDPs to improve store forwarding

Alex Coplan <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
On 16/08/2026 20:51, Andrea Pinski wrote:
> On Sun, Aug 16, 2026 at 8:17 PM huzife <[email protected]> wrote:
> >
> > A load pair that partially overlaps a recent scalar store can prevent the
> > load from using store-to-load forwarding.  Splitting the pair lets the
> > overlapping half use the forwarding path.
> >
> > Add a late AArch64 RTL pass that handles non-writeback SI and DI GPR load
> > pairs.  Search a bounded part of the containing basic block for a scalar
> > store to either half, and replace the pair only when both scalar loads are
> > recognized.  Reject volatile, frame-related and exception-sensitive forms,
> > and disable the transformation for size optimization.  Keep the pass off by
> > default behind a target parameter while its profitability is evaluated.
> 
> 
> I am not sure we want this.  Is this dependent on the micro-arch? Can
> you expand on which micro-arch where this helps out?
> Also shouldn't instead of a separate pass which undoes what an earlier
> pass. Won't it be better to improve the heuristics of ldp_fusion pass
> to reject this from happening in the first place for these cases?

I agree with everything Andrea said here, it would be good to know which
u-arches are expected to benefit, and potentially which workloads show a
benefit with this transformation.

If we do decide to to do this, I agree that it would be better to try
and prevent the pairs from being formed in the first place (i.e. in
pair-fusion) if possible.

Thanks,
Alex

> 
> >
> > gcc/ChangeLog:
> >
> >         * config.gcc (aarch64*-*-*): Add aarch64-ldp-split.o.
> >         * config/aarch64/aarch64-ldp-split.cc: New file.
> >         * config/aarch64/aarch64-passes.def: Insert pass_split_ldp_stlf before
> >         pass_sched2.
> >         * config/aarch64/aarch64-protos.h (make_pass_split_ldp_stlf): Declare.
> >         * config/aarch64/aarch64.opt: Add aarch64-split-ldp-stlf parameter.
> >         * config/aarch64/t-aarch64 (aarch64-ldp-split.o): New rule.
> >         * doc/params.texi (aarch64-split-ldp-stlf): Document.
> >
> > gcc/testsuite/ChangeLog:
> >
> >         * gcc.target/aarch64/split-ldp-stlf-1.c: New test.
> >         * gcc.target/aarch64/split-ldp-stlf-2.c: New test.
> >         * gcc.target/aarch64/split-ldp-stlf-3.c: New test.
> >         * gcc.target/aarch64/split-ldp-stlf-4.c: New test.
> >         * gcc.target/aarch64/split-ldp-stlf-5.c: New test.
> >         * gcc.target/aarch64/split-ldp-stlf-6.c: New test.
> >
> > Signed-off-by: huzife <[email protected]>
> > ---
> >  gcc/config.gcc                                |   1 +
> >  gcc/config/aarch64/aarch64-ldp-split.cc       | 316 ++++++++++++++++++
> >  gcc/config/aarch64/aarch64-passes.def         |   1 +
> >  gcc/config/aarch64/aarch64-protos.h           |   1 +
> >  gcc/config/aarch64/aarch64.opt                |   5 +
> >  gcc/config/aarch64/t-aarch64                  |   6 +
> >  gcc/doc/params.texi                           |   8 +
> >  .../gcc.target/aarch64/split-ldp-stlf-1.c     |  43 +++
> >  .../gcc.target/aarch64/split-ldp-stlf-2.c     |  13 +
> >  .../gcc.target/aarch64/split-ldp-stlf-3.c     |  22 ++
> >  .../gcc.target/aarch64/split-ldp-stlf-4.c     |  14 +
> >  .../gcc.target/aarch64/split-ldp-stlf-5.c     |  13 +
> >  .../gcc.target/aarch64/split-ldp-stlf-6.c     |  13 +
> >  13 files changed, 456 insertions(+)
> >  create mode 100644 gcc/config/aarch64/aarch64-ldp-split.cc
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-1.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-2.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-3.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-4.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-5.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-6.c
> >
> > diff --git a/gcc/config.gcc b/gcc/config.gcc
> > index 0477bf22c8a..00aa965f162 100644
> > --- a/gcc/config.gcc
> > +++ b/gcc/config.gcc
> > @@ -362,6 +362,7 @@ aarch64*-*-*)
> >         extra_objs="${extra_objs} aarch-bti-insert.o"
> >         extra_objs="${extra_objs} aarch64-early-ra.o"
> >         extra_objs="${extra_objs} aarch64-ldp-fusion.o"
> > +       extra_objs="${extra_objs} aarch64-ldp-split.o"
> >         extra_objs="${extra_objs} aarch64-sched-dispatch.o"
> >         extra_objs="${extra_objs} aarch64-json-tunings-printer.o"
> >         extra_objs="${extra_objs} aarch64-json-tunings-parser.o"
> > diff --git a/gcc/config/aarch64/aarch64-ldp-split.cc b/gcc/config/aarch64/aarch64-ldp-split.cc
> > new file mode 100644
> > index 00000000000..a4c2c159529
> > --- /dev/null
> > +++ b/gcc/config/aarch64/aarch64-ldp-split.cc
> > @@ -0,0 +1,316 @@
> > +/* Split selected load pairs to improve store-to-load forwarding on AArch64.
> > +   Copyright (C) 2026 Free Software Foundation, Inc.
> > +
> > +   This file is part of GCC.
> > +
> > +   GCC is free software; you can redistribute it and/or modify it under
> > +   the terms of the GNU General Public License as published by the Free
> > +   Software Foundation; either version 3, or (at your option) any later
> > +   version.
> > +
> > +   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> > +   WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > +   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> > +   for more details.
> > +
> > +   You should have received a copy of the GNU General Public License
> > +   along with GCC; see the file COPYING3.  If not see
> > +   <http://www.gnu.org/licenses/>.  */
> > +
> > +#define IN_TARGET_CODE 1
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +#include "backend.h"
> > +#include "rtl.h"
> > +#include "df.h"
> > +#include "memmodel.h"
> > +#include "emit-rtl.h"
> > +#include "tree-pass.h"
> > +#include "insn-attr.h"
> > +#include "insn-constants.h"
> > +#include "insn-config.h"
> > +#include "recog.h"
> > +#include "cfgrtl.h"
> > +#include "predict.h"
> > +
> > +namespace {
> > +
> > +/* A non-writeback GPR load pair that is safe for this pass to split.  */
> > +struct ldp_info
> > +{
> > +  rtx dests[2];
> > +  rtx mem;
> > +  rtx base;
> > +  machine_mode mode;
> > +  HOST_WIDE_INT offset;
> > +  HOST_WIDE_INT access_size;
> > +};
> > +
> > +/* Return true if REG is a hard general-purpose register in MODE.  */
> > +
> > +static bool
> > +gpr_operand_p (rtx reg, machine_mode mode)
> > +{
> > +  return (REG_P (reg)
> > +         && HARD_REGISTER_P (reg)
> > +         && GP_REGNUM_P (REGNO (reg))
> > +         && GET_MODE (reg) == mode);
> > +}
> > +
> > +/* Extract the MEM from one half of an AArch64 load pair.  */
> > +
> > +static rtx
> > +extract_ldp_mem (rtx set, rtx dest, int unspec_code)
> > +{
> > +  if (GET_CODE (set) != SET || !rtx_equal_p (SET_DEST (set), dest))
> > +    return NULL_RTX;
> > +
> > +  rtx src = SET_SRC (set);
> > +  if (GET_CODE (src) != UNSPEC
> > +      || XINT (src, 1) != unspec_code
> > +      || XVECLEN (src, 0) != 1)
> > +    return NULL_RTX;
> > +
> > +  rtx mem = XVECEXP (src, 0, 0);
> > +  return MEM_P (mem) ? mem : NULL_RTX;
> > +}
> > +
> > +/* If INSN is an ordinary, non-writeback SI or DI GPR LDP, describe it in
> > +   INFO and return true.  Deliberately reject FP/SIMD, TImode, volatile,
> > +   frame-related, and EH-sensitive forms.  */
> > +
> > +static bool
> > +extract_ldp (rtx_insn *insn, ldp_info *info)
> > +{
> > +  if (!NONDEBUG_INSN_P (insn)
> > +      || INSN_CODE (insn) < 0
> > +      || get_attr_ldpstp (insn) != LDPSTP_LDP
> > +      || RTX_FRAME_RELATED_P (insn)
> > +      || find_reg_note (insn, REG_EH_REGION, NULL_RTX))
> > +    return false;
> > +
> > +  rtx pattern = PATTERN (insn);
> > +  if (GET_CODE (pattern) != PARALLEL || XVECLEN (pattern, 0) != 2)
> > +    return false;
> > +
> > +  rtx sets[2] = { XVECEXP (pattern, 0, 0), XVECEXP (pattern, 0, 1) };
> > +  if (GET_CODE (sets[0]) != SET || GET_CODE (sets[1]) != SET)
> > +    return false;
> > +
> > +  info->dests[0] = SET_DEST (sets[0]);
> > +  info->dests[1] = SET_DEST (sets[1]);
> > +  info->mode = GET_MODE (info->dests[0]);
> > +  if ((info->mode != SImode && info->mode != DImode)
> > +      || !gpr_operand_p (info->dests[0], info->mode)
> > +      || !gpr_operand_p (info->dests[1], info->mode)
> > +      || reg_overlap_mentioned_p (info->dests[0], info->dests[1]))
> > +    return false;
> > +
> > +  rtx mem0 = extract_ldp_mem (sets[0], info->dests[0], UNSPEC_LDP_FST);
> > +  rtx mem1 = extract_ldp_mem (sets[1], info->dests[1], UNSPEC_LDP_SND);
> > +  if (!mem0
> > +      || !mem1
> > +      || !rtx_equal_p (mem0, mem1)
> > +      || MEM_VOLATILE_P (mem0)
> > +      || side_effects_p (XEXP (mem0, 0))
> > +      || !MEM_SIZE_KNOWN_P (mem0)
> > +      || !MEM_SIZE (mem0).is_constant ())
> > +    return false;
> > +
> > +  info->access_size = GET_MODE_SIZE (info->mode).to_constant ();
> > +  if (!known_eq (MEM_SIZE (mem0), 2 * info->access_size))
> > +    return false;
> > +
> > +  poly_int64 offset;
> > +  info->base = strip_offset (XEXP (mem0, 0), &offset);
> > +  if (!REG_P (info->base) || !offset.is_constant (&info->offset))
> > +    return false;
> > +
> > +  info->mem = mem0;
> > +  return true;
> > +}
> > +
> > +/* Return true if INSN is a nonvolatile GPR store of one LDP half, using the
> > +   same unmodified base as INFO and writing exactly the low or high half.  */
> > +
> > +static bool
> > +forwarding_store_p (rtx_insn *insn, const ldp_info &info)
> > +{
> > +  if (!NONDEBUG_INSN_P (insn) || RTX_FRAME_RELATED_P (insn))
> > +    return false;
> > +
> > +  rtx set = single_set (insn);
> > +  if (!set || !MEM_P (SET_DEST (set)))
> > +    return false;
> > +
> > +  rtx mem = SET_DEST (set);
> > +  rtx value = SET_SRC (set);
> > +  if (GET_MODE (mem) != info.mode
> > +      || !gpr_operand_p (value, info.mode)
> > +      || MEM_VOLATILE_P (mem)
> > +      || side_effects_p (XEXP (mem, 0))
> > +      || !MEM_SIZE_KNOWN_P (mem)
> > +      || !known_eq (MEM_SIZE (mem), info.access_size))
> > +    return false;
> > +
> > +  poly_int64 poly_offset;
> > +  rtx base = strip_offset (XEXP (mem, 0), &poly_offset);
> > +  HOST_WIDE_INT offset;
> > +  if (!REG_P (base)
> > +      || !rtx_equal_p (base, info.base)
> > +      || !poly_offset.is_constant (&offset))
> > +    return false;
> > +
> > +  return (offset == info.offset
> > +         || offset == info.offset + info.access_size);
> > +}
> > +
> > +/* Search backwards in the containing basic block for a nearby store that
> > +   makes splitting PAIR profitable.  Stop once the base value changes.  */
> > +
> > +static rtx_insn *
> > +find_forwarding_store (rtx_insn *pair, const ldp_info &info)
> > +{
> > +  basic_block bb = BLOCK_FOR_INSN (pair);
> > +  unsigned int distance = 0;
> > +  unsigned int max_distance = (unsigned int) aarch64_split_ldp_stlf;
> > +
> > +  for (rtx_insn *insn = PREV_INSN (pair);
> > +       insn && BLOCK_FOR_INSN (insn) == bb;
> > +       insn = PREV_INSN (insn))
> > +    {
> > +      if (!NONDEBUG_INSN_P (insn))
> > +       continue;
> > +
> > +      if (++distance > max_distance)
> > +       break;
> > +
> > +      if (CALL_P (insn) || modified_in_p (info.base, insn))
> > +       break;
> > +
> > +      if (forwarding_store_p (insn, info))
> > +       return insn;
> > +    }
> > +
> > +  return NULL;
> > +}
> > +
> > +/* Replace PAIR with two individually-recognized scalar loads.  Load the
> > +   non-base destination first when the low destination overlaps the base.  */
> > +
> > +static bool
> > +split_ldp (rtx_insn *pair, const ldp_info &info)
> > +{
> > +  rtx mems[2] = {
> > +    adjust_address_nv (info.mem, info.mode, 0),
> > +    adjust_address_nv (info.mem, info.mode, info.access_size)
> > +  };
> > +
> > +  int order[2] = { 0, 1 };
> > +  if (reg_overlap_mentioned_p (info.base, info.dests[0]))
> > +    std::swap (order[0], order[1]);
> > +
> > +  start_sequence ();
> > +  emit_insn (gen_rtx_SET (info.dests[order[0]], mems[order[0]]));
> > +  emit_insn (gen_rtx_SET (info.dests[order[1]], mems[order[1]]));
> > +  rtx_insn *seq = get_insns ();
> > +  end_sequence ();
> > +
> > +  unshare_all_rtl_in_chain (seq);
> > +  for (rtx_insn *insn = seq; insn; insn = NEXT_INSN (insn))
> > +    if (!NONDEBUG_INSN_P (insn) || recog_memoized (insn) < 0)
> > +      {
> > +       if (dump_file)
> > +         fprintf (dump_file,
> > +                  "Rejecting LDP %d: scalar replacement is not recognized\n",
> > +                  INSN_UID (pair));
> > +       return false;
> > +      }
> > +
> > +  if (dump_file)
> > +    {
> > +      fprintf (dump_file, "Splitting LDP %d into:\n", INSN_UID (pair));
> > +      for (rtx_insn *insn = seq; insn; insn = NEXT_INSN (insn))
> > +       print_rtl_single (dump_file, insn);
> > +    }
> > +
> > +  emit_insn_before_setloc (seq, pair, INSN_LOCATION (pair));
> > +  delete_insn (pair);
> > +  return true;
> > +}
> > +
> > +const pass_data pass_data_split_ldp_stlf =
> > +{
> > +  RTL_PASS, /* type.  */
> > +  "split_ldp_stlf", /* name.  */
> > +  OPTGROUP_NONE, /* optinfo_flags.  */
> > +  TV_MACH_DEP, /* tv_id.  */
> > +  0, /* properties_required.  */
> > +  0, /* properties_provided.  */
> > +  0, /* properties_destroyed.  */
> > +  0, /* todo_flags_start.  */
> > +  TODO_df_verify | TODO_df_finish, /* todo_flags_finish.  */
> > +};
> > +
> > +class pass_split_ldp_stlf : public rtl_opt_pass
> > +{
> > +public:
> > +  pass_split_ldp_stlf (gcc::context *ctxt)
> > +    : rtl_opt_pass (pass_data_split_ldp_stlf, ctxt)
> > +  {}
> > +
> > +  bool gate (function *fn) final override
> > +  {
> > +    return (optimize >= 1
> > +           && !optimize_debug
> > +           && reload_completed
> > +           && aarch64_split_ldp_stlf
> > +           && !optimize_function_for_size_p (fn)
> > +           && !fn->can_throw_non_call_exceptions);
> > +  }
> > +
> > +  unsigned int execute (function *fn) final override
> > +  {
> > +    unsigned int split_count = 0;
> > +    basic_block bb;
> > +
> > +    FOR_EACH_BB_FN (bb, fn)
> > +      {
> > +       rtx_insn *insn;
> > +       rtx_insn *next;
> > +       FOR_BB_INSNS_SAFE (bb, insn, next)
> > +         {
> > +           ldp_info info;
> > +           if (!extract_ldp (insn, &info))
> > +             continue;
> > +
> > +           rtx_insn *store = find_forwarding_store (insn, info);
> > +           if (!store)
> > +             continue;
> > +
> > +           if (dump_file)
> > +             {
> > +               fprintf (dump_file, "Found forwarding store for LDP %d:\n",
> > +                        INSN_UID (insn));
> > +               print_rtl_single (dump_file, store);
> > +             }
> > +
> > +           split_count += split_ldp (insn, info);
> > +         }
> > +      }
> > +
> > +    if (dump_file)
> > +      fprintf (dump_file, "Split %u LDP instructions\n", split_count);
> > +    return 0;
> > +  }
> > +};
> > +
> > +} // namespace
> > +
> > +rtl_opt_pass *
> > +make_pass_split_ldp_stlf (gcc::context *ctxt)
> > +{
> > +  return new pass_split_ldp_stlf (ctxt);
> > +}
> > diff --git a/gcc/config/aarch64/aarch64-passes.def b/gcc/config/aarch64/aarch64-passes.def
> > index 2df7459af92..594348a0532 100644
> > --- a/gcc/config/aarch64/aarch64-passes.def
> > +++ b/gcc/config/aarch64/aarch64-passes.def
> > @@ -26,4 +26,5 @@ INSERT_PASS_BEFORE (pass_late_thread_prologue_and_epilogue, 1, pass_late_track_s
> >  INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_bti);
> >  INSERT_PASS_BEFORE (pass_early_remat, 1, pass_ldp_fusion);
> >  INSERT_PASS_BEFORE (pass_peephole2, 1, pass_ldp_fusion);
> > +INSERT_PASS_BEFORE (pass_sched2, 1, pass_split_ldp_stlf);
> >  INSERT_PASS_BEFORE (pass_free_cfg, 1, pass_narrow_gp_writes);
> > diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
> > index bcc833cfaa1..ec8be386c29 100644
> > --- a/gcc/config/aarch64/aarch64-protos.h
> > +++ b/gcc/config/aarch64/aarch64-protos.h
> > @@ -1268,6 +1268,7 @@ rtl_opt_pass *make_pass_late_track_speculation (gcc::context *);
> >  rtl_opt_pass *make_pass_insert_bti (gcc::context *ctxt);
> >  rtl_opt_pass *make_pass_switch_pstate_sm (gcc::context *ctxt);
> >  rtl_opt_pass *make_pass_ldp_fusion (gcc::context *);
> > +rtl_opt_pass *make_pass_split_ldp_stlf (gcc::context *);
> >  rtl_opt_pass *make_pass_narrow_gp_writes (gcc::context *);
> >
> >  poly_uint64 aarch64_regmode_natural_size (machine_mode);
> > diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt
> > index deccc6c8887..21e9500996d 100644
> > --- a/gcc/config/aarch64/aarch64.opt
> > +++ b/gcc/config/aarch64/aarch64.opt
> > @@ -461,6 +461,11 @@ individual writeback accesses where possible.  A value of two means we
> >  also try to opportunistically form writeback opportunities by folding in
> >  trailing destructive updates of the base register used by a pair.
> >
> > +-param=aarch64-split-ldp-stlf=
> > +Target Joined UInteger Var(aarch64_split_ldp_stlf) Init(0) IntegerRange(0, 32) Param
> > +Maximum number of preceding instructions to search when splitting an LDP
> > +to improve store-to-load forwarding.  A value of zero disables the pass.
> > +
> >  -param=aarch64-tag-memory-loop-threshold=
> >  Target Joined UInteger Var(aarch64_tag_memory_loop_threshold) Init(10) IntegerRange(0, 65536) Param
> >  Param to control the threshold in number of granules beyond which an
> > diff --git a/gcc/config/aarch64/t-aarch64 b/gcc/config/aarch64/t-aarch64
> > index 3942a1049b9..936540aeb4e 100644
> > --- a/gcc/config/aarch64/t-aarch64
> > +++ b/gcc/config/aarch64/t-aarch64
> > @@ -238,6 +238,12 @@ aarch64-ldp-fusion.o: $(srcdir)/config/aarch64/aarch64-ldp-fusion.cc \
> >         $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
> >                 $(srcdir)/config/aarch64/aarch64-ldp-fusion.cc
> >
> > +aarch64-ldp-split.o: $(srcdir)/config/aarch64/aarch64-ldp-split.cc \
> > +    $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) $(BACKEND_H) $(RTL_H) $(DF_H) \
> > +    $(INSN_ATTR_H) insn-config.h $(RECOG_H) tree-pass.h
> > +       $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
> > +               $(srcdir)/config/aarch64/aarch64-ldp-split.cc
> > +
> >  aarch64-sched-dispatch.o: $(srcdir)/config/aarch64/aarch64-sched-dispatch.cc \
> >      $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) $(BACKEND_H) $(RTL_H) \
> >      $(INSN_ATTR_H) $(REGSET_H) sched-int.h $(DUMPFILE_H) \
> > diff --git a/gcc/doc/params.texi b/gcc/doc/params.texi
> > index 26b34a59aa9..1ae93ae6c75 100644
> > --- a/gcc/doc/params.texi
> > +++ b/gcc/doc/params.texi
> > @@ -2061,6 +2061,14 @@ accesses where possible.  A value of two means we also try to opportunistically
> >  form writeback opportunities by folding in trailing destructive updates of the
> >  base register used by a pair.
> >
> > +@paindex aarch64-split-ldp-stlf
> > +@item aarch64-split-ldp-stlf
> > +Maximum number of preceding instructions that the AArch64 backend searches
> > +for a scalar store that writes one half of a later @code{ldp}.  When such a
> > +store is found, the backend can split the @code{ldp} into two scalar loads to
> > +improve store-to-load forwarding.  A value of zero, which is the default,
> > +disables the transformation.
> > +
> >  @paindex aarch64-loop-vect-issue-rate-niters
> >  @item aarch64-loop-vect-issue-rate-niters
> >  The tuning for some AArch64 CPUs tries to take both latencies and issue
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-1.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-1.c
> > new file mode 100644
> > index 00000000000..ca4ca4821b1
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-1.c
> > @@ -0,0 +1,43 @@
> > +/* { dg-options "-O2 --param=aarch64-split-ldp-stlf=16" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +split_low (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +split_high (long *p, long value)
> > +{
> > +  p[2] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +__attribute__ ((noinline, noclone))
> > +int
> > +split_low_si (int *p, int value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +extern void consume (long, long);
> > +
> > +__attribute__ ((noinline, noclone))
> > +void
> > +split_base_dest_overlap (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  consume (p[1], p[2]);
> > +}
> > +
> > +/* { dg-final { scan-assembler-not {\tldp\t} } } */
> > +/* { dg-final { scan-assembler-times {\tldr\t} 8 } } */
> > +/* { dg-final { scan-assembler-times {\tldr\tx1, \[x0, 16\]\n\tldr\tx0, \[x0, 8\]} 1 } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-2.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-2.c
> > new file mode 100644
> > index 00000000000..2ad7e2d5825
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-2.c
> > @@ -0,0 +1,13 @@
> > +/* { dg-options "-O2" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +disabled_by_default (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +/* { dg-final { scan-assembler-times {\tldp\t} 1 } } */
> > +/* { dg-final { scan-assembler-not {\tldr\t} } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-3.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-3.c
> > new file mode 100644
> > index 00000000000..930ada6836d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-3.c
> > @@ -0,0 +1,22 @@
> > +/* { dg-options "-O2 --param=aarch64-split-ldp-stlf=16" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +no_overlap (long *p, long value)
> > +{
> > +  p[4] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +wrong_store_width (long *p, int value)
> > +{
> > +  *(int *) ((char *) p + 8) = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +/* { dg-final { scan-assembler-times {\tldp\t} 2 } } */
> > +/* { dg-final { scan-assembler-not {\tldr\t} } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-4.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-4.c
> > new file mode 100644
> > index 00000000000..3e08bcbea05
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-4.c
> > @@ -0,0 +1,14 @@
> > +/* { dg-options "-O2 --param=aarch64-split-ldp-stlf=16" } */
> > +/* { dg-additional-options "-fnon-call-exceptions" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +preserve_faulting_pair (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +/* { dg-final { scan-assembler-times {\tldp\t} 1 } } */
> > +/* { dg-final { scan-assembler-not {\tldr\t} } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-5.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-5.c
> > new file mode 100644
> > index 00000000000..a19551c4374
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-5.c
> > @@ -0,0 +1,13 @@
> > +/* { dg-options "-Os --param=aarch64-split-ldp-stlf=16" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +optimize_for_size (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +/* { dg-final { scan-assembler-times {\tldp\t} 1 } } */
> > +/* { dg-final { scan-assembler-not {\tldr\t} } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-6.c b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-6.c
> > new file mode 100644
> > index 00000000000..bd59fe77b47
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/split-ldp-stlf-6.c
> > @@ -0,0 +1,13 @@
> > +/* { dg-options "-O2 --param=aarch64-split-ldp-stlf=1" } */
> > +
> > +__attribute__ ((noinline, noclone))
> > +long
> > +outside_search_window (long *p, long value)
> > +{
> > +  p[1] = value;
> > +  __asm__ __volatile__ ("" ::: "memory");
> > +  return p[1] + p[2];
> > +}
> > +
> > +/* { dg-final { scan-assembler-times {\tldp\t} 1 } } */
> > +/* { dg-final { scan-assembler-not {\tldr\t} } } */
> > --
> > 2.43.0
> >
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.