This commit is contained in:
bQUARKz 2026-02-18 15:19:07 +00:00
parent 6d875784ea
commit a5daebd849
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 413 additions and 15 deletions

View File

@ -0,0 +1,127 @@
use prometeu_bytecode::decode_next;
use prometeu_bytecode::isa::core::{CoreOpCode, CoreOpCodeSpecExt};
fn encode_instr(op: CoreOpCode, imm: Option<&[u8]>) -> Vec<u8> {
let mut out = Vec::new();
let code = op as u16;
out.extend_from_slice(&code.to_le_bytes());
let spec = op.spec();
let need = spec.imm_bytes as usize;
match (need, imm) {
(0, None) => {}
(n, Some(bytes)) if bytes.len() == n => out.extend_from_slice(bytes),
(n, Some(bytes)) => panic!("immediate size mismatch for {:?}: expected {}, got {}", op, n, bytes.len()),
(n, None) => panic!("missing immediate for {:?}: need {} bytes", op, n),
}
out
}
fn disasm(bytes: &[u8]) -> String {
// Minimal test-only disasm: NAME [operands]
let mut pc = 0usize;
let mut lines = Vec::new();
while pc < bytes.len() {
match decode_next(pc, bytes) {
Ok(instr) => {
let name = instr.opcode.spec().name;
let mut line = String::from(name);
let imm_len = instr.opcode.spec().imm_bytes as usize;
if imm_len > 0 {
// Heuristic formatting based on known op immediates
line.push(' ');
let s = match instr.opcode {
CoreOpCode::Jmp | CoreOpCode::JmpIfFalse | CoreOpCode::JmpIfTrue => {
format!("{}", instr.imm_u32().unwrap())
}
CoreOpCode::PushI64 => format!("{}", instr.imm_i64().unwrap()),
CoreOpCode::PushF64 => format!("{}", instr.imm_f64().unwrap()),
CoreOpCode::PushBool => format!("{}", instr.imm_u8().unwrap()),
CoreOpCode::PushI32 => format!("{}", instr.imm_i32().unwrap()),
CoreOpCode::PopN | CoreOpCode::PushConst | CoreOpCode::PushBounded => {
format!("{}", instr.imm_u32().unwrap())
}
_ => format!("0x{}", hex::encode(instr.imm)),
};
line.push_str(&s);
}
lines.push(line);
pc = instr.next_pc;
}
Err(_) => break,
}
}
lines.join("\n")
}
#[test]
fn encode_decode_roundtrip_preserves_structure() {
// Program: PUSH_I32 42; PUSH_I32 100; ADD; PUSH_BOOL 1; JMP 12; NOP; HALT
let mut prog = Vec::new();
prog.extend(encode_instr(CoreOpCode::PushI32, Some(&42i32.to_le_bytes())));
prog.extend(encode_instr(CoreOpCode::PushI32, Some(&100i32.to_le_bytes())));
prog.extend(encode_instr(CoreOpCode::Add, None));
prog.extend(encode_instr(CoreOpCode::PushBool, Some(&[1u8])));
// Jump to the HALT (compute absolute PC within this byte slice)
// Current pc after previous: 2+4 + 2+4 + 2 + 2+1 = 17 bytes
// Next we place: JMP (2+4), NOP (2), HALT (2)
// We want JMP target to land at the HALT's pc
let jmp_target: u32 = 17 + 2 + 4 + 2; // pc where HALT starts
prog.extend(encode_instr(CoreOpCode::Jmp, Some(&jmp_target.to_le_bytes())));
prog.extend(encode_instr(CoreOpCode::Nop, None));
prog.extend(encode_instr(CoreOpCode::Halt, None));
// Decode sequentially and check opcodes and immediates
let mut pc = 0usize;
let mut seen = Vec::new();
while pc < prog.len() {
let instr = decode_next(pc, &prog).expect("decode ok");
seen.push(instr);
pc = instr.next_pc;
}
assert_eq!(seen.len(), 7);
assert_eq!(seen[0].opcode, CoreOpCode::PushI32);
assert_eq!(seen[0].imm_i32().unwrap(), 42);
assert_eq!(seen[1].opcode, CoreOpCode::PushI32);
assert_eq!(seen[1].imm_i32().unwrap(), 100);
assert_eq!(seen[2].opcode, CoreOpCode::Add);
assert_eq!(seen[3].opcode, CoreOpCode::PushBool);
assert_eq!(seen[3].imm_u8().unwrap(), 1);
assert_eq!(seen[4].opcode, CoreOpCode::Jmp);
assert_eq!(seen[4].imm_u32().unwrap(), jmp_target);
assert_eq!(seen[5].opcode, CoreOpCode::Nop);
assert_eq!(seen[6].opcode, CoreOpCode::Halt);
}
#[test]
fn disasm_contains_expected_mnemonics_and_operands() {
// Tiny deterministic sample: NOP; PUSH_I32 -7; PUSH_BOOL 0; ADD; HALT
let mut prog = Vec::new();
prog.extend(encode_instr(CoreOpCode::Nop, None));
prog.extend(encode_instr(CoreOpCode::PushI32, Some(&(-7i32).to_le_bytes())));
prog.extend(encode_instr(CoreOpCode::PushBool, Some(&[0u8])));
prog.extend(encode_instr(CoreOpCode::Add, None));
prog.extend(encode_instr(CoreOpCode::Halt, None));
let text = disasm(&prog);
// Must contain stable opcode names and operand text
assert!(text.contains("NOP"));
assert!(text.contains("PUSH_I32 -7"));
assert!(text.contains("PUSH_BOOL 0"));
assert!(text.contains("ADD"));
assert!(text.contains("HALT"));
}
// Minimal hex helper to avoid extra deps in tests
mod hex {
pub fn encode(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
const HEX: &[u8; 16] = b"0123456789abcdef";
for &b in bytes {
s.push(HEX[(b >> 4) as usize] as char);
s.push(HEX[(b & 0x0f) as usize] as char);
}
s
}
}

