On Mon, Aug 24, 2026 at 10:12 PM Oleg Tolmatcev
<[email protected]> 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.
>
> Finally, defer_stack_allocation and expand_one_stack_var_1 computed a
> local's stack alignment in two different ways, so a variable the former
> considered small enough to allocate immediately could trip the
> large-alignment assert in the latter (PR target/120192).  Use
> align_local_variable in both.
>
> Regtested on x86_64-pc-linux-gnu and, under QEMU, on
> aarch64-unknown-linux-gnu, with no new failures.
>
> gcc/ChangeLog:
>
>         PR target/54412
>         PR target/120192
>         * cfgexpand.cc (defer_stack_allocation): Use align_local_variable
>         to compute the alignment, matching expand_one_stack_var_1.
>         * 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/cfgexpand.cc |  8 +++--
>  gcc/function.cc  | 87 +++++++++++++++++++++++++++++++++++-------------
>  2 files changed, 68 insertions(+), 27 deletions(-)
>
> diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
> index 128cfc7768..8720b77ced 100644
> --- a/gcc/cfgexpand.cc
> +++ b/gcc/cfgexpand.cc
> @@ -1995,9 +1995,11 @@ defer_stack_allocation (tree var, bool toplevel)
>    if (flag_stack_protect || asan_sanitize_stack_p ())
>      return true;
>
> -  unsigned int align = TREE_CODE (var) == SSA_NAME
> -    ? TYPE_ALIGN (TREE_TYPE (var))
> -    : DECL_ALIGN (var);
> +  /* Keep this in sync with expand_one_stack_var_1.  For some targets,
> +     LOCAL_DECL_ALIGNMENT can raise a local variable's effective stack
> +     alignment above DECL_ALIGN, and immediate allocation would then
> +     trip the high-alignment assert in expand_one_stack_var_1.  */
> +  unsigned int align = align_local_variable (var, false) * BITS_PER_UNIT;

If you do only this change, which cases improve and why is this not sufficient
as fix?  I'm mostly looking for a possibility to split up the change and clearly
associate test coverage with fix parts here.

Thanks,
Richard.

>    /* We handle "large" alignment via dynamic allocation.  We want to handle
>       this extra complication in only one place, so defer them.  */
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 5ade058d72..dbce86f60f 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);
> --
> 2.55.0.windows.5
>

Reply via email to