Skip to content

[perf] Recover OSR coverage for observed-assign loops (surgical fix for #211) #231

Description

@InauguralPhysicist

Context

#211 (OSR observed-assign per-iteration leak) was fixed conservatively: the OSR scanner (jit_supported_prefix, entry_offset > 0) now stops at OBSERVE_ASSIGN / OBSERVE_ASSIGN_LOCAL, so a hot loop that reassigns an observed variable in its body falls back to the interpreter instead of OSR-compiling. Correct and leak-clean, but it forfeits JIT acceleration for exactly the canonical loop while improving: x is refine(x) shape.

Measured cost: ≈16% on an observed-var-reassigned-in-hot-OSR-loop microbench (n=5: ~2.70s OSR-active vs ~3.13s interpreter); 0% elsewhere (the bail only triggers on OBSERVE_ASSIGN). This is the optimization to recover.

Root cause (from #211 diagnosis)

The OSR loop-thunk's observe-assign path (jit_helper_observe_assign[_local], run from native code) orphaned a lifted stack slot when the observed variable's prior binding was a heap pointer (e.g. best is pop[i]). The interpreter later re-lifted and leaked it via the CALL arg path (vm_slot_lift). Data-dependent: pointer rebinds leaked, number rebinds were clean — through the same emitted code — so it's an sp/refcount interaction at the native boundary, not a plain emitter typo. From-zero function thunks (entry_offset == 0) are leak-clean.

Repro / bisection technique

  • Minimal repro: an observed variable conditionally assigned a list element inside a loop, forced to OSR with EIGS_JIT_OSR_THRESHOLD=1.
  • EIGS_JIT_OFF=1 → clean. High EIGS_JIT_OSR_THRESHOLD → clean. Both isolate the bug to OSR loop-thunks.
  • ASAN_OPTIONS="detect_leaks=1:malloc_context_size=40:fast_unwind_on_malloc=0" for deep traces.

Task

Find the exact sp/refcount imbalance in the OSR thunk's observe-assign + CALL handoff (likely needs live thunk disassembly: EIGS_JIT_DEBUG=1 EIGS_JIT_DUMP_NATIVE=1 + objdump -D -b binary -m i386:x86-64), fix it, and remove the entry_offset > 0 bail in jit_supported_prefix so observed-assign loops OSR-compile again.

Acceptance

  • The entry_offset > 0 bails for OBSERVE_ASSIGN / OBSERVE_ASSIGN_LOCAL are removed.
  • tests/test_optimize.eigs (genetic_optimize, 120 generations) stays leak-clean under ASan detect_leaks=1.
  • The observed-assign microbench recovers toward the OSR-active timing.
  • Full suite green release + ASan; harness leak tally unchanged (10).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions