Skip to content

[AUTOMATED] Add experimental C++ type graphs and scoped native provenance - #103

Draft
sjkim1127 wants to merge 22 commits into
Noelo-Lab:mainfrom
sjkim1127:codex/cpp-type-policy
Draft

sjkim1127 wants to merge 22 commits into
Noelo-Lab:mainfrom
sjkim1127:codex/cpp-type-policy

Conversation

@sjkim1127

@sjkim1127 sjkim1127 commented Oct 13, 2026 •

Copy link
Copy Markdown

[AUTOMATED]

Qualification / dependency experiment follow-up (proposed)

This follow-up strengthens evidence reuse, specifies experimental qualification
boundaries and adds reproducible compiler controls. It does not adopt official
C++ semantics, update the Rust-Joern pin, or qualify corpus C++ byte matching.

Native/source provenance

  • Guard reused C++ GED/type preparation against canonical PDB/dSYM replacement,
    modification, removal or appearance, including changes during preparation.
    Preserve UUID/GUID/Age matching and embedded-DWARF priority; track caller-path
    aliases. This is a filesystem-version guard, not cryptographic attestation.
  • Retain decoded preprocessed input-text digests and available native units,
    physical unit and compilation directory even when file/line evidence is missing.
    No source definition is guessed or newly admitted by this production patch.
  • Preserve all 250 native requests; a local recollection plan retains the 196
    unconnected identities, including 27 missing Windows TUs and 19 missing locations.
    A new build does not provide ground truth for the old image/address plan.

Counterfactual Rust-Joern replay

Using identical prepared bytes, four frozen producer outputs and 45 fixed cells,
compare the pinned 9fd97f5 with Rust-Joern PR Noelo-Lab/rust-joern#3 (52a8899),
with producer-output defects rejected before graph admission:

Tool Pinned source / paired / GED perfect Candidate source / paired / GED perfect
Fission 54 / 54 / 18 56 / 56 / 20
Kuna 54 / 53 / 21 56 / 56 / 24
angr 54 / 34 / 14 56 / 43 / 18
Ghidra 54 / 50 / 19 56 / 54 / 22

These are dependency/profile diagnostics, not producer quality or leaderboard
improvements
. The two new source rows are optimized variants of the same Snappy
definition, not independent programs. Type eligibility is not expanded.

  • Retain 1,026 unique parser inputs: 32 become available, 55 previously parsed
    inputs change directed CFG topology/entry-exit roles, none is lost.
  • All 87 receive fresh unchanged original PyJoern 4.0.150.4 / Joern v4.0.150
    references, isolated from the replacement shim. Three initial .ii reference
    attempts were rejected by the original generator; identical-byte .cpp retries
    complete all captures, with initial infrastructure errors retained separately.
  • The candidate also includes upstream constant-true loop normalization. Default
    candidate matches original references on 32/87; joern_loop_conditions=True
    matches 82/87, including all 55 previously parsed changed inputs and 27 new
    inputs. Four new inputs have no original exported function; one newly parsed
    Ghidra pseudocode input remains non-isomorphic (194/289 versus 199/297 nodes/edges).
  • Compatibility options leave 984/1,026 inputs unchanged, admit 32 and change 10
    prior graphs; those 10 now match original Joern. Full CPG/type validity/execution
    parity and the historical 8,808-case audit are not established by this replay.

Keep the production pin and parser options unchanged. Loop policy, the one
remaining non-isomorphism and unavailable original exports require review before
adopting a new diagnostic profile/cache generation. No function/address exception
is added to implementation.

Semantic policy and compiler controls

docs/cpp-evaluation-qualification.md proposes separate packaging, exact source
identity, syntactic-CFG and instantiated/effect qualification gates. Generic
syntax does not supply concrete template arguments, construction/destruction or
exception cleanup. The proposal is not an official maintainer-adopted metric.

