From 76660294d5b7b1419991077a8025d267d75ab284 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Fri, 30 Jan 2026 11:58:15 +0000 Subject: [PATCH] pr 27 --- .../src/backend/emit_bytecode.rs | 15 +- crates/prometeu-compiler/src/compiler.rs | 17 +- crates/prometeu-compiler/src/ir_vm/instr.rs | 191 +++++++++++++++++- crates/prometeu-compiler/src/ir_vm/mod.rs | 6 +- .../src/lowering/core_to_vm.rs | 37 ++-- docs/specs/pbs/files/PRs para Junie.md | 69 ------- 6 files changed, 230 insertions(+), 105 deletions(-) diff --git a/crates/prometeu-compiler/src/backend/emit_bytecode.rs b/crates/prometeu-compiler/src/backend/emit_bytecode.rs index c589f2e6..fa287d73 100644 --- a/crates/prometeu-compiler/src/backend/emit_bytecode.rs +++ b/crates/prometeu-compiler/src/backend/emit_bytecode.rs @@ -161,13 +161,18 @@ 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::LoadRef(offset) => { + InstrKind::Alloc { .. } => asm_instrs.push(Asm::Op(OpCode::Alloc, vec![])), + InstrKind::GateLoad { offset } => { asm_instrs.push(Asm::Op(OpCode::LoadRef, vec![Operand::U32(*offset)])); } - InstrKind::StoreRef(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 => { + asm_instrs.push(Asm::Op(OpCode::Nop, vec![])); + } } let end_idx = asm_instrs.len(); @@ -253,8 +258,8 @@ mod tests { params: vec![], return_type: Type::Void, body: vec![ - Instruction::new(InstrKind::PushConst(id_int), None), - Instruction::new(InstrKind::PushConst(id_str), None), + Instruction::new(InstrKind::PushConst(ir_vm::ConstId(id_int.0)), None), + Instruction::new(InstrKind::PushConst(ir_vm::ConstId(id_str.0)), None), Instruction::new(InstrKind::Ret, None), ], }; diff --git a/crates/prometeu-compiler/src/compiler.rs b/crates/prometeu-compiler/src/compiler.rs index 951691ea..ad13f568 100644 --- a/crates/prometeu-compiler/src/compiler.rs +++ b/crates/prometeu-compiler/src/compiler.rs @@ -241,14 +241,15 @@ mod tests { 0066 SetLocal U32(1) 006C GetLocal U32(1) 0072 SetLocal U32(2) -0078 LoadRef U32(0) -007E SetLocal U32(3) -0084 GetLocal U32(3) -008A PushConst U32(5) -0090 Add -0092 SetLocal U32(4) -0098 Nop -009A Ret +0078 Nop +007A LoadRef U32(0) +0080 SetLocal U32(3) +0086 GetLocal U32(3) +008C PushConst U32(5) +0092 Add +0094 SetLocal U32(4) +009A Nop +009C Ret "#; assert_eq!(disasm_text, expected_disasm); diff --git a/crates/prometeu-compiler/src/ir_vm/instr.rs b/crates/prometeu-compiler/src/ir_vm/instr.rs index 4db2d4e4..50528088 100644 --- a/crates/prometeu-compiler/src/ir_vm/instr.rs +++ b/crates/prometeu-compiler/src/ir_vm/instr.rs @@ -5,7 +5,8 @@ //! easy to lower into VM-specific bytecode. use crate::common::spans::Span; -use crate::ir_core::ids::{ConstId, FunctionId}; +use crate::ir_vm::types::{ConstId, TypeId}; +use crate::ir_core::ids::FunctionId; /// An `Instruction` combines an instruction's behavior (`kind`) with its /// source code location (`span`) for debugging and error reporting. @@ -139,10 +140,186 @@ pub enum InstrKind { // --- HIP / Memory --- - /// Allocates memory on the heap. Pops size from stack. - Alloc, - /// Reads from heap at reference + offset. Pops reference, pushes value. - LoadRef(u32), - /// Writes to heap at reference + offset. Pops reference and value. - StoreRef(u32), + /// Allocates memory on the heap. + Alloc { type_id: TypeId, slots: u32 }, + /// Reads from heap at gate + offset. Pops gate, pushes value. + GateLoad { offset: u32 }, + /// Writes to heap at gate + offset. Pops gate and value. + GateStore { offset: u32 }, + + // --- Scope Markers --- + GateBeginPeek, + GateEndPeek, + GateBeginBorrow, + GateEndBorrow, + GateBeginMutate, + GateEndMutate, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ir_vm::types::{ConstId, TypeId}; + + #[test] + fn test_instr_kind_is_cloneable() { + let instr = InstrKind::Alloc { type_id: TypeId(1), slots: 2 }; + let cloned = instr.clone(); + match cloned { + InstrKind::Alloc { type_id, slots } => { + assert_eq!(type_id, TypeId(1)); + assert_eq!(slots, 2); + } + _ => panic!("Clone failed"), + } + } + + #[test] + fn test_isa_surface_snapshot() { + // This test ensures that the instruction set surface remains stable. + // If you add/remove/change instructions, this test will fail, + // prompting an explicit review of the ISA change. + let instructions = vec![ + InstrKind::Nop, + InstrKind::Halt, + InstrKind::PushConst(ConstId(0)), + InstrKind::PushBool(true), + InstrKind::PushNull, + InstrKind::Pop, + InstrKind::Dup, + InstrKind::Swap, + InstrKind::Add, + InstrKind::Sub, + InstrKind::Mul, + InstrKind::Div, + InstrKind::Neg, + InstrKind::Eq, + InstrKind::Neq, + InstrKind::Lt, + InstrKind::Gt, + InstrKind::Lte, + InstrKind::Gte, + InstrKind::And, + InstrKind::Or, + InstrKind::Not, + InstrKind::BitAnd, + InstrKind::BitOr, + InstrKind::BitXor, + InstrKind::Shl, + InstrKind::Shr, + InstrKind::GetLocal(0), + InstrKind::SetLocal(0), + InstrKind::GetGlobal(0), + InstrKind::SetGlobal(0), + InstrKind::Jmp(Label("target".to_string())), + InstrKind::JmpIfFalse(Label("target".to_string())), + InstrKind::Label(Label("target".to_string())), + InstrKind::Call { func_id: FunctionId(0), arg_count: 0 }, + InstrKind::Ret, + InstrKind::Syscall(0), + InstrKind::FrameSync, + InstrKind::Alloc { type_id: TypeId(0), slots: 0 }, + InstrKind::GateLoad { offset: 0 }, + InstrKind::GateStore { offset: 0 }, + InstrKind::GateBeginPeek, + InstrKind::GateEndPeek, + InstrKind::GateBeginBorrow, + InstrKind::GateEndBorrow, + InstrKind::GateBeginMutate, + InstrKind::GateEndMutate, + ]; + + let serialized = serde_json::to_string_pretty(&instructions).unwrap(); + + // This is a "lock" on the ISA surface. + // If the structure of InstrKind changes, the serialization will change. + let expected_json = r#"[ + "Nop", + "Halt", + { + "PushConst": 0 + }, + { + "PushBool": true + }, + "PushNull", + "Pop", + "Dup", + "Swap", + "Add", + "Sub", + "Mul", + "Div", + "Neg", + "Eq", + "Neq", + "Lt", + "Gt", + "Lte", + "Gte", + "And", + "Or", + "Not", + "BitAnd", + "BitOr", + "BitXor", + "Shl", + "Shr", + { + "GetLocal": 0 + }, + { + "SetLocal": 0 + }, + { + "GetGlobal": 0 + }, + { + "SetGlobal": 0 + }, + { + "Jmp": "target" + }, + { + "JmpIfFalse": "target" + }, + { + "Label": "target" + }, + { + "Call": { + "func_id": 0, + "arg_count": 0 + } + }, + "Ret", + { + "Syscall": 0 + }, + "FrameSync", + { + "Alloc": { + "type_id": 0, + "slots": 0 + } + }, + { + "GateLoad": { + "offset": 0 + } + }, + { + "GateStore": { + "offset": 0 + } + }, + "GateBeginPeek", + "GateEndPeek", + "GateBeginBorrow", + "GateEndBorrow", + "GateBeginMutate", + "GateEndMutate" +]"#; + assert_eq!(serialized, expected_json); + } } diff --git a/crates/prometeu-compiler/src/ir_vm/mod.rs b/crates/prometeu-compiler/src/ir_vm/mod.rs index 7d748843..43ff1bfa 100644 --- a/crates/prometeu-compiler/src/ir_vm/mod.rs +++ b/crates/prometeu-compiler/src/ir_vm/mod.rs @@ -15,9 +15,7 @@ pub mod validate; pub use instr::{Instruction, InstrKind, Label}; pub use module::{Module, Function, Global, Param}; -pub use types::{Type, Value, GateId}; -// Note: ConstId and TypeId are not exported here to avoid conflict with ir_core::ids -// until the crates are fully decoupled. +pub use types::{Type, Value, GateId, ConstId, TypeId}; #[cfg(test)] mod tests { @@ -40,7 +38,7 @@ mod tests { params: vec![], return_type: Type::Null, body: vec![ - Instruction::new(InstrKind::PushConst(ConstId(0)), None), + Instruction::new(InstrKind::PushConst(crate::ir_vm::types::ConstId(0)), None), Instruction::new(InstrKind::Call { func_id: FunctionId(2), arg_count: 1 }, None), Instruction::new(InstrKind::Ret, None), ], diff --git a/crates/prometeu-compiler/src/lowering/core_to_vm.rs b/crates/prometeu-compiler/src/lowering/core_to_vm.rs index 0b9f4c93..cb5394fd 100644 --- a/crates/prometeu-compiler/src/lowering/core_to_vm.rs +++ b/crates/prometeu-compiler/src/lowering/core_to_vm.rs @@ -47,7 +47,7 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result for instr in &block.instrs { let kind = match instr { - ir_core::Instr::PushConst(id) => ir_vm::InstrKind::PushConst(*id), + ir_core::Instr::PushConst(id) => ir_vm::InstrKind::PushConst(ir_vm::ConstId(id.0)), ir_core::Instr::Call(func_id, arg_count) => ir_vm::InstrKind::Call { func_id: *func_id, arg_count: *arg_count @@ -71,15 +71,27 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result ir_core::Instr::And => ir_vm::InstrKind::And, ir_core::Instr::Or => ir_vm::InstrKind::Or, ir_core::Instr::Not => ir_vm::InstrKind::Not, - ir_core::Instr::Alloc { .. } => ir_vm::InstrKind::Alloc, - ir_core::Instr::BeginPeek { .. } | - ir_core::Instr::BeginBorrow { .. } | - ir_core::Instr::BeginMutate { .. } => ir_vm::InstrKind::LoadRef(0), - ir_core::Instr::EndPeek | - ir_core::Instr::EndBorrow | - ir_core::Instr::EndMutate => ir_vm::InstrKind::Nop, - ir_core::Instr::LoadRef(offset) => ir_vm::InstrKind::LoadRef(*offset), - ir_core::Instr::StoreRef(offset) => ir_vm::InstrKind::StoreRef(*offset), + ir_core::Instr::Alloc { ty, slots } => ir_vm::InstrKind::Alloc { + type_id: ir_vm::TypeId(ty.0), + slots: *slots + }, + ir_core::Instr::BeginPeek { .. } => { + vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::GateBeginPeek, None)); + ir_vm::InstrKind::GateLoad { offset: 0 } + } + ir_core::Instr::BeginBorrow { .. } => { + vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::GateBeginBorrow, None)); + ir_vm::InstrKind::GateLoad { offset: 0 } + } + ir_core::Instr::BeginMutate { .. } => { + vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::GateBeginMutate, None)); + ir_vm::InstrKind::GateLoad { offset: 0 } + } + ir_core::Instr::EndPeek => ir_vm::InstrKind::GateEndPeek, + ir_core::Instr::EndBorrow => ir_vm::InstrKind::GateEndBorrow, + ir_core::Instr::EndMutate => ir_vm::InstrKind::GateEndMutate, + ir_core::Instr::LoadRef(offset) => ir_vm::InstrKind::GateLoad { offset: *offset }, + ir_core::Instr::StoreRef(offset) => ir_vm::InstrKind::GateStore { offset: *offset }, ir_core::Instr::Free => ir_vm::InstrKind::Nop, }; vm_func.body.push(ir_vm::Instruction::new(kind, None)); @@ -133,7 +145,8 @@ fn lower_type(ty: &ir_core::Type) -> ir_vm::Type { mod tests { use super::*; use crate::ir_core; - use crate::ir_core::*; + use crate::ir_core::{Block, Instr, Terminator, ConstantValue, Program, ConstPool}; + use crate::ir_core::ids::{FunctionId, ConstId as CoreConstId}; use crate::ir_vm::*; #[test] @@ -154,7 +167,7 @@ mod tests { Block { id: 0, instrs: vec![ - Instr::PushConst(ConstId(0)), + Instr::PushConst(CoreConstId(0)), Instr::Call(FunctionId(2), 1), ], terminator: Terminator::Jump(1), diff --git a/docs/specs/pbs/files/PRs para Junie.md b/docs/specs/pbs/files/PRs para Junie.md index 8ea9d27e..25594e3f 100644 --- a/docs/specs/pbs/files/PRs para Junie.md +++ b/docs/specs/pbs/files/PRs para Junie.md @@ -1,72 +1,3 @@ -# PR-02 — Define `ir_vm` ISA v0 (Memory & Gates Only) - -### Goal - -Define a minimal, PBS-compatible but PBS-agnostic VM instruction set. - -### Required Instructions - -#### Constant Pool - -* `PushConst(ConstId)` - -#### HIP Allocation (Deterministic) - -* `Alloc { type_id: TypeId, slots: u32 }` - -#### Gate-Based Heap Access - -* `GateLoad { offset: u32 }` -* `GateStore { offset: u32 }` - -> All heap access must follow: gate validation → base+slots resolution → bounds check → read/write. - -#### Scope Markers (Semantic Preservation) - -* `GateBeginPeek` / `GateEndPeek` -* `GateBeginBorrow` / `GateEndBorrow` -* `GateBeginMutate` / `GateEndMutate` - -> These may be runtime no-ops in v0 but must exist to preserve semantics and debug invariants. - -#### Safe Point Hook - -* `FrameSync` (optional but recommended) - -### Non-goals - -* No RC implementation -* No VM execution logic - -### Tests - -* Unit tests ensuring the instruction enum is stable and cloneable -* Snapshot or debug-format test to lock the ISA surface - ---- - -# PR-03 — Remove “Ref” Leakage from `ir_vm` - -### Goal - -Eliminate pointer-based mental models from the VM IR. - -### Required Changes - -* Rename any existing `LoadRef` / `StoreRef` to: - - * `LocalLoad { slot: u32 }` - * `LocalStore { slot: u32 }` -* Remove or rename any type named `Ref` that refers to HIP - -**Hard rule:** the word `Ref` must never refer to HIP memory in `ir_vm`. - -### Tests - -* Grep-style or unit test ensuring no `Ref`-named HIP ops exist in `ir_vm` - ---- - # PR-04 — Update `core_to_vm` Lowering (Kill Placeholders) ### Goal