Skip to content

v0.8.0 β€” GameCube and Wii

Choose a tag to compare

@macabeus macabeus released this 19 Sep 18:09
· 184 commits to main since this release
16f09fd

GameCube and Wii arrive, every benchmark row compiles at its own build's flags, and the corpus
grows to 1,198 rows across nine decomp projects.

GameCube and Wii

asmlift lifts PowerPC for CodeWarrior, and the benchmark's real tier now includes three dtk
projects β€” Animal Crossing, Mario Party 4 and Pikmin β€” alongside the six it already had.

  • Three CodeWarrior targets: mwcc_233_163n (GC/1.2.5n), mwcc_242_81 (GC/1.3.2) and
    mwcc_247_107 (GC/2.6). Seven --target ids in total, up from five.
  • REL modules. A module's code is placed by the game's loader, so its ELF is relocatable and
    every section starts at zero. --module <name> reads that module's own .plf beside the base
    ELF, unions in the DOL's globals, and narrows the objdiff.json unit lookup β€” REL code repeats
    names across modules, and a function several units define is refused rather than guessed.
  • Gekko disassembly. The PowerPC reader wants -M gekko; without it objdump decodes the
    console's paired-single opcodes as POWER VSX, turning psq_l f30,120(r1),0,0 into
    lq r30,112(r1) β€” a different register file at a different offset.
  • C++. Free and non-virtual member functions with word-sized fields, CodeWarrior mangling
    included. Virtual dispatch, references and ctors/dtors decline.
  • Several .text sections per object. CodeWarrior gives one translation unit many sections,
    all named .text and all starting at address 0, so --name is required there and the function
    is read from the section its st_shndx names.

Per-unit compiler flags

A project's units rarely build with one flag set. Every benchmark row now carries the flags its
own build compiles it with, and the CLI takes them from the first of --cflags, the objdiff.json
unit that defines the function, or the words already in your compiler command. The run says which
it used:

asmlift: [flags] -mthumb-interwork -O2 -fhex-asm (compiler command)

Templates gained {{cflags}} and {{cc}}, so a dtk project writes the flags once and lets asmlift
find each function's unit. targetFor(toolchain, cflags) is the library route from a toolchain and
its flags to a codegen profile.

What the decompiler learned

Seven capability rounds, each measured against the corpus:

  • A MIPS high half is a link-time placeholder, not the address zero β€” a half-materialised
    address refuses instead of becoming a literal 0.
  • A likely branch nullifies its delay slot, so the slot is modelled by placement: its own block
    on the taken edge alone.
  • A PowerPC global is recovered from its relocation, with a per-kind policy that refuses the
    linker names C cannot spell (extern u32 __vt__6System; compiles, and would be wrong).
  • Outgoing stack arguments are consumed where the declaration and the code agree.
  • A void return; is an edge the assembly decides, emitted only where the epilogue's in-edges
    show the source wrote one.
  • A MIPS parameter is narrowed where the object says it was declared narrow β€” a conjunction of
    a dead home store and a widen-in-place, never one alone.
  • The objdump reader stops losing words it could not decode.

The honest numbers

Raw totals flatter this release, so here is the split.

0.7.0 0.8.0
corpus 1,064 rows 1,198
projects 6 9
asmlift matches 626 654
m2c matches 416 446
asmlift match rate 58.8% 54.6%
  • On the 1,064 rows shared with 0.7.0, asmlift moves 626 β†’ 643; m2c moves 416 β†’ 419.
    That +17 is what the capability rounds bought.
  • On the 134 new rows, asmlift matches 11 where m2c matches 27.

So the match rate falls, and it falls because the GameCube corpus is hard for asmlift and the
benchmark is not arranged to hide that. The remaining PowerPC walls are floats and stack-as-data;
on MIPS they are calls, unmodelled effect instructions, floating-point condition codes and
unrecovered back-edges.

Breaking

  • tools.asmlift.candidateCache is removed. A decomp.yaml that still declares it is silently
    ignored, and that project's candidate-object cache turns back on β€” unset means on. It only
    ever silenced a refusal asmlift already reaches on its own: a command that runs the compiler
    through docker, ssh, wine or another opaque runtime refuses the cache and says so. If you need it
    off, ASMLIFT_CANDCACHE=0.
  • CompileCommandOptions drops candidateCache and gains cflags and cc, required exactly
    when the command carries the matching placeholder.

Also

@asmlift/cli now documents the ten environment variables it reads, including ASMLIFT_CANDCACHE,
which is on when unset. The pinned m2c baseline moved. @asmlift/core's README no longer
claims the package is unpublished.

Install

npm install -g @asmlift/cli

Full changelog: v0.7.0...v0.8.0