For align == -1, assign_stack_local does not reduce the alignment according to the size; it rounds the size up to a multiple of BIGGEST_ALIGNMENT and only caps the alignment at MAX_SUPPORTED_STACK_ALIGNMENT, which is what the patch records. The other reduction in assign_stack_local_2, to stack_alignment_estimated, only happens once stack realignment has been processed, i.e. not while expand_builtin_apply allocates the block.
The per-register offsets are not lost either: each restore is made with adjust_address (result, mode, size), which derives its MEM_ALIGN from the block's alignment and that offset. On x86_64 the restores at offsets 0, 8, 16, 32, 48 and 64 (%rax, %rdx, %st, %st(1), %xmm0, %xmm1) get A128, A64, A128, A128, A128 and A128. Or did you mean the size of the block itself, which __builtin_return only gets as a pointer? I could pass the alignment from the MEM that assign_stack_local returns instead of recomputing it, if you prefer. Thanks, Igor ________________________________ From: Richard Biener <[email protected]> Sent: Thursday, October 8, 2026 1:53:29 AM To: Igor Shevlyakov Cc: [email protected]; [email protected] Subject: Re: [PATCH] builtins: Give the __builtin_return result block its real alignment [PR112417] [EXTERNAL] Caution: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe. On Wed, 7 Oct 2026, Igor Shevlyakov wrote: > From: Igor Shevlyakov <[email protected]> > > expand_builtin_return wraps the address returned by __builtin_apply in > a BLKmode MEM with the default MEM_ALIGN of 8 bits, so every > return-register restore produced by adjust_address is treated as > byte-aligned. On strict-alignment targets, or targets whose address > legitimacy or load/store selection depends on MEM_ALIGN, this picks > misaligned access sequences or addresses the target cannot handle. > > The argument of __builtin_return must be the value returned by > __builtin_apply, and expand_builtin_apply allocates that block with > assign_stack_local (..., -1), which aligns it to BIGGEST_ALIGNMENT > capped at MAX_SUPPORTED_STACK_ALIGNMENT. Record that alignment on the > MEM. > > The patch attached to the PR goes the other way: it expands each restore > through expand_misaligned_mem_ref whenever MEM_ALIGN is below the mode's > alignment. That only pessimizes valid code: the documentation requires > the argument of __builtin_return to be a value returned by > __builtin_apply, and that block always has the alignment recorded here. > The PR's testcase passes an arbitrary pointer, which is outside that > contract. The alignment is actually rounded down according to 'size' but that size is lost? > PR rtl-optimization/112417 > > gcc/ChangeLog: > > * builtins.cc (expand_builtin_return): Set the alignment of the > result block. > > gcc/testsuite/ChangeLog: > > * gcc.target/i386/pr112417.c: New test. > > Signed-off-by: Igor Shevlyakov <[email protected]> > --- > Bootstrapped and regtested on x86_64-pc-linux-gnu (-m32/-m64), no new > regressions. > > gcc/builtins.cc | 4 ++++ > gcc/testsuite/gcc.target/i386/pr112417.c | 19 +++++++++++++++++++ > 2 files changed, 23 insertions(+) > create mode 100644 gcc/testsuite/gcc.target/i386/pr112417.c > > diff --git a/gcc/builtins.cc b/gcc/builtins.cc > index e31947bfed6..b8cad151624 100644 > --- a/gcc/builtins.cc > +++ b/gcc/builtins.cc > @@ -1845,6 +1845,10 @@ expand_builtin_return (rtx result) > > apply_result_size (); > result = gen_rtx_MEM (BLKmode, result); > + /* RESULT was allocated by __builtin_apply with assign_stack_local > + (..., -1); record the alignment that gives it. */ > + set_mem_align (result, MIN (BIGGEST_ALIGNMENT, > + MAX_SUPPORTED_STACK_ALIGNMENT)); > > if (targetm.have_untyped_return ()) > { > diff --git a/gcc/testsuite/gcc.target/i386/pr112417.c > b/gcc/testsuite/gcc.target/i386/pr112417.c > new file mode 100644 > index 00000000000..2f60af37284 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/pr112417.c > @@ -0,0 +1,19 @@ > +/* PR rtl-optimization/112417 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -mno-avx -mtune=generic" } */ > + > +/* The block returned by __builtin_apply is aligned to BIGGEST_ALIGNMENT, > + so __builtin_return can restore the SSE return registers with aligned > + loads. */ > + > +extern void target (int, double); > + > +void > +fwd (int a, double d) > +{ > + void *args = __builtin_apply_args (); > + void *ret = __builtin_apply ((void (*) ()) target, args, 64); > + __builtin_return (ret); > +} > + > +/* { dg-final { scan-assembler "\tmovdqa\t" } } */ > -- 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)
