When returning a value from a switch case statement when calling a function with the z88dk__fastcall convention, sdcc returns the incorrect value.
/// GPL 2.0
#include <stdint.h>
#include <stdio.h>
#ifndef __SDCC
#define __z88dk_fastcall
#endif
int global = 42;
int GetValue() { return global; }
int SwitchValue( int value ) __z88dk_fastcall {
switch( value ) {
case 1: return GetValue();
case 2: return GetValue();
case 3: return GetValue();
case 4: return GetValue();
}
return 0;
}
void main() {
printf( "%d\n", SwitchValue( 2 ) );
}
#ifdef __SDCC
__sfr __at 0xff sif;
int putchar(int c)
{
sif= 'p';
sif= c;
return c;
}
#endif
$ sdcc -mz80 ./fastcall.c -o fastcall.ihx
$ ucsim_z80 -I if=outputs[0xff] fastcall.ihx
uCsim 0.7.5, Copyright (C) 1997 Daniel Drotos.
0> Loading from fastcall.ihx
3130 words read from fastcall.ihx
r
Simulation started, PC=0x000000
2
$ gcc ./fastcall.c
$ ./a.out
42
A summary of the assembler.
_GetValue::
ld de, (_global)
ret
_SwitchValue::
;./fastcall.c:12: switch( value ) {
ld a, l
dec a
or a, h
jp Z,_GetValue
...
The optimization of jumping to the next function fails to account for the caller using z88dk_fastcall calling convention.
I use z88dk_fastcall for all my single parameter functions since it provides a large speedup and program size reduction in sdcc pre-4.2 versions. I can remove it now I know it's problem, since I'm using a macro and can simply define it as empty.
I simplified the code a bit. Turns out it can happen in any function returning a value from a function call when using z88dk_fastcall. The switch isn't necessary (I just happened to be returning a function from a switch in the function it happened in).
I also take back being able to remove fastcall, I have a couple of assembler functions that rely on it, so I'll have to slowly deprecate using it when I switch fully to 4.2.
Fixed in [r13537] a few days ago.
Related
Commit: [r13537]