//! This module defines the Application Binary Interface (ABI) of the Prometeu Virtual Machine. //! It specifies how instructions are encoded in bytes and how they interact with memory. use crate::opcode::OpCode; /// Returns the size in bytes of the operands for a given OpCode. /// /// Note: This does NOT include the 2 bytes of the OpCode itself. /// For example, `PushI32` has a size of 4, but occupies 6 bytes in ROM (2 for OpCode + 4 for value). pub fn operand_size(opcode: OpCode) -> usize { match opcode { OpCode::PushConst => 4, OpCode::PushI32 => 4, OpCode::PushBounded => 4, OpCode::PushI64 => 8, OpCode::PushF64 => 8, OpCode::PushBool => 1, OpCode::PopN => 4, OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue => 4, OpCode::GetGlobal | OpCode::SetGlobal => 4, OpCode::GetLocal | OpCode::SetLocal => 4, OpCode::Call => 4, // func_id(u32) OpCode::Syscall => 4, OpCode::Alloc => 8, // type_id(u32) + slots(u32) OpCode::GateLoad | OpCode::GateStore => 4, // offset(u32) _ => 0, } } // --- HIP Trap Codes --- /// Attempted to access a gate that does not exist or has been recycled incorrectly. pub const TRAP_INVALID_GATE: u32 = 0x01; /// Attempted to access a gate that has been explicitly released (RC=0). pub const TRAP_DEAD_GATE: u32 = 0x02; /// Attempted to access a field or index beyond the allocated slots for a gate. pub const TRAP_OOB: u32 = 0x03; /// Attempted a typed operation on a gate whose storage type does not match. pub const TRAP_TYPE: u32 = 0x04; /// The syscall ID provided is not recognized by the system. pub const TRAP_INVALID_SYSCALL: u32 = 0x0000_0007; /// Not enough arguments on the stack for the requested syscall. pub const TRAP_STACK_UNDERFLOW: u32 = 0x0000_0008; /// Attempted to access a local slot that is out of bounds for the current frame. pub const TRAP_INVALID_LOCAL: u32 = 0x0000_0009; /// Division or modulo by zero. pub const TRAP_DIV_ZERO: u32 = 0x0000_000A; /// Attempted to call a function that does not exist in the function table. pub const TRAP_INVALID_FUNC: u32 = 0x0000_000B; /// Executed RET with an incorrect stack height (mismatch with function metadata). pub const TRAP_BAD_RET_SLOTS: u32 = 0x0000_000C; use serde::{Deserialize, Serialize}; /// Detailed information about a source code span. #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct SourceSpan { pub file_id: u32, pub start: u32, pub end: u32, } /// Detailed information about a runtime trap. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TrapInfo { /// The specific trap code (e.g., TRAP_OOB). pub code: u32, /// The numeric value of the opcode that triggered the trap. pub opcode: u16, /// A human-readable message explaining the trap. pub message: String, /// The absolute Program Counter (PC) address where the trap occurred. pub pc: u32, /// Optional source span information if debug symbols are available. pub span: Option, } /// Checks if an instruction is a jump (branch) instruction. pub fn is_jump(opcode: OpCode) -> bool { match opcode { OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue => true, _ => false, } } /// Checks if an instruction has any immediate operands in the instruction stream. pub fn has_immediate(opcode: OpCode) -> bool { operand_size(opcode) > 0 } #[cfg(test)] mod tests { use super::*; #[test] fn test_trap_code_stability() { // These numeric values are normative and must not change. assert_eq!(TRAP_INVALID_GATE, 0x01); assert_eq!(TRAP_DEAD_GATE, 0x02); assert_eq!(TRAP_OOB, 0x03); assert_eq!(TRAP_TYPE, 0x04); assert_eq!(TRAP_INVALID_SYSCALL, 0x07); assert_eq!(TRAP_STACK_UNDERFLOW, 0x08); assert_eq!(TRAP_INVALID_LOCAL, 0x09); assert_eq!(TRAP_DIV_ZERO, 0x0A); assert_eq!(TRAP_INVALID_FUNC, 0x0B); assert_eq!(TRAP_BAD_RET_SLOTS, 0x0C); } #[test] fn test_abi_documentation_snapshot() { // Snapshot of the ABI rules for traps and operands. let abi_info = r#" HIP Traps: - INVALID_GATE (0x01): Non-existent gate handle. - DEAD_GATE (0x02): Gate handle with RC=0. - OOB (0x03): Access beyond allocated slots. - TYPE (0x04): Type mismatch during heap access. System Traps: - INVALID_SYSCALL (0x07): Unknown syscall ID. - STACK_UNDERFLOW (0x08): Missing syscall arguments. - INVALID_LOCAL (0x09): Local slot out of bounds. - DIV_ZERO (0x0A): Division by zero. - INVALID_FUNC (0x0B): Function table index out of bounds. - BAD_RET_SLOTS (0x0C): Stack height mismatch at RET. Operand Sizes: - Alloc: 8 bytes (u32 type_id, u32 slots) - GateLoad: 4 bytes (u32 offset) - GateStore: 4 bytes (u32 offset) - PopN: 4 bytes (u32 count) "#; // This test serves as a "doc-lock". // If you change the ABI, you must update this string. let current_info = format!( "\nHIP Traps:\n- INVALID_GATE (0x{:02X}): Non-existent gate handle.\n- DEAD_GATE (0x{:02X}): Gate handle with RC=0.\n- OOB (0x{:02X}): Access beyond allocated slots.\n- TYPE (0x{:02X}): Type mismatch during heap access.\n\nSystem Traps:\n- INVALID_SYSCALL (0x{:02X}): Unknown syscall ID.\n- STACK_UNDERFLOW (0x{:02X}): Missing syscall arguments.\n- INVALID_LOCAL (0x{:02X}): Local slot out of bounds.\n- DIV_ZERO (0x{:02X}): Division by zero.\n- INVALID_FUNC (0x{:02X}): Function table index out of bounds.\n- BAD_RET_SLOTS (0x{:02X}): Stack height mismatch at RET.\n\nOperand Sizes:\n- Alloc: {} bytes (u32 type_id, u32 slots)\n- GateLoad: {} bytes (u32 offset)\n- GateStore: {} bytes (u32 offset)\n- PopN: {} bytes (u32 count)\n", TRAP_INVALID_GATE, TRAP_DEAD_GATE, TRAP_OOB, TRAP_TYPE, TRAP_INVALID_SYSCALL, TRAP_STACK_UNDERFLOW, TRAP_INVALID_LOCAL, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_BAD_RET_SLOTS, operand_size(OpCode::Alloc), operand_size(OpCode::GateLoad), operand_size(OpCode::GateStore), operand_size(OpCode::PopN) ); assert_eq!(current_info.trim(), abi_info.trim()); } }