log v1 experiments: nineteen cycles, organised by the method that produced them
cycles/ one file per Ishikawa -> scientific method -> Six Sigma loop, named
for the DEFECT not the fix, carrying the commit record as written at
the time
findings/ what the cycles produced, cross-cut: live bugs, architecture answers,
and defects in my own measurement
The organising finding is that predictions which came back FALSE produced every
significant result. Eleven of sixty-one failed, and those eleven found: that the
arity table was not drifted but 40% incomplete; that the AST traversal is
irreducible and only rules and judgments move; that guards could refuse through
the seam after all; and that routing el_bin_lookup through the gate did NOT fix
the SIGSEGV, because the fallback strlen was the hazard -- a wrong fix I would
otherwise have shipped as verified.
One cycle was run without committing predictions first and had to be discarded
as rigged. It is kept, in full, as 18-async-half-expressible.md.
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
# the crossing resolves at emission
|
||||
|
||||
One `Ishikawa → scientific method → Six Sigma` loop. The record below is the
|
||||
commit message as written at the time, before the outcome was known to anyone
|
||||
reading this file.
|
||||
|
||||
## Record — `35b07ba`
|
||||
|
||||
```
|
||||
EXPERIMENT: resolve the crossing at execution, not at emission
|
||||
|
||||
HYPOTHESIS (Will's): a compiler whose one compiled mechanism is extending the
|
||||
LANGUAGE — not the compiler — can compose without recompilation.
|
||||
|
||||
ISHIKAWA — why does a construct require a recompile today?
|
||||
method codegen inlines the target call into the body
|
||||
machine the binary has no table to consult
|
||||
material the declaration lives in source, read at compile time
|
||||
measurement nothing observes what applied at runtime
|
||||
root cause the crossing is resolved at EMISSION, not at EXECUTION
|
||||
|
||||
CHANGE: codegen emits one unconditional indirection per fn. Which constructs
|
||||
apply is read from a table that can be written AFTER the binary exists;
|
||||
targets resolve through dlsym against the running image.
|
||||
|
||||
PREDICTIONS AND RESULTS
|
||||
P1 a construct declared after the build applies TRUE
|
||||
P2 an unlinked target is skipped, not fatal TRUE
|
||||
P3 emitting on every fn is measurably slower FALSE — 0.37s -> 0.36s
|
||||
with 267 indirections and
|
||||
no bindings. Free unused.
|
||||
P4 the compiler still self-hosts TRUE (see note)
|
||||
|
||||
DEMONSTRATED: an El program with NO decorator in its source, already compiled
|
||||
and linked, picked up a construct declared afterwards:
|
||||
|
||||
$ /tmp/seamrun -> 7
|
||||
$ echo 'work audited entry audit_entry' > constructs.txt
|
||||
$ EL_CONSTRUCTS=constructs.txt /tmp/seamrun
|
||||
AUDIT: work applied by audited
|
||||
7
|
||||
|
||||
P4 note: my first fixpoint test was wrong, not the code. I compared gen1 to
|
||||
gen2, which must differ whenever codegen's output changes. gen2 == gen3, 267
|
||||
seam sites, stable.
|
||||
|
||||
MEASURED COST, and the root cause was not where I looked
|
||||
0 bindings 0.36s vs 0.37s baseline free
|
||||
2 bindings, dlsym per call 2.45s 6.6x
|
||||
2 bindings, resolved once 0.69s 3.5x recovered
|
||||
The table scan was never the cost. dlsym walks the dynamic symbol table on
|
||||
every call. Resolve once and cache — which is the smallest form of what
|
||||
salience does for memory: what is hot stays resolved. The 0.69s residual is
|
||||
audit_entry's own printf on two of the compiler's hottest functions, not seam
|
||||
overhead.
|
||||
|
||||
CONSEQUENCE: the five compile-time declaration kinds on iteration-1 are a
|
||||
compile-time specialisation of something that resolves at runtime. They are not
|
||||
wrong, but they are not the mechanism — the mechanism is one indirection, and a
|
||||
kind is data.
|
||||
```
|
||||
|
||||
## Record — `886626a`
|
||||
|
||||
```
|
||||
seam refusal + control tests: a runtime binding can short-circuit
|
||||
|
||||
Prediction 3 was FALSE. I expected refusal to be impossible through the seam
|
||||
because the entry indirection discarded its return. One line:
|
||||
|
||||
{ el_val_t __s = el_seam_run(EL_STR(f), 0, 0); if (__s) return __s; }
|
||||
|
||||
work() returns 7; bound to a refusing construct AFTER the build it returns 42.
|
||||
So three of the five compile-time kinds are runtime-bindable: entry injection,
|
||||
exit injection, and refusal. wraps_body needs invocation control and
|
||||
prohibits_outside is compile-time by nature.
|
||||
|
||||
104/104 native compiler tests pass.
|
||||
```
|
||||
|
||||
## Record — `28d19da`
|
||||
|
||||
```
|
||||
strip the compile-time machinery the seam replaces
|
||||
|
||||
PREDICTION: codegen.el drops below 4661, its size before any of these passes.
|
||||
RESULT: FALSE. 5157 -> 5096. Still +435 over baseline.
|
||||
|
||||
injects_at_entry collapsed into the seam removed
|
||||
guards_at_entry collapsed into the seam removed
|
||||
injects_at_exit needs the body-helper wrapper STRUCTURAL
|
||||
wraps_body needs the closure + wrapper structural
|
||||
prohibits_outside a #error cannot be emitted at runtime
|
||||
|
||||
The wrapper is not a consequence of compile-time resolution. Early returns must
|
||||
be routed through something no matter when the target is resolved, so exit
|
||||
injection was never going to collapse. I predicted it would because I had
|
||||
conflated "resolved late" with "emitted less".
|
||||
|
||||
What did collapse is entry injection and refusal -- 61 lines of compiler
|
||||
replaced by one refusable indirection, with the capability now bindable after
|
||||
the binary exists.
|
||||
|
||||
8 tests fail, and they are exactly the 8 controls for compile-time entry
|
||||
injection and guards. No unrelated breakage: the controls reported precisely
|
||||
what moved. They assert emission of something that now happens at runtime, so
|
||||
they need rewriting as integration tests -- which the framework does not
|
||||
currently support, because runtime binding needs a built binary and an
|
||||
environment, not compile_capture.
|
||||
|
||||
Verified after the strip: fixpoint gen2==gen3, observation and refusal both
|
||||
work through the seam with the compiler knowing nothing about either.
|
||||
```
|
||||
|
||||
## Record — `8bbb750`
|
||||
|
||||
```
|
||||
control the claim that cannot be unit tested
|
||||
|
||||
The seam's whole claim is that a construct declared AFTER a binary exists
|
||||
applies to that already-built program. compile_capture only sees emitted text,
|
||||
so it structurally cannot check this: it needs a built binary, a linked target,
|
||||
and an environment. Verified by hand until now, which is the standing problem
|
||||
this session has been about.
|
||||
|
||||
tests/integration/seam_binding.sh builds a probe from El source containing no
|
||||
construct at all, links a target that El never references, and asserts:
|
||||
|
||||
ok unbound program is unaffected
|
||||
ok a construct declared AFTER the build applies
|
||||
ok a construct declared after the build can REFUSE
|
||||
ok an unlinked target is skipped, not fatal
|
||||
ok a binding for a different fn does not fire
|
||||
ok two constructs compose on one crossing
|
||||
|
||||
6 assertions, 6 passed, 0 failed
|
||||
|
||||
The eight controls that failed after the strip were replaced, not repaired.
|
||||
They asserted compile-time emission of capability that moved to runtime;
|
||||
contorting them would have kept an assertion whose subject no longer exists.
|
||||
Three took their place, asserting the emitted shape, and the behaviour they
|
||||
used to cover is now the integration harness's job -- which is the honest
|
||||
division, since the shape and the behaviour are no longer the same fact.
|
||||
|
||||
99/99 native compiler tests pass. Fixpoint holds.
|
||||
```
|
||||
|
||||
## Record — `24f7fb5`
|
||||
|
||||
```
|
||||
land the runtime seam: resolve the crossing at execution
|
||||
|
||||
Five compile-time passes added 491 lines to the thing that was supposed to stop
|
||||
growing. The seam is ~55 lines of C and one line of emission, and it does at
|
||||
runtime what three of those five kinds did at compile time -- for programs that
|
||||
are already built.
|
||||
|
||||
a construct declared AFTER the binary exists applies to it
|
||||
free when unused: 0.36s vs 0.37s baseline across 267 indirections
|
||||
dlsym was the cost, not the table scan; resolve-once recovered 3.5x
|
||||
refusal works, composition works, unlinked targets are skipped not fatal
|
||||
|
||||
injects_at_exit and wraps_body do NOT collapse: early returns must route
|
||||
through the body-helper wrapper regardless of when the target is resolved. The
|
||||
wrapper is structural, which I had wrong. prohibits_outside cannot move at all
|
||||
-- a #error has no runtime.
|
||||
|
||||
Controls: 99/99 native compiler tests, plus tests/integration/seam_binding.sh
|
||||
(6/6) for the claim compile_capture structurally cannot see.
|
||||
```
|
||||
Reference in New Issue
Block a user