The following code reproduces the problem:
/// GPL 2.0 or later
/// SDCC z80 miscompiles a local struct initialised from a ternary of dereferences:
/// room2 = room ? *room : *RoomGet();
/// The generated code loads 2 bytes from the struct and uses them as an address (double dereference).
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint8_t x;
uint8_t y;
} point;
point room1 = { 11, 12 };
const point *RoomGet(void) { return &room1; }
// Expect room2 = 5,6 (copied from room). Broken on sdcc z80
static void Initialiser( const point *room ) {
point room2 = room ? *room : *RoomGet();
printf( "Initialiser %d,%d (expect 5,6)\n", room2.x, room2.y );
}
// Workaround, select pointer first
static void Workaround( const point *room ) {
if ( room == 0 ) room = RoomGet();
point room2 = *room;
printf( "Workaround %d,%d (expect 5,6)\n", room2.x, room2.y );
}
void main( void ) {
point room = { 5, 6 };
Initialiser( &room );
Workaround( &room );
}
#ifdef __SDCC
__sfr __at 0xff sif;
int putchar( int c ) {
sif = 'p';
sif = c;
return c;
}
#endif
// sdcc -mz80 --fverbose-asm ./ternary_deref_init.c -o ternary_deref_init.ihx && ucsim_z80 -I if=outputs[0xff] ternary_deref_init.ihx
// gcc ./ternary_deref_init.c && ./a.out
$ gcc ./ternary_deref_init.c && ./a.out
Initialiser 5,6 (expect 5,6)
Workaround 5,6 (expect 5,6)
$ sdcc -mz80 --fverbose-asm ./ternary_deref_init.c -o ternary_deref_init.ihx && ucsim_z80 -I if=outputs[0xff] ternary_deref_init.ihx
r
Initialiser 84,202 (expect 5,6)
Workaround 5,6 (expect 5,6)
SDCC : z80/sm83/ez80/z80n/mos6502/mos65c02/huc6280 4.6.4 #17016 (Linux)
I believe that the existing candidate fix for [bugs:#4131] (awaiting approval) also fixes this bug.
Related
Bugs: #4131