Skip to content

R12 — the Ghidra seam is an interface, and its P-code vocabulary cannot flatten - #34

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/ghidra-valhalla-seam
Aug 25, 2026
Merged

R12 — the Ghidra seam is an interface, and its P-code vocabulary cannot flatten#34
AdaWorldAPI merged 1 commit into
mainfrom
claude/ghidra-valhalla-seam

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Starting the r2il-machine-semantic-contract-v1 arc (lance-graph #1027, merged) from the Ghidra end, while a sibling session drives W0–W4. Measurement + source trace only: nothing swapped, nothing minted, no layout touched.

1. The seam is an interface — no core fork required

This was flagged twice this session as unverified, and it gates the entire Java half. Traced:

Language (INTERFACE, model/lang/Language.java:29)
  .parse(MemBuffer, ProcessorContext, boolean)
      → InstructionPrototype (INTERFACE, :35)
           .getPcode(context, override) → PcodeOp[]
  • InstructionDB.getPcode() (:608-628) does nothing but delegate to proto.getPcode(...).
  • InstructionPrototype has exactly two implementations — SleighInstructionPrototype and InvalidPrototype.
  • SleighLanguage constructs the real one at exactly one site (SleighLanguage.java:392).
  • Instruction is itself an interface, with an InstructionStub already in tree.

So a third implementation is the insertion point. The answer is favourable.

2. Ghidra's P-code vocabulary cannot be carried as value classes — its identity can

R12, field shapes transcribed from Varnode.java:51-54 and PcodeOp.java:102-105 (not invented), run through R2/R4's harness on the JEP 401 EA build with -XX:+PrintFlatArrayLayout so the VM reports element sizes rather than the program asserting them:

shape nonAtomic atomic VM element size
VarnodePayload(int,int,long) — 16 B false false
PcodeOpPayload(int,long) — 12 B false false
VarnodeNarrow — 8 B packed content true true 8 (NULL_FREE_ATOMIC_FLAT)
VarnodeRef(long) true true 8 (NULL_FREE_NON_ATOMIC_FLAT)
PcodeOpRef(long) true true 8
InstructionRef(long) true true 8

The two *Payload rows are the optimistic lower bound — every reference deleted. The real Varnode additionally holds an Address; the real PcodeOp holds a SequenceNumber, a Varnode[] and a Varnode. So a 2-input PcodeOp is five heap objects: the op, its sequence number, the input array, and one Varnode per operand. That is ONE ROW IS NOT ONE JAVA OBJECT at its worst — one instruction becomes a small object graph.

Verdict: the W5 facade ADDRESSES the vocabulary rather than carrying it. And that needs nothing new — it is the same result LaneId / Ordinal / MaskId already rely on, and the same reason RowRange (16 B) does not flatten. W5's central question is answered before W5 starts.

The finding the plan did not anticipate

VarnodeNarrowspaceId:u8, size:u8, 48-bit offset — also flattens. So 8 bytes is enough to carry a varnode's real content, not merely a pointer to it: a descriptor that reads space and size with no lane round-trip. It is bounded by exactly one condition — a 48-bit offset — and whether that suffices is a W0/W1 address-space question, not a Valhalla one.

Recorded as an option so it is not lost; deliberately not proposed as the design. Pre-empting W1's tenant carving from this side is precisely what the plan's own R1 rule ("no private object graph then serialize") forbids one layer up.

Also worth noting: the single-long refs come back NON_ATOMIC_FLAT while the multi-field VarnodeNarrow is ATOMIC_FLAT. Both flatten at element size 8; only the tearing guarantee differs.

What this deliberately does not do

No InstructionPrototype implementation, no facade types, no descriptor mint. Those wait on W1's tenant spec — this establishes only what is true regardless of what W1 decides.

Gates

lgj-abi 134 lib tests green (nothing here touches it). R12 compiles and runs on /opt/jdks/jdk-27 (JEP 401 EA); the run is banked verbatim in R12-observed.txt, including the VM's own PrintFlatArrayLayout output.


🤖 Generated with Claude Code

https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv


Generated by Claude Code

…annot flatten

Starting the r2il-machine-semantic-contract-v1 arc (lance-graph PR #1027)
from the GHIDRA end while a sibling session drives W0-W4. Two findings,
pointing the same way. Nothing swapped, nothing minted, no layout touched.

1. THE SEAM IS AN INTERFACE, and no core fork is required:

     Language (INTERFACE)
       .parse(...) -> InstructionPrototype (INTERFACE)
                        .getPcode(...) -> PcodeOp[]

   InstructionDB.getPcode() only delegates. InstructionPrototype has
   exactly two implementations, and SleighLanguage constructs the real
   one at exactly one site (SleighLanguage.java:392). Instruction is
   itself an interface with an InstructionStub already in tree.

   This had been flagged twice this session as unverified. It gates the
   whole Java half; the answer is favourable.

2. GHIDRA'S P-CODE VOCABULARY CANNOT BE CARRIED AS VALUE CLASSES — its
   identity can. R12 transcribes the field shapes from Varnode.java:51-54
   and PcodeOp.java:102-105 and runs them through R2/R4's harness on the
   JEP 401 EA build, with -XX:+PrintFlatArrayLayout so the VM reports
   element sizes rather than the program asserting them:

     VarnodePayload(int,int,long)  16 B  -> not flat
     PcodeOpPayload(int,long)      12 B  -> not flat
     VarnodeRef/PcodeOpRef/InstructionRef(long) -> flat, element size 8

   Those payload rows are the OPTIMISTIC bound — every reference deleted.
   The real Varnode holds an Address; the real PcodeOp holds a
   SequenceNumber, a Varnode[] and a Varnode, so a 2-input PcodeOp is
   FIVE heap objects.

   Verdict: the W5 facade ADDRESSES the vocabulary rather than carrying
   it — which needs nothing new. It is the same result LaneId/Ordinal/
   MaskId already rely on, and the same reason RowRange (16 B) does not
   flatten.

Unanticipated, recorded so it is not lost: an 8-byte VarnodeNarrow
(u8 space, u8 size, 48-bit offset) ALSO flattens, so 8 bytes can carry a
varnode's real content rather than a pointer to it. Bounded by exactly
one condition, a 48-bit offset — a W0/W1 address-space question, not a
Valhalla one. Named as an option, deliberately NOT proposed as the
design: pre-empting W1's tenant carving from this side is what the
plan's own R1 rule forbids one layer up.

lgj-abi 134 lib tests still green (nothing in this commit touches it).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d613667d-b224-41d6-a8be-947a91ecb45e


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Aug 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d37b5c12-6fc6-470c-880b-effc7b88e2d7)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review August 25, 2026 17:11
@AdaWorldAPI
AdaWorldAPI merged commit 23369f8 into main Aug 25, 2026
2 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Aug 27, 2026
PR_ARC_INVENTORY had entries for #1-#12, #14, #16, #18, #20 and #32, and
nothing else. Missing: #13, #22-#31, #33-#41 — twenty PRs. (#15/#17/#19/#21
are also absent and correctly so: each is itself an arc-entry-only PR,
exempt under the termination clause.) #32's own entry still read
"(draft, opened …)" with no merge sha.

