On Mon, 14 Sep 2026, Oleg Tolmatcev wrote:

> Targets that cannot realign the stack cap their stack alignment.  Win64 SEH
> caps MAX_STACK_ALIGNMENT at 128 bits; aarch64 does not define
> MAX_STACK_ALIGNMENT at all and is capped at STACK_BOUNDARY, also 128.
> assign_stack_local silently ignored any alignment beyond that cap.
> 
> A 256-bit vector requires 32-byte alignment, its slot gets the capped 16
> bytes, and MEM_ALIGN is set to 32 anyway.  x86 selects vmovaps from
> MEM_ALIGN and faults on the under-aligned slot (PR54412,
> MSYS2/MINGW-packages#1209).  Even with MEM_ALIGN consistent, an escaped
> pointer carries only the alignment of the type, and the callee uses an
> aligned access on the under-aligned object.  aarch64 does not crash only
> because its loads and stores do not fault on a misaligned address.
> 
> Make assign_stack_local_1 honour an explicit alignment beyond the cap by
> overallocating the slot 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.  Such a slot has no fixed frame offset, so
> assign_stack_temp_for_type keeps an over-aligned temporary out of the temp
> slot list, and caps the merely preferred alignment of other temporaries so
> they can still be reused.

That is because we cannot release the full slot (I don't remember
of we have the "free" corresponding to assign_stack_local)?  The
oversized whole block should still be aligned to
MAX_SUPPORTED_STACK_ALIGNMENT, no?

> Callers that can do with less pass ASLK_REDUCE_ALIGN: LRA spill slots, which
> are allocated after register allocation, and the sjlj function context, whose
> alignment is a deliberate overestimate.
> 
> Regtested on x86_64-pc-linux-gnu and, under QEMU, on 
> aarch64-unknown-linux-gnu,
> with no new failures.
> 
> gcc/ChangeLog:
> 
>       PR target/54412
>       * except.cc (sjlj_build_landing_pads): Pass ASLK_REDUCE_ALIGN.
>       * function.cc (assign_stack_local_1): Honour a positive ALIGN beyond
>       MAX_SUPPORTED_STACK_ALIGNMENT unless KIND has ASLK_REDUCE_ALIGN, by
>       overallocating the slot and aligning its address at run time.
>       (assign_stack_temp_for_type): Allocate a temporary whose required
>       alignment exceeds MAX_SUPPORTED_STACK_ALIGNMENT without a temp_slot
>       entry.  Cap the preferred alignment of other temporaries at
>       MAX_SUPPORTED_STACK_ALIGNMENT and adjust the BLKmode assert.
>       (assign_parm_setup_block): Let assign_stack_local align the slot.
>       * lra-spills.cc (assign_mem_slot): Pass ASLK_REDUCE_ALIGN.
> 
> Signed-off-by: Oleg Tolmatcev <[email protected]>
> ---
>  gcc/except.cc     | 12 ++++-----
>  gcc/function.cc   | 68 +++++++++++++++++++++++++++++++++--------------
>  gcc/lra-spills.cc |  5 ++--
>  3 files changed, 57 insertions(+), 28 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..fbe341b96e 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -370,6 +370,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
> @@ -385,7 +389,7 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
>    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 (align == 0)
>      {
> @@ -404,9 +408,19 @@ 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))

can you elaborate on maybe_ne (size, 0)?  I am surprised we get here
for a known_eq (size, 0)

> +     {
> +       gcc_assert (can_create_pseudo_p ());

Can we try asserting currently_expanding_to_rtl instead?

And when called later we still get a wrong MEM_ALIGN downstream?
Given we do a

  set_mem_align (x, alignment_in_bits);

here, so currently with the capped MAX_SUPPORTED_STACK_ALIGNMENT value,
maybe we should refuse to override this with a larger value later.
We could mark the MEM with an additional flag, indicating a larger
alignment cannot be guaranteed.

> +       dynamic_align = alignment_in_bits;
> +       /* 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 +520,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 +545,13 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
>                         (slot_offset + bigend_correction,
>                          Pmode));
>  
> +  if (dynamic_align)
> +    {
> +      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;
> @@ -801,7 +822,24 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>  
>    gcc_assert (known_size_p (size));
>  
> -  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));
> +
> +  /* Alignment beyond what the frame can guarantee needs an address aligned
> +     at run time, so such a temporary is never reused or freed.  */

So I think this means we should avoid creating such over-aligned
stack temps.  At least code expects we can free/reuse them and
as explained I see no reason we couldn't do that?  But yes,
assign_stack_local_1 does not get us the pointer to the
allocated block and size - but it could, as alternate output?
Or maybe the use of assign_stack_local_1 below is not really
the best API as seen by the comment before the call.

> +  if (required_align > MAX_SUPPORTED_STACK_ALIGNMENT)
> +    {
> +      slot = assign_stack_local_1 (mode, size, required_align, 0);
> +      align = MEM_ALIGN (slot);
> +      goto finalize_slot;
> +    }
> +
> +  /* Cap the preferred alignment at MAX_SUPPORTED_STACK_ALIGNMENT so that the
> +     slot can be reused like any other temporary.  */
> +  align = MIN (get_stack_local_alignment (type, mode),
> +            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
> @@ -883,7 +921,9 @@ 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);
> +      gcc_assert (mode != BLKmode
> +               || align == MIN (BIGGEST_ALIGNMENT,
> +                                MAX_SUPPORTED_STACK_ALIGNMENT));
>        p->slot = assign_stack_local_1 (mode,
>                                     (mode == BLKmode
>                                      ? aligned_upper_bound (size,
> @@ -935,6 +975,7 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64 
> size, tree type)
>    slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
>    vec_safe_push (stack_slot_list, slot);
>  
> + finalize_slot:
>    /* If we know the alias set for the memory that will be used, use
>       it.  If there's no TYPE, then we don't know anything about the
>       alias set for the memory.  */
> @@ -2948,21 +2989,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);

This has the side-effect of eventually doing

  /* We can't attempt to minimize alignment necessary, because we don't
     know the final value of preferred_stack_boundary yet while executing
     this code.  */
  if (crtl->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
    crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;

we might want to preserve that?


I'll add as general disclaimer that while I touched some of the
alignment code before I have nowhere from a complete picture of
this, so I welcome input from others.

Overall I think not simply ignoring an alignment request is the
correct thing to do.

So thanks for keeping to work on this,
Richard.

> -       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