The optional development script scripts/audit_cpp_toolchains.py preserves
commands and source/preprocessing/AST/IR/object evidence for a header-free fixture.
Apple Clang 17.0.0 and Homebrew Clang 22.1.8 each pass ELF/AArch64, COFF/x86-64
MSVC ABI and Mach-O/arm64 at O0/O2/O2-noinline: 18 controls. They verify target
and data-model assertions, concrete template arguments -2/2, construction/throw
AST, exceptional cleanup IR and identical code sections after preprocessing.
The script rejects wrong object targets and does not link or execute the fixture.

Zero corpus C++ recompiles are qualified. Clang targeting PE is not native
MSVC, and Clang targeting ELF is not original GCC 13.3. Standard-library/runtime
ABI, exact original commands, link/LTO/ICF and recovered-code recompilation still
need qualification. No new runtime dependency is added; legacy byte arithmetic
and cache generation remain unchanged.

Final production-patch regression evidence

  • Frozen 250 requests/tool, four tools, 45 cells: all prior individual GED/type
    results, populations, exclusions, native addresses and producer hashes match
    .
    Source CFG eligibility remains 54/250; source/ABI type eligibility remains 26/250.
    Two complete fresh replays have identical semantic reports for 45/45 cells after
    removing only computation-time fields. Frozen producer outputs are reused.
  • New targeted tests: 17 passed, including real PDB/dSYM, preparation races,
    aliases, embedded debug priority and compiler/ABI controls.
  • Focused workflow locally: 316 passed /5 skips, Black/Ruff and mypy for ten
    selected owners pass. Full baseline: 980 passed; candidate: 997 passed. Both
    retain identical 19 failures /24 errors /28 skips (43 failure/error identities).
    Full tests are not green. A separately expanded binfmt/PDB mypy audit has 27
    baseline versus 26 candidate errors, no new error message; it is not green.
  • The new delta is unsubmitted and has no remote CI result yet. Previous-head CI
    cannot validate it. Fix C++ function identity collisions in the benchmark pipeline #91 remains open and this remains a dependent draft.

Complete proposed patch, source hashes, original references, comparisons, compiler
controls and JUnit evidence are retained locally in Fission
benchmark/artifacts/cpp-pipeline-qualification/2026-10-19/.

Datasets remain qualification,
frozen pilot
and earlier raw corpus.
No corpus membership, Hub files, published results, default metrics or Union
are updated by this follow-up.

Earlier implementation and evidence

Producer-output inspection follow-up

This revision proposes a bounded, oracle-free producer-output contract for the experimental C++ evaluators. It does not adopt an official C++ policy or update the Rust-Joern dependency pin.

  • Add optional typed metadata.output_contract; the Ghidra/Fission/angr/Kuna adapters declare C-like pseudocode with partial declarations. Legacy records stay unknown. Code, variables and native address/line evidence are preserved.
  • Detect numeric qualified-name components, explicit unsupported IR placeholders and unambiguous same-block builtin/elaborated object/formal redeclarations. Ignore comments/literals, extern/prototype/type-alias declarations and nested shadowing. A clean inspection means validity not established, not valid C++.
  • Reject proven-invalid C++ GED producer graphs and retain unresolved syntax/dialect versus frontend-rejection evidence. Diagnostic profile becomes cpp-ged-origin-v2-diagnostic.
  • Propose zero for a proven-invalid producer in experimental C++ types after oracle qualification. Eligible functions/variables remain in the denominator; unsupported oracle evidence remains an exclusion. This is a proposed admission/scoring policy, not proof that every individual transported type is wrong. Experimental type cache generation becomes 5.
  • Unexpected inspection faults and malformed contracts propagate rather than reducing eligibility. No compiler dependency, oracle-based source repair, function/address condition or default metric/Union change is introduced.

Follow-up measured evidence

