Problem
The issue/no-llvm-kompile-perf-regression branch (based on EXPERIMENT-no-llvm-kompile-updated) attempts to avoid LLVM kompile by splitting inline lookupTy/lookupAlloc/lookupFunction calls out of rule side conditions into two-step rules with #resolved* intermediate terms. This results in a ~5x performance regression in symbolic execution (prove) tests.
Benchmark: iterator-simple test
| Branch |
Duration |
origin/master |
~57s |
issue/no-llvm-kompile-perf-regression |
~288s |
Root Cause Analysis
Proof topology is nearly identical (master: 3 nodes, current: 4 nodes), and total rewrite steps only increased ~7%. The real bottleneck is kore-rpc fallback:
|
Master |
Current branch |
| Total rewrite steps |
798 |
857 (586 + 271) |
| Booster steps |
798 (100%) |
793 (92.5%) |
| Kore-RPC fallback |
0 (0%) |
64 (7.5%) |
Master's proof is handled entirely by the booster (fast). The current branch falls back to kore-rpc 64 times — each fallback is orders of magnitude slower than a booster step.
Fallback distribution by rule
| Count |
Rule |
Source |
| 44x |
#resolvedExecStmt consumer (execStmt) |
kmir.md:113 |
| 8x |
#resolvedOperandConstant consumer |
rt/data.md:133 |
| 4x |
#resolvedCastTransmute consumer |
rt/data.md:758 |
| 2x |
#resolvedDiscriminant consumer |
rt/data.md:1191 |
| 2x |
#resolvedCastPtrToPtr consumer |
rt/data.md:1502 |
| 2x |
#resolvedSetupCalleeClosure consumer |
kmir.md |
| 1x |
#resolvedCastTransmute (MaybeUninit) |
rt/data.md:1877 |
| 1x |
#resolvedCastTransmute (MaybeUninit) |
rt/data.md:1864 |
Key finding: preserves-definedness is NOT the cause
Adding [preserves-definedness] to all #resolved* rules (both producers and consumers) did not reduce the fallback count. The 64 kore-rpc fallbacks remained identical.
Hypothesis: booster cannot evaluate side conditions on resolved intermediate terms
In master, rules like execStmt have side conditions such as notBool #isUnionType(lookupTy(tyOfLocal(getLocal(LOCALS, I)))) where the entire expression is evaluated in a single step with the LLVM backend. In the split version, the consumer rule has notBool #isUnionType(TYINFO) where TYINFO is already a concrete TypeInfo value. The booster appears unable to evaluate these function calls on concrete constructor terms, despite the functions being [function, total].
The pattern is consistent: the booster successfully executes the producer rule (which calls lookupTy etc.), but then falls back to kore-rpc for the consumer rule (which evaluates predicates on the resolved value).
Next Steps
- Investigate why booster cannot evaluate side conditions on
#resolved* consumer rules: The functions (#isUnionType, typeInfoVoidType =/=K, etc.) are [function, total] and should be evaluable. This may be a booster limitation with evaluating functions on concrete K terms that were produced by a previous rewrite step rather than by LLVM evaluation.
- Address at the Haskell backend / booster level rather than continuing to work around it in mir-semantics:
- Cached server: persistent K Haskell backend server to amortize startup costs
- Static cell flags: mark configuration cells as static/immutable so the backend can skip unnecessary copying or matching
- More efficient MAP operations: optimize MAP lookup/update in the Haskell backend
Acceptance Criteria
Problem
The
issue/no-llvm-kompile-perf-regressionbranch (based onEXPERIMENT-no-llvm-kompile-updated) attempts to avoid LLVM kompile by splitting inlinelookupTy/lookupAlloc/lookupFunctioncalls out of rule side conditions into two-step rules with#resolved*intermediate terms. This results in a ~5x performance regression in symbolic execution (prove) tests.Benchmark:
iterator-simpletestorigin/masterissue/no-llvm-kompile-perf-regressionRoot Cause Analysis
Proof topology is nearly identical (master: 3 nodes, current: 4 nodes), and total rewrite steps only increased ~7%. The real bottleneck is kore-rpc fallback:
Master's proof is handled entirely by the booster (fast). The current branch falls back to kore-rpc 64 times — each fallback is orders of magnitude slower than a booster step.
Fallback distribution by rule
#resolvedExecStmtconsumer (execStmt)#resolvedOperandConstantconsumer#resolvedCastTransmuteconsumer#resolvedDiscriminantconsumer#resolvedCastPtrToPtrconsumer#resolvedSetupCalleeClosureconsumer#resolvedCastTransmute(MaybeUninit)#resolvedCastTransmute(MaybeUninit)Key finding:
preserves-definednessis NOT the causeAdding
[preserves-definedness]to all#resolved*rules (both producers and consumers) did not reduce the fallback count. The 64 kore-rpc fallbacks remained identical.Hypothesis: booster cannot evaluate side conditions on resolved intermediate terms
In master, rules like
execStmthave side conditions such asnotBool #isUnionType(lookupTy(tyOfLocal(getLocal(LOCALS, I))))where the entire expression is evaluated in a single step with the LLVM backend. In the split version, the consumer rule hasnotBool #isUnionType(TYINFO)whereTYINFOis already a concreteTypeInfovalue. The booster appears unable to evaluate these function calls on concrete constructor terms, despite the functions being[function, total].The pattern is consistent: the booster successfully executes the producer rule (which calls
lookupTyetc.), but then falls back to kore-rpc for the consumer rule (which evaluates predicates on the resolved value).Next Steps
#resolved*consumer rules: The functions (#isUnionType,typeInfoVoidType =/=K, etc.) are[function, total]and should be evaluable. This may be a booster limitation with evaluating functions on concrete K terms that were produced by a previous rewrite step rather than by LLVM evaluation.Acceptance Criteria
#resolved*consumer rulesiterator-simpleprove performance on par with or better than current master (~57s)