On Thu, 27 Aug 2026, Oleg Tolmatcev wrote:

> Targets that cannot realign the stack cap their stack alignment and rely
> on the middle end to align over-aligned stack objects inside their slots.
> 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.
> 
> In several places the middle end does not do that alignment.  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).
> There is an even bigger problem.  Even with MEM_ALIGN and the slot's
> alignment consistent, an escaped pointer loses the information about the
> alignment cap: GCC assumes the object's natural alignment and uses an
> aligned access on an under-aligned object, which faults on x86.
> 
> aarch64 does not crash only because its loads and stores do not fault on
> a misaligned address; the object is under-aligned there just the same.
> 
> Add assign_stack_local_aligned, which preserves the requested alignment by
> overallocating a slot and aligning an address inside it, and use it
> wherever an object gets a stack slot of its own.  This is what
> assign_parm_setup_block already did for BLKmode parameters and what
> expand_stack_vars does for locals.  Such a slot has no fixed frame offset,
> so it cannot be reused as a temp slot.

It seems to me that assign_stack_local itself guarantees alignment.
Why does it (or rather assign_stack_local_1 with kind == ASLK_RECORD_PAD)
not work?

Richard.

> Regtested on x86_64-pc-linux-gnu and, under QEMU, on
> aarch64-unknown-linux-gnu, with no new failures.
> 
> gcc/ChangeLog:
> 
>       PR target/54412
>       * function.cc (assign_stack_local_aligned): New function, split
>       out of assign_parm_setup_block.
>       (assign_stack_temp_for_type): Compute the required alignment from
>       the type and the mode.  Allocate an over-aligned temporary with
>       assign_stack_local_aligned and give it no temp_slot entry.  Take
>       the resulting alignment from the allocated slot.
>       (assign_parm_setup_block): Use assign_stack_local_aligned.
>       (assign_parm_setup_reg): Likewise.
>       (assign_parm_setup_stack): Likewise.
> 
> Signed-off-by: Oleg Tolmatcev <[email protected]>
> ---
>  gcc/function.cc | 87 +++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 63 insertions(+), 24 deletions(-)
> 
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 9358787b26..0bc8e6da89 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -551,6 +551,36 @@ assign_stack_local (machine_mode mode, poly_int64 size, 
> int align)
>    return assign_stack_local_1 (mode, size, align, ASLK_RECORD_PAD);
>  }
>  
> +/* Like assign_stack_local, but preserve requested over-alignment by
> +   overallocating a BLKmode slot and aligning an address within it.  */
> +
> +static rtx
> +assign_stack_local_aligned (machine_mode mode, poly_int64 size,
> +                         unsigned int align)
> +{
> +  if (align > MAX_SUPPORTED_STACK_ALIGNMENT)
> +    {
> +      /* align_dynamic_address emits the alignment arithmetic into a pseudo. 
>  */
> +      gcc_assert (can_create_pseudo_p ());
> +
> +      /* The slot's base is aligned to MAX_SUPPORTED_STACK_ALIGNMENT, so 
> moving
> +      it to the next ALIGN boundary needs at most the difference between the
> +      two alignments.  Keep the size polynomial for scalable modes.  */
> +      poly_int64 allocsize
> +     = size + ((align - MAX_SUPPORTED_STACK_ALIGNMENT) / BITS_PER_UNIT);
> +      rtx slot = assign_stack_local (BLKmode, allocsize,
> +                                  MAX_SUPPORTED_STACK_ALIGNMENT);
> +      rtx addr = align_dynamic_address (XEXP (slot, 0), align);
> +      mark_reg_pointer (addr, align);
> +      slot = gen_rtx_MEM (mode, addr);
> +      set_mem_align (slot, align);
> +      MEM_NOTRAP_P (slot) = 1;
> +      return slot;
> +    }
> +
> +  return assign_stack_local (mode, size, align);
> +}
> +
>  /* In order to evaluate some expressions, such as function calls returning
>     structures in memory, we need to temporarily allocate stack locations.
>     We record each allocated temporary in the following structure.
> @@ -801,6 +831,22 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>  
>    gcc_assert (known_size_p (size));
>  
> +  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 inside an overallocated slot.  BASE_OFFSET and FULL_SIZE
> +     cannot describe such a slot, so it gets no temp_slot entry: it is never
> +     reused or freed.  */
> +  if (required_align > MAX_SUPPORTED_STACK_ALIGNMENT)
> +    {
> +      slot = assign_stack_local_aligned (mode, size, required_align);
> +      align = MEM_ALIGN (slot);
> +      goto finalize_slot;
> +    }
> +
>    align = get_stack_local_alignment (type, mode);
>  
>    /* Try to find an available, already-allocated temporary of the proper
> @@ -892,7 +938,7 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64 
> size, tree type)
>                                      : size),
>                                     align, 0);
>  
> -      p->align = align;
> +      p->align = MEM_ALIGN (p->slot);
>  
>        /* The following slot size computation is necessary because we don't
>        know the actual size of the temporary slot until assign_stack_local
> @@ -931,8 +977,11 @@ assign_stack_temp_for_type (machine_mode mode, 
> poly_int64 size, tree type)
>    insert_slot_to_list (p, pp);
>    insert_temp_slot_address (XEXP (p->slot, 0), p);
>  
> +  align = MEM_ALIGN (p->slot);
> +
>    /* Create a new MEM rtx to avoid clobbering MEM flags of old slots.  */
>    slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
> +finalize_slot:
>    vec_safe_push (stack_slot_list, slot);
>  
>    /* If we know the alias set for the memory that will be used, use
> @@ -2948,24 +2997,13 @@ 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_aligned (BLKmode, size_stored, DECL_ALIGN (parm));
> +      int align = MEM_ALIGN (stack_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);
> +      set_mem_align (stack_parm, align);
>      }
>  
>    /* If a BLKmode arrives in registers, copy it to a stack slot.  Handle
> @@ -3366,11 +3404,13 @@ assign_parm_setup_reg (struct assign_parm_data_all 
> *all, tree parm,
>         int align = STACK_SLOT_ALIGNMENT (TREE_TYPE (parm),
>                                           TYPE_MODE (TREE_TYPE (parm)),
>                                           TYPE_ALIGN (TREE_TYPE (parm)));
> -       parmreg
> -         = assign_stack_local (TYPE_MODE (TREE_TYPE (parm)),
> -                               GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (parm))),
> -                               align);
> +       parmreg = assign_stack_local_aligned (TYPE_MODE (TREE_TYPE (parm)),
> +                                             GET_MODE_SIZE (
> +                                               TYPE_MODE (TREE_TYPE (parm))),
> +                                             align);
> +       align = MEM_ALIGN (parmreg);
>         set_mem_attributes (parmreg, parm, 1);
> +       set_mem_align (parmreg, align);
>       }
>  
>        /* We need to preserve an address based on VIRTUAL_STACK_VARS_REGNUM 
> for
> @@ -3523,10 +3563,9 @@ assign_parm_setup_stack (struct assign_parm_data_all 
> *all, tree parm,
>                 || targetm.slow_unaligned_access (GET_MODE (data->entry_parm),
>                                                   align)))
>           align = GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm));
> -       data->stack_parm
> -         = assign_stack_local (GET_MODE (data->entry_parm),
> -                               GET_MODE_SIZE (GET_MODE (data->entry_parm)),
> -                               align);
> +       data->stack_parm = assign_stack_local_aligned (
> +         GET_MODE (data->entry_parm),
> +         GET_MODE_SIZE (GET_MODE (data->entry_parm)), align);
>         align = MEM_ALIGN (data->stack_parm);
>         set_mem_attributes (data->stack_parm, parm, 1);
>         set_mem_align (data->stack_parm, align);
> 

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

Reply via email to