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
>