Home /
Pragma /
uplc
Mar 09, 11-12 AM (0)
Mar 10, 12-1 AM (0)
Mar 10, 1-2 AM (0)
Mar 10, 2-3 AM (0)
Mar 10, 3-4 AM (0)
Mar 10, 4-5 AM (0)
Mar 10, 5-6 AM (0)
Mar 10, 6-7 AM (0)
Mar 10, 7-8 AM (0)
Mar 10, 8-9 AM (0)
Mar 10, 9-10 AM (0)
Mar 10, 10-11 AM (0)
Mar 10, 11-12 PM (0)
Mar 10, 12-1 PM (0)
Mar 10, 1-2 PM (0)
Mar 10, 2-3 PM (0)
Mar 10, 3-4 PM (0)
Mar 10, 4-5 PM (0)
Mar 10, 5-6 PM (0)
Mar 10, 6-7 PM (0)
Mar 10, 7-8 PM (0)
Mar 10, 8-9 PM (0)
Mar 10, 9-10 PM (0)
Mar 10, 10-11 PM (0)
Mar 10, 11-12 AM (0)
Mar 11, 12-1 AM (0)
Mar 11, 1-2 AM (0)
Mar 11, 2-3 AM (0)
Mar 11, 3-4 AM (0)
Mar 11, 4-5 AM (0)
Mar 11, 5-6 AM (0)
Mar 11, 6-7 AM (0)
Mar 11, 7-8 AM (0)
Mar 11, 8-9 AM (0)
Mar 11, 9-10 AM (0)
Mar 11, 10-11 AM (0)
Mar 11, 11-12 PM (0)
Mar 11, 12-1 PM (0)
Mar 11, 1-2 PM (0)
Mar 11, 2-3 PM (0)
Mar 11, 3-4 PM (0)
Mar 11, 4-5 PM (0)
Mar 11, 5-6 PM (0)
Mar 11, 6-7 PM (0)
Mar 11, 7-8 PM (2)
Mar 11, 8-9 PM (0)
Mar 11, 9-10 PM (0)
Mar 11, 10-11 PM (0)
Mar 11, 11-12 AM (0)
Mar 12, 12-1 AM (0)
Mar 12, 1-2 AM (0)
Mar 12, 2-3 AM (0)
Mar 12, 3-4 AM (0)
Mar 12, 4-5 AM (0)
Mar 12, 5-6 AM (0)
Mar 12, 6-7 AM (0)
Mar 12, 7-8 AM (0)
Mar 12, 8-9 AM (0)
Mar 12, 9-10 AM (0)
Mar 12, 10-11 AM (0)
Mar 12, 11-12 PM (0)
Mar 12, 12-1 PM (0)
Mar 12, 1-2 PM (0)
Mar 12, 2-3 PM (0)
Mar 12, 3-4 PM (0)
Mar 12, 4-5 PM (0)
Mar 12, 5-6 PM (0)
Mar 12, 6-7 PM (0)
Mar 12, 7-8 PM (0)
Mar 12, 8-9 PM (0)
Mar 12, 9-10 PM (0)
Mar 12, 10-11 PM (0)
Mar 12, 11-12 AM (0)
Mar 13, 12-1 AM (0)
Mar 13, 1-2 AM (0)
Mar 13, 2-3 AM (0)
Mar 13, 3-4 AM (0)
Mar 13, 4-5 AM (0)
Mar 13, 5-6 AM (0)
Mar 13, 6-7 AM (0)
Mar 13, 7-8 AM (0)
Mar 13, 8-9 AM (0)
Mar 13, 9-10 AM (0)
Mar 13, 10-11 AM (0)
Mar 13, 11-12 PM (0)
Mar 13, 12-1 PM (0)
Mar 13, 1-2 PM (0)
Mar 13, 2-3 PM (0)
Mar 13, 3-4 PM (0)
Mar 13, 4-5 PM (0)
Mar 13, 5-6 PM (0)
Mar 13, 6-7 PM (0)
Mar 13, 7-8 PM (0)
Mar 13, 8-9 PM (0)
Mar 13, 9-10 PM (0)
Mar 13, 10-11 PM (0)
Mar 13, 11-12 AM (0)
Mar 14, 12-1 AM (0)
Mar 14, 1-2 AM (0)
Mar 14, 2-3 AM (0)
Mar 14, 3-4 AM (0)
Mar 14, 4-5 AM (0)
Mar 14, 5-6 AM (0)
Mar 14, 6-7 AM (0)
Mar 14, 7-8 AM (0)
Mar 14, 8-9 AM (0)
Mar 14, 9-10 AM (0)
Mar 14, 10-11 AM (0)
Mar 14, 11-12 PM (0)
Mar 14, 12-1 PM (0)
Mar 14, 1-2 PM (0)
Mar 14, 2-3 PM (0)
Mar 14, 3-4 PM (0)
Mar 14, 4-5 PM (0)
Mar 14, 5-6 PM (0)
Mar 14, 6-7 PM (0)
Mar 14, 7-8 PM (0)
Mar 14, 8-9 PM (0)
Mar 14, 9-10 PM (0)
Mar 14, 10-11 PM (0)
Mar 14, 11-12 AM (30)
Mar 15, 12-1 AM (0)
Mar 15, 1-2 AM (0)
Mar 15, 2-3 AM (0)
Mar 15, 3-4 AM (0)
Mar 15, 4-5 AM (0)
Mar 15, 5-6 AM (0)
Mar 15, 6-7 AM (0)
Mar 15, 7-8 AM (0)
Mar 15, 8-9 AM (0)
Mar 15, 9-10 AM (0)
Mar 15, 10-11 AM (0)
Mar 15, 11-12 PM (0)
Mar 15, 12-1 PM (0)
Mar 15, 1-2 PM (0)
Mar 15, 2-3 PM (0)
Mar 15, 3-4 PM (0)
Mar 15, 4-5 PM (0)
Mar 15, 5-6 PM (0)
Mar 15, 6-7 PM (0)
Mar 15, 7-8 PM (6)
Mar 15, 8-9 PM (0)
Mar 15, 9-10 PM (0)
Mar 15, 10-11 PM (0)
Mar 15, 11-12 AM (0)
Mar 16, 12-1 AM (0)
Mar 16, 1-2 AM (0)
Mar 16, 2-3 AM (0)
Mar 16, 3-4 AM (0)
Mar 16, 4-5 AM (0)
Mar 16, 5-6 AM (0)
Mar 16, 6-7 AM (0)
Mar 16, 7-8 AM (0)
Mar 16, 8-9 AM (0)
Mar 16, 9-10 AM (0)
Mar 16, 10-11 AM (0)
Mar 16, 11-12 PM (0)
Mar 16, 12-1 PM (0)
Mar 16, 1-2 PM (0)
Mar 16, 2-3 PM (0)
Mar 16, 3-4 PM (0)
Mar 16, 4-5 PM (0)
Mar 16, 5-6 PM (0)
Mar 16, 6-7 PM (0)
Mar 16, 7-8 PM (0)
Mar 16, 8-9 PM (0)
Mar 16, 9-10 PM (0)
Mar 16, 10-11 PM (0)
Mar 16, 11-12 AM (0)
38 commits this week
Mar 10, 2026
-
Mar 17, 2026
chore: exclude uplc-fuzz from workspace, add utility examples
- Exclude crates/uplc-fuzz from workspace (broken dep after rebase) - Add bench pre-check to skip scripts that fail decode/eval - Add utility examples: eval_flat, eval_flat_bc, check_max_var, bc_dump, bench_loop (for profiling)
perf: step countdown + inner return drain loop + boxed CasesFrame
Three optimizations to the VM dispatch loop: 1. Replace unbudgeted_steps[9] total counter with a countdown that decrements to zero. Saves one array increment per step and turns the >= comparison into a free zero-check after decrement. 2. Restructure the run loop to drain return chains without going back through the outer compute loop. Eliminates Phase construction and matching on the hot Compute→Return transition. 3. Box the CasesFrame (rare, 0.3% of opcodes) to shrink Frame from 32 to 16 bytes, halving memory bandwidth for every frame push/pop.
test: add large_var conformance test for De Bruijn index > 255
256 nested lambdas with a reference to the outermost variable, exercising the VarBig opcode. Without VarBig, the u8 index silently wraps and the VM looks up the wrong variable.
feat: bytecode VM support for new builtins, case-on-constants, and VarBig
- Expand builtin pre-wrap range from 0..92 to 0..101 for 9 new builtins (Value ops, MultiScalarMul) - Add case_on_constant handler: Bool (with branch count validation), Unit, Integer, List (head/tail field pushing), Pair (fst/snd) - Add VarBig opcode (0x16) with u32 index for De Bruijn indices > 255, fixing silent truncation that caused wrong variable lookups in large scripts (e.g. stablecoin contracts) - Compiler emits Var (u8) for indices <= 255, VarBig (u32) otherwise; ApplyVar/ForceVar superinstructions restricted to u8 range
fix: update new builtins to use arg() accessor and fix blst pointer casts
The rvcas/value-builtins branch added new builtins that used the old runtime.args[n] field access. Updated to use runtime.arg(n) accessor (fixed-size array with unsafe unchecked access). Also fixed blst_p1/p2 pointer casts in MultiScalarMul builtins.
test: remove redundant case_bool_conformance.rs (covered by conformance suite)
test: add upstreamable case-on-boolean conformance tests
5 new conformance tests in standard .uplc/.expected/.budget.expected format, suitable for upstreaming to the official Plutus conformance test suite: - case_bool-1: case (con bool True) → selects branch 1 - case_bool-2: case (con bool False) → selects branch 0 - case_bool-3: case (equalsInteger 1 1) → True → branch 1 - case_bool-4: case (equalsInteger 1 2) → False → branch 0 - case_bool-5: case (lessThanInteger 1 2) → True → branch 1 These tests verify that Bool values (from builtins or constants) can be used as scrutinees in case expressions, with False=tag 0 and True=tag 1. Both Scalus and uplc-turbo previously failed these. Also retains the 10 Rust-level case_bool_conformance tests. Total: 1723 tests passing.
fix: handle case expressions on boolean values in V3
In Plutus V3, Bool is matchable via case expressions: False maps to tag 0, True maps to tag 1 (matching the Constr encoding). Builtins like equalsInteger return Con(Boolean), but case expects Constr. Added Bool-to-tag conversion in the FrameCases handler for both the AST interpreter and bytecode VM. This fixes coop-1 through coop-7 benchmark scripts which were previously failing silently. All 1713 tests pass including 10 new case-on-boolean tests.
perf: box ConstrFrame to shrink Frame enum size
The Constr variant contained 6 fields including a BumpVec, making the Frame enum ~72 bytes. Boxing it puts only an 8-byte pointer in the enum, reducing Frame to ~24 bytes. This halves the per-push/pop copy cost for the common frame variants (AwaitArg, AwaitFunTerm, Force). Small improvement on diverse workloads (escrow: 231→221µs, uniswap: 852→832µs). 818/818 conformance tests passing.
perf: add ForceVar superinstruction
Force(Var(idx)) is extremely common (56% of all Force opcodes). ForceVar looks up the variable and forces it directly, skipping the FrameForce push/pop cycle entirely. Reduces total opcode count by ~8% (8493 → 7807 on auction_1-2). 818/818 conformance tests passing.
perf: jump table dispatch + AOT profiling binary
Replace guarded match patterns (op if op == Op::Xxx as u8 =>) with direct numeric literals (0x01 =>) in the bytecode dispatch loop. This allows the compiler to generate a jump table instead of chained comparisons. Results (AOT, pre-compiled): - auction_1-1: 120µs (matching AST interpreter's 118µs) - auction_1-2: 369µs (vs AST's 325µs — 14% gap) The bytecode VM shows dramatically better cache behavior (60% fewer L1 misses) and branch prediction (33% fewer misses) compared to AST, but executes ~19% more instructions due to opcode decoding overhead.
perf: slim LambdaBC/DelayBC values + zero-alloc Constr/Case frames
Two major optimizations to the bytecode VM: 1. LambdaBC/DelayBC values now store only (body_ip, id, env) instead of (body_ip, env, parameter, body). The AST refs for discharge are looked up from CompiledProgram tables only when needed (final output). This cuts the value size significantly, reducing arena allocation pressure. 2. Frame::Constr and Frame::Cases no longer heap-allocate Vec<u32> for field/branch offsets. Instead they store (offsets_start, count) and read offsets directly from the bytecode array on demand. Bytecode VM now 25-44% faster than the AST interpreter: auction_1-1: 118µs → 82µs (1.44x) auction_1-2: 325µs → 259µs (1.25x) auction_1-3: 332µs → 257µs (1.29x) All 1703 tests passing (818 bytecode conformance + 818 AST + 36 unit + 31 FLAT).
feat: working bytecode VM with compiler, dispatch loop, and 8 passing tests
Complete bytecode VM implementation: Compiler (compiler.rs): - Compiles Term AST to flat bytecode with backpatching - Detects and emits superinstructions for common patterns - Interns constants into a side-table pool - 11 compiler unit tests VM (vm.rs): - Tight dispatch loop over u8 opcodes - LambdaBC/DelayBC value variants for bytecode closures (body_ip + env) - Delegates builtin execution to existing Machine::call - Full CEK semantics: env, continuation stack, budget tracking Passing integration tests: - bc_eval_integer, bc_eval_unit, bc_eval_true - bc_eval_identity (lambda + apply) - bc_eval_force_delay - bc_eval_add_integer (builtin) - bc_eval_nested_apply (deep lambda nesting) - bc_eval_if_then_else (polymorphic builtin with force/delay)
perf: pre-wrap constant pool Values at execution start
Instead of allocating a Value::Con wrapper in the arena for every Const opcode execution, pre-wrap all constant pool entries into Values once at the start of execute(). The Const opcode now does a simple array index instead of an arena allocation. ~10% improvement on auction scripts (120→107µs on auction_1-1). 818/818 conformance tests passing.
feat: UPLC_BENCH_MODE=bytecode toggle for Docker benchmarks
The use_cases benchmark now supports UPLC_BENCH_MODE env var: - "ast" (default): standard AST interpreter (decode + eval per iteration) - "bytecode": AOT bytecode VM (compile once, execute per iteration) Docker runner script passes the env var through to the benchmark binary.
perf: AOT bytecode benchmark + Vec-indexed lambda/delay discharge
- Replace HashMap lookups for lambda_info/delay_info with Vec indexing via compile-time u16 IDs embedded in the bytecode - Benchmark pre-compiles bytecode once, only measures execution (AOT) - ConstrBig opcode for large tags (u64), Constr stays u8 for common case - 818/818 conformance tests still passing
feat: bytecode VM passes all 818 conformance tests
Key fixes: - LambdaBC/DelayBC values now store original AST terms for discharge, enabling correct term reconstruction for output/error reporting - Compiler records lambda_info/delay_info mappings from body_ip → AST - Constr uses u8 tag (common case) with ConstrBig (u64) for large tags, keeping the hot path cache-friendly via separate opcodes - ForceBuiltin/Force2Builtin superinstructions now only emitted when the builtin actually requires forcing (fixes argExpected test) Test results: 1703 total passing - 818 conformance tests via bytecode VM (NEW - all passing) - 818 conformance tests via AST interpreter - 36 unit tests (compiler + VM integration) - 31 FLAT round-trip tests
test: add failing case-on-boolean conformance tests
7 failing tests that document a pre-existing upstream bug where case expressions cannot match on boolean values. In Plutus V3, Bool should be matchable via case (False=tag 0, True=tag 1), but uplc-turbo returns NonConstrScrutinized because builtins return Con(Boolean) instead of Constr(0/1). This bug causes the coop-1 through coop-7 benchmark scripts to fail. The benchmark framework was silently measuring failure-path speed. 3 passing tests confirm case-on-constr already works correctly.
perf: add ApplyVar superinstruction
When Apply's function is a Var (variable lookup), the function evaluation is instant — just an array index into the environment. The ApplyVar superinstruction skips FrameAwaitFunTerm entirely, directly pushing FrameAwaitArg with the looked-up value. Captures ~19% of Apply opcodes (444 out of 2344 in auction_1-2). Saves 1 frame push + 1 frame pop + 1 Phase transition per occurrence. 818/818 conformance tests passing.
perf: pre-create builtin Runtime+Value objects at execution start
All 92 builtin values are pre-allocated once per execute() call. The Builtin opcode now does a simple array lookup instead of arena-allocating a Runtime struct + Value wrapper per occurrence.
perf: pre-allocate Unit/True/False values, reuse across execution
Specialized constant opcodes (ConstUnit, ConstTrue, ConstFalse) now return pre-allocated values instead of arena-allocating on each use. These values are created once per execute() call and shared across all occurrences.
perf: optimize bytecode read helpers with slice-based conversion
Use slice try_into for read_u32/read_u16 instead of individual byte accesses. This lets the compiler elide per-byte bounds checks and generate a single aligned load instruction.
wip: bytecode compiler foundation
Add bytecode module with: - Opcode definitions (10 core + 4 superinstructions + 4 specialized constants) - Compiler that translates Term AST to flat bytecode with backpatching - 11 compiler tests verifying correct opcode generation for all patterns Superinstructions detected at compile time: - ForceDelay: Force(Delay(body)) → single opcode, body inline - ApplyLambda: Apply(Lambda(body), arg) → single opcode - ForceBuiltin: Force(Builtin(f)) → single opcode - Force2Builtin: Force(Force(Builtin(f))) → single opcode Specialized constants for Unit, true, false, small integers avoid constant pool lookup. VM execution loop is stubbed — needs Value type extension for bytecode closures (LambdaBC/DelayBC variants) before full implementation.
perf: enable LTO and replace Context linked list with Vec stack
Two changes: 1. Enable LTO (lto=true) and single codegen unit (codegen-units=1) for release and bench profiles. Gives the compiler full cross-crate visibility for inlining and dead code elimination. ~20-25% improvement. 2. Replace arena-allocated Context linked list with a pre-allocated Vec<Frame> stack. Eliminates per-frame arena allocation, improves cache locality (contiguous memory), and simplifies MachineState (no longer carries Context reference, reducing enum size). Combined effect: uplc-turbo now ranks 3rd across all VM implementations, ahead of Scalus CEK, Plutuz, and both Chrysalis variants. Geo mean: 266µs → 201µs (24.6% faster on this benchmark run).