From c797be928722bd07d46fa5609ab50479ef697c2d Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Fri, 30 Jan 2026 16:07:45 +0000 Subject: [PATCH] pr 34 --- crates/prometeu-bytecode/src/disasm.rs | 12 +- crates/prometeu-bytecode/src/opcode.rs | 127 ++++++++++++++++-- .../src/backend/emit_bytecode.rs | 22 +-- crates/prometeu-compiler/src/compiler.rs | 46 +++---- .../src/virtual_machine/virtual_machine.rs | 24 +++- docs/specs/pbs/files/PRs para Junie.md | 50 ------- 6 files changed, 173 insertions(+), 108 deletions(-) diff --git a/crates/prometeu-bytecode/src/disasm.rs b/crates/prometeu-bytecode/src/disasm.rs index bda3c592..91d655bf 100644 --- a/crates/prometeu-bytecode/src/disasm.rs +++ b/crates/prometeu-bytecode/src/disasm.rs @@ -41,7 +41,7 @@ pub fn disasm(rom: &[u8]) -> Result, String> { match opcode { OpCode::PushConst | OpCode::PushI32 | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue | OpCode::GetGlobal | OpCode::SetGlobal | OpCode::GetLocal | OpCode::SetLocal - | OpCode::PopN | OpCode::Syscall | OpCode::LoadRef | OpCode::StoreRef => { + | OpCode::PopN | OpCode::Syscall | OpCode::GateLoad | OpCode::GateStore => { let v = read_u32_le(&mut cursor).map_err(|e| e.to_string())?; operands.push(DisasmOperand::U32(v)); } @@ -58,11 +58,11 @@ pub fn disasm(rom: &[u8]) -> Result, String> { cursor.read_exact(&mut b_buf).map_err(|e| e.to_string())?; operands.push(DisasmOperand::Bool(b_buf[0] != 0)); } - OpCode::Call => { - let addr = read_u32_le(&mut cursor).map_err(|e| e.to_string())?; - let args = read_u32_le(&mut cursor).map_err(|e| e.to_string())?; - operands.push(DisasmOperand::U32(addr)); - operands.push(DisasmOperand::U32(args)); + OpCode::Call | OpCode::Alloc => { + let v1 = read_u32_le(&mut cursor).map_err(|e| e.to_string())?; + let v2 = read_u32_le(&mut cursor).map_err(|e| e.to_string())?; + operands.push(DisasmOperand::U32(v1)); + operands.push(DisasmOperand::U32(v2)); } _ => {} } diff --git a/crates/prometeu-bytecode/src/opcode.rs b/crates/prometeu-bytecode/src/opcode.rs index ade51676..34bce8d8 100644 --- a/crates/prometeu-bytecode/src/opcode.rs +++ b/crates/prometeu-bytecode/src/opcode.rs @@ -153,19 +153,46 @@ pub enum OpCode { /// Ends the current local scope, discarding its local variables. PopScope = 0x53, - // --- 6.7 Heap --- + // --- 6.7 HIP (Heap Interface Protocol) --- - /// Allocates `size` slots on the heap. - /// Stack: [size] -> [reference] + /// Allocates `slots` slots on the heap with the given `type_id`. + /// Operands: type_id (u32), slots (u32) + /// Stack: [] -> [gate] Alloc = 0x60, - /// Reads a value from the heap at `reference + offset`. + /// Reads a value from the heap at `gate + offset`. /// Operand: offset (u32) - /// Stack: [reference] -> [value] - LoadRef = 0x61, - /// Writes a value to the heap at `reference + offset`. + /// Stack: [gate] -> [value] + GateLoad = 0x61, + /// Writes a value to the heap at `gate + offset`. /// Operand: offset (u32) - /// Stack: [reference, value] -> [] - StoreRef = 0x62, + /// Stack: [gate, value] -> [] + GateStore = 0x62, + + /// Marks the beginning of a Peek scope for a gate. + /// Stack: [gate] -> [gate] + GateBeginPeek = 0x63, + /// Marks the end of a Peek scope for a gate. + /// Stack: [gate] -> [gate] + GateEndPeek = 0x64, + /// Marks the beginning of a Borrow scope for a gate. + /// Stack: [gate] -> [gate] + GateBeginBorrow = 0x65, + /// Marks the end of a Borrow scope for a gate. + /// Stack: [gate] -> [gate] + GateEndBorrow = 0x66, + /// Marks the beginning of a Mutate scope for a gate. + /// Stack: [gate] -> [gate] + GateBeginMutate = 0x67, + /// Marks the end of a Mutate scope for a gate. + /// Stack: [gate] -> [gate] + GateEndMutate = 0x68, + + /// Increments the reference count of a gate. + /// Stack: [gate] -> [gate] + GateRetain = 0x69, + /// Decrements the reference count of a gate. + /// Stack: [gate] -> [] + GateRelease = 0x6A, // --- 6.8 Peripherals and System --- @@ -226,8 +253,16 @@ impl TryFrom for OpCode { 0x52 => Ok(OpCode::PushScope), 0x53 => Ok(OpCode::PopScope), 0x60 => Ok(OpCode::Alloc), - 0x61 => Ok(OpCode::LoadRef), - 0x62 => Ok(OpCode::StoreRef), + 0x61 => Ok(OpCode::GateLoad), + 0x62 => Ok(OpCode::GateStore), + 0x63 => Ok(OpCode::GateBeginPeek), + 0x64 => Ok(OpCode::GateEndPeek), + 0x65 => Ok(OpCode::GateBeginBorrow), + 0x66 => Ok(OpCode::GateEndBorrow), + 0x67 => Ok(OpCode::GateBeginMutate), + 0x68 => Ok(OpCode::GateEndMutate), + 0x69 => Ok(OpCode::GateRetain), + 0x6A => Ok(OpCode::GateRelease), 0x70 => Ok(OpCode::Syscall), 0x80 => Ok(OpCode::FrameSync), _ => Err(format!("Invalid OpCode: 0x{:04X}", value)), @@ -283,10 +318,76 @@ impl OpCode { OpCode::PushScope => 3, OpCode::PopScope => 3, OpCode::Alloc => 10, - OpCode::LoadRef => 3, - OpCode::StoreRef => 3, + OpCode::GateLoad => 3, + OpCode::GateStore => 3, + OpCode::GateBeginPeek => 1, + OpCode::GateEndPeek => 1, + OpCode::GateBeginBorrow => 1, + OpCode::GateEndBorrow => 1, + OpCode::GateBeginMutate => 1, + OpCode::GateEndMutate => 1, + OpCode::GateRetain => 1, + OpCode::GateRelease => 1, OpCode::Syscall => 1, OpCode::FrameSync => 1, } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::asm::{assemble, Asm, Operand}; + + #[test] + fn test_opcode_stability() { + // Normative test: ensures opcode numeric values are frozen. + assert_eq!(OpCode::Nop as u16, 0x00); + assert_eq!(OpCode::PushConst as u16, 0x10); + assert_eq!(OpCode::Alloc as u16, 0x60); + assert_eq!(OpCode::GateLoad as u16, 0x61); + assert_eq!(OpCode::GateStore as u16, 0x62); + assert_eq!(OpCode::GateBeginPeek as u16, 0x63); + assert_eq!(OpCode::GateEndPeek as u16, 0x64); + assert_eq!(OpCode::GateBeginBorrow as u16, 0x65); + assert_eq!(OpCode::GateEndBorrow as u16, 0x66); + assert_eq!(OpCode::GateBeginMutate as u16, 0x67); + assert_eq!(OpCode::GateEndMutate as u16, 0x68); + assert_eq!(OpCode::GateRetain as u16, 0x69); + assert_eq!(OpCode::GateRelease as u16, 0x6A); + assert_eq!(OpCode::FrameSync as u16, 0x80); + } + + #[test] + fn test_hip_bytecode_golden() { + // Golden test for HIP opcodes and their encodings. + // Rule: All multi-byte operands are little-endian. + + let instructions = vec![ + Asm::Op(OpCode::Alloc, vec![Operand::U32(0x11223344), Operand::U32(0x55667788)]), + Asm::Op(OpCode::GateLoad, vec![Operand::U32(0xAABBCCDD)]), + Asm::Op(OpCode::GateStore, vec![Operand::U32(0x11223344)]), + Asm::Op(OpCode::GateBeginPeek, vec![]), + Asm::Op(OpCode::GateRetain, vec![]), + Asm::Op(OpCode::GateRelease, vec![]), + ]; + + let bytes = assemble(&instructions).unwrap(); + + let mut expected = Vec::new(); + // Alloc (0x60, 0x00) + type_id (44 33 22 11) + slots (88 77 66 55) + expected.extend_from_slice(&[0x60, 0x00, 0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55]); + // GateLoad (0x61, 0x00) + offset (DD CC BB AA) + expected.extend_from_slice(&[0x61, 0x00, 0xDD, 0xCC, 0xBB, 0xAA]); + // GateStore (0x62, 0x00) + offset (44 33 22 11) + expected.extend_from_slice(&[0x62, 0x00, 0x44, 0x33, 0x22, 0x11]); + // GateBeginPeek (0x63, 0x00) + expected.extend_from_slice(&[0x63, 0x00]); + // GateRetain (0x69, 0x00) + expected.extend_from_slice(&[0x69, 0x00]); + // GateRelease (0x6A, 0x00) + expected.extend_from_slice(&[0x6A, 0x00]); + + assert_eq!(bytes, expected); + } +} diff --git a/crates/prometeu-compiler/src/backend/emit_bytecode.rs b/crates/prometeu-compiler/src/backend/emit_bytecode.rs index cb234919..be04eb5d 100644 --- a/crates/prometeu-compiler/src/backend/emit_bytecode.rs +++ b/crates/prometeu-compiler/src/backend/emit_bytecode.rs @@ -161,19 +161,23 @@ impl<'a> BytecodeEmitter<'a> { asm_instrs.push(Asm::Op(OpCode::Syscall, vec![Operand::U32(*id)])); } InstrKind::FrameSync => asm_instrs.push(Asm::Op(OpCode::FrameSync, vec![])), - InstrKind::Alloc { .. } => asm_instrs.push(Asm::Op(OpCode::Alloc, vec![])), + InstrKind::Alloc { type_id, slots } => { + asm_instrs.push(Asm::Op(OpCode::Alloc, vec![Operand::U32(type_id.0), Operand::U32(*slots)])); + } InstrKind::GateLoad { offset } => { - asm_instrs.push(Asm::Op(OpCode::LoadRef, vec![Operand::U32(*offset)])); + asm_instrs.push(Asm::Op(OpCode::GateLoad, vec![Operand::U32(*offset)])); } InstrKind::GateStore { offset } => { - asm_instrs.push(Asm::Op(OpCode::StoreRef, vec![Operand::U32(*offset)])); - } - InstrKind::GateBeginPeek | InstrKind::GateEndPeek | - InstrKind::GateBeginBorrow | InstrKind::GateEndBorrow | - InstrKind::GateBeginMutate | InstrKind::GateEndMutate | - InstrKind::GateRetain | InstrKind::GateRelease => { - asm_instrs.push(Asm::Op(OpCode::Nop, vec![])); + asm_instrs.push(Asm::Op(OpCode::GateStore, vec![Operand::U32(*offset)])); } + InstrKind::GateBeginPeek => asm_instrs.push(Asm::Op(OpCode::GateBeginPeek, vec![])), + InstrKind::GateEndPeek => asm_instrs.push(Asm::Op(OpCode::GateEndPeek, vec![])), + InstrKind::GateBeginBorrow => asm_instrs.push(Asm::Op(OpCode::GateBeginBorrow, vec![])), + InstrKind::GateEndBorrow => asm_instrs.push(Asm::Op(OpCode::GateEndBorrow, vec![])), + InstrKind::GateBeginMutate => asm_instrs.push(Asm::Op(OpCode::GateBeginMutate, vec![])), + InstrKind::GateEndMutate => asm_instrs.push(Asm::Op(OpCode::GateEndMutate, vec![])), + InstrKind::GateRetain => asm_instrs.push(Asm::Op(OpCode::GateRetain, vec![])), + InstrKind::GateRelease => asm_instrs.push(Asm::Op(OpCode::GateRelease, vec![])), } let end_idx = asm_instrs.len(); diff --git a/crates/prometeu-compiler/src/compiler.rs b/crates/prometeu-compiler/src/compiler.rs index 75865e27..c8947509 100644 --- a/crates/prometeu-compiler/src/compiler.rs +++ b/crates/prometeu-compiler/src/compiler.rs @@ -158,10 +158,10 @@ mod tests { let opcodes: Vec<_> = instrs.iter().map(|i| i.opcode).collect(); assert!(opcodes.contains(&OpCode::Alloc)); - assert!(opcodes.contains(&OpCode::LoadRef)); - // After PR-05, BeginMutate/EndMutate map to GateLoad/Nop for now - // because VM is feature-frozen. StoreRef is removed from lowering. - assert!(opcodes.contains(&OpCode::Nop)); + assert!(opcodes.contains(&OpCode::GateLoad)); + // After PR-09, BeginMutate/EndMutate map to their respective opcodes + assert!(opcodes.contains(&OpCode::GateBeginMutate)); + assert!(opcodes.contains(&OpCode::GateEndMutate)); assert!(opcodes.contains(&OpCode::Add)); assert!(opcodes.contains(&OpCode::Ret)); } @@ -237,25 +237,25 @@ mod tests { 0052 SetLocal U32(1) 0058 Jmp U32(100) 005E Jmp U32(100) -0064 Alloc -0066 SetLocal U32(1) -006C GetLocal U32(1) -0072 Nop -0074 SetLocal U32(2) -007A Nop -007C GetLocal U32(2) -0082 LoadRef U32(0) -0088 SetLocal U32(3) -008E GetLocal U32(3) -0094 PushConst U32(5) -009A Add -009C SetLocal U32(4) -00A2 Nop -00A4 GetLocal U32(1) -00AA Nop -00AC GetLocal U32(2) -00B2 Nop -00B4 Ret +0064 Alloc U32(2) U32(1) +006E SetLocal U32(1) +0074 GetLocal U32(1) +007A GateRetain +007C SetLocal U32(2) +0082 GateBeginMutate +0084 GetLocal U32(2) +008A GateLoad U32(0) +0090 SetLocal U32(3) +0096 GetLocal U32(3) +009C PushConst U32(5) +00A2 Add +00A4 SetLocal U32(4) +00AA GateEndMutate +00AC GetLocal U32(1) +00B2 GateRelease +00B4 GetLocal U32(2) +00BA GateRelease +00BC Ret "#; assert_eq!(disasm_text, expected_disasm); diff --git a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs index ad0fabdb..ebd793e0 100644 --- a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs +++ b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs @@ -544,15 +544,16 @@ impl VirtualMachine { self.operand_stack.truncate(frame.scope_stack_base); } OpCode::Alloc => { - // Allocates 'size' values on the heap and pushes a reference to the stack - let size = self.read_u32()? as usize; + // Allocates 'slots' values on the heap and pushes a reference to the stack + let _type_id = self.read_u32()?; + let slots = self.read_u32()? as usize; let ref_idx = self.heap.len(); - for _ in 0..size { + for _ in 0..slots { self.heap.push(Value::Null); } self.push(Value::Ref(ref_idx)); } - OpCode::LoadRef => { + OpCode::GateLoad => { // Reads a value from a heap reference at a specific offset let offset = self.read_u32()? as usize; let ref_val = self.pop()?; @@ -560,10 +561,10 @@ impl VirtualMachine { let val = self.heap.get(base + offset).cloned().ok_or("Invalid heap access")?; self.push(val); } else { - return Err("Expected reference for LOAD_REF".into()); + return Err("Expected reference for GATE_LOAD".into()); } } - OpCode::StoreRef => { + OpCode::GateStore => { // Writes a value to a heap reference at a specific offset let offset = self.read_u32()? as usize; let val = self.pop()?; @@ -574,9 +575,18 @@ impl VirtualMachine { } self.heap[base + offset] = val; } else { - return Err("Expected reference for STORE_REF".into()); + return Err("Expected reference for GATE_STORE".into()); } } + OpCode::GateBeginPeek | OpCode::GateEndPeek | + OpCode::GateBeginBorrow | OpCode::GateEndBorrow | + OpCode::GateBeginMutate | OpCode::GateEndMutate | + OpCode::GateRetain => { + // These are no-ops in v0, but they preserve the gate on the stack. + } + OpCode::GateRelease => { + self.pop()?; + } OpCode::Syscall => { // Calls a native function implemented by the Firmware/OS. // ABI Rule: Arguments are pushed in call order (LIFO). diff --git a/docs/specs/pbs/files/PRs para Junie.md b/docs/specs/pbs/files/PRs para Junie.md index b610e75a..a5364065 100644 --- a/docs/specs/pbs/files/PRs para Junie.md +++ b/docs/specs/pbs/files/PRs para Junie.md @@ -1,53 +1,3 @@ -## PR-09 — HIP ISA Freeze v0: Opcode Table + Encoding Contract (Bytecode) - -### Goal - -Freeze the HIP-related opcode set and encoding so bytecode becomes stable. - -### Required Changes - -1. Update `prometeu-bytecode`: - -* Define the canonical HIP opcode subset: - - * `PUSH_CONST` - * `ALLOC(type_id, slots)` - * `GATE_BEGIN_PEEK`, `GATE_END_PEEK` - * `GATE_BEGIN_BORROW`, `GATE_END_BORROW` - * `GATE_BEGIN_MUTATE`, `GATE_END_MUTATE` - * `GATE_LOAD(offset)` - * `GATE_STORE(offset)` - * `GATE_RETAIN`, `GATE_RELEASE` - * `FRAME_SYNC` (if included) - -2. Define canonical encodings (normative in comments/doc): - -* `GateId` encoding: `u32` little-endian -* `TypeId` encoding: `u32` little-endian -* `ConstId` encoding: `u32` little-endian -* `slots`: `u32` little-endian -* `offset`: `u32` little-endian - -3. Update bytecode emitter so it emits these exact opcodes with these exact payloads. - -### Non-goals - -* No runtime execution changes - -### Tests (Mandatory) - -1. **Golden bytecode tests**: - -* Given a minimal VM IR program using each HIP opcode, assert the exact emitted bytes. - -2. **Opcode stability test**: - -* Snapshot test of the opcode enum ordering and numeric values. - -> If opcode numeric values already exist, DO NOT renumber. If new opcodes are added, append them. - ---- - ## PR-10 — HIP ABI Freeze v0: Trap Conditions + Debug Surface ### Goal