Repository navigation
[0.31][Backend] Reduce repeated policy adapters and choose a dispatch strategy #32
Description
Engineering closure — 2026-09-25
Engineering closure through 0.31 follows the maintainer’s September 25 instruction to skip #30 and exclude 0.32. Release version promotion, fresh distribution bundles, the final release gate and independent release review remain under #30. This closure neither transfers the 0.30 review waiver nor claims an independent security audit or published 0.31 release.
Commit 648bb836, boundary review and acceptance map, isolated attribution, and reproduction/coverage map reconcile the checklist below.
Accepted decisions — 2026-09-21
D3: share helpers only by the complete decoder contract and retain explicit direct per-action stubs. Keep linear dispatch and existing unknown-tag/common-check ordering in the initial 0.31 implementation. Measure through 64 actions after the immediate change. Saved-selector routing, comparison trees, and jump tables are deferred to a follow-up targeting 0.32; no crossover threshold is invented.
Full decision record — baseline: 8d6e200caa58d81429912df925a9322f691cb41d; research baseline remains 6d75a6e7. The choices below were the original open questions; this decision record now governs implementation. The issue stays open until its implementation acceptance passes.
Parent: #30
Target: 0.31. Baseline: branch 0.30, commit 6d75a6e7e4ce55b940697428d895eda74a51ca86. Implementation and engineering acceptance are complete; the accepted decisions define the bounded scope and deferred work.
Problem and evidence
src/codegen/policy.rs emits sequential tag comparisons and one positional adapter for each declared action, including actions with matching parameter shapes.
The existing 32-byte witness corpus reports:
| Actions | ELF bytes | Maximum successful cycles |
|---|---|---|
| 1 | 3,984 | 11,163 |
| 2 | 5,080 | 11,441 |
| 4 | 7,280 | 11,999 |
| 8 | 11,680 | 13,115 |
These figures include action bodies and validation. The 7,696-byte increase is not all removable dispatch overhead. Baseline: docs/CELLSCRIPT_COST_REGRESSION.md.
Decisions to record
- Share decoding helpers by exact parameter layout, share complete adapters, or retain per-action adapters? Define the sharing key, including parameter source, width, ABI placement, and any validation that affects behavior.
- Keep linear dispatch for small policies, use a comparison tree above a threshold, or introduce another bounded form? Measure bytes and worst-case cycles for dense and sparse tags before choosing thresholds.
- How will the checker bind each admitted tag to its exact decoder and action after sharing? The current checker requires one adapter per variant and specific dispatch shapes: crates/cellscript-artifact-checker/src/checker.rs.
- Does the chosen representation require a versioned lowering/checker contract? Keep wire tags, envelope rules, action interfaces, and common-check behavior unchanged unless a separate ABI decision is approved.
Acceptance
- Record separate decisions for decoder sharing and dispatch, including measured rejected alternatives.
- Compare 1/2/4/8 actions at 8/32/128-byte widths, mixed layouts, sparse tags, and the declared maximum supported action count.
- Measure every action and report the worst successful path; measure unknown-tag and malformed-argument rejection paths.
- Demonstrate lower ELF growth for repeated layouts; choose concrete byte/cycle budgets from the experiment before merging.
- Preserve exact action routing, common-check order, failure propagation, and boundary validation. Add mutations that attempt decoder/action substitution and undeclared control-flow targets.
- Run
./scripts/cellscript_gate.sh backendfor the implementation and./scripts/cellscript_gate.sh cibefore claiming merge readiness. Retain fresh cost reports from the exact tested commit. Preserve accept/reject behavior and independent artifact validation; changes to machine contracts must update producers, checker, lowering records, source maps, and negative mutation tests together.
Coordinate with the entry-frame/copy issue so both changes use one adapter contract. A jump table is an option to evaluate, not an assumed improvement.
Activity
a19q3 commentedon Sep 21, 2026
Research notes, 2026-09-21. CellScript source checked at
6d75a6e7. These are proposed experiments and decisions, not implementation results.There are three separate costs to isolate before choosing a new dispatch scheme.
Additional findings in the current implementation
LLVM's switch lowering considers case count, range and target suitability, and can partition cases into dense groups. That supports measuring density as well as action count; it does not supply a CKB-specific crossover threshold.
Proposed experiments
Measure code ranges for dispatch, adapters and action bodies separately, plus total ELF and per-action/worst-path cycles. The existing 1-to-8 growth does not identify how much each component contributes.
I would prototype shared decoder helpers with direct per-action stubs before sharing whole adapters or introducing indirect jumps. This keeps the final action target explicit, though extra helper calls may cost cycles. For common checks, evaluate a validated selector stored safely across calls; reusing a caller-clobbered register is insufficient. Even a selector design must preserve unknown-tag rejection before common checks and first-failure propagation.
A jump table adds table bytes, range checks, hole handling, and an independently checkable target set. The current checker binds adapters to exact actions. Do not trade that binding for an unchecked function pointer.
Decision to record: choose decoder sharing and tag routing independently. Retain linear dispatch for ranges where it wins; make any crossover threshold a result of the matrix above. No new dispatch strategy has been benchmarked yet.
a19q3 commentedon Sep 23, 2026
Review after the 0.31 implementation, 2026-09-23.
D3 is implemented at
65df937f: identical complete decoder contracts share a helper behind direct per-action stubs, while linear dispatch and the unknown-tag/common-check order remain. See policy emission, the decoder/adapter contract and the implementation report. Backend and CI passed in the final implementation and validation record.Remaining work:
policy_code_bytes/policy_code_ranges: dispatch, decoder, stub and action bytes, with the measured successful/rejected paths. The final 8-action ELF change, 11,680 → 8,048 bytes, is the combined cost pass; the intermediate comparison is needed to attribute decoder sharing itself.The open work for this child is review and evidence acceptance. Replacing linear dispatch is not a missing 0.31 requirement.
a19q3 commentedon Sep 25, 2026
Engineering acceptance complete at 648bb836.
Validation on clean commit
648bb8362a82586821c0d4ae9f8b2250dd56ab11: localdev, fullci, and fullbackendpassed; GitHub CI also passed and retains the fresh cost/shape/strict-audit artifacts. The final clean-source cost pair passes another 6,782-metric comparison against the archived candidate. The full backend gate rebuilt pinned CKBf7fa4436and passed the local-node 43-action, 17-Lock and 26-stateful-scenario matrix. Local acceptance report SHA-256:8a1aeb4221409f187e82a342c7335db19432a3fd800e9182e1c65806655a889c. This is local-node evidence, not a new public-network deployment.The D3 ablation holds D2/D4/D5 constant and disables sharing only. The native comparison passes 6,782 comparable metrics with no regression. The 8-action/32-byte sample has unchanged dispatch/action code; 2,208 dedicated-adapter bytes become a 252-byte decoder plus 320 bytes of direct stubs. ELF 9,680 → 8,048 bytes; success/rejection cycles 13,588/11,890 → 13,189/11,491. The report contains all 41 rows and their component ranges. Linear dispatch and common-check ordering remain; #37 owns alternate routing.
Boundary review / acceptance map · attribution and exact report pairs · frozen coverage and reproduction.
Engineering closure through 0.31 follows the maintainer’s September 25 instruction to skip #30 and exclude 0.32. Release version promotion, fresh distribution bundles, the final release gate and independent release review remain under #30. This closure neither transfers the 0.30 review waiver nor claims an independent security audit or published 0.31 release.