Skip to content

v0.9.0 — 64-bit values, floating point and the stack frame

Choose a tag to compare

@macabeus macabeus released this 07 Oct 00:21
· 89 commits to main since this release
f46f352

Upgrading: two changes to decomp.yaml

Both refuse the run with a message naming the problem, so a config that needs them cannot be misread.

  • A decomp.yaml must meet the decomp_settings spec (#274). It needs name, platform, and versions, each version with its fullname (and its paths). A config with only platform and tools now exits 66:
    asmlift: decomp.yaml: name: Invalid input: expected string, received undefined
    decomp.yaml: versions: Invalid input: expected array, received undefined
    
    A project that builds no versions can write versions: [].
  • The flags placeholder in tools.asmlift.compiler is {{flags}} (#291), replacing {{cflags}}. A command still writing {{cflags}} is refused as an unknown placeholder. The --cflags flag keeps its name and fills {{flags}}.

tools.asmlift is also checked now: a key of the wrong type or a typo such as targt: exits 66 and names the key, where before it was ignored.

What the decompiler learned

  • 64-bit integers (#244, #249, #250, #255). A 64-bit value is one value across its register pair: a carry chain is one 64-bit add, a 64-bit argument crosses a call when a prototype states its width, and a 64-bit return comes back in a register pair.
  • Floating point. The first hardware-float layer for MIPS and PowerPC single precision (#262), agbcc's soft-double ABI (#275), and double arguments and literals (#279).
  • Structs returned by value (#257, #277), through a hidden pointer where each compiler uses one.
  • The stack frame. Address-taken locals at a frame offset (#270), a frame object above the outgoing argument block (#288), several captured addresses a callee writes through kept as one object (#296), and a frame word that survives a call (#276).
  • Calls. One call plan for every frontend, including MIPS calls (#285, #286), and a call through a register, as agbcc's _call_via_rN thunks make (#290).
  • Spelling. A global's pointer value plus a byte offset is no longer scaled by its pointee size when no declaration reaches it (#292); a local narrowed at its reads or at its declaration (#282, #289); a static defined in the lifted body (#273); loops no other form takes as while (1) (#271).

The honest numbers

0.8.1 0.9.0
corpus 1,198 rows 1,334
asmlift matches 654 785
m2c matches 446 467
asmlift match rate 54.6% 58.8%
asmlift declines 303 227
  • On the 1,194 rows shared with 0.8.1, asmlift moves 652 → 719 (+67: gcc 2.7.2 KMC 24, CodeWarrior 20, agbcc 18, IDO 7). m2c holds at 444.
  • On the 140 new rows (94 synthetic, 46 real GBA functions from pokeemerald, kleod and sa3), asmlift matches 66 and m2c 23. Most real ones were chosen because asmlift's own work touched them, or from a random sample; the dataset PRs (#287, #294, #295) say which.
  • Two rows stopped matching on purpose. llshl and llshr on CodeWarrior matched in 0.8.1 by emitting return __shl2i();: the bytes matched, and the C dropped both 64-bit operands. Since #244 they decline ("no model for the runtime helper") rather than publish that.
  • Four 0.8.1 rows are gone: three kleod functions upstream renamed or moved, and one retired synthetic cell.

Also

  • --help and --version answer on stdout with exit 0 (#297).
  • A GameCube or N64 project needs no tools.asmlift.target when its objdiff.json names the compiler of the unit defining --name (#297).
  • Compiles go through @match-kit/compiler (#291), which asmlift shares with Transmuter. Compiler output goes to files rather than pipes, which fixes compilers run under Wine. Failure messages name what happened (crashed (SIGSEGV), did not run (exit 127)), and the compile directory reads <compile-dir>.
  • Scoring goes through @match-kit/scoring (#263).

Breaking, for programmatic use

@asmlift/cli publishes its sources, so these reach anyone importing them:

  • decompileRanked, scoreSource, scoreCandidate and decompileWithReport are async, and CandidateCompiler returns a promise (#291). Core's tracer hook is probeSource.
  • @asmlift/cli/objdiff and @asmlift/cli/objdiff-version are gone; scoring lives in @match-kit/scoring. @asmlift/cli/score keeps scoreObjects, releaseTarget and the score types (#263).
  • @asmlift/cli/config no longer exports loadDecompConfig, DecompConfig, DecompVersion or LoadedConfig; read a config with @match-kit/decomp-yaml and check it with asmliftBlock (#274).

Install

npm install -g @asmlift/cli

Full changelog: v0.8.1...v0.9.0