Bug: inline times codegen doesn't flush virtual stack before loop #264

Closed
opened 2026-01-16 03:37:18 +00:00 by navicore · 2 comments
navicore commented 2026-01-16 03:37:18 +00:00 (Migrated from github.com)

Summary

The inline times loop codegen has a virtual stack synchronization bug that causes incorrect results when the loop body operates on values pushed before the loop.

Reproduction

: main ( -- )
    1
    [ dup i.+ ] 3 times
    int->string io.write-line
;

Expected: 8 (1→2→4→8)
Actual: 2

Root Cause

The inline codegen keeps the initial value (1) in a virtual register (%0), but the loop body re-materializes it every iteration instead of working with the accumulated value:

%0 = add i64 0, 1   ; Initial value computed once

times_body1:
  store i64 0, ptr %times_cond0_stack
  store i64 %0, ptr %2    ; BUG: overwrites stack with %0 (always 1!) every iteration
  ; ... dup i.+ ...

After each iteration, the result is stored to the stack, but then immediately overwritten with the original %0 value at the start of the next iteration.

Affected Code

crates/compiler/src/codegen/inline/ops.rs - codegen_inline_times_literal

Proposed Fix

Flush the virtual stack to the real stack BEFORE entering the loop, so the loop body works purely on real stack values. The virtual stack state should be "empty" (all values materialized) when the loop begins.

Notes

  • The runtime patch_seq_times function works correctly - this only affects the inlined codegen path
  • Rust unit tests pass because they test the runtime directly, not the inlined path
  • Discovered via seqlings exercise 10-quotations/03-times.seq
## Summary The inline `times` loop codegen has a virtual stack synchronization bug that causes incorrect results when the loop body operates on values pushed before the loop. ## Reproduction ```seq : main ( -- ) 1 [ dup i.+ ] 3 times int->string io.write-line ; ``` **Expected:** `8` (1→2→4→8) **Actual:** `2` ## Root Cause The inline codegen keeps the initial value (`1`) in a virtual register (`%0`), but the loop body re-materializes it every iteration instead of working with the accumulated value: ```llvm %0 = add i64 0, 1 ; Initial value computed once times_body1: store i64 0, ptr %times_cond0_stack store i64 %0, ptr %2 ; BUG: overwrites stack with %0 (always 1!) every iteration ; ... dup i.+ ... ``` After each iteration, the result is stored to the stack, but then immediately overwritten with the original `%0` value at the start of the next iteration. ## Affected Code `crates/compiler/src/codegen/inline/ops.rs` - `codegen_inline_times_literal` ## Proposed Fix Flush the virtual stack to the real stack BEFORE entering the loop, so the loop body works purely on real stack values. The virtual stack state should be "empty" (all values materialized) when the loop begins. ## Notes - The runtime `patch_seq_times` function works correctly - this only affects the inlined codegen path - Rust unit tests pass because they test the runtime directly, not the inlined path - Discovered via seqlings exercise `10-quotations/03-times.seq`
navicore commented 2026-01-16 03:46:13 +00:00 (Migrated from github.com)

Fix Applied

Fixed by spilling virtual stack before entering loop and using a proper preloop block for phi node predecessors.

Changes:

  • codegen_inline_times_literal: Added spill_virtual_stack() call and preloop block
  • codegen_inline_while: Same fix
  • codegen_inline_until: Same fix

Files modified:

  • crates/compiler/src/codegen/inline/ops.rs

All 293 integration tests pass.

## Fix Applied Fixed by spilling virtual stack before entering loop and using a proper preloop block for phi node predecessors. **Changes:** - `codegen_inline_times_literal`: Added `spill_virtual_stack()` call and preloop block - `codegen_inline_while`: Same fix - `codegen_inline_until`: Same fix **Files modified:** - `crates/compiler/src/codegen/inline/ops.rs` All 293 integration tests pass.
navicore commented 2026-01-16 03:48:10 +00:00 (Migrated from github.com)
https://github.com/navicore/patch-seq/pull/265
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-seq#264
No description provided.