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)
