SDCC bug, present on the latest snapshot (4.2.10 #13772):
./sdcc -v
SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/sm83/tlcs90/ez80_z80/z80n/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15/mos6502 4.2.10 #13772 (Linux)
published under GNU General Public License (GPL)
Example offending code:
typedef struct tSpriteStruct {
char dummy;
signed char handle;
} SpriteStruct;
SpriteStruct sprites[2];
signed char SMS_addSprite (unsigned int y, unsigned int x_tile) __naked __preserves_regs(d,e,iyh,iyl) __sdcccall(1) {
__asm
ld a,l
ret
__endasm;
}
void main(void) {
sprites[0].handle = SMS_addSprite(0, 0);
sprites[1].handle = SMS_addSprite(0, 0);
}
issue: value in DE (the variable address) gets overwritten with the parameter value (constant) - thus lost - prior to the fuction call, but it's used nevertheless like if it was already there:
_main::
;main.c:17: sprites[0].handle = SMS_addSprite(0, 0);
; skipping iCode since result will be rematerialized
; genPlus
;fetchLitPair de
ld de, #_sprites + 1
; genSend
; genMove_o
;fetchLitPair de
ld de, #0x0000
; genSend
; genMove_o
;fetchLitPair hl
ld hl, #0x0000
; genCall
call _SMS_addSprite
; genMove_o
; genAssign (pointer)
ld (de), a
workaround: remove D and E from __preserves_regs(d,e,iyh,iyl)
CL: sdcc -c -mz80 --fverbose-asm --no-peep main.c
I see two possible solutions:
1) The caller could realize that de is used for a parameter, and thus cannot be actually used to hold another value.
2) Another perspective is that de is not actually unused, since it is used for passing a parameter. So SDCC should emit a diagnostic when it sees such a declaration.
Actually, doing both would also make sense.
I think your point 1 is absolutely correct and it should be sufficient to address this issue.
Regarding the point 2 instead, I tend to disagree. As far as I know, the preserves_regs attribute should indicate that a function body doesn't change the contents of those listed registers, which does include the fact that it will use the registers used to pass the parameter(s) but not update them, as in the simple example above.
Keeping this intact would mean that two successive calls to the same function that preserves the only passed parameter should be able to turn into a single LD opcode prior to the first call if the parameter is the same, as in
I agree. I'll fix the bug first. The reuse of the parameter could be added as an optimization later.
Fixed in [r13806].
Related
Commit: [r13806]