Menu ▾ ▴

#4229 z80: live IY value is overwritten while accessing a global operand

closed-fixed
z80 (24)
Z80
5
17 hours ago
2 days ago
No

Version

SDCC : z80 4.6.4 #17075 (MSVC) from the official 2026-10-23 snapshot. The problem also
reproduces with released SDCC 4.5.0 and 4.6.0.

Command

sdcc -mz80 --opt-code-size --max-allocs-per-node 3000 -S sdcc-iy-live-global-reproducer.c

Problem

In the second checksum expression, generated code reads actors[index].mode and .hits through
IY after IY has been reassigned to the global checksum. The compiler therefore reads bytes 4
and 5 after checksum, not bytes 4 and 5 of the selected ActorState.

Representative invalid output from the larger runtime fixture:

    ld iy,#_actors
    add iy,de
    ; reads x, y and timer
    ld iy,#_checksum
    ; updates checksum
    push iy
    pop de
    ld hl,#4
    add hl,de
    ld e,(hl)       ; reads checksum+4, not actor->mode
    ld l,5(iy)      ; reads checksum+5, not actor->hits

The full ColecoVision runtime oracle expects world state 1, 13 collisions, score 425 and checksum

  1. The unmodified compiler produces checksum 1742 at 3000 nodes.

Source-level diagnosis and candidate fix

src/z80/ralloc2.cc:IYinst_ok() returns true for + and non-pointer = before applying its own
protection against using IY as a pointer register for a direct/global operand. Restricting those
two broad allowances when an unrelated live allocation occupies IY prevents the overwrite. See
sdcc-iy-live-global-fix.patch.

An exact local build of unmodified revision 17075 reproduces checksum 1742. Rebuilding the same
source with this patch produces checksum 1478. Runtime validation also passes at allocation-node
budgets 500, 1000, 2000, 3000, 5000 and 10000. At 3000 nodes the valid cartridge occupies 2253
bytes; the invalid one occupied 2248 bytes.

This candidate patch is deliberately narrow. A budget of only 100 nodes exposes a separate result
mismatch and is not claimed fixed here.

1 Attachments

Discussion

  • Daniel Bienvenu

    Daniel Bienvenu - 2 days ago

    Originally discovered while compiling the devkitSMS Gameplay State Update benchmark for ColecoVision, but the attached standalone C reproducer has no devkitSMS dependency. The defect is in SDCC’s Z80 register allocator.

     
  • Philipp Klaus Krause

    • labels: --> z80
    • assigned_to: Philipp Klaus Krause
    • Category: other --> Z80
     
  • Philipp Klaus Krause

    • status: open --> closed-fixed
     
  • Philipp Klaus Krause

    Hello, Daniel. More than 25 years ago, I first saw a ColecoVision at a friend, then looked into writing my own games for it, and reading your documentation. Which then got me to start improving the z80 backend of SDCC, and becoming an SDCC developer.

    This bug should be fixed in [r17093]. The approach used is differnet from your patch, since adding restrictions in the register allocator tends to have a higher code quality cost vs. improvements in code generation.

     

    Related

    Commit: [r17093]


Log in to post a comment.