On Sat, Oct 3, 2026 at 4:03 PM Sarvesh Chandra <[email protected]> wrote:
>
> Add ix86_bundle_align_loops, an RTL pass that marks selected loop regions
> with .bundle_lock flex.  The assembler aligns each marked region after
> instruction relaxation, when the final instruction sizes are known, and
> flex mode accepts a region that ends up larger than one bundle.
>
> Candidates are formed from layout-backward loop edges and selected as a
> disjoint set by weighted interval scheduling.
>
> Enable the pass for Zen4 and later, add the target options and assembler
> feature check, and add tests for candidate formation, union construction,
> weight calculation, selection, and representative loop cases.
>
> gcc/
>         * config/i386/i386-features.cc (struct bundle_cand): New.
>         (bundle_cand_cmp_hi, bundle_hwi_cmp, bundle_hwi_less)
>         (bundle_cand_hi_less, bundle_compute_layout)
>         (bundle_collect_candidates, bundle_add_unions)
>         (bundle_compute_weights, bundle_select_and_emit)
>         (ix86_do_bundle_align_loops): New functions.
>         (class pass_bundle_align_loops): New.
>         (make_pass_bundle_align_loops): New function.
>         * config/i386/i386-protos.h (make_pass_bundle_align_loops): Declare.
>         * config/i386/i386-passes.def (pass_bundle_align_loops): Insert before
>         pass_shorten_branches.
>         * config/i386/i386-options.cc (ix86_default_bundle_align): New 
> function.
>         (ix86_default_align): Call it.
>         * config/i386/i386.md (bundle_lock, bundle_unlock): New insns.
>         * config/i386/i386.opt (mbundle-align-loops): New option.
>         (x86-tight-loop-max-size, x86-bundle-align-branch-limit): New params.
>         * config/i386/i386.opt.urls: Regenerate.
>         * config/i386/x86-tune.def (X86_TUNE_BUNDLE_ALIGN_LOOPS): New tune 
> flag.
>         * configure.ac: Check for .bundle_lock flex support.
>         * configure: Regenerate.
>         * config.in: Regenerate.
>         * doc/invoke.texi (-mbundle-align-loops, x86-tight-loop-max-size):
>         Document.
>         * doc/extend.texi (x86 Function Attributes): Document
>         bundle-align-loops.
>         * doc/sourcebuild.texi (gcc_bundle_align_loops): Document.
>
> gcc/testsuite/
>         * gcc.target/i386/bundle-align-1.c: New test.
>         * gcc.target/i386/bundle-align-2.c: New test.
>         * gcc.target/i386/bundle-align-3.c: New test.
>         * gcc.target/i386/bundle-align-4.c: New test.
>         * gcc.target/i386/bundle-align-5.c: New test.
>         * gcc.target/i386/bundle-align-6.c: New test.
>         * gcc.target/i386/bundle-align-7.c: New test.
>         * lib/target-supports.exp
>         (check_effective_target_gcc_bundle_align_loops): New.
>
> Co-authored-by: Branden Moore <[email protected]>
> Co-authored-by: Venkataramanan Kumar <[email protected]>
> Signed-off-by: Sarvesh Chandra <[email protected]>
> ---
> [Presented at GNU Tools Cauldron 2026.]
>
> Cosmetic changes in v2:
>   - Add co-authors Branden Moore and Venkataramanan Kumar.
>   - Drop the slide references from the test comments.  They pointed at
>     the Cauldron presentation, but GCC does not allow attaching the PPT
>     to the patch, so the references are irrelevant here.
>   - Correct the i386.md comment: the simple flex mode accepts an
>     oversized locked region and still aligns its start; it does not skip
>     padding.  An experimental post-relaxation patch does skip padding,
>     but that is not part of this proposal.
>   - bundle-align-5.c: rename the scan kernel instead of redefining
>     memchr with -D.
>   - bundle-align-6.c: drop the unneeded x86-tight-loop-max-size param.
>   - Reduce the tests to the scanned kernel: drop the main drivers and
>     headers.
>
>  gcc/config.in                                 |   6 +
>  gcc/config/i386/i386-features.cc              | 331 +++++++++++++++++-
>  gcc/config/i386/i386-options.cc               |  26 +-
>  gcc/config/i386/i386-passes.def               |   6 +-
>  gcc/config/i386/i386-protos.h                 |   2 +-
>  gcc/config/i386/i386.md                       |  35 ++
>  gcc/config/i386/i386.opt                      |  12 +
>  gcc/config/i386/i386.opt.urls                 |   3 +
>  gcc/config/i386/x86-tune.def                  |   7 +
>  gcc/configure                                 |  38 ++
>  gcc/configure.ac                              |  11 +
>  gcc/doc/extend.texi                           |   6 +
>  gcc/doc/invoke.texi                           |  15 +-
>  gcc/doc/sourcebuild.texi                      |   4 +
>  .../gcc.target/i386/bundle-align-1.c          |  16 +
>  .../gcc.target/i386/bundle-align-2.c          |  21 ++
>  .../gcc.target/i386/bundle-align-3.c          |  26 ++
>  .../gcc.target/i386/bundle-align-4.c          |  22 ++
>  .../gcc.target/i386/bundle-align-5.c          |  17 +
>  .../gcc.target/i386/bundle-align-6.c          |  24 ++
>  .../gcc.target/i386/bundle-align-7.c          |  17 +
>  gcc/testsuite/lib/target-supports.exp         |  16 +
>  22 files changed, 641 insertions(+), 20 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-1.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-2.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-3.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-4.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-5.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-6.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/bundle-align-7.c
>
> diff --git a/gcc/config.in b/gcc/config.in
> index b90caa0dd57..51dac287796 100644
> --- a/gcc/config.in
> +++ b/gcc/config.in
> @@ -1448,6 +1448,12 @@
>  #endif
>
>
> +/* Define if your assembler accepts the flex argument to .bundle_lock. */
> +#ifndef USED_FOR_TARGET
> +#undef HAVE_GAS_BUNDLE_FLEX_LOCK
> +#endif
> +
> +
>  /* Define 0/1 if your assembler supports CFI directives. */
>  #undef HAVE_GAS_CFI_DIRECTIVE
>
> diff --git a/gcc/config/i386/i386-features.cc 
> b/gcc/config/i386/i386-features.cc
> index eb5f82e42cb..6743c0ebcfd 100644
> --- a/gcc/config/i386/i386-features.cc
> +++ b/gcc/config/i386/i386-features.cc
> @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "ifcvt.h"
>  #include "symbol-summary.h"
>  #include "sreal.h"
> +#include "fibonacci_heap.h"
>  #include "ipa-cp.h"
>  #include "ipa-prop.h"
>  #include "ipa-fnsummary.h"
> @@ -5646,12 +5647,314 @@ ix86_align_loops ()
>    free_dominance_info (CDI_DOMINATORS);
>  }
>
> +/* ix86_bundle_align_loops pass.
> +
> +   1.  Compute layout offsets and identify feasible loop-backward edges.
> +   2.  Add synthetic unions for candidates sharing a K-byte window.
> +   3.  Weight candidates by the back-edge counts of loops contained in
> +       them.
> +   4.  Select a maximum-weight disjoint set with weighted interval
> +       scheduling.
> +   5.  Emit .bundle_lock flex for the selected regions.
> +
> +   K is param_x86_tight_loop_max_size, or ix86_cost->prefetch_block when
> +   unset.  */
> +
> +/* A candidate loop region: the byte interval [lo, hi] on the layout axis
> +   (hi inclusive), weight w, interval-scheduling predecessor p, the lock and
> +   unlock blocks, and a feasibility flag.  A synthetic union has w == -1 
> until
> +   the weight sweep replaces it with its contained weight.  */
> +
> +struct bundle_cand
> +{
> +  class loop *loop;
> +  basic_block lock, unlock;
> +  HOST_WIDE_INT lo, hi;
> +  gcov_type w;
> +  int p;
> +  bool feasible;
> +};
> +
> +/* Sort by hi ascending.  Break ties by lo descending (a contained candidate
> +   before its container), then by real loops before a synthetic union of the
> +   same span.  */
> +
> +static int
> +bundle_cand_cmp_hi (const void *a, const void *b)
> +{
> +  const bundle_cand *x = (const bundle_cand *)a, *y = (const bundle_cand *)b;
> +  if (x->hi != y->hi)
> +    return x->hi < y->hi ? -1 : 1;
> +  if (x->lo != y->lo)
> +    return x->lo > y->lo ? -1 : 1;
> +  if ((x->w == -1) != (y->w == -1))
> +    return x->w == -1 ? 1 : -1;
> +  return 0;
> +}
> +
> +/* qsort comparator for HOST_WIDE_INT, ascending (lo-value compression).  */
> +
> +static int
> +bundle_hwi_cmp (const void *a, const void *b)
> +{
> +  HOST_WIDE_INT x = *(const HOST_WIDE_INT *)a, y = *(const HOST_WIDE_INT *)b;
> +  return x < y ? -1 : x > y ? 1 : 0;
> +}
> +
> +/* Less-than on HOST_WIDE_INT, for vec::lower_bound over the compressed
> +   lo-value list.  */
> +
> +static bool
> +bundle_hwi_less (const HOST_WIDE_INT &a, const HOST_WIDE_INT &b)
> +{
> +  return a < b;
> +}
> +
> +/* Less-than on interval end, for vec::lower_bound in the WIS predecessor
> +   search over the hi-sorted feasible set.  */
> +
> +static bool
> +bundle_cand_hi_less (const bundle_cand &a, const bundle_cand &b)
> +{
> +  return a.hi < b.hi;
> +}
> +
> +static void
> +bundle_compute_layout (HOST_WIDE_INT *bb_lo, HOST_WIDE_INT *bb_hi,
> +                      int *asm_lo, int *asm_hi)
> +{
> +  HOST_WIDE_INT off = 0;
> +  int nasm = 0;
> +  basic_block bb;
> +  FOR_EACH_BB_FN (bb, cfun)

can you avoid doing this for non-candidate loop blocks?

> +    {
> +      rtx_insn *insn;
> +      bb_lo[bb->index] = off;
> +      asm_lo[bb->index] = nasm;
> +      FOR_BB_INSNS (bb, insn)
> +       if (NONDEBUG_INSN_P (insn))
> +         {
> +           /* Inline asm has no reliable min size; count its presence so any
> +              candidate spanning this block can be excluded outright.  */
> +           if (asm_noperands (PATTERN (insn)) >= 0)
> +             nasm++;
> +           else
> +             off += ix86_min_insn_size (insn);
> +         }
> +      bb_hi[bb->index] = off;
> +      asm_hi[bb->index] = nasm;
> +    }
> +}
> +
> +static void
> +bundle_collect_candidates (vec<bundle_cand> &feas, HOST_WIDE_INT k,
> +                          HOST_WIDE_INT *bb_lo, HOST_WIDE_INT *bb_hi,
> +                          int *asm_lo, int *asm_hi)
> +{
> +  /* Step 1: identify feasible candidates from layout-backward loop edges.  
> */
> +  basic_block bb;
> +  FOR_EACH_BB_FN (bb, cfun)

I wonder if you can instead walk the loop tree?

> +    {
> +      edge e;
> +      edge_iterator ei;
> +      FOR_EACH_EDGE (e, ei, bb->succs)
> +       if (e->count ().initialized_p ()
> +           && e->dest->index >= NUM_FIXED_BLOCKS
> +           && (e->dest == bb
> +               || bb_lo[e->dest->index] < bb_lo[bb->index]))
> +         {
> +           bundle_cand c = {};
> +           c.loop = bb->loop_father;
> +           c.lock = e->dest;
> +           c.unlock = bb;
> +           c.lo = bb_lo[e->dest->index];
> +           c.hi = bb_hi[bb->index];
> +           c.w = e->count ().to_gcov_type ();
> +           /* Both ends must lie in a real loop, not the function-wide fake
> +              loop whose latch is EXIT.  This rejects a layout-backward exit
> +              edge whose destination is outside any loop.  */
> +           c.feasible = (c.loop
> +                         && c.loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun)
> +                         && e->dest->loop_father
> +                         && (e->dest->loop_father->latch
> +                             != EXIT_BLOCK_PTR_FOR_FN (cfun))
> +                         && asm_hi[bb->index] == asm_lo[e->dest->index]
> +                         && c.hi - c.lo <= k
> +                         && optimize_bb_for_speed_p (c.lock));
> +           if (c.feasible)
> +             feas.safe_push (c);
> +         }
> +    }
> +}
> +
> +static void
> +bundle_add_unions (vec<bundle_cand> &feas, HOST_WIDE_INT k)
> +{
> +  /* Step 2: add synthetic unions for candidates sharing a K-byte window.  */
> +  feas.qsort (bundle_cand_cmp_hi);
> +  unsigned base = feas.length ();
> +  /* At most one union is added per candidate, so reserve now; this keeps the
> +     &feas[i] pointers held by the heap valid across the push below.  */
> +  feas.reserve (2 * base);
> +  fibonacci_heap<HOST_WIDE_INT, bundle_cand> heap (HOST_WIDE_INT_MIN);
> +  for (unsigned i = 0; i < base; i++)
> +    {
> +      heap.insert (feas[i].lo, &feas[i]);
> +      HOST_WIDE_INT thr = feas[i].hi - k;
> +      while (heap.min_key () < thr)
> +       heap.extract_min ();
> +      if (heap.nodes () >= 2)
> +       {
> +         bundle_cand *s = heap.min ();
> +         bundle_cand c = {};
> +         c.loop = s->loop;
> +         c.lock = s->lock;
> +         c.unlock = feas[i].unlock;
> +         c.lo = s->lo;
> +         c.hi = feas[i].hi;
> +         c.w = -1;  /* Union; the weight sweep fills this in.  */
> +         c.feasible = true;
> +         feas.safe_push (c);
> +       }
> +    }
> +}
> +
> +static void
> +bundle_compute_weights (vec<bundle_cand> &feas)
> +{
> +  /* Step 3: compute containment weights with a Fenwick tree.  */
> +  feas.qsort (bundle_cand_cmp_hi);
> +  unsigned n_all = feas.length ();
> +  /* Compress lo values into a sorted, unique list; POS below maps a lo to 
> its
> +     reversed rank via lower_bound.  */
> +  auto_vec<HOST_WIDE_INT> los;
> +  los.reserve (n_all);
> +  for (unsigned p = 0; p < n_all; p++)
> +    los.quick_push (feas[p].lo);
> +  los.qsort (bundle_hwi_cmp);
> +  int m = 0;
> +  for (unsigned p = 0; p < n_all; p++)
> +    if (m == 0 || los[m - 1] != los[p])
> +      los[m++] = los[p];
> +  los.truncate (m);
> +  /* BIT[1..m] keyed by reversed lo-rank, so a prefix sum [1..pos] covers
> +     exactly lo_j >= lo_i.  */
> +  gcov_type *bit = XCNEWVEC (gcov_type, m + 1);
> +  for (unsigned p = 0; p < n_all; p++)
> +    {
> +      /* Reversed rank of feas[p].lo: LOWER_BOUND is the first LOS >= lo.  */
> +      int pos = m - (int) los.lower_bound (feas[p].lo, bundle_hwi_less);
> +      /* A union is not a real loop, so do not insert it into the tree.  Its
> +        weight is the contained sum, less one to break ties toward it.  */
> +      bool u = feas[p].w == -1;
> +      if (!u)
> +       for (int i = pos; i <= m; i += i & -i)
> +         bit[i] += feas[p].w;
> +      gcov_type sum = 0;
> +      for (int i = pos; i > 0; i -= i & -i)
> +       sum += bit[i];
> +      feas[p].w = u ? sum - 1 : sum;
> +    }
> +  XDELETEVEC (bit);
> +}
> +
> +static void
> +bundle_select_and_emit (vec<bundle_cand> &feas)
> +{
> +  /* Step 4: select the maximum-weight disjoint set.  */
> +  unsigned n = feas.length ();
> +  /* dp[i+1] is the best weight over feas[0..i].  Whether i is taken is
> +     recomputed while backtracking rather than stored.  */
> +  gcov_type *dp = XCNEWVEC (gcov_type, n + 1);
> +
> +  for (unsigned i = 0; i < n; i++)
> +    {
> +      /* Predecessor: the last candidate that ends before feas[i] starts.
> +        hi is inclusive, so find the first candidate with hi >= lo.  */
> +      bundle_cand key = {};
> +      key.hi = feas[i].lo;
> +      int p = (int) feas.lower_bound (key, bundle_cand_hi_less) - 1;
> +      feas[i].p = p;
> +      gcov_type take = feas[i].w + dp[p + 1], skip = dp[i];
> +      dp[i + 1] = take >= skip ? take : skip;
> +      if (dump_file)
> +       fprintf (dump_file,
> +                "bundle-align: cand %u loop %d [bb%d..bb%d] "
> +                "lo=%ld hi=%ld w=%ld p=%d take=%ld skip=%ld => %s\n",
> +                i, feas[i].loop->num, feas[i].lock->index,
> +                feas[i].unlock->index, (long) feas[i].lo,
> +                (long) feas[i].hi, (long) feas[i].w, p,
> +                (long) take, (long) skip, take >= skip ? "take" : "skip");
> +    }
> +
> +  /* Step 5: emit the selected regions.  */
> +  for (int i = (int) n - 1; i >= 0;)
> +    if (feas[i].w + dp[feas[i].p + 1] >= dp[i])
> +      {
> +       basic_block h = feas[i].lock, l = feas[i].unlock;
> +       emit_insn_before (gen_bundle_lock (), BB_HEAD (h));
> +       emit_insn_after (gen_bundle_unlock (), BB_END (l));
> +       if (dump_file)
> +         fprintf (dump_file,
> +                  "bundle-align: wrap loop %d [bb%d..bb%d] "
> +                  "w=%ld span=%ld\n", feas[i].loop->num, h->index,
> +                  l->index, (long) feas[i].w,
> +                  (long) (feas[i].hi - feas[i].lo));
> +       i = feas[i].p;
> +      }
> +    else
> +      i--;
> +
> +  XDELETEVEC (dp);
> +}
> +
> +/* Wrap the selected disjoint hot loop regions in 
> .bundle_lock/.bundle_unlock.
> +   Each region is an interval on the layout byte axis, which handles rotated
> +   (latch-before-header) loops uniformly.  */
> +
> +static void
> +ix86_do_bundle_align_loops ()
> +{
> +  if (ix86_cost->prefetch_block == 0)

|| number_of_loops (cfun) <= 1

> +    return;
> +
> +  /* Region size cap: tuning param if set, else the CPU bundle size.  */
> +  HOST_WIDE_INT k = (param_x86_tight_loop_max_size > 0
> +                        ? (HOST_WIDE_INT)param_x86_tight_loop_max_size
> +                        : (HOST_WIDE_INT)ix86_cost->prefetch_block);
> +
> +  loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
> +
> +  /* Per-block layout byte offsets [bb_lo, bb_hi).  */
> +  int nbb = last_basic_block_for_fn (cfun);
> +  HOST_WIDE_INT *bb_lo = XCNEWVEC (HOST_WIDE_INT, nbb);
> +  HOST_WIDE_INT *bb_hi = XCNEWVEC (HOST_WIDE_INT, nbb);
> +  /* Running count of inline-asm insns in layout order; [asm_lo, asm_hi) 
> lets a
> +     candidate test whether any block it spans contains an asm.  */
> +  int *asm_lo = XCNEWVEC (int, nbb);
> +  int *asm_hi = XCNEWVEC (int, nbb);
> +  bundle_compute_layout (bb_lo, bb_hi, asm_lo, asm_hi);
> +
> +  auto_vec<bundle_cand> feas;
> +  bundle_collect_candidates (feas, k, bb_lo, bb_hi, asm_lo, asm_hi);
> +  bundle_add_unions (feas, k);
> +  bundle_compute_weights (feas);
> +  if (!feas.is_empty ())
> +    bundle_select_and_emit (feas);
> +
> +  XDELETEVEC (bb_lo);
> +  XDELETEVEC (bb_hi);
> +  XDELETEVEC (asm_lo);
> +  XDELETEVEC (asm_hi);
> +  loop_optimizer_finalize ();
> +}
> +
>  namespace {
>
> -const pass_data pass_data_align_tight_loops =
> +const pass_data pass_data_bundle_align_loops =
>  {
>    RTL_PASS, /* type */
> -  "align_tight_loops", /* name */
> +  "bundle_align_loops", /* name */
>    OPTGROUP_NONE, /* optinfo_flags */
>    TV_MACH_DEP, /* tv_id */
>    0, /* properties_required */
> @@ -5661,40 +5964,44 @@ const pass_data pass_data_align_tight_loops =
>    0, /* todo_flags_finish */
>  };
>
> -class pass_align_tight_loops : public rtl_opt_pass
> +class pass_bundle_align_loops : public rtl_opt_pass
>  {
>  public:
> -  pass_align_tight_loops (gcc::context *ctxt)
> -    : rtl_opt_pass (pass_data_align_tight_loops, ctxt)
> +  pass_bundle_align_loops (gcc::context *ctxt)
> +    : rtl_opt_pass (pass_data_bundle_align_loops, ctxt)
>    {}
>
>    /* opt_pass methods: */
>    bool gate (function *) final override
>      {
> -      return TARGET_ALIGN_TIGHT_LOOPS
> -            && optimize
> -            && optimize_function_for_speed_p (cfun);
> +      return optimize
> +            && optimize_function_for_speed_p (cfun)
> +            && (ix86_bundle_align_loops > 0 || TARGET_ALIGN_TIGHT_LOOPS);
>      }
>
>    unsigned int execute (function *) final override
>      {
>        timevar_push (TV_MACH_DEP);
> +      if (ix86_bundle_align_loops > 0)
> +       ix86_do_bundle_align_loops ();
>  #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
> -      ix86_align_loops ();
> +      else
> +       ix86_align_loops ();
>  #endif
>        timevar_pop (TV_MACH_DEP);
>        return 0;
>      }
> -}; // class pass_align_tight_loops
> +}; // class pass_bundle_align_loops
>
>  } // anon namespace
>
>  rtl_opt_pass *
> -make_pass_align_tight_loops (gcc::context *ctxt)
> +make_pass_bundle_align_loops (gcc::context *ctxt)
>  {
> -  return new pass_align_tight_loops (ctxt);
> +  return new pass_bundle_align_loops (ctxt);
>  }
>
> +
>  /* This compares the priority of target features in function DECL1
>     and DECL2.  It returns positive value if DECL1 is higher priority,
>     negative value if DECL2 is higher priority and 0 if they are the
> diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> index e7f06154103..db0a9426b29 100644
> --- a/gcc/config/i386/i386-options.cc
> +++ b/gcc/config/i386/i386-options.cc
> @@ -1872,8 +1872,16 @@ set_ix86_tune_features (struct gcc_options *opts,
>  /* Default align_* from the processor table.  */
>
>  static void
> -ix86_default_align (struct gcc_options *opts)
> +ix86_default_align (struct gcc_options *opts,
> +                   struct gcc_options *opts_set)
>  {
> +  /* Bundle alignment replaces generic loop, jump, and label padding.  */
> +  if (opts->x_ix86_bundle_align_loops > 0)
> +    {
> +      SET_OPTION_IF_UNSET (opts, opts_set, str_align_loops, "1");
> +      SET_OPTION_IF_UNSET (opts, opts_set, str_align_jumps, "1");
> +      SET_OPTION_IF_UNSET (opts, opts_set, str_align_labels, "1");
> +    }
>    /* -falign-foo without argument: supply one.  */
>    if (opts->x_flag_align_loops && !opts->x_str_align_loops)
>      opts->x_str_align_loops = processor_cost_table[ix86_tune]->align_loop;
> @@ -1885,6 +1893,16 @@ ix86_default_align (struct gcc_options *opts)
>      opts->x_str_align_functions = 
> processor_cost_table[ix86_tune]->align_func;
>  }
>
> +/* Default -mbundle-align-loops from the active tune model.  */
> +
> +static void
> +ix86_default_bundle_align (struct gcc_options *opts)
> +{
> +  if (opts->x_ix86_bundle_align_loops == -1)
> +    opts->x_ix86_bundle_align_loops
> +      = ix86_tune_features[X86_TUNE_BUNDLE_ALIGN_LOOPS] ? 1 : 0;
> +}
> +
>  #ifndef USE_IX86_FRAME_POINTER
>  #define USE_IX86_FRAME_POINTER 0
>  #endif
> @@ -1986,7 +2004,8 @@ ix86_override_options_after_change_1 (struct 
> gcc_options *opts,
>  void
>  ix86_override_options_after_change (void)
>  {
> -  ix86_default_align (&global_options);
> +  ix86_default_bundle_align (&global_options);
> +  ix86_default_align (&global_options, &global_options_set);
>
>    ix86_recompute_optlev_based_flags (&global_options, &global_options_set);
>
> @@ -2619,7 +2638,8 @@ ix86_option_override_internal (bool main_args_p,
>      opts->x_ix86_regparm = REGPARM_MAX;
>
>    /* Default align_* from the processor table.  */
> -  ix86_default_align (&global_options);
> +  ix86_default_bundle_align (&global_options);
> +  ix86_default_align (&global_options, &global_options_set);
>
>    /* Provide default for -mbranch-cost= value.  */
>    SET_OPTION_IF_UNSET (opts, opts_set, ix86_branch_cost,
> diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def
> index 4f461116cac..82d4d7411df 100644
> --- a/gcc/config/i386/i386-passes.def
> +++ b/gcc/config/i386/i386-passes.def
> @@ -31,9 +31,9 @@ along with GCC; see the file COPYING3.  If not see
>    INSERT_PASS_BEFORE (pass_cse2, 1, pass_stv, true /* timode_p */);
>
>    INSERT_PASS_BEFORE (pass_shorten_branches, 1, 
> pass_insert_endbr_and_patchable_area);
> -  /* pass_align_tight_loops must be after 
> pass_insert_endbr_and_patchable_area.
> -     PR116174.  */
> -  INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_align_tight_loops);
> +  /* Tight/bundle loop aligner; after pass_insert_endbr_and_patchable_area
> +     (PR116174) and before pass_shorten_branches.  */
> +  INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_bundle_align_loops);
>
>    INSERT_PASS_AFTER (pass_late_combine, 1, pass_x86_cse);
>    INSERT_PASS_AFTER (pass_late_combine, 1, 
> pass_remove_partial_avx_dependency);
> diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
> index 2687de646b0..84d2a7ad74c 100644
> --- a/gcc/config/i386/i386-protos.h
> +++ b/gcc/config/i386/i386-protos.h
> @@ -434,7 +434,7 @@ extern rtl_opt_pass 
> *make_pass_remove_partial_avx_dependency
>    (gcc::context *);
>  extern rtl_opt_pass *make_pass_x86_cse (gcc::context *);
>  extern rtl_opt_pass *make_pass_apx_nf_convert (gcc::context *);
> -extern rtl_opt_pass *make_pass_align_tight_loops (gcc::context *);
> +extern rtl_opt_pass *make_pass_bundle_align_loops (gcc::context *);
>
>  extern bool ix86_has_no_direct_extern_access;
>  extern bool ix86_rpad_gate ();
> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
> index 5768a93d5b0..f85010a41aa 100644
> --- a/gcc/config/i386/i386.md
> +++ b/gcc/config/i386/i386.md
> @@ -233,6 +233,8 @@
>    UNSPECV_STACK_PROBE
>    UNSPECV_PROBE_STACK_RANGE
>    UNSPECV_ALIGN
> +  UNSPECV_BUNDLE_LOCK
> +  UNSPECV_BUNDLE_UNLOCK
>    UNSPECV_PROLOGUE_USE
>    UNSPECV_SPLIT_STACK_RETURN
>    UNSPECV_CLD
> @@ -21756,6 +21758,39 @@
>  }
>    [(set_attr "length" "16")])
>
> +;; ix86_bundle_align_loops pass: align the locked region's start to the cache
> +;; line (prefetch_block).  Final instruction sizes may grow during assembly
> +;; because of indirect calls, prefixes, and relaxation, so a selected region
> +;; can exceed one bundle; flex .bundle_lock accepts such a region instead of
> +;; raising an error.  Requires flex .bundle_lock support.
> +(define_insn "bundle_lock"
> +  [(unspec_volatile [(const_int 0)] UNSPECV_BUNDLE_LOCK)]
> +  ""
> +{
> +#ifdef HAVE_GAS_BUNDLE_FLEX_LOCK
> +  fprintf (asm_out_file, "\t.bundle_align_mode %d\n\t.bundle_lock flex\n",
> +          ceil_log2 (ix86_cost->prefetch_block));
> +#endif
> +  return "";
> +}
> +  [(set_attr "length" "0")
> +   (set_attr "length_immediate" "0")
> +   (set_attr "modrm" "0")])
> +
> +;; Close the bundle and turn bundle alignment mode back off.
> +(define_insn "bundle_unlock"
> +  [(unspec_volatile [(const_int 0)] UNSPECV_BUNDLE_UNLOCK)]
> +  ""
> +{
> +#ifdef HAVE_GAS_BUNDLE_FLEX_LOCK
> +  fputs ("\t.bundle_unlock\n\t.bundle_align_mode 0\n", asm_out_file);
> +#endif
> +  return "";
> +}
> +  [(set_attr "length" "0")
> +   (set_attr "length_immediate" "0")
> +   (set_attr "modrm" "0")])
> +
>  (define_expand "prologue"
>    [(const_int 0)]
>    ""
> diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
> index 57ba3fbfa8f..148d26b6053 100644
> --- a/gcc/config/i386/i386.opt
> +++ b/gcc/config/i386/i386.opt
> @@ -240,6 +240,10 @@ malign-loops=
>  Target Undocumented RejectNegative Joined UInteger
>  Loop code aligned to this power of 2.
>
> +mbundle-align-loops
> +Target Var(ix86_bundle_align_loops) Init(-1) Save
> +Wrap small hot loops with .bundle_lock so the assembler keeps them inside 
> one cache line.
> +
>  malign-stringops
>  Target RejectNegative InverseMask(NO_ALIGN_STRINGOPS, ALIGN_STRINGOPS) Save
>  Align destination of the string operations.
> @@ -1221,6 +1225,14 @@ Do not use GOT to access external symbols.
>  Target Joined UInteger Var(x86_stlf_window_ninsns) Init(64) Param
>  Instructions number above which STFL stall penalty can be compensated.
>
> +-param=x86-tight-loop-max-size=
> +Target Joined UInteger Var(param_x86_tight_loop_max_size) Init(0) Param
> +Maximum body size in bytes for the i386 tight-loop aligner; 0 picks the 
> active tune prefetch_block.
> +
> +-param=x86-bundle-align-branch-limit=
> +Target Joined UInteger Var(param_x86_bundle_align_branch_limit) Init(2) Param
> +Maximum internal conditional branches allowed in a bundle-locked loop region 
> (the back-edge branch is not counted).
> +
>  mavxifma
>  Target Mask(ISA2_AVXIFMA) Var(ix86_isa_flags2) Save
>  Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and
> diff --git a/gcc/config/i386/i386.opt.urls b/gcc/config/i386/i386.opt.urls
> index d52e6de8014..20a638ea1f7 100644
> --- a/gcc/config/i386/i386.opt.urls
> +++ b/gcc/config/i386/i386.opt.urls
> @@ -28,6 +28,9 @@ UrlSuffix(gcc/x86-Options.html#index-malign-double)
>
>  ; skipping UrlSuffix for 'malign-loops=' due to finding no URLs
>
> +mbundle-align-loops
> +UrlSuffix(gcc/x86-Options.html#index-mbundle-align-loops)
> +
>  malign-stringops
>  UrlSuffix(gcc/x86-Options.html#index-malign-stringops)
>
> diff --git a/gcc/config/i386/x86-tune.def b/gcc/config/i386/x86-tune.def
> index d4c27351ad7..4716f26b2e1 100644
> --- a/gcc/config/i386/x86-tune.def
> +++ b/gcc/config/i386/x86-tune.def
> @@ -259,6 +259,13 @@ DEF_TUNE (X86_TUNE_ALIGN_TIGHT_LOOPS, 
> "align_tight_loops",
>          ~(m_ZHAOXIN | m_CASCADELAKE | m_SKYLAKE_AVX512
>            | m_CORE_ATOM))
>
> +/* X86_TUNE_BUNDLE_ALIGN_LOOPS: when true, use the ix86_bundle_align_loops
> +   pass with the assembler's .bundle_lock/.bundle_unlock directives instead
> +   of static .p2align padding.  Targets cores where loops straddling a 
> 64-byte
> +   instruction-cache line are particularly costly (AMD Zen4/Zen5/Zen6).  */
> +DEF_TUNE (X86_TUNE_BUNDLE_ALIGN_LOOPS, "bundle_align_loops",
> +         m_ZNVER4 | m_ZNVER5 | m_ZNVER6)
> +
>  
> /*****************************************************************************/
>  /* Integer instruction selection tuning                                      
> */
>  
> /*****************************************************************************/
> diff --git a/gcc/configure b/gcc/configure
> index 43ca45144b8..dd5a0c0a539 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -26306,6 +26306,44 @@ $as_echo "#define HAVE_GAS_MAX_SKIP_P2ALIGN 1" 
> >>confdefs.h
>  fi
>
>
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .bundle_lock 
> flex argument" >&5
> +$as_echo_n "checking assembler for .bundle_lock flex argument... " >&6; }
> +if ${gcc_cv_as_bundle_flex_lock+:} false; then :
> +  $as_echo_n "(cached) " >&6
> +else
> +  gcc_cv_as_bundle_flex_lock=no
> +  if test x"$gcc_cv_as" != x; then
> +    $as_echo '.bundle_align_mode 6
> +.bundle_lock flex
> +.rept 100
> +nop
> +.endr
> +.bundle_unlock' > conftest.s
> +    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
> +  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
> +  (eval $ac_try) 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; }
> +    then
> +        gcc_cv_as_bundle_flex_lock=yes
> +    else
> +      echo "configure: failed program was" >&5
> +      cat conftest.s >&5
> +    fi
> +    rm -f conftest.o conftest.s
> +  fi
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: 
> $gcc_cv_as_bundle_flex_lock" >&5
> +$as_echo "$gcc_cv_as_bundle_flex_lock" >&6; }
> +if test $gcc_cv_as_bundle_flex_lock = yes; then
> +
> +$as_echo "#define HAVE_GAS_BUNDLE_FLEX_LOCK 1" >>confdefs.h
> +
> +fi
> +
> +
> +
>  { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .literal16" 
> >&5
>  $as_echo_n "checking assembler for .literal16... " >&6; }
>  if ${gcc_cv_as_literal16+:} false; then :
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index 075b6b68ffa..346bf254de3 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -3142,6 +3142,17 @@ gcc_GAS_CHECK_FEATURE([.p2align with maximum skip], 
> gcc_cv_as_max_skip_p2align,,
>    [Define if your assembler supports specifying the maximum number
>     of bytes to skip when using the GAS .p2align command.])])
>
> +gcc_GAS_CHECK_FEATURE([.bundle_lock flex argument], 
> gcc_cv_as_bundle_flex_lock,,
> + [.bundle_align_mode 6
> +.bundle_lock flex
> +.rept 100
> +nop
> +.endr
> +.bundle_unlock],,
> +[AC_DEFINE(HAVE_GAS_BUNDLE_FLEX_LOCK, 1,
> +  [Define if your assembler accepts the flex argument to .bundle_lock
> +   that permits bundles larger than .bundle_align_mode alignment.])])
> +
>  gcc_GAS_CHECK_FEATURE([.literal16], gcc_cv_as_literal16,,
>   [.literal16],,
>  [AC_DEFINE(HAVE_GAS_LITERAL16, 1,
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 397b05ab87d..9081317927d 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -9598,6 +9598,12 @@ operations and calling the library routines for large 
> operations.
>  @itemx no-align-stringops
>  Do/do not align destination of inlined string operations.
>
> +@atindex @code{target("bundle-align-loops")}, x86
> +@item bundle-align-loops
> +@itemx no-bundle-align-loops
> +Enable/disable wrapping of small hot loops in assembler
> +@code{.bundle_lock}/@code{.bundle_unlock} directives.
> +
>  @atindex @code{target("recip")}, x86
>  @item recip
>  @itemx no-recip
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index e1870c9c227..619f92185fd 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1554,7 +1554,8 @@ See RS/6000 and PowerPC Options.
>  -mamx-complex  -mavxvnniint16  -msm3  -msha512  -msm4  -mapxf
>  -musermsr  -mavx10.1  -mavx10.2  -mamx-avx512  -mmovrs  -mamx-movrs
>  -mavx512bmm  -mavx10v2aux
> --mcldemote  -mms-bitfields  -mno-align-stringops  -minline-all-stringops
> +-mcldemote  -mms-bitfields  -mbundle-align-loops  -mno-align-stringops
> +-minline-all-stringops
>  -minline-stringops-dynamically  -mstringop-strategy=@var{alg}
>  -mkl  -mwidekl
>  -mmemcpy-strategy=@var{strategy}  -mmemset-strategy=@var{strategy}
> @@ -37155,6 +37156,18 @@ Here, @code{t5} takes up 2 bytes.
>  @end enumerate
>
>
> +@opindex mbundle-align-loops
> +@opindex mno-bundle-align-loops
> +@item -mbundle-align-loops
> +@itemx -mno-bundle-align-loops
> +Wrap small hot loops in assembler @code{.bundle_lock}/@code{.bundle_unlock}
> +directives so the assembler keeps each loop within a single instruction-cache
> +line, aligning the loop header without inserting executed padding.  Enabled 
> by
> +default for AMD Zen4 and later.  It defaults @option{-falign-loops},
> +@option{-falign-jumps}, and @option{-falign-labels} to 1 unless explicitly 
> set.
> +The maximum loop-body size considered is set by the
> +@option{x86-tight-loop-max-size} parameter.
> +
>  @opindex mno-align-stringops
>  @opindex malign-stringops
>  @item -mno-align-stringops
> diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
> index aad51550eba..35324653699 100644
> --- a/gcc/doc/sourcebuild.texi
> +++ b/gcc/doc/sourcebuild.texi
> @@ -3114,6 +3114,10 @@ Target supports @option{-fstack-protector}.
>  @item gas
>  Target uses GNU @command{as}.
>
> +@item gcc_bundle_align_loops
> +Target compiler and assembler support @option{-mbundle-align-loops} and
> +@code{.bundle_lock flex}.
> +
>  @item gc_sections
>  Target supports @option{--gc-sections}.
>
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-1.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-1.c
> new file mode 100644
> index 00000000000..c7120731f5f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-1.c
> @@ -0,0 +1,16 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler "\\.bundle_lock flex" } } */
> +/* { dg-final { scan-assembler "\\.bundle_unlock" } } */
> +
> +typedef int (*callee_t) (int, int *);
> +
> +int
> +indirect_sum (callee_t callee, int n, int *array, long calls)
> +{
> +  int sink = 0;
> +  for (long call = 0; call < calls; ++call)
> +    sink += callee (n, array);
> +  return sink;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-2.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-2.c
> new file mode 100644
> index 00000000000..957e853dfb2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-2.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler "\\.bundle_lock flex" } } */
> +/* { dg-final { scan-assembler "\\.bundle_unlock" } } */
> +
> +__attribute__((noinline,noipa))
> +int
> +f (int n, int *p)
> +{
> +  int i = 0, s = 0;
> +  if (n & 1)
> +    goto mid;
> +  while (i < n)
> +    {
> +      s += p[i & 1023];
> +    mid:
> +      i++;
> +    }
> +  return s;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-3.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-3.c
> new file mode 100644
> index 00000000000..7cdfa8db985
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-3.c
> @@ -0,0 +1,26 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler "\\.bundle_lock flex" } } */
> +/* { dg-final { scan-assembler "\\.bundle_unlock" } } */
> +
> +int a[1024];
> +__attribute__((hot,noinline,noipa))
> +int
> +two_backedges (int n, int m)
> +{
> +  int i = 0, s = 0;
> +outer_head:
> +  {
> +    int j = 0;
> +  inner_head:
> +    s += a[(i + j) & 1023];
> +    if (__builtin_expect (s < 0, 0))
> +      goto outer_head;
> +    if (++j < m)
> +      goto inner_head;
> +  }
> +  if (++i < n)
> +    goto outer_head;
> +  return s;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-4.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-4.c
> new file mode 100644
> index 00000000000..80bca4ec200
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-4.c
> @@ -0,0 +1,22 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver5 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler-times "\\.bundle_lock flex" 1 } } */
> +/* { dg-final { scan-assembler-times "\\.bundle_unlock" 1 } } */
> +
> +volatile int sink;
> +int a[1024];
> +int
> +classify (int n)
> +{
> +  int s = 0;
> +  asm volatile (".rept 0\n\t.byte 0x90\n\t.endr" ::: "memory");
> +  for (int i = 0; i < n; i++)
> +    {
> +      if (a[i] & 1) sink = i;
> +      if (a[i] & 2) sink = i + 1;
> +      if (a[i] & 4) s += 4;
> +      s += a[i];
> +    }
> +  return s;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-5.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-5.c
> new file mode 100644
> index 00000000000..7aa07effa6e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-5.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler "\\.bundle_lock flex" } } */
> +/* { dg-final { scan-assembler "\\.bundle_unlock" } } */
> +
> +void *mc (const void *s, int c, unsigned long n)
> +{
> +  const unsigned char *p = s;
> +  while (n--)
> +    {
> +      if (*p == (unsigned char) c)
> +        return (void *) p;
> +      p++;
> +    }
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-6.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-6.c
> new file mode 100644
> index 00000000000..1f561e07caa
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-6.c
> @@ -0,0 +1,24 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O3 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler-times "\\.bundle_lock flex" 1 } } */
> +/* { dg-final { scan-assembler-times "\\.bundle_unlock" 1 } } */
> +
> +struct O { long rc; };
> +extern void dealloc (struct O *);
> +void
> +f (struct O **v, long n)
> +{
> +  asm volatile (".rept 10\n\t.byte 0x90\n\t.endr" ::: "memory");
> +  while (--n >= 0)
> +    {
> +      struct O *x = v[n];
> +      if (x)
> +        {
> +          if (x->rc < 0)
> +            ;
> +          else if (--x->rc == 0)
> +            dealloc (x);
> +        }
> +    }
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/bundle-align-7.c 
> b/gcc/testsuite/gcc.target/i386/bundle-align-7.c
> new file mode 100644
> index 00000000000..b713d25d0b7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/bundle-align-7.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O1 -march=znver3 -falign-loops=1 -falign-jumps=1 
> -falign-labels=1 -mbundle-align-loops" } */
> +/* { dg-require-effective-target gcc_bundle_align_loops } */
> +/* { dg-final { scan-assembler "\\.bundle_lock flex" } } */
> +/* { dg-final { scan-assembler "\\.bundle_unlock" } } */
> +
> +static int A[1 << 20];
> +
> +int
> +nested_sum (int n, int m)
> +{
> +  int sum = 0;
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < m; ++j)
> +      sum += A[i * m + j];
> +  return sum;
> +}
> diff --git a/gcc/testsuite/lib/target-supports.exp 
> b/gcc/testsuite/lib/target-supports.exp
> index d467e9fa3b7..3286c40b07d 100644
> --- a/gcc/testsuite/lib/target-supports.exp
> +++ b/gcc/testsuite/lib/target-supports.exp
> @@ -12491,6 +12491,22 @@ proc check_effective_target_gas { } {
>      return $use_gas_saved
>  }
>
> +# Return 1 if the compiler was configured with assembler support for
> +# the flex .bundle_lock argument used by -mbundle-align-loops.
> +
> +proc check_effective_target_gcc_bundle_align_loops { } {
> +    return [check_no_messages_and_pattern gcc_bundle_align_loops \
> +                   "\\.bundle_lock flex" assembly {
> +       int a[1024];
> +       int foo (int n) {
> +         int s = 0;
> +         for (int i = 0; i < n; i++)
> +           s += a[i];
> +         return s;
> +       }
> +    } "-O2 -march=znver4 -mbundle-align-loops -falign-loops=1"]
> +}
> +
>  # Return 1 if GNU ld is used.
>
>  proc check_effective_target_gld { } {
> --
> 2.34.1
>

Reply via email to