Same 45 cells / 250 fixed native requests per tool, same saved producer code hashes and exact source evidence, fresh metric computation, no corpus/recovered-code execution:

  • All four tools retain source CFG eligibility 54/250, type oracle eligibility 26/250, and identical oracle exclusion lists.
  • Fission, Kuna and Ghidra values are unchanged. All GED/type perfect counts are unchanged.
  • Angr has one previously connected invalid GED output rejected (35 → 34 paired, previous distance 8); source eligibility remains 54. Two invalid outputs change partial source/ABI type values 1/3 → 0 and 1/4 → 0, retaining all 26 eligible functions. A third invalid oracle-qualified output was already zero. These losses are disclosed policy effects, not a decompiler quality/ranking improvement.
  • Across all 1,000 outputs, 23 proven-invalid outputs are found (all angr), including three not present in the earlier parser-failure-only triage. A producer-only replay with Rust-Joern PR [AUTOMATED] Parse qualified C++ template and operator-function names rust-joern#3 rejects all eight known-bad duplicate-binding outputs that become parseable under that candidate. This counterfactual is not a new source-oracle population or score; the configured pin remains 9fd97f50885202596b125ebd2504e64910874bcb.
  • Locally, the focused CI lane reports 299 passed / 5 skipped; Black/Ruff and mypy (9 owners) pass. The full suite changes 949 → 980 passed, with identical 19 failures / 24 errors / 28 skips, all 43 failure/error identities unchanged. No new full-suite failure identity remains; the full suite is not claimed green.
  • Initial Tree-sitter 0.26 Point.column access crashed locally. Byte-offset coordinate calculation preserves diagnostics and then passes 10,000 fresh inspections with identical evidence. This is an integration workaround, not a claim to fix the native dependency; see py-tree-sitter issue 487 and docs/cpp-producer-contract.md.
  • The qualification snapshot's 549 listed files (3,114,987,373 bytes) match hashes and sizes; its immutable Hub manifest matches the local manifest. Packaging integrity is not full oracle qualification. qualified=false/scoring_ready=false and incomplete C++ feature/provenance gates remain explicit.

Documentation: docs/cpp-producer-contract.md. Complete local patch, evaluator deltas, original fault logs, JUnit reports and dataset audit are retained in Fission benchmark/artifacts/cpp-output-contract/2026-10-19/.

Qualification corpus · Frozen pilot manifest · Earlier raw corpus

Remote CI has not run for this proposed follow-up. The PR remains a dependent draft; #91 sequencing and maintainer review still apply.

Earlier implementation and qualification evidence

Scope

Add an opt-in C++ source/ABI type graph proposal, native debug transport and
scoped Fission definition-line evidence for the qualification corpus.

  • Complete object fields, bit positions, recursive pointees, ordinary bases,
    enums and virtual-base locators are compared structurally after type-blind
    variable correspondence. Name/total size alone never earns object credit.
  • Read complete DWARF graphs and CodeView TPI field lists/continuations with
    ambiguity, layout, cycle and incomplete-evidence abstention.
  • Accept explicit producer type graphs or complete ordinary emitted C record
    definitions for the bounded LP64/LLP64 natural-layout subset.
  • Forward Fission line_mappings_nir with defined_variables scope. Direct
    variable PCs stay primary; RHS occurrences do not inherit LHS definition PCs.
    Legacy whole-line mappings preserve their serialization/ownership contract.
  • Fix original-source C++ overload extraction using a proven definition line,
    including namespace syntax that the C grammar can misinterpret.
  • Add a reproducible evaluator entry point and a focused Ubuntu CI workflow
    with real DWARF and CodeView/PDB compiler fixtures.

CppTypeMatchMetric remains experimental and outside default metrics/Union.
Maintainers must choose official C++ policy and qualification before adoption.
No leaderboard, website, snapshot or CHANGELOG update is included. Legacy native
provenance/parser changes bump type_match cache generation to 25.

Prerequisites

Depends on #91 (currently open, head 8eeceb8).
This branch includes its C++ identity prerequisites and the subsequent native
PDB/Mach-O/source transport work. The base main is
14e14b8. Please treat this as a dependent draft;
the prerequisite diff can be removed after #91 lands.

The companion Fission patch adds conservative producer line origins. Old Fission
CLIs remain supported using direct variable addresses and empty line maps.

Datasets

Measured evidence

