Skip to content

v0.7.0

Choose a tag to compare

@macabeus macabeus released this 14 Sep 23:14
· 207 commits to main since this release
dd0c74e

asmlift 0.7.0 lifts one function's assembly to source that recompiles byte-identical. This release renames the
enumeration's vocabulary
, which is an npm API break with no aliases. It also re-sources the kleod benchmark by ROM
address, and adds the Fan Explorer, a benchmark page that defines, prices and shows every variation asmlift
enumerates.

Measured against 0.6.0

Both releases use the same pinned m2c (ad5c529a). The numbers come from each release's committed results.json.

0.6.0 0.7.0
benchmark rows 1,022 1,064 +42
asmlift byte-exact 581 626 +45
— synthetic rows 501 / 770 546 / 812 +45
— real project rows 80 / 252 80 / 252 ±0 (see kleod below)
m2c byte-exact 407 416 +9
matches lost, over the 986 rows present in both releases 0 (8 gained)

Real decompilation projects, byte-exact rows out of 42 each:

project 0.6.0 0.7.0
pokeemerald (GBA) 22 23
kleod (GBA) 21 19 ¹
sa3 (GBA) 15 15
marioparty3 (N64) 15 15
af (N64) 4 4
snowboardkids2 (N64) 3 4

¹ Not comparable. kleod's 42 rows are now drawn from testyourmine/kleod and keyed by ROM address (#198). They are a
different set of functions, not the same functions scoring worse. No row in the other five projects moved in that swap.

A second check ran the CLI the way a user does, from each project's root. Both releases decompiled 3,541 undecompiled
functions
drawn from the checkouts' own asm/ trees:

  • GBA: sa3, kleod, FireEmblem7J.
  • N64: marioparty3, af, snowboardkids2.

The result was 0 regressed and 1 newly lifted. 3,423 outputs are byte-identical; the other 118 carry the improvements
below. The packages as published to npm were then installed from the registry and run over the same 3,541 functions, and
every output and stderr was byte-identical to the build the release gates tested.

Breaking changes

The enumeration's words are now candidate, fan, winner, variation, dropped, withheld (#199). docs/vocabulary.md
defines them. axis, lever, variant, and label for a candidate's name are gone, and a candidate's name is now its
list of variations.

Package Old New
@asmlift/core/rank-axes the module @asmlift/core/rank-variations
@asmlift/core/rank-variations STRUCTURING_AXES / StructuringAxis (.probeGate, .variantGate) STRUCTURE_VARIATIONS / StructureVariation (.sharedGate, .perLiftGate)
SHAPE_PRODUCTS / SHAPE_SUBSETS / applyShapes STACKED_VARIATIONS / STACKED_SUBSETS / applyStacked
PRE_FAN_PRODUCTS PRE_RESPELL_VARIATIONS
LIVEBASE_ / BASEFOLD_ / UNFOLDED_ / ORDERBASE_ADMISSIONS; BaseAdmission *_HOISTS; BaseHoist
SIGN_CANDS ([i].label) SIGNEDNESS ([i].variation)
@asmlift/core/rank Candidate.label: string, DroppedCandidate.label, WithheldCandidate.label .variations: readonly string[]
Candidate.group Candidate.preference
RankedResult.best RankedResult.winner
composeLevers composeRespellVariations
EnumerateOptions.onLeverError(label, error) onEnumerationError(variations, error)
EnumerateOptions.onAxisGated(suffix) onVariationGated(variation): defsite, not /defsite
@asmlift/core/variation-tokens (new) — VARIATION_TOKENS, VARIATION_KINDS, variationToken, parseVariation, hasVariation, hasVariations, joinVariations, splitVariations
@asmlift/core/variation-definitions (new) — VARIATION_DEFINITIONS, VARIATION_KIND_DEFINITIONS, READER_WORDS
@asmlift/cli RankOptions.onLeverError onEnumerationError
CandidateReport.label (DecompileReport.candidates[]) CandidateReport.variations: readonly string[]
rankedSummaryLine({ best }) rankedSummaryLine({ winner })

Printed output.

  • asmlift: [lever] <name> threw … is now asmlift: [threw] <function> <variations> threw (no candidate from it): ….
  • [ranked] … best <name>: is now [ranked] … winner <name>:.

What is new

  • Fan Explorer (#200). A benchmark tab that catalogues every variation by kind: signedness, lift, structure, respell,
    and symbol map.
    • Every variation has a definition. Its example compiles to two different objects, and "offered when" is read from the
      gate that decides it.
    • It prices each variation by candidates per win, and plots cost against win rate.
    • The function detail shows the winning spelling, and the variations that were considered and lost.
    • Every benchmark row now publishes fanVariations, which variations its fan carried.
  • A closed variation registry (#199, #200). Core mints every variation name through the registry's types, so an
    unregistered name is a type error.
  • Decompilation:
    • Early returns recovered from a one-set-arm select diamond (#188).
    • A short-circuit's branch sense, read per site from all its facts (#163, #187).
    • A cross-jumped store tail, spelled once after the if (#191) or pushed back into every else-if arm (#169).
    • Comparison ladders spelled as ladders when the compiler's layout says so (#170).
    • Bitfield stores of zero (#168), and variable indices into a pointer global's array member (#167).
    • Narrowing casts fused with their scale (#190).
    • Pool addresses advanced in place (#196).
    • Loop accumulators and nested-loop carried values kept in their variables (#164, #185).
    • Unconsumed volatile reads emitted (#183).
    • Kleod's CountCollectedGems, taken from 290 to a byte-exact match over #156–#191.
  • Benchmark and tooling:
    • pnpm bench sweep, a corpus-wide compile-free differential re-lift (#195).
    • Each row's candidate fan and ranking cost in the artifact (#192).
    • An IR oracle for structure(), so a dropped call fails a test (#193).
    • One dated cost doc, with a staleness gate (#194).
    • Reproducing a row with its own script (#178).
    • bench fan (#180) and bench gates (#181).
    • Preflight checks before a run (#175), and denominators on every score (#174).
    • The unmatchable-quirks register (#197).

Known limits

  • N64 is honest but narrow. The wall is still branch-likely instructions, then hardware float. These are loud
    declines, never wrong C.
  • GameCube / PowerPC still has synthetic rows only, and no real-project coverage. Real rows from three GameCube
    decomps are planned next.
  • @asmlift/core ships raw TypeScript. The asmlift binary is bundled and runs on stock Node, but importing the library
    needs a TypeScript loader such as tsx.

Install

npm i -g @asmlift/cli     # the binary
npm i @asmlift/core       # the library (needs a TS loader)