Back-merge riina #11 — structured WASM control flow for selagi/ulang - #82
Merged
Conversation
riinac verify --full at a30830e: 3318 Rust tests, 0 clippy warnings, 331 .vo compiled, 0 admits, 0 axioms — the tree with the riina back-merge in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
The WASM backend refused every loop. `emit_structured` knew only forward
if/else regions, so a back edge had no structure to map onto; it failed
closed (REQ-78) rather than emit the one-shot shape that loops were
introduced to fix.
It now emits real structured control flow: a `block` wrapping a `loop`,
with the condition re-tested INSIDE the `loop` so it observes the body's
writes, `br_if` to leave, `br 0` for the back edge and `lanjut`, and
`br 1` for `putus`. Depths come from a frame stack, so nesting and loop
control from inside an `if` arm resolve to the right label.
Back edges are found by DOMINANCE over reachable blocks, not block order.
Both parts are load-bearing: the lowerer allocates a loop's exit block
BEFORE the body it follows, so `putus` branches to a lower index than the
block it leaves; and it leaves an unreachable block behind after a `putus`
that branches back into the middle of the loop it was cut out of. Index
order accepts both as back edges and invents loops that are not there.
Dominators are Cooper/Harvey/Kennedy (immediate-dominator array over
reverse postorder) — the set-of-dominators fixpoint was written first and
measured 1.8x the cost of all other WASM emission on a ~2,400-block
function, worsening quadratically.
Also fixes a WASM-only silent wrong answer this uncovered: the lowerer
typed every `+` result `Int` unless an operand was in the numeric tower,
so string concatenation came out `Ty::Int`. The concat was emitted
correctly, but WASM dispatches `cetak`/`cetakln` on the static type (its
values are untagged, unlike C's runtime tags), so it sent the result
pointer through the integer path — every `cetakln("x=" + ke_teks(x))`
printed a heap ADDRESS as a decimal number.
`loops_are_refused_by_the_wasm_backend` is replaced by four differential
tests asserting the opposite, and the four pre-existing loop tests gain a
WASM leg. `untuk` keeps a refusal test, retargeted at the reason that
actually blocks it (list literals, REQ-79). corpus_differential: 34 -> 35
dual-backend examples, all byte-equal.
The four tests riina's #11 adds move the count that 47 doc banners quote. Re-derived by command. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
ib823
pushed a commit
that referenced
this pull request
Aug 24, 2026
Metrics provenance stamp, rebuilt playground WASM, refreshed reports. Counts unchanged. riina/main was NOT pushed — this chain used --reconcile throughout, and riina/main is at f20e7ba8a, which carries a fourth commit (#11, structured WASM control flow) that proof is still landing via PR #82. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
main moved during the #81 sync chain — metrics provenance and deploy artifacts. The only conflict was website/public/metrics.json, generated content, resolved to main's side and then RE-DERIVED rather than hand-picked. No source conflicts. Re-derived on the merged tree, not carried over: 3322 tests, 47 banner docs re-synced, doc/metrics parity test green, audit-docs 0 discrepancies and 0 warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
riinac verify --full on the merged tree: 3322 Rust tests, 0 clippy warnings, 331 .vo compiled, 0 admits, 0 axioms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
ib823
marked this pull request as ready for review
August 25, 2026 15:44
ib823
pushed a commit
that referenced
this pull request
Aug 25, 2026
The four tests riina's #11 adds move the count 47 doc banners quote. Re-derived by command on main after the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
ib823
pushed a commit
that referenced
this pull request
Aug 25, 2026
Byproducts of scripts/deploy-website.sh — metrics provenance stamp, rebuilt playground WASM, report timestamps. Derived counts unchanged. riina main was again NOT pushed: it carries commit e71160270 (the OpenSSL wire differential fix), which is back-merging via PR #83 and does not yet exist here. This chain used --reconcile throughout; only gh-pages was updated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
ib823
pushed a commit
that referenced
this pull request
Aug 25, 2026
riinac verify --full at 5f2c631 — the tree with riina's structured WASM control flow merged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
ib823
pushed a commit
that referenced
this pull request
Aug 25, 2026
main moved during the #82 sync chain — the 3318 -> 3322 banner sync plus deploy artifacts. 120 files conflicted, ALL generated content: metrics.json and the doc banners quoting it (3322 on main vs 3323 here). No source conflicts; the OpenSSL port-race fix is untouched. Resolved to main's side and then RE-DERIVED rather than hand-picked, per Prime Directive 8. Re-verified on the merged tree: 3323 tests / 0 failed, 47 banners re-synced, audit-docs 0 discrepancies and 0 warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uu28z8CdRQ1SLzTv8yszth
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.
A fourth riina commit that landed while #81 was in flight. My earlier "the fork is closed" was premature — this is the correction.
ib823/riinamain is nowf20e7ba8a, notc83f0e0c9. I caught it because the mirror-safety check I ran before considering a force-push showed proof'sloops_differential.rswas 250 lines smaller than riina's, which had no business being true after #81. That traced to a commit that did not exist when I first surveyed the divergence.Had I trusted the earlier survey and run the normal sync path, this commit would have been destroyed.
What it does
The WASM backend refused every loop.
emit_structuredknew only forward if/else regions, so a back edge had no structure to map onto — it failed closed (REQ-78) rather than emit the one-shot shape that loops were introduced to fix. So after #8 made loops work in the interpreter and C, WASM still could not compile one.It now emits a
blockwrapping aloop, condition re-tested inside theloopso it observes the body's writes,br_ifto leave,br 0for the back edge andlanjut,br 1forputus. Depths come from a frame stack, so nesting and loop control from inside anifarm resolve to the right label.Back edges are found by dominance over reachable blocks, not block order — and the commit is explicit that both halves are load-bearing: the lowerer allocates a loop's exit block before the body it follows, so
putusbranches to a lower index than the block it leaves. Index order would read that as a back edge and invent loops that are not there. Dominators are Cooper/Harvey/Kennedy; the set-of-dominators fixpoint was written first and measured 1.8× the cost of all other WASM emission on a ~2,400-block function.It also fixes a second WASM-only silent wrong answer it uncovered: the lowerer typed every
+resultIntunless an operand was in the numeric tower, so string concatenation came outTy::Int. WASM dispatchescetak/cetaklnon the static type — its values are untagged, unlike C's runtime tags — so everycetakln("x=" + ke_teks(x))sent a pointer through the integer path.Verification
Cherry-picked cleanly onto main (no conflicts). The check that matters is three-backend agreement on a program exercising both fixes — a loop, a mutable local, and a string concatenation in the printed value:
i=0 i=1 i=2 i=3 i=4i=0 i=1 i=2 i=3 i=4i=0 i=1 i=2 i=3 i=4Byte-identical. Before #11 the WASM column would have been a build refusal; before the concat fix it would have printed a pointer.
audit-docs.sh0 discrepancies, 0 warningsThe mirror is still not safe to force-push
riina/mainis a moving target — it gained a commit during a single session's work. Until the mirror direction is settled,sync-public.shshould still only be run with--reconcile, and any future mirror push should be preceded by the same content check that caught this one rather than by a remembered survey.Generated by Claude Code