Decompile compete — capability + measured scoreboard

How Spark competes at every level on SPARK_BC without false marketing. Spark / SparkLang only.

Never publish “Spark beats Ghidra / IDA / Binary Ninja / LLM4Decompile / OpenBin” without the measured JSON from make decompile-bench. Measurement only. Do not copy OpenBin code.

Related: Decompile · research/LLM_DECOMPILE.md · site /docs/decompile-compete.html.

Beat-axes (SPARK_BC domain — where we can actually win)

Axis Why it is a Spark win
100% round-trip compile → dump → recompile same .spark → identical sha256 on published fixtures (tools/spark-bc-dump/roundtrip.py)
Best structured dump Sections + string symbols + const/op xrefs + txt/JSON/HTML export (bc_dump.analyze_bc)
Best local privacy No upload — analysis project stays on disk
Integrated train / weights Same BC drives TRAIN/STEP + init safetensors

Classic RE tools win ELF/PE/mach-O interactive decompile. That is loss / N/A for Spark SoT — not a silent claim.

Feature parity matrix

Legend: win / tie / loss / na (wrong domain) / not probed (tool absent, or no scripted byte-level probe on the bench box). Every badge is computed by tools/spark-bc-dump/decompile_bench.py from measured probe output — round-trip %, symbol/xref extraction on real bytes, the local ELF probe, and byte-level probes of any competitor tool actually present. The matrix is rendered from website/data/decompile-scoreboard.json in the measured scoreboard below — there is no hand-painted table on this page.

Commands

make spark-bootstrap spark
        make test-decompile-compete # unit: richer dump + project
        make decompile-roundtrip # compile→dump→recompile hash
        make decompile-bench # metrics + scoreboard JSON
        # also records elf_local_probe from ./spark-binary-probe --elf
        ./spark-binary-probe --elf ./spark
        

Local ELF probe emits claim: local_elf_probe_not_ghidra and a sections[] index — still a loss on Multi-format ELF/PE vs Ghidra/IDA/Binja/OpenBin. Status note, not a domain flip.

Analysis project (single file):

PYTHONPATH=python python3 tools/spark-bc-dump/analyze_project.py \
         docs/examples/spark-train-step.sparkbc \
         -o out/analyze/train-step \
         --source examples/spark_train_step.spark
        

Richer dump exports:

PYTHONPATH=python python3 tools/spark-bc-dump/dump.py \
         docs/examples/spark-train-step.sparkbc --json -o /tmp/bc.json
        PYTHONPATH=python python3 tools/spark-bc-dump/dump.py \
         docs/examples/spark-train-step.sparkbc --html -o /tmp/bc.html
        

Measured scoreboard (data-driven)

After make decompile-bench:

  • website/data/decompile-scoreboard.json
  • docs/examples/decompile-scoreboard.json (mirror)
  • sample project: out/decompile-bench/sample-project/

The site page loads the JSON and renders win/tie/loss/na/not-probed from disk data — no hand-painted green checkmarks, no author-declared badges.

External tools (objdump, r2, ghidra, ida, binaryninja, openbin) are probed; if absent → status: absent / verdict: skip and every parity cell for that tool is not probed. If present but not a SPARK_BC decoder → verdict: na-sparkbc, with byte-level probe results (e.g. objdump -d on a real .sparkbc, objdump -h on ./spark) driving the measured cells.

What is still not claimed

Claim Status
Lossless BC → .spark source not implemented
Spark beats Ghidra on ELF false / loss
LLM perfectly decompiles SPARK_BC never
OpenBin clone never — local SoT only

Live measured scoreboard — loaded from /data/decompile-scoreboard.json after make decompile-bench. Empty box means regenerate JSON.