https://gcc.gnu.org/g:8d381a5e520aeebe387c6f6353357e76d489ed7f
commit r17-5257-g8d381a5e520aeebe387c6f6353357e76d489ed7f Author: Stefan Schulze Frielinghaus <[email protected]> Date: Fri Oct 16 16:40:26 2026 +0200 explow: Restore stack_pointer_delta for allocate_stack [PR114432] Function allocate_dynamic_stack_space() saves and restores stack_pointer_delta only if expander allocate_stack is not available. On s390 with -mbackchain the expander becomes available and changes stack_pointer_delta indirectly by calling into anti_adjust_stack() (as the else branch does when the expander is not available). In that case nothing is saved or restored, so the altered stack_pointer_delta leaks. With -fstack-check, expand_function_end() emits a function-entry probe through anti_adjust_stack_and_probe() which then picks up the leaked value. This can be observed in reproducer pr114432-1.c where the entry-probe insns 47 and 50 carry wrong REG_ARGS_SIZE notes: (insn 47 2 48 2 (parallel [ (set (reg/f:DI 15 %r15) (plus:DI (reg/f:DI 15 %r15) (const_int -8216 [0xffffffffffffdfe8]))) (clobber (reg:CC 33 %cc)) ]) "pr114432-1.c":7:1 -1 (expr_list:REG_ARGS_SIZE (const_int 8304 [0x2070]) <--- should be 8216 (nil))) ... (insn 50 49 3 2 (parallel [ (set (reg/f:DI 15 %r15) (plus:DI (reg/f:DI 15 %r15) (const_int 8216 [0x2018]))) (clobber (reg:CC 33 %cc)) ]) "pr114432-1.c":7:1 -1 (expr_list:REG_ARGS_SIZE (const_int 88 [0x58]) <--- should be 0 (nil))) ... (insn 30 29 31 7 (set (reg/f:DI 15 %r15) (reg/f:DI 62 [ saved_stack.1_6 ])) -1 (expr_list:REG_ARGS_SIZE (const_int 0 [0]) <--- ok (nil))) Here the constant-sized alloca with a size of 88 bytes leaks into REG_ARGS_SIZE (8304 = 8216 + 88). Note, REG_ARGS_SIZE for insn 30 is correct since the insn restores the stack pointer after the alloca (the zero is hard coded in expand_stack_restore()). The entry probe, ending in insn 50, falls through into the loop head with the leaked 88 bytes, whereas the back edge of the loop arrives with 0 bytes (insn 30 restores the stack). Due to those two traces with differing args_sizes, args_size_undefined is set. Because -fexceptions makes a call in the loop trace possibly throwing, and no insn in that trace sets args_size before the first throwing call, args_size_defined_for_eh is not set. This renders the assert gcc_assert (!ti->args_size_undefined || ti->args_size_defined_for_eh); failing in connect_traces(). Fixed by saving/restoring stack_pointer_delta for allocate_stack, too. PR middle-end/114432 gcc/ChangeLog: * explow.cc (allocate_dynamic_stack_space): Save and restore stack_pointer_delta also if expander allocate_stack is available. gcc/testsuite/ChangeLog: * gcc.target/s390/pr114432-1.c: New test. Diff: --- gcc/explow.cc | 16 +++++++--------- gcc/testsuite/gcc.target/s390/pr114432-1.c | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/gcc/explow.cc b/gcc/explow.cc index 9c7c834aaabb..6acecc701c76 100644 --- a/gcc/explow.cc +++ b/gcc/explow.cc @@ -1603,6 +1603,8 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align, /* Don't let anti_adjust_stack emit notes. */ suppress_reg_args_size = true; + poly_int64 saved_stack_pointer_delta = stack_pointer_delta; + /* Perform the required allocation from the stack. Some systems do this differently than simply incrementing/decrementing from the stack pointer, such as acquiring the space by calling malloc(). */ @@ -1618,8 +1620,6 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align, } else { - poly_int64 saved_stack_pointer_delta; - if (!STACK_GROWS_DOWNWARD) emit_move_insn (target, force_operand (addr, target)); @@ -1647,8 +1647,6 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align, emit_label (space_available); } - saved_stack_pointer_delta = stack_pointer_delta; - /* If stack checking or stack clash protection is requested, then probe the stack while allocating space from it. */ if (flag_stack_check && STACK_CHECK_MOVING_SP) @@ -1658,15 +1656,15 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align, else anti_adjust_stack (size); - /* Even if size is constant, don't modify stack_pointer_delta. - The constant size alloca should preserve - crtl->preferred_stack_boundary alignment. */ - stack_pointer_delta = saved_stack_pointer_delta; - if (STACK_GROWS_DOWNWARD) emit_move_insn (target, force_operand (addr, target)); } + /* Even if size is constant, don't modify stack_pointer_delta. + The constant size alloca should preserve + crtl->preferred_stack_boundary alignment. */ + stack_pointer_delta = saved_stack_pointer_delta; + suppress_reg_args_size = false; /* Finish up the split stack handling. */ diff --git a/gcc/testsuite/gcc.target/s390/pr114432-1.c b/gcc/testsuite/gcc.target/s390/pr114432-1.c new file mode 100644 index 000000000000..5e53755875f0 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/pr114432-1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fexceptions -fstack-check -mbackchain" } */ + +void a (int *); +int b (char *); +void c (void) +{ + for (;;) + { + char d[88]; + int e __attribute__ ((cleanup (a))) = 0; + b (d); + } +}