Frozen qualification-v2 dataset revision 07dd95dbe6214b8a78fc1e4a55961ab515986489:
250 address-guided stripped functions across 45 cells (Linux 108 / Windows 97 /
macOS 45). Source/debug information stays exclusively with the evaluator.

  • Same-source clean Fission baseline/candidate: 250/250 nonempty outputs,
    zero changed NIR texts or direct variable lists.
  • 105 functions / 237 scoped definition lines, all exact instruction starts;
    all 9,482 direct variable/PC pairs also survive native validation.
  • Final-binary Linux/Windows/macOS two-run probes are deterministic.
  • C++ policy population: 26 eligible functions for both tools/views, including
    21 with object graphs; v1 admitted 5. Perfect source/ABI: Fission 0/26,
    Kuna 1.808 1/26. Kuna outputs are reused from the earlier frozen run.
  • 224 oracle exclusions are reported explicitly. Supported-subset coverage is
    not a decompiler quality/ranking improvement or a full C++ score.
  • Legacy PDB ABI diagnostic controls are unchanged: Fission 19 eligible / 0
    perfect, Kuna 32 / 0 perfect (97 requested Windows functions).

See docs/validation/cpp-type-policy.md, the compact summary and frozen population
for hashes, profiles, exclusions and reproduction commands. Full artifacts are
retained locally in Fission benchmark/artifacts/cpp-type-policy/2026-10-13/.

Validation

  • Fission companion: 1,328 nextest tests passed (1 skipped); crate checks, release
    CLI, workspace fmt and owner-boundary scan passed.
  • DecBench focused CI lane locally: 156 passed / 5 platform-specific skips.
  • New C++ policy mypy owners clean; scoped Black/Ruff checks passed.
  • Full local repository pytest has existing failures; see the report for exact
    baseline/final counts and identities. No new failure identity remains.
  • Remote Ubuntu CI runs after submission; it has not been claimed green locally.
    The existing Rust-Joern CFG dependency is pinned to
    9fd97f50885202596b125ebd2504e64910874bcb for this lane.

Bounds

Maps represent single result definitions, not full expression/effect lineage.
Computed/split debug locations, formal/storage ambiguity, anonymous/forward
objects, function/member pointers, inline identity and unlocated native vptrs
still abstain. Virtual locators retain their debug format; cross-format
normalization is not guessed. Instance storage/inheritance is measured, not a
complete class API, RTTI, exception behavior or execution parity.

Follow-up review fixes (2026-10-18)

  • Only explicit unsupported/invalid oracle representations abstain during oracle
    preparation (UnsupportedType / PdbFormatError). Unrelated evaluator/parser
    ValueError now fails evaluation rather than shrinking eligibility; recovery
    scoring runs outside the oracle-abstention boundary.
  • Scoped display names must be nonempty and function-unique at adapter and
    shared-matcher boundaries. Ambiguous names receive no scoped PCs. Direct
    variable PCs remain primary; legacy whole-line maps retain their contract.
  • Resolve configured/PATH Fission executable paths before changing child cwd.
  • Correct the normal tree-sitter-cpp dependency wording and explicitly retain
    native virtual-base locator equality as an unresolved official-policy limit.
  • At that follow-up: legacy type-match cache revision 24; experimental evaluator revision 3.
    The latest correctness follow-up below advances them to 25 and 4.
    Formal C++ comparison policy remains cpp-types-v2-proposal, outside Union.

Ten new regression cases fail before and pass after these fixes. Focused local
lane: 166 passed /5 off-host skips, including real DWARF/dSYM and CodeView/PDB
compiler fixtures. Four policy mypy owners and scoped Black/Ruff pass. Shared
Ruff still has one unchanged baseline SIM105; whole-repository lint is not green.
Full local pytest: baseline 863 passed /20 failed /28 skipped /24 errors;
candidate 873 passed /20 failed /28 skipped /24 errors. All 44 failure/error
identities agree; no new identity.

Fresh evaluator replay of the same frozen 250 Fission and 250 Kuna outputs across
45 cells yields identical baseline/candidate reports after excluding only legacy
PDB diagnostic computation_time_seconds: eligibility 26/250,
Fission source/ABI perfect 0/26, Kuna 1.808 perfect 1/26. Producers are not rerun;
these are not new current-tool scores or a ranking update. All 572 input/debug/
producer artifact hashes are verified. Full follow-up evidence is in
docs/validation/cpp-review-fixes.md and its compact JSON.

