On Wed, 16 Sep 2026, Oleg Tolmatcev wrote:

> Targets that cannot guarantee arbitrary stack alignment cap stack slots at
> MAX_SUPPORTED_STACK_ALIGNMENT.  Win64 SEH caps MAX_STACK_ALIGNMENT at 128
> bits; aarch64 does not define MAX_STACK_ALIGNMENT and is capped at
> STACK_BOUNDARY, also 128 bits.  assign_stack_local silently ignored any
> alignment beyond that cap.
> 
> A 256-bit vector requires 32-byte alignment, but its slot could therefore
> receive only 16-byte alignment while later MEM attributes still claimed 32.
> x86 could select vmovaps from that MEM_ALIGN and fault on the under-aligned
> slot (PR54412, MSYS2/MINGW-packages#1209).  An escaped pointer has the same
> problem even when the local move is unaligned, since its type still promises
> the callee a 32-byte-aligned object.
> 
> Make assign_stack_local_1 honour an explicit alignment beyond the cap by
> reserving enough space and aligning an address inside it at run time, as
> assign_parm_setup_block already did for BLKmode parameters and
> expand_stack_vars does for locals.  Base the extra space on the alignment
> guaranteed for VIRTUAL_STACK_VARS_REGNUM rather than on the maximum alignment
> supported by the backend, since the frame itself is not guaranteed to have
> the latter alignment on all targets.
> 
> Let a new internal assign_stack_local_2 return the address before run-time
> alignment.  Store it in the temp slot and realign the address on reuse, while
> avoiding splitting or combining over-aligned slots.
> 
> Honour required temporary alignment at run time, but cap merely preferred
> alignment.  Preserve zero-size behavior and the target's preferred stack
> boundary, and emit run-time alignment only during expansion to RTL.
> 
> Use ASLK_REDUCE_ALIGN where less alignment is acceptable: LRA spill slots and
> the deliberately over-aligned SJLJ function context.
> 
> Regtested on x86_64-pc-linux-gnu and, under QEMU, on
> aarch64-unknown-linux-gnu, with no new failures.

Thanks a lot, this is OK for trunk.

Thanks,
Richard.

> gcc/ChangeLog:
> 
>       PR target/54412
>       * except.cc (sjlj_build_landing_pads): Pass ASLK_REDUCE_ALIGN.
>       * function.cc (assign_stack_local_2): New function, split from
>       assign_stack_local_1.  Honour an explicit alignment beyond
>       MAX_SUPPORTED_STACK_ALIGNMENT by overallocating the slot and aligning
>       its address at run time.  Optionally return the address before run-time
>       alignment.
>       (assign_stack_local_1): Wrap assign_stack_local_2.
>       (temp_slot): Add base_addr.
>       (assign_stack_temp_for_type): Distinguish required and preferred
>       alignment.  Record the address before run-time alignment and recalculate
>       the aligned address when reusing an over-aligned slot.  Do not split an
>       over-aligned BLKmode slot.
>       (combine_temp_slots): Do not combine over-aligned slots.
>       (assign_parm_setup_block): Let assign_stack_local align the slot.
>       * lra-spills.cc (assign_mem_slot): Pass ASLK_REDUCE_ALIGN.
> 
> gcc/testsuite/ChangeLog:
> 
>       PR target/54412
>       * g++.target/i386/pr54412-highpart-extract-rt.C: Adjust the comment to
>       describe safe reuse of an over-aligned temporary.
> 
> Signed-off-by: Oleg Tolmatcev <[email protected]>
> ---
>  gcc/except.cc     |  12 ++---
>  gcc/function.cc   | 131 +++++++++++++++++++++++++++++++++++-----------
>  gcc/lra-spills.cc |   5 +-
>  3 files changed, 108 insertions(+), 40 deletions(-)
> 
> diff --git a/gcc/except.cc b/gcc/except.cc
> index 98876833ee..e3b8645733 100644
> --- a/gcc/except.cc
> +++ b/gcc/except.cc
> @@ -1478,9 +1478,9 @@ sjlj_build_landing_pads (void)
>                                       TYPE_MODE (sjlj_fc_type_node),
>                                       TYPE_ALIGN (sjlj_fc_type_node));
>        crtl->eh.sjlj_fc
> -     = assign_stack_local (TYPE_MODE (sjlj_fc_type_node),
> -                           int_size_in_bytes (sjlj_fc_type_node),
> -                           align);
> +     = assign_stack_local_1 (TYPE_MODE (sjlj_fc_type_node),
> +                             int_size_in_bytes (sjlj_fc_type_node),
> +                             align, ASLK_RECORD_PAD | ASLK_REDUCE_ALIGN);
>  
>        sjlj_mark_call_sites ();
>        sjlj_emit_function_enter (dispatch_label);
> @@ -1496,9 +1496,9 @@ sjlj_build_landing_pads (void)
>                                       TYPE_MODE (sjlj_fc_type_node),
>                                       TYPE_ALIGN (sjlj_fc_type_node));
>        crtl->eh.sjlj_fc
> -     = assign_stack_local (TYPE_MODE (sjlj_fc_type_node),
> -                           int_size_in_bytes (sjlj_fc_type_node),
> -                           align);
> +     = assign_stack_local_1 (TYPE_MODE (sjlj_fc_type_node),
> +                             int_size_in_bytes (sjlj_fc_type_node),
> +                             align, ASLK_RECORD_PAD | ASLK_REDUCE_ALIGN);
>  
>        sjlj_mark_call_sites ();
>        sjlj_emit_function_enter (NULL);
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 9358787b26..02796e2f7a 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -155,6 +155,7 @@ static bool contains (const rtx_insn *, 
> hash_table<insn_cache_hasher> *);
>  static void prepare_function_start (void);
>  static void do_clobber_return_reg (rtx, void *);
>  static void do_use_return_reg (rtx, void *);
> +static rtx assign_stack_local_2 (machine_mode, poly_int64, int, int, rtx *);
>  
>  
>  /* Stack of nested functions.  */
> @@ -370,6 +371,10 @@ add_frame_space (poly_int64 start, poly_int64 end)
>     -2 means use BITS_PER_UNIT,
>     positive specifies alignment boundary in bits.
>  
> +   A positive ALIGN that exceeds MAX_SUPPORTED_STACK_ALIGNMENT is honoured
> +   by overallocating the slot and aligning its address at run time, with
> +   insns emitted at the current position, unless KIND has ASLK_REDUCE_ALIGN.
> +
>     KIND has ASLK_REDUCE_ALIGN bit set if it is OK to reduce
>     alignment and ASLK_RECORD_PAD bit set if we should remember
>     extra space we allocated for alignment purposes.  When we are
> @@ -381,11 +386,24 @@ add_frame_space (poly_int64 start, poly_int64 end)
>  rtx
>  assign_stack_local_1 (machine_mode mode, poly_int64 size,
>                     int align, int kind)
> +{
> +  return assign_stack_local_2 (mode, size, align, kind, NULL);
> +}
> +
> +/* Like assign_stack_local_1.  If BASE_ADDR is nonnull, set it to the slot
> +   address before run-time alignment, or to NULL_RTX if none was needed.  */
> +
> +static rtx
> +assign_stack_local_2 (machine_mode mode, poly_int64 size,
> +                   int align, int kind, rtx *base_addr)
>  {
>    rtx x, addr;
>    poly_int64 bigend_correction = 0;
>    poly_int64 slot_offset = 0, old_frame_offset;
> -  unsigned int alignment, alignment_in_bits;
> +  unsigned int alignment, alignment_in_bits, dynamic_align = 0;
> +
> +  if (base_addr)
> +    *base_addr = NULL_RTX;
>  
>    if (align == 0)
>      {
> @@ -404,9 +422,23 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
>  
>    alignment_in_bits = alignment * BITS_PER_UNIT;
>  
> -  /* Ignore alignment if it exceeds MAX_SUPPORTED_STACK_ALIGNMENT.  */
> +  /* The frame cannot be aligned beyond MAX_SUPPORTED_STACK_ALIGNMENT.  If 
> more
> +     is requested, pad the slot so that an over-aligned object still fits.  
> */
>    if (alignment_in_bits > MAX_SUPPORTED_STACK_ALIGNMENT)
>      {
> +      if (align > 0 && !(kind & ASLK_REDUCE_ALIGN) && maybe_ne (size, 0))
> +     {
> +       gcc_assert (currently_expanding_to_rtl);
> +       dynamic_align = alignment_in_bits;
> +
> +       if (crtl->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
> +         crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
> +
> +       /* Pad from the alignment the frame base is guaranteed to have.  */
> +       size += ((alignment_in_bits
> +                 - REGNO_POINTER_ALIGN (VIRTUAL_STACK_VARS_REGNUM))
> +                / BITS_PER_UNIT);
> +     }
>        alignment_in_bits = MAX_SUPPORTED_STACK_ALIGNMENT;
>        alignment = MAX_SUPPORTED_STACK_ALIGNMENT / BITS_PER_UNIT;
>      }
> @@ -506,7 +538,7 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
>   found_space:
>    /* On a big-endian machine, if we are allocating more space than we will 
> use,
>       use the least significant bytes of those that are allocated.  */
> -  if (mode != BLKmode)
> +  if (mode != BLKmode && !dynamic_align)
>      {
>        /* The slot size can sometimes be smaller than the mode size;
>        e.g. the rs6000 port allocates slots with a vector mode
> @@ -531,6 +563,15 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
>                         (slot_offset + bigend_correction,
>                          Pmode));
>  
> +  if (dynamic_align)
> +    {
> +      if (base_addr)
> +     *base_addr = addr;
> +      addr = align_dynamic_address (addr, dynamic_align);
> +      mark_reg_pointer (addr, dynamic_align);
> +      alignment_in_bits = dynamic_align;
> +    }
> +
>    x = gen_rtx_MEM (mode, addr);
>    set_mem_align (x, alignment_in_bits);
>    MEM_NOTRAP_P (x) = 1;
> @@ -592,6 +633,9 @@ public:
>    /* The size of the slot, including extra space for alignment.  This
>       info is for combine_temp_slots.  */
>    poly_int64 full_size;
> +  /* The address before run-time alignment, or NULL_RTX if the slot does not
> +     need run-time alignment.  */
> +  rtx base_addr;
>  };
>  
>  /* Entry for the below hash table.  */
> @@ -803,6 +847,18 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>  
>    align = get_stack_local_alignment (type, mode);
>  
> +  unsigned int required_align
> +    = (mode == BLKmode ? BITS_PER_UNIT : GET_MODE_ALIGNMENT (mode));
> +  if (type)
> +    required_align = MAX (required_align, TYPE_ALIGN (type));
> +
> +  bool dynamic_align = required_align > MAX_SUPPORTED_STACK_ALIGNMENT;
> +
> +  if (dynamic_align)
> +    align = required_align;
> +  else
> +    align = MAX (required_align, MIN (align, MAX_SUPPORTED_STACK_ALIGNMENT));
> +
>    /* Try to find an available, already-allocated temporary of the proper
>       mode which meets the size and alignment requirements.  Choose the
>       smallest one with the closest alignment.
> @@ -844,7 +900,8 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64 
> size, tree type)
>        /* If there are enough aligned bytes left over, make them into a new
>        temp_slot so that the extra bytes don't get wasted.  Do this only
>        for BLKmode slots, so that we can be sure of the alignment.  */
> -      if (GET_MODE (best_p->slot) == BLKmode)
> +      if (GET_MODE (best_p->slot) == BLKmode
> +       && best_p->align <= MAX_SUPPORTED_STACK_ALIGNMENT)
>       {
>         int alignment = best_p->align / BITS_PER_UNIT;
>         poly_int64 rounded_size = aligned_upper_bound (size, alignment);
> @@ -852,6 +909,7 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64 
> size, tree type)
>         if (known_ge (best_p->size - rounded_size, alignment))
>           {
>             p = ggc_alloc<temp_slot> ();
> +           p->base_addr = NULL_RTX;
>             p->in_use = false;
>             p->size = best_p->size - rounded_size;
>             p->base_offset = best_p->base_offset + rounded_size;
> @@ -869,12 +927,20 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>       }
>      }
>  
> +  bool reused_p = selected != 0;
> +
>    /* If we still didn't find one, make a new temporary.  */
>    if (selected == 0)
>      {
>        poly_int64 frame_offset_old = frame_offset;
>  
>        p = ggc_alloc<temp_slot> ();
> +      p->base_addr = NULL_RTX;
> +
> +      poly_int64 slot_size
> +     = (mode == BLKmode
> +          ? aligned_upper_bound (size, (int) align / BITS_PER_UNIT)
> +          : size);
>  
>        /* We are passing an explicit alignment request to assign_stack_local.
>        One side effect of that is assign_stack_local will not round SIZE
> @@ -883,14 +949,15 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>        So for requests which depended on the rounding of SIZE, we go ahead
>        and round it now.  We also make sure ALIGNMENT is at least
>        BIGGEST_ALIGNMENT.  */
> -      gcc_assert (mode != BLKmode || align == BIGGEST_ALIGNMENT);
> -      p->slot = assign_stack_local_1 (mode,
> -                                   (mode == BLKmode
> -                                    ? aligned_upper_bound (size,
> -                                                           (int) align
> -                                                           / BITS_PER_UNIT)
> -                                    : size),
> -                                   align, 0);
> +      gcc_assert (mode != BLKmode
> +               || dynamic_align
> +               || (align
> +                   >= MIN (BIGGEST_ALIGNMENT,
> +                           MAX_SUPPORTED_STACK_ALIGNMENT)));
> +
> +      rtx base_addr;
> +      p->slot = assign_stack_local_2 (mode, slot_size, align, 0, &base_addr);
> +      p->base_addr = base_addr;
>  
>        p->align = align;
>  
> @@ -901,7 +968,9 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64 
> size, tree type)
>        can be either above or below this stack slot depending on which
>        way the frame grows.  We include the extra space if and only if it
>        is above this slot.  */
> -      if (FRAME_GROWS_DOWNWARD)
> +      if (dynamic_align)
> +     p->size = slot_size;
> +      else if (FRAME_GROWS_DOWNWARD)
>       p->size = frame_offset_old - frame_offset;
>        else
>       p->size = size;
> @@ -929,10 +998,19 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>  
>    pp = temp_slots_at_level (p->level);
>    insert_slot_to_list (p, pp);
> -  insert_temp_slot_address (XEXP (p->slot, 0), p);
> +
> +  rtx addr = XEXP (p->slot, 0);
> +
> +  if (reused_p && p->align > MAX_SUPPORTED_STACK_ALIGNMENT)
> +    {
> +      addr = align_dynamic_address (p->base_addr, p->align);
> +      mark_reg_pointer (addr, p->align);
> +    }
> +
> +  insert_temp_slot_address (addr, p);
>  
>    /* Create a new MEM rtx to avoid clobbering MEM flags of old slots.  */
> -  slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
> +  slot = gen_rtx_MEM (mode, addr);
>    vec_safe_push (stack_slot_list, slot);
>  
>    /* If we know the alias set for the memory that will be used, use
> @@ -1061,7 +1139,8 @@ combine_temp_slots (void)
>  
>        next = p->next;
>  
> -      if (GET_MODE (p->slot) != BLKmode)
> +      if (GET_MODE (p->slot) != BLKmode
> +       || p->align > MAX_SUPPORTED_STACK_ALIGNMENT)
>       continue;
>  
>        for (q = p->next; q; q = next_q)
> @@ -1070,7 +1149,8 @@ combine_temp_slots (void)
>  
>         next_q = q->next;
>  
> -       if (GET_MODE (q->slot) != BLKmode)
> +       if (GET_MODE (q->slot) != BLKmode
> +           || q->align > MAX_SUPPORTED_STACK_ALIGNMENT)
>           continue;
>  
>         if (known_eq (p->base_offset + p->full_size, q->base_offset))
> @@ -2948,21 +3028,8 @@ assign_parm_setup_block (struct assign_parm_data_all 
> *all,
>          ? MAX (DECL_ALIGN (parm), BITS_PER_WORD) : DECL_ALIGN (parm));
>  
>        SET_DECL_ALIGN (parm, parm_align);
> -      if (DECL_ALIGN (parm) > MAX_SUPPORTED_STACK_ALIGNMENT)
> -     {
> -       rtx allocsize = gen_int_mode (size_stored, Pmode);
> -       get_dynamic_stack_size (&allocsize, 0, DECL_ALIGN (parm), NULL);
> -       stack_parm = assign_stack_local (BLKmode, UINTVAL (allocsize),
> -                                        MAX_SUPPORTED_STACK_ALIGNMENT);
> -       rtx addr = align_dynamic_address (XEXP (stack_parm, 0),
> -                                         DECL_ALIGN (parm));
> -       mark_reg_pointer (addr, DECL_ALIGN (parm));
> -       stack_parm = gen_rtx_MEM (GET_MODE (stack_parm), addr);
> -       MEM_NOTRAP_P (stack_parm) = 1;
> -     }
> -      else
> -     stack_parm = assign_stack_local (BLKmode, size_stored,
> -                                      DECL_ALIGN (parm));
> +      stack_parm = assign_stack_local (BLKmode, size_stored,
> +                                    DECL_ALIGN (parm));
>        if (known_eq (GET_MODE_SIZE (GET_MODE (entry_parm)), size))
>       PUT_MODE (stack_parm, GET_MODE (entry_parm));
>        set_mem_attributes (stack_parm, parm, 1);
> diff --git a/gcc/lra-spills.cc b/gcc/lra-spills.cc
> index 07801e39d5..f55839f50b 100644
> --- a/gcc/lra-spills.cc
> +++ b/gcc/lra-spills.cc
> @@ -146,8 +146,9 @@ assign_mem_slot (int i)
>    x = slots[slot_num].mem;
>    if (!x)
>      {
> -      x = assign_stack_local (BLKmode, slots[slot_num].size,
> -                           slots[slot_num].align);
> +      x = assign_stack_local_1 (BLKmode, slots[slot_num].size,
> +                             slots[slot_num].align,
> +                             ASLK_RECORD_PAD | ASLK_REDUCE_ALIGN);
>        slots[slot_num].mem = x;
>      }
>  
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to