binding-compiler-investigation.md (11354B)
Unpublished binding compiler: broader testing and a compilation roadblock
This follows the isolated substitution candidate. The requested sequence was broader semantic tests, an unpublished compiler build, and compile/runtime measurement, stopping at a roadblock or decision point.
Historical stage: stopped at a cold-workspace compilation roadblock. The subsequent failure investigation completed unchanged retries, the repeated arithmetic comparison and existing regression checks without reproducing the timeout. This page preserves the original failed observation rather than overwriting it. The accepted compiler, production sources, reducer, published fixtures and encodings remain unchanged. No compiler generation, acceptance, or promotion is claimed.
Broader semantic screen
test/binding_semantics.py compiles 32 closed, two-argument programs containing
eligible Boolean bindings. The grammar includes nested construction, exclusive
alternatives, conditions using the bound Boolean, and duplicated occurrences in
exclusive branches. All transforms are performed by the compiled tricu pass;
compilation then uses the exact accepted stable core worker established in the
previous stage.
The harness executes 928 paired observations, each once with the original and once with the transformed program:
| Category | Paired checks |
|---|---|
| Fully applied finite values, including noncanonical Boolean trees | 512 |
| Partially applied functions, including partial/divergent first arguments | 192 |
| Partial data/conditions with full, root, head, and tail observation | 224 |
All checked results and termination classifications agree with expectations. Across 1,856 executions there are 394 expected three-second timeouts. These are bounded observations, not proofs of divergence. Cases which have concrete values are checked as complete trees; the data oracle independently models lazy constructor fields and selector demand, not stable abstraction or the reducer. Four additional unit tests check that oracle's lazy-field and selector behavior.
Partial application requires an explicit distinction: 96 checks produce different finite function trees, while retaining matching termination status. Those differences are recorded, not hidden or treated as equality. This candidate changes function representations, as already disclosed in the earlier stage. It is not a proof of equivalence in every intensional source context. The screen specifically rejects a newly divergent observation even when a function-tree change itself is an expected toolchain difference.
The expanded corpus found no additional demand counterexample. It is still a bounded grammar and does not cover every reflective, recursive, effectful, or higher-order program.
Actual unpublished source compiler
test/binding_compiler_candidate.py builds a real sourceBuildGraph compiler,
not just a wrapper for hand-authored core inputs.
The builder copies and authenticates every source snapshot listed by the shipped
compiler metadata into a private workspace. Only that copy of source_stable
is changed: its stableCompileCore entry runs the screened transformation before
unchanged stable elimination. The analyzer, substituter and their helpers are
private candidate dependencies. Selector and Boolean-negation certificates use
the retained known compiled literal identities; arbitrary source names do not
establish a certificate.
The accepted compiler builds this candidate. Its private build completed in approximately 379 seconds, producing a roughly 603 KiB bundle, versus roughly 575 KiB for the shipped compiler. These are one-off preparation measurements, not compiler performance comparisons or self-reproduction evidence.
Artifact: /tmp/tricu-binding-compiler-first/candidate.arboricx.
All source captures, compiler identities and the staged source edit remain in
that directory with its report. Nothing is installed into bootstrap/ or lib/.
Cold arithmetic-workspace roadblock
The planned workload builds the actual adder/multiplication investigation modules and their native-math, layout, significand, and pipeline dependencies. The same source bytes are supplied to both compilers. Output stores are fresh; tool loading uses an explicit private store. Processes use one GHC capability and a 2 GiB heap limit. The planned repeated comparison randomizes order and excludes first samples.
The first pair did not complete:
| Compiler | Cold compilation result |
|---|---|
| Accepted | Completed in 136.13 seconds; about 148.61 GB cumulative allocation |
| Candidate | Exceeded the 270-second process guard; no completed application bundle |
The candidate's completed dependency receipts include native_math, kernels,
and layouts. The accepted run also completed pipelines, multiply, and the
driver. These receipts identify the unfinished portion of the graph, not an exact
failing definition or reduction.
The CLI's selected compilation timeout was 240 seconds; the enclosing process was independently guarded at 270 seconds. The recorded failure is the enclosing wall-time guard. At this stage limits were not raised and the large run was not retried. The subsequent linked investigation repeated it under the same limits.
This is not a statistically established slowdown ratio. There is one completed baseline and one censored candidate run. It does not establish whether the cause is excess analysis work, a compilation/demand stall, workload-specific behavior, or environmental timing. No allocation total is available for the killed process. It would be incorrect to claim an exact candidate overhead or blame the reducer.
The repeated cold-compilation experiment, recursive arithmetic runtime benchmarks, and broader existing compiler-suite sweep therefore remain incomplete.
One bounded small-module diagnostic
To distinguish a total integration failure from a workload-specific/scaling
problem, test/binding_compiler_small.py builds a standalone, import-free module
containing the 18 earlier controls and small batch consumers.
The actual source compiler reproduces the isolated-core results:
- the single-use, exclusive-branch, and adder Boolean bindings become exactly the accepted compiler's manually inlined function trees;
- the other 15 controls retain their exact function trees;
- partial branch/carry observations return their independently expected values;
- the rejected captured closure and selected divergent branch retain their expected full-observation timeouts;
- the shared traversal remains exact and has identical measured allocation.
Five fresh-store compilations per tool and five fresh-process runtime invocations per workload were performed, in randomized order. The first repetition is excluded from medians. Complete results are independently checked. Runtime measurement spans invocation through sharing-aware full forcing, before rendering; compilation allocation is separately reported as a whole-process total.
Standalone compilation
| Metric | Accepted median | Candidate median | Candidate / accepted |
|---|---|---|---|
| Wall time | 1.860 s | 1.837 s | 0.987 |
| Cumulative allocation | 9.883 GB | 9.929 GB | 1.0047 |
The small module shows approximately 0.47% more compilation allocation, with no convincing wall-time difference. It does not resolve the cold arithmetic workspace failure or justify extrapolating negligible overhead to large programs.
Standalone runtime
| Workload | Allocation change | Wall-time change |
|---|---|---|
| 1,024 exclusive-branch cases | 5.1% less | 1.0% higher |
| 1,024 isolated full-adder cases | 9.2% less | 7.3% lower |
| Shared traversal control | Identical | 3.9% lower |
These runs take roughly 3–7 milliseconds, so small timing differences should be treated cautiously. The unchanged sharing control's timing movement illustrates the noise. Allocation is stronger evidence here: the transformed controls reduce work while the untouched control stays identical.
These are not complete recursive-addition or multiplication measurements. The previous source-form arithmetic results remain separate evidence; this run cannot claim that those gains have been realized by the new compiler on the large workload.
Decision point and recommended diagnosis
The executable transformation now works through an actual source compiler on the small controls and shows measurable allocation benefits. But the cold arithmetic build failure blocks proceeding toward adoption or interpreting the pass as a practical general improvement.
The next bounded investigation should isolate the unfinished dependency/module and compare compilation with transformation enabled, disabled, and analysis-only. Useful questions include:
- Is the work spent scanning bindings that the cheap Boolean RHS gate would reject anyway? Currently full use/path analysis precedes that RHS gate.
- Does repeated traversal of expanded/shared core syntax dominate on particular source constructs?
- Is there a specific rewrite which changes full function construction or compilation termination, rather than simply adding overhead?
- Can a narrowed specimen reproduce the stall under a complete allocation/CPU profile, excluding unrelated graph transport and cold-store work?
These are hypotheses, not established root causes. A cheap eligibility prefilter might help, but changing guard order should not precede establishing what actually stalled. No speculative optimizer change was made after the failure.
Before promotion, the existing stable/scope/diagnostic/intensional suites, broader real workloads, source freezing, fresh compiler generations, independent audits, smokes and retention remain required. This candidate has not reached those gates.
Artifacts and reproduction
Combined recorded evidence: bench/binding-compiler-results.json.
Raw runs:
/tmp/tricu-binding-semantics-first/tmp/tricu-binding-compiler-first/tmp/tricu-binding-compiler-measure— incomplete, timed out/tmp/tricu-binding-compiler-small
Harnesses:
test/binding_semantics.py,test/binding_semantics_test.pytest/binding_compiler_candidate.pytest/binding_compiler_measure.pytest/binding_compiler_small.py
The first two integration stages take the previous isolated substitution run as an explicitly authenticated input. All output directories must be fresh:
HOST=$(nix build .# --no-link --print-out-paths)/bin/tricu
python3 test/binding_semantics_test.py
python3 test/binding_semantics.py "$HOST" \
--previous /tmp/tricu-binding-substitution-final \
--directory /tmp/binding-semantics-new
python3 test/binding_compiler_candidate.py "$HOST" \
--previous /tmp/tricu-binding-substitution-final \
--semantics /tmp/binding-semantics-new --directory /tmp/binding-compiler-new
python3 test/binding_compiler_small.py "$HOST" \
--candidate /tmp/binding-compiler-new --directory /tmp/binding-small-new
The large measurement harness is retained for reproduction/diagnosis, not as a passing benchmark. It stops on the first failed compilation and preserves an incomplete report. Do not present its planned repetitions as completed samples.