The program below confuses gcc in such a way that it generates code loading the byte-address of bar() into the Z register, which causes icall to jump off to neverneverland. Rather, the double-byte address of bar() should be loaded into Z before the indirect call. This bug is also present in gcc 4.0.3 and 3.4.3. avr-gcc -v gives: Target: avr Configured with: ../gcc-4.1.0/configure --prefix=/home/regehr/gcc-4.1.0-avr-bin --disable-libssp --disable-nls --target=avr Thread model: single gcc version 4.1.0 Compile with: avr-gcc -Os -Wall shifty.c -o shifty.elf Program: struct fseqp_void { void (*p) (void); char *e; }; struct fseqp_void c[2]; void bar (void) { } void foo (void) { int i; for (i=0; i<32; i++); } int main (void) { c[0].e = (char *)bar + 2; c[0].p = bar; foo (); struct fseqp_void x = c[0]; void (*start) (void) = x.p; (*start)(); return 0; }
Dr. John, Can you provide additional information: - What AVR processor was this compiled for? You don't have the required -mmcu= flag in your command line. - Can you provide a disassembly listing showing the problem? I don't necessarily see the problem with my 4.1.1 compiler, -mmcu=atmega128. Maybe I'm just dense. Eric
Created attachment 13324 [details] Pre-processed testcase.
Created attachment 13325 [details] Disassembly of the shifty3.i test case.
Confirmed bug. shifty3.i is a test case showing the problem. Compiled with avr-gcc 4.1.2, with: avr-gcc -Os shifty.c -o shifty.o shifty3.dis is a disassembly of shifty.o (with avr-objdump -d shifty.o). The problem is exhibited here: 0000005a <main>: 5a: 89 e2 ldi r24, 0x29 ; 41 5c: 90 e0 ldi r25, 0x00 ; 0 5e: 90 93 61 00 sts 0x0061, r25 62: 80 93 60 00 sts 0x0060, r24 66: 82 e5 ldi r24, 0x52 ; 82 68: 92 e0 ldi r25, 0x02 ; 2 6a: 90 93 64 00 sts 0x0064, r25 6e: 80 93 63 00 sts 0x0063, r24 72: e0 91 60 00 lds r30, 0x0060 76: f0 91 61 00 lds r31, 0x0061 7a: 09 95 icall 7c: e0 91 63 00 lds r30, 0x0063 80: f0 91 64 00 lds r31, 0x0064 84: 09 95 icall The correct word address of the function foo is taken and stored (address 5a to 62), but then the *byte* address of the function foo is taken, and an offset added, and stored (address 66 to 6e). The following indirect call (address 7c to 84) is then incorrect. The PC has to be a *word* address. Adding a fixed offset to a function pointer, and then doing an indirect call, is valid in an AVR application as it is possible to copy or place functions at fixed offsets like this. Please mark this bug as NEW. Known to fail: 4.1.1, 4.1.2
Bug still present in 4.2.0, and 4.3-20070525. To test bug (fixed from last comment): avr-gcc -Os shifty3.i -o shifty3.o avr-objdump -d shifty3.o > shifty3.dis Compare output.
Bug is still present in 4.2.2. Some more info: I rewrote the example to (atleast for me) little more clear example. struct fseqp_void { void (*p) (void); char *e; }; struct fseqp_void c; void bar (void){} int main (void) { c.e = (char *)bar + 2; c.p = bar; c.p(); return 0; } The problem is the re-use after loading bar into the struct: ldi r24,lo8(bar+2) <<Hmm this should need gs() I guess ldi r25,hi8(bar+2) sts (c+2)+1,r25 sts c+2,r24 sbiw r24,2 <<Sub 2, but we also need a shift! sts (c)+1,r25 sts c,r24 However if comment out the dump the load off the variable e, then all is well int main (void) { //c.e = (char *)bar + 2; c.p = bar; c.p(); return 0; } ldi r24,lo8(gs(bar)) ldi r25,hi8(gs(bar)) sts (c)+1,r25 sts c,r24 ldi r24,lo8(0) ldi r25,hi8(0) And if the order of assigment is reverted then all is well also c.e = (char *)bar + 2; c.p = bar; c.p(); return 0; ldi r24,lo8(gs(bar)) ldi r25,hi8(gs(bar)) sts (c)+1,r25 sts c,r24 adiw r24,2 sts (c+2)+1,r25 sts c+2,r24 ldi r24,lo8(0) ldi r25,hi8(0) So the problem is the optimizer, which forget that the 16-bit program address is different from the data address.
Fixed 4.5