Remote CI for this follow-up must still run on the exact submitted head. The
previous upstream run remains action_required; local checks do not substitute
for maintainer approval/remote CI. This remains a dependent draft on #91.

Experimental C++ GED connection diagnostics (follow-up)

Add an explicitly opt-in cpp-ged-origin-v1-diagnostic evaluator and CLI, outside
default GED/Union. Keep every parser definition, bind native entries to exact
qualified source definitions and producer-record storage keys, support GCC/MSVC
line origins and native compilation-directory / recorded saved-temp TU transport.
Ambiguous identities and conflicting compile commands abstain; unexpected
evaluator/infrastructure failures do not silently reduce the source population.

Frozen same-250 / 45-cell replay: source CFG population 48/250 for both tools;
producer CFGs connect 249/250 Fission and 246/250 Kuna 1.808. Actual diagnostic GED
is measurable for 48 and 47 rows, with 18 and 20 distance-zero results. This is
evaluator coverage evidence, not a decompiler quality/ranking/Union improvement.
The 202 shared oracle/policy exclusions and per-output parser failures are
explicit. Two fresh CFG passes are byte-identical for 45/45 cell reports.

Definition-only parsing, template specialization, implicit lifetime/exception
edges, full-TU context and independent build/link qualification remain bounded.
Compilation metadata is asserted evaluator input. See
docs/validation/cpp-ged-identity.md for populations, exclusions and limits.

New regressions: 33 passed. Updated focused workflow locally: 202 passed / 5
platform skips. Full local baseline 874 passed, candidate 907 passed; both have
the same 19 failures / 24 errors / 28 skips, with no changed failure identity.
New owners pass mypy and scoped Black/Ruff. Remote CI for this follow-up is
pending submission/maintainer approval and is not claimed green.

Follow-up: exact native-origin retention and recompiler target checks

This follow-up is based on ea2b461 and remains experimental. The
qualification/raw corpus links above and the frozen 250 requests are unchanged.
It does not implement full C++ semantics or a default C++ metric.

  • Retain an exactly requested, native-proven defining header in its proven TU
    rather than discarding it through system-header filtering. Record retained
    and filtered origins, primary TU, exact definition digest and native identity.
    No basename/leaf-name fallback, resampling or generic template substitution.
  • Re-ingesting frozen Fission/Kuna 1.808 outputs increases common source CFG
    coverage from 48 to 54/250. Fission paired/zero: 54/18; Kuna: 53/21. Every
    previously measured function result is identical, and no source row is lost.
    This is evaluator coverage, not a Fission quality delta or leaderboard score.
  • Two fresh source/producer CFG passes are byte-identical for 45/45 cells; the
    native index is shared within each cell and bound to immutable inputs.
    Six new identities have independent original-source checks; one requires
    direct namespace inspection because of a raw-parser scope limitation.
  • A real Darwin/arm64 probe selected gcc for ELF/AArch64 but emitted Mach-O.
    Native ELF gcc selection is now Linux-only. Compiled relocatable objects must
    match the expected format family/architecture/bitness before byte extraction;
    unproven/incompatible targets abstain and stay out of the binary aggregate.
    Byte-match cache generation becomes 9. Existing Linux selection and compatible
    byte-comparison arithmetic are retained; no published C results are re-scored.
  • Format/ISA validation does not prove C++ ABI/toolchain equivalence. This Mac
    lacks the Linux cross-compiler; native MSVC and Mach-O C++ byte matching remain
    unqualified. No C++ byte score was produced.

The current source exclusions include 84 template-specialization cases, 27
Windows TU misses without retained preprocessing (Detours/WinSparkle), and 19
Windows entries without usable native file/line facts. Implicit object lifetime,
exception cleanup and full-TU callee context are unqualified. Split/computed
locations, ambiguous PDB formal status, incomplete objects and vptr placement
still abstain; the prior 26/250 type-policy population was not re-scored here.

