feat: add symbolic stack backend path#85
Conversation
# Conflicts: # Cargo.lock # Cargo.toml # crates/jet_ir/src/types.rs
Add symbolic block-state handling for static jumps and joins, share non-jump opcode dispatch between stack backends, and document the current lowering model. Also add targeted JUMPI join tests and jumpdest indexing so symbolic target lookup stays explicit and cheap.
Lower symbolic dynamic JUMP and taken JUMPI through site-local switches over known JUMPDEST targets, carrying the post-pop symbolic stack to each conservative successor. Add ROM coverage for computed JUMP and JUMPI targets in both runtime and symbolic modes, and update the symbolic stack design note.
There was a problem hiding this comment.
Code Review
This pull request introduces a symbolic stack lowering path to the Jet EVM JIT compiler, enabling compile-time tracking of stack values and more efficient IR generation. It refactors opcode implementations to use a generic StackBackend trait, supporting both runtime and symbolic modes, and upgrades the project to LLVM 22. A new jetdbg CLI tool is also included for contract debugging. Review feedback identifies a bug in the symbolic handling of PUSH data where bytes are incorrectly reversed, and recommends using safe Inkwell methods to replace unnecessary unsafe blocks in the stack backend implementation.
| IterItem::PushData(_, _, data) => { | ||
| let mut new_data = [0u8; 32]; | ||
| new_data[..data.len()].copy_from_slice(data); | ||
| new_data[..data.len()].reverse(); |
There was a problem hiding this comment.
The reverse() call here appears to be a bug. According to the architecture documentation and the instructions::Iter implementation, PUSH data is already converted to little-endian (bytes reversed) by the iterator. Since ops::push expects little-endian bytes to construct its i256 limbs, reversing them again here will turn them back into big-endian, leading to incorrect values for multi-byte PUSH opcodes.
ops::push(bctx, new_data)?;| let loaded = bctx | ||
| .builder | ||
| .build_load(bctx.env.types().i256, ptr, "load_int")?; | ||
| let word = unsafe { IntValue::new(loaded.as_value_ref()) }; |
There was a problem hiding this comment.
| &[bctx.registers.exec_ctx.into(), index_value.into()], | ||
| "stack_peek_word_result", | ||
| )?; | ||
| let ptr = unsafe { PointerValue::new(ret.as_value_ref()) }; |
There was a problem hiding this comment.
Build symbolic-mode control flow from a fixed-point abstract stack pass before IR emission. Pre-create entry phis for planned block variants, specialize bytecode blocks by incoming stack height, and lower dynamic jumps through variants without falling back to runtime stack traffic. Preserve known-u64 metadata through symbolic DUP so planned static jump edges and emitted symbolic state agree. Add ROM coverage for backward-only targets, duplicate-carried jump targets, and differing-height joins.
Summary
Verification