[AUTOMATED] feat(p3,p8): simd-constant-string-initializer — resolve constant-mask pshufb lanes and stop cloning a global-writing epilogue (options simdlane + retsplitglobal, DIV-115/116) - #406
Merged
Conversation
…onstant-mask pshufb lanes, and stop cloning a global-writing epilogue (options simdlane + retsplitglobal, DIV-115/116) RE-friction round 2, need `simd-constant-string-initializer`, challenge 653d88600f4238b24302b0ec (macOS x86-64 Crackme0x04). sub_100003790 came back as 15,532 bytes / 469 lines: 32 SUB161 lane temporaries off two pshufb results, and one 72-store epilogue emitted three times. The filed hypothesis STANDS and is half the defect. `simdlane` (P3, DIV-115): pshufb has no p-code semantics, so ActionLaneDivide leaves every byte lane a SUBPIECE of an opaque CALLOTHER. The instruction is a pure permutation with an exact per-lane definition once the mask is known, so the rewrite is an identity. Measured alone it removes 32 SUB161 and 30 declarations and makes the function 278 bytes LONGER, because the broadcast source is a global spelled at each use. 45.8% of the bytes was somewhere else: `retsplitglobal` (P8, DIV-116). ActionReturnSplit::isSplittable never looks at a COPY's OUTPUT, so an epilogue that stores to globals reads as a bare epilogue; both splitters share the predicate, which is why `option returndup off` moved nothing. A bound, not a veto, settled by measurement: a plain is_persist() veto moved four stage assertions (three guardReturns self-copies, one GNU grep nlscan with two genuine stores). More than 8 observable global stores declines. Acceptance PASS: 11,264 bytes, SUB161 absent, both positive anchors matched. Gates: make test PARITY OK 675/675; make test-stages PARITY OK 628/628 with no opt-out or re-pin; make rust-test green; check-spec OK; catalog OK; tests/cli 29/29. Sweep 70 binaries / 15,365 functions: 2 changed, both pure epilogue de-duplication, zero distinct statements lost. Speed +0.19% min on the witness. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mahaloz
added a commit
that referenced
this pull request
Sep 5, 2026
…1 ended on the budget cap with #406 pushed The builder for this need ran out of its per-worker USD budget at the merge step, after the branch was complete and pushed. Record that as attempt 1 rather than leaving the need looking untouched: attempts 0 -> 1, pr -> 406, hypothesis upheld (with the measured correction that the filed cause is under half the symptom — the rest was a triplicated global-store epilogue). The decision log now carries what a salvage needs: the branch is based on the current main, its worktree is clean, and the only work left is the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mahaloz
added a commit
that referenced
this pull request
Sep 5, 2026
…406, re-file decompiling-3396-byte-main as regressed (B_DONE) Round 2 B_DONE at merged main 9622446. The acceptance suite closed simd-constant-string-initializer (closing_pr 406; its probe was already promoted to tests/cli/, 29/29). decompiling-3396-byte-main flips back to `regressed`: three independent 7-rep medians at this sha (10425 / 11378 / 10176 ms) sit over its unmoved `< 10000 ms` bar, against four passing medians at the four shas before it (9153 / 9180 / 9494). The record carries the evidence and the code-level A/B the next builder must run before attempting any fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mahaloz
added a commit
to dobin/kuna
that referenced
this pull request
Sep 5, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TzfafnkDMQiYoifXP5HSC8
mahaloz
added a commit
that referenced
this pull request
Sep 5, 2026
…ession by code A/B, and keep the profiler that showed it (#411) * [AUTOMATED] perf(infra): decompiling-3396-byte-main — refute the regression by code A/B, and keep the profiler that showed it The need was re-filed as `regressed` on the theory that #406 cost this witness ~680 ms. An interleaved 8-pair A/B of `fba4ebd8` against `96224463`, each arm pinned to its own `kuna` AND `decomp_dbg` (the CLI forks the engine), measures 9,619 vs 9,742 ms median — +3.00% +/- 6.77, i.e. 0.4 sigma — with stdout byte-identical between the two builds, so #406 never fires on this function. The named follow-up, cumulative default-ON schedule cost, was ablated the same way by deleting the `simdshufflelane` rrow! line: +0.23% +/- 5.33. The finding is that the bar is inside this box's noise floor: six 7-8 sample medians on one build ranged 9,002-9,742 ms against 10,000 ms. The bar was not moved, and no fix was attempted. What ships instead is the instrument the previous five attempts each rebuilt as a throwaway patch: `KUNA_ACTION_PROF=<path>` writes an exclusive-time, call-counted table of the Action tree, keyed by root variant so the reduced `jumptable` pipeline is separated from the function's own `decompile` pass. It cannot change emitted C, so it takes no option row — one cached read per `apply` when unset, measured at +0.89% +/- 3.84 on the same witness. Gates: make test PARITY OK 675/675; make test-stages PARITY OK 628/628; make rust-test green; check-spec OK; catalog OK; tests/cli 29/29; acceptance a-53d616afcb6a PASS at a 7-rep median of 9,227 ms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * [AUTOMATED] docs(re-needs): decompiling-3396-byte-main — point pr at #411 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was broken
RE-friction round 2, need
simd-constant-string-initializer(major, 1 instance), challenge653d88600f4238b24302b0ec(macOS x86-64Crackme0x04). The tester wantedand got, for
sub_100003790:15,532 bytes, 469 lines, 56 declarations.
Two defects, and the filed one is the smaller half
The filed hypothesis stands:
simdlane(P3, DIV-115).pshufbhas no p-code semantics —the x86 SLEIGH spec models it as an opaque
CALLOTHERover a 16-byte value — so afterActionLaneDividesplits the vector consumers into byte lanes, every lane read is aSUBPIECEof something no rule can see through, and 32SUB161(v7,k)temporaries all namethe same broadcast byte. The instruction is a pure permutation with an exact per-lane
definition once the mask is known,
dst[i] = (m[i] & 0x80) ? 0 : src[m[i] & (N-1)], so therewrite is an identity: the lane read is re-anchored on the shuffle's source.
But it is half the defect, and that was measured before any code was written: folding the
lanes makes the function 278 bytes longer (15,532 → 15,810), because the broadcast source is
a global the printer spells at each of its 96 uses. 45.8% of the bytes — 216 statements — was
one 72-store epilogue emitted three times.
print rawshows three basic blocks over theidentical range
0x1000038d6-0x100003a5c, and a sweep of all 153 non-default option valuesmoved it by at most 33 bytes,
returndup offincluded.retsplitglobal(P8, DIV-116) is the other half.ActionReturnSplit::isSplittable(
blockaction.cc:2242) admitsMULTIEQUAL/COPY/RETURNand, forCOPY, checks only thatno input is free — it never looks at the output. Upstream that is harmless because the
epilogue COPYs it was written for move registers; a COPY into a persistent Varnode is a
store to a global, and it is what P9 prints as
dat_<addr> = .... Both splitters share theone predicate, which is why
option returndup offchanged nothing.A bound, not a veto — settled by measurement. A plain
is_persist()veto moved four stageassertions. Three were
ghdec-returncopysplit:Heritage::guardReturnsinserts a syntheticglob = globliveness self-copy per persistent global before every return, so counting themprices a bare epilogue by how many globals the whole program has (now excluded two ways, either
sufficient: the
return_copyflag, and input/output naming the same storage). The fourth wasgh180-implied-cover-scarry— GNU grepnlscan, whose epilogue has two genuine globalstores, where declining also makes the merged form print
dat_2b818 = v2;on a path the binarydoes not store on. So the shipped rule counts the stores and declines past 8, this phase's
house range (
crossjumprevert/dedupitetailcap a duplicated leaf run at 8 printed ops,taildupat 12). GH180's two still split, byte-identical to upstream; the witness's 72 do not.The acceptance probe now passes
exit_code== 0stdout_bytes600 < n < 12000stdout_matches[0]void sub_100003790\(void\)stdout_matches[1]100008050stdout_absent[0]SUB161\(15,532 → 11,264 bytes; 32
SUB161(→ 0; 30 declarations dropped; the epilogue emitted once.With both options off the output is byte-identical to
main(15,532).Sweep
90 RE-dataset binaries, 70 loaded under both arms (20 fail to load identically in both),
15,365 functions: 2 changed (0.013%), both
retsplitglobal, both a purede-duplication —
KeyVal2.exesub_417ca0libmodplug-1.0.soModPlug_1SetPatternLoopRangereturn;removedZero statements added anywhere, zero distinct statements lost, function sets identical on
every binary.
simdlanechanged 0 functions in the sweep — a constant-maskpshufbisrare outside a vectorised byte loop, which is what makes it inert rather than risky.
Gates
make testmake test-stagesmake rust-testmake check-speckuna catalog --checkmake test-cliSpeed on the witness, interleaved min/median of 11 alternating runs (measured while the workspace suite was running, so the median is noisy): +0.19% min / -6.93% median — no measurable cost.
In-repo cover
binary_sourceisdataset, soverify --promoterefuses. A new fixturedecompiler/crates/kuna-analysis/tests/fixtures/simdlane_x86_64(gcc -O3 -mssse3, sourcebeside it) reproduces the
simdlanearm exactly — 32SUB161with the option off, none withit on — and
tests/cli/simd-constant-string-initializer.jsonis pinned to it. Theretsplitglobalarm needs the witness's three-predecessor CFG, so both options are pinnedtwo-pass on the real witness bytes by
tests/stages/kuna-simdlane.xmlandtests/stages/kuna-retsplitglobal.xml.Residue
pshufbCALLOTHER loses its last reader but is not removed (a userop is notdead-code-eliminable), so it stays as a bare
pshufb(ZEXT116(dat_100008040),0);statement.vpshufbis two independent 16-byte permutations, not one N=32 permutation,so the width is deliberately not matched.
🤖 Generated with Claude Code