Local validation: focused workflow 246 passed / 5 platform skips, scoped Black
and Ruff plus existing C++ owner mypy clean. Full baseline 907 passed; candidate
927 passed. Both have the same 19 failures / 24 errors / 28 skips, with all 43
failure/error identities unchanged. The updated remote CI has not run before
submission; the prior head's green fork run does not validate this new delta.

See docs/validation/cpp-oracle-qualification.md. Full local evidence is retained
in Fission benchmark/artifacts/cpp-oracle-qualification/2026-10-18/. No official
C++ policy, Union, site/snapshot, result overlay or Hugging Face upload is included.

Correctness review follow-up (base 9f50ebf)

  • Reject duplicate producer records at one requested native address before oracle
    preparation/scoring. Validate after plan filtering, preserving empty recovery
    for missing eligible output and excluding extras outside the plan.
  • Prefer a clean C++ AST when C parsing fails, covering ordinary identifier
    functions with reference/rvalue-reference parameters; valid C keeps C grammar.
  • Tokenize nominal object indirections as &&, &, *. Object&& becomes
    rref(Object), ABI pointer(Object). Invalid nested references abstain; bounded
    LP64/LLP64 layout and ILP32 explicit-evidence requirements are retained.
  • C++ type cache 4; shared-parser legacy type-match cache 25. Experimental policy
    remains cpp-types-v2-proposal, outside default metrics/Union.

Before fixes, new regressions: 14 failed /6 passed. Current focused workflow
locally: 268 passed /5 platform skips; scoped Black/Ruff and seven existing
C++ mypy owners pass. Full baseline: 927 passed /19 failed /28 skipped /24 errors;
candidate: 949 passed /19 failed /28 skipped /24 errors. All 43 existing
failure/error identities agree; no new/removed/changed identity. Full tests are
not green. Real compiler-generated DWARF and clang-cl/PDB fixtures compare
nominal rvalue-reference graphs in source/ABI views without execution; they do
not qualify the complete MSVC pipeline.

Fresh type-policy replay of all frozen 250 requests /45 cells keeps eligibility
26/250 for both tools/views, Fission perfect 0/26 and Kuna 1.808 perfect 1/26.
All function results and exclusions are identical. Frozen producer outputs are
re-ingested, not regenerated; this is evaluator regression evidence, not a new
GED result, decompiler quality/ranking/Union update or full C++ score.

The local harness freshly prepares immutable native readers, source indexes and
oracle rows once per image process, independently for baseline and candidate;
metric caches are disabled and all metric results computed fresh. An initial
unshared large-image attempt timed out at 300 seconds and is retained as partial
evidence. Nineteen completed cell executions match unshared controls after
excluding timing/preparation metadata. The final 45-cell replay completes with
zero failed/timed-out cells. Production performance code is unchanged.

See docs/validation/cpp-type-policy.md. Complete local patch, logs, commands,
hashes and reports: Fission benchmark/artifacts/cpp-review-correctness/2026-10-18/.
#91 remains open; wait for it to land before rebase/prerequisite removal. The new
submitted head still needs exact-SHA remote CI/maintainer approval; previous-head
fork success does not validate this delta. Virtual-base cross-format equivalence
and future malformed-PDB fuzz/property gates remain separate qualification work.

sjkim1127 and others added 15 commits September 17, 2026 14:42
Results are stored under the unqualified function name. That is
unambiguous for the C corpus, but C++ overloads and same-named methods in
different scopes share a short name while being different functions, so
they collapsed into one entry. Fixing a single backend was not enough:
relabeling, metrics, source lookup, result artifacts and re-evaluation
could each lose the distinction again.

Identity now derives from the complete DWARF target set rather than from
whatever subset a backend recovered. A name that is unique in the binary
keeps its historical plain key; only an ambiguous name becomes
name@0x<canonical-low-pc>. Backend addresses are normalized to the
matched DWARF low_pc, so every consumer keys off the same primitive.

The map used to decide ambiguity is the binary-wide target set, kept
separate from the per-run selection. Selecting one member of an overload
group therefore does not rename the others, which makes storage identity
invariant under sample-set slicing.