Corrects this branch's first count, which said nineteen and read the gap as
starting at #21. Both were wrong — it is twenty, and it starts at #13. The
first count was a range subtraction over a file with holes; the enumeration
is what found the difference.

Method, which is the point rather than an aside: each entry drafted from
that PR's own body and diff, five parallel agents over four PRs each, none
permitted to work from a later session's recall. Every backfilled entry's
Confidence bullet ends "Backfilled 2026-08-27 from the PR body and diff,
not written at merge time", so reconstructed entries are distinguishable
from ones written at merge time; several state which claims are the PR
body's own and were not re-verified. Every cited sha, date and merge-vs-
squash label machine-checked against git.

Four things the backfill turned up, each recorded in the entry it belongs
to: #25's body asserts "no code, no reproducer changes" and its own diff
contradicts it; #39 left its lgj_hop doc comment describing the pre-change
design; #34's banked evidence file did not identify its own JDK; and #41 is
on main while its own title reads [DO NOT MERGE AS-IS], recorded as
unresolved disposition rather than an endorsement.

ISS-LGJ-ARC-INVENTORY-STOPPED-AT-32 moves to RESOLVED with the corrected
count and the standing rule restated: the entry goes in at open, in the
PR's own commit. The backfill is the repair, not the process.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
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.

2 participants