The test case is gcc.c-torture/execute/built-in-setjmp.c.
The __builtin_setjmp stores Y+1 in the setjmp buffer. With -O0 the first
instruction after the jmp does a "sbiw r28, 1" that restores the
original value, but for some reason, with higher optimization levels,
this instruction is optimized away, leaving R28 pointing to the wrong
address by one.
The __builtin_setjmp code:
if (__builtin_setjmp (buf))
16a: ce 01 movw r24, r28
16c: 01 96 adiw r24, 0x01 ; 1
Notice the increment here, before storing R28
16e: 90 93 07 01 sts 0x0107, r25
172: 80 93 06 01 sts 0x0106, r24
176: 8f ee ldi r24, 0xEF ; 239
178: 90 e0 ldi r25, 0x00 ; 0
17a: 90 93 09 01 sts 0x0109, r25
17e: 80 93 08 01 sts 0x0108, r24
182: ed b7 in r30, 0x3d ; 61
184: fe b7 in r31, 0x3e ; 62
186: f0 93 0b 01 sts 0x010B, r31
18a: e0 93 0a 01 sts 0x010A, r30
The __builtin_longjmp code:
__builtin_longjmp (buf, 1);
10c: e0 91 08 01 lds r30, 0x0108
110: f0 91 09 01 lds r31, 0x0109
114: c0 91 06 01 lds r28, 0x0106
118: d0 91 07 01 lds r29, 0x0107
11c: 80 91 0a 01 lds r24, 0x010A
120: 90 91 0b 01 lds r25, 0x010B
no decrement, R28 is used "as is".
Created attachment 15395 [details]
Setjmp patch for AVR
The attached patch is a fix for AVR target. MIPS does something similar to get around same issue.
The real problem is with gcc builin setjmp receiver being removed by optimizers.
Optimizers think that frame_pointer load in receiver is unneeded and remove it!
The patch loads the frame pointer in the nonlocal_goto, making the receiver (where it jumps to) empty, so bad optimization cannot remove it. Additionally, it avoids the unnecessary arithmetic around frame pointer offsets.
This patch was tested and the testcase passes.
Further changes may be required in the future if AVR 24bit jumps are to be supported.
Subject: Bug 34879
Date: Mon Jun 2 22:08:25 2008
New Revision: 136297
* config/avr/avr.c (TARGET_BUILTIN_SETJMP_FRAME_VALUE): Redefine.
(avr_builtin_setjmp_frame_value): New function.
* config/avr/avr.md (nonlocal_goto_receiver): Define.
Fixed in 4.4.0.