Where an exact identity is unavailable the code abstains instead of
guessing: an ambiguous CFG is not scored, a collision-qualified function
does not borrow name-keyed ground truth, and it contributes no prototype
to another overload's compile context. Likewise a DWARF range lookup
given an address matches that address or fails, rather than falling back
to the first DIE sharing the short name.

DWARF low_pc == 0 sentinels left by linker ICF are rejected only when the
ELF section table proves address zero is not executable, so genuine
bare-metal code at zero is preserved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the collisions the identity change exists for: DWARF extraction
that must not collapse overloads, slice invariance of canonical keys,
metric and CFG abstention for ambiguous names, address-keyed source
recovery, linker-ICF zero-address sentinels, ARM Thumb address
normalization, and end-to-end backend behaviour.

Tests needing a cross-compiler or linker (arm-none-eabi, ld.lld, mingw)
skip when it is absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review found three places where the new storage-key format was written but
not read back, so the distinction this change exists to preserve was lost
again at the boundary.

pipeline/materialized.py rebuilt results with its own `(\S+)` marker regex
and put the marker's first field straight into FunctionDecompilation.name.
For a collision that made the name `same@0x1000`, and ByteMatch feeds that
field to function_bytes(), the compile fixup and object symbol lookup as a
real C identifier -- so re-evaluating a materialized C++ artifact fed it a
string that is not a function name. The same regex could not see a marker
whose DWARF name contains a space (`operator new`) at all. It now reads
through results_tree.split_functions() and recovers the semantic name with
parse_function_storage_key(), keeping the storage key only as the map key.

TypeMatch's address-keyed ground-truth walker visited only the compile
unit's direct children, while identity extraction walks the whole DIE tree.
A C++ subprogram inside a namespace or class scope was therefore named
`foo@0x...` correctly and then had no ground truth at that address, so it
dropped out of the metric silently. It now walks CU.iter_DIEs().

Three further copies of the marker pattern are folded into the shared one:
repair_decompiled_flags compares against FunctionData keys and so wants the
storage key, while reeval_ged and audit_historical_ged_iso compare against
Joern body names and historical score keys and so want the semantic half.

Tests cover same@0x1000 / same@0x2000 / `operator new` round-tripping, and
namespace-scoped overloads reaching the address-keyed ground truth. The
latter needs real ELF: `g++` exists on macOS but links Mach-O, which made
this module raise ELFError instead of skipping, so the guard now probes the
linker output. That also turns the pre-existing sibling test from a failure
into a skip off Linux.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sjkim1127

Copy link
Copy Markdown
Author

A few follow-up concerns I found while reviewing this draft more closely. I don't plan to address all of these in the current revision immediately, but I want to document them explicitly before further review:

  1. Evaluator exceptions vs. oracle abstention
    CppTypeMatchMetric.compute_for_binary() currently catches both UnsupportedType and general ValueError at the per-function boundary. Since UnsupportedType already derives from ValueError, an unrelated evaluator/parser ValueError could potentially be reported as an unmeasurable function rather than surfacing as an evaluator failure. For a benchmark metric this is worth tightening, since evaluator bugs should never silently shrink the eligible population.
  2. defined_variables currently uses display names
    Scoped Fission line provenance associates mappings with variables by name. This is conservative for the current Fission output, but it relies on function-local variable names being unique. Shadowed or duplicate display names could make the scope ambiguous. A future revision should either enforce/document that uniqueness invariant or transport a stable variable/binding identifier.
  3. Virtual-base locator representation
    The current proposal deliberately preserves native DWARF/CodeView locator encodings rather than attempting cross-format normalization. This is intentional for the experimental policy, but it probably needs another policy discussion before becoming an official metric: exact debug-format locator equality may be stricter than binary-observable semantic equivalence.
  4. Runtime dependency wording
    The proposal text currently says the profile introduces no new runtime dependencies, while tree-sitter-cpp is added to the normal project dependencies. The documentation or packaging should be made consistent.
  5. Fission executable path
    FISSION_BIN may be supplied as a relative path while FISSION_WORKING_DIR changes the child process cwd. Resolving FISSION_BIN to an absolute path before Popen() would avoid a small path-resolution edge case.