View File

@ -1,31 +1,302 @@
# PR-1.7 — Bytecode Roundtrip Tests (Encode/Decode/Disasm Sanity) # PR-2.1 — Remove ScopeFrame and HIP Runtime Structures
### Briefing ### Briefing
Before touching VM behavior, we want confidence that the bytecode toolchain is coherent after the ISA reset. The new architecture removes HIP, borrow/mutate/peek semantics, and any gate-based lifetime tracking. The VM must no longer depend on `ScopeFrame` or related structures.
### Target ### Target
* Add roundtrip tests that validate: * Remove `ScopeFrame` and any HIP-related runtime data structures.
* Ensure the VM compiles and runs without any scope/gate logic.
* Encode → decode preserves structure.
* Disasm prints stable, readable output.
### Work items ### Work items
* Add a small set of “known-good” bytecode samples built using the new minimal ISA. * Delete `scope_frame.rs` and any modules dedicated to HIP or gate lifetimes.
* Implement tests: * Remove fields in VM state that track scope frames, gates, or borrow state.
* Update the main VM execution loop to no longer push/pop scope frames.
* Encode then decode equals original structure. * Remove any trap logic that references scope or gate violations.
* Disasm output contains expected instruction names and operands.
* Keep samples intentionally tiny and deterministic.
### Acceptance checklist ### Acceptance checklist
* [ ] Roundtrip tests exist and pass. * [ ] `ScopeFrame` and related modules are fully removed.
* [ ] Samples do not depend on legacy semantics. * [ ] VM compiles without scope/gate concepts.
* [ ] No HIP-related symbols remain in the VM crate.
* [ ] `cargo test` passes. * [ ] `cargo test` passes.
### Tests ### Tests
* New unit tests for encode/decode/disasm roundtrip. * Existing tests only.
### Junie instructions
**You MAY:**
* Delete scope/gate-related modules and fields.
* Update code to remove references to them.
**You MUST NOT:**
* Introduce new lifetime or ownership systems.
* Replace scope frames with another architecture.
* Add compatibility layers.
**If unclear:**
* Ask for clarification instead of inventing new runtime concepts.
---
# PR-2.2 — Simplify VM Execution Loop (Pure Stack Machine)
### Briefing
The VM should operate as a simple stack-based interpreter with a clear fetchdecodeexecute loop, without hidden side channels or legacy behaviors.
### Target
* Normalize the VM main loop to a clean stack-machine structure.
* Remove any legacy control paths tied to HIP/RC behavior.
### Work items
* Refactor the main interpreter loop to:
* Fetch instruction at PC.
* Decode opcode.
* Execute operation on stack/frames.
* Remove any conditional logic that depends on HIP/RC state.
* Ensure PC advancement is canonical and centralized.
### Acceptance checklist
* [ ] VM loop is structurally simple and readable.
* [ ] No HIP/RC conditionals remain.
* [ ] VM compiles and runs basic programs.
* [ ] `cargo test` passes.
### Tests
* Existing tests only.
### Junie instructions
**You MAY:**
* Refactor the interpreter loop for clarity.
* Remove legacy conditionals.
**You MUST NOT:**
* Change opcode semantics.
* Introduce GC, closures, or coroutines here.
* Redesign the instruction set.
**If unclear:**
* Ask before modifying control flow assumptions.
---
# PR-2.3 — Normalize Value Model (Stack vs Heap References)
### Briefing
The VM must use a clear value model: primitives and tuples on the stack, heap objects referenced through opaque handles. This PR prepares the VM for the GC-based heap.
### Target
* Define a single `Value` representation that distinguishes:
* Immediate primitives.
* Heap references (opaque handles).
* Remove any gate-based or borrow-based value types.
### Work items
* Review the `Value` or equivalent enum/struct.
* Remove variants related to gates, borrows, or HIP.
* Ensure only the following categories remain:
* Primitives (int, bool, etc.).
* Tuples or small aggregates.
* Heap reference handle (placeholder for future GC objects).
* Update stack operations accordingly.
### Acceptance checklist
* [ ] `Value` type has no HIP/gate-related variants.
* [ ] All stack operations compile with the new value model.
* [ ] No borrow/mutate semantics remain.
* [ ] `cargo test` passes.
### Tests
* Existing tests only.
### Junie instructions
**You MAY:**
* Simplify the `Value` enum/struct.
* Remove legacy variants and adjust matches.
**You MUST NOT:**
* Design the GC handle layout.
* Introduce new object systems.
* Change instruction semantics beyond type cleanup.
**If unclear:**
* Ask what the intended value shape should be.
---
# PR-2.4 — Consolidate Trap and Error Surface
### Briefing
With HIP removed, the runtime trap surface must be simplified and aligned with the new stack+heap model.
### Target
* Define a minimal, coherent set of runtime traps.
* Remove traps that only existed for HIP/RC semantics.
### Work items
* Audit the VMs trap/error enums.
* Remove HIP/RC-related traps.
* Keep only traps that remain meaningful, such as:
* Illegal instruction.
* Stack underflow/overflow.
* Invalid jump target.
* Out-of-bounds memory access.
* Ensure trap handling paths are consistent and deterministic.
### Acceptance checklist
* [ ] Trap enum contains only relevant traps.
* [ ] No HIP/RC trap names remain.
* [ ] VM compiles and tests pass.
### Tests
* Adjust any tests expecting removed trap codes.
### Junie instructions
**You MAY:**
* Delete unused trap variants.
* Refactor match statements accordingly.
**You MUST NOT:**
* Add new trap categories without approval.
* Change the meaning of existing non-legacy traps.
**If unclear:**
* Ask before modifying trap semantics.
---
# PR-2.5 — Prepare Call Frame Structure for Closures and Coroutines
### Briefing
Before introducing closures and coroutines, the call frame structure must be neutral and future-proof, without HIP-specific fields.
### Target
* Simplify the call frame to a minimal, generic structure.
* Remove any HIP/borrow/gate-related fields.
### Work items
* Review the call frame struct.
* Remove fields tied to scope frames, borrow state, or gates.
* Ensure the frame contains only:
* Function identifier.
* Program counter.
* Base stack pointer.
* Locals or register area (if applicable).
* Keep the structure simple and extensible.
### Acceptance checklist
* [ ] Call frame struct has no HIP-related fields.
* [ ] VM call/return paths compile and work.
* [ ] `cargo test` passes.
### Tests
* Existing call-related tests must still pass.
### Junie instructions
**You MAY:**
* Remove unused fields from the frame.
* Refactor call/return code to match the new structure.
**You MUST NOT:**
* Introduce closure or coroutine logic yet.
* Redesign the call stack architecture.
**If unclear:**
* Ask before changing frame responsibilities.
---
# PR-2.6 — Remove Dead Runtime Modules and Symbols
### Briefing
After the VM reset, there will be leftover modules, helpers, or symbols that are no longer referenced. This PR performs a final cleanup pass.
### Target
* Remove dead or unreachable runtime code.
* Ensure the VM crate has a minimal, clean surface.
### Work items
* Use compiler warnings and search tools to find:
* Unused modules.
* Unused structs/enums/functions.
* Legacy HIP/RC terminology.
* Remove dead code.
* Update module trees and public exports.
### Acceptance checklist
* [ ] No dead modules remain.
* [ ] No HIP/RC terminology found in the VM crate.
* [ ] `cargo test` passes.
### Tests
* Existing tests only.
### Junie instructions
**You MAY:**
* Remove unused code and modules.
* Update `mod.rs` and exports.
**You MUST NOT:**
* Remove code that is still referenced.
* Replace deleted modules with new experimental ones.
**If unclear:**
* Ask before deleting anything that looks structurally important.