None of these change the intended status of this PR: CppTypeMatchMetric remains experimental and outside the default metrics / Union. The main thing I want to avoid is presenting the current qualification results as stronger than the implementation contract actually guarantees.

I also still consider remote CI approval/run necessary before treating the focused validation lane as green; the current Actions run is action_required, so only the local validation results are available at the moment.

@sjkim1127

Copy link
Copy Markdown
Author

One additional related PR worth calling out is #36.

I don't think #36 and this PR have a large direct file-level conflict today: #36 is currently limited to the experimental MSVC/Wine compile container, smoke script, and MSVC_SUPPORT.md, and intentionally leaves the DecBench Python integration for follow-up work.

There is, however, substantial design and follow-up overlap, so I think the two paths should be reconciled rather than developed independently.

Relation to #36 — MSVC/PDB integration

#36 established that real MSVC builds are feasible on the benchmark host and, importantly, identified PDB/CodeView as the native ground-truth path for Windows targets.

Its follow-up plan includes:

  • a PDB function / variable / type reader;
  • PDB-backed project-function ownership;
  • PDB function ranges for byte extraction;
  • explicit byte_match abstention for PE binaries without DWARF;
  • eventual pipeline integration for native MSVC targets.

PR #103 implements or changes several of those same boundaries:

  • binfmt.pdb_info() and the native pdb_info.py CodeView/PDB reader;
  • PDB-backed source/function ownership and exact-address identity;
  • PDB variable/type transport into type evaluation;
  • native function identity that does not collapse C++ overloads;
  • explicit prevention of MinGW recompilation for PE binaries without DWARF;
  • C++-aware source/type correspondence on top of the native debug transport.

So although #36 itself should still be independently useful as the compiler/container side of native MSVC support, its documented Python integration plan is now partially superseded or at least needs to be updated against the implementation in this draft.

There are also a few concrete places where the contracts should be reconciled:

  1. PDB module/API naming
    docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36 proposes a future decbench/utils/pdbinfo.py, while this PR currently implements decbench/utils/pdb_info.py and exposes it through binfmt.pdb_info(). We should settle on one public/internal API rather than ending up with parallel PDB readers.

  2. byte_match behavior
    docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36 correctly identifies that a native MSVC PE without DWARF must not be silently recompiled with MinGW. This PR already encodes that rule by making recompilation depend on the actual original debug/toolchain evidence. That behavior should probably become the shared contract for docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36's eventual pipeline integration.

  3. Function identity
    docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36 originally treats PDB procedure names/addresses mainly as the DWARF replacement needed for filtering and metrics. The C++ corpus work in Qualified C++ Target Candidates for multi-lang DecBench #73 and the implementation here show that exact binary-local address identity is more than an implementation detail: it is necessary to prevent overloads or same-named functions from borrowing each other's oracle data.

  4. Type metric assumptions
    One statement in docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36 that probably needs revisiting is that "nothing about the metrics' logic changes, only the ground-truth reader." That is mostly true for adding PDB support to C-like programs, but not for general C++. Support C++ #46 and the qualification work in Qualified C++ Target Candidates for multi-lang DecBench #73 show that C++ also changes the identity and type-comparison contract itself. This PR therefore adds a separate experimental C++ type policy rather than simply feeding PDB variables into the existing C metric.

  5. Source parsing / GED-side identity
    docker: experimental MSVC (msvc-wine) compile container + zlib smoke #36 notes that cl -P gives us the preprocessed source path needed for GED. That remains useful, but C++ source extraction also needs namespace/overload-aware identity; this PR starts adding that through the C++ Tree-sitter path rather than treating function names as ordinary C identifiers.

My current view is therefore:

If #36 lands independently, I think its follow-up integration work should use or adapt the native-debug contracts from this branch rather than introducing a second PDB/type pipeline.

Conversely, this PR should not be interpreted as replacing the useful compiler/container work from #36: the native MSVC build path is still required if those Windows targets are eventually brought into the real benchmark corpus.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant