Skip to main content

wasm_pvm/
abi.rs

1//! PVM ABI constants (Registers, Memory Layout, Frame Layout).
2//!
3//! This module centralizes all definitions related to the PVM execution environment
4//! to ensure consistency between the compiler frontend, backend, and tests.
5
6// ── Register Assignments ──
7
8/// Return address register (ra).
9/// Holds the return address (jump table index) for function calls.
10pub const RETURN_ADDR_REG: u8 = 0;
11
12/// Stack pointer register (sp).
13/// Points to the current top of the stack (grows downwards).
14pub const STACK_PTR_REG: u8 = 1;
15
16/// Temporary register 1 (t0).
17/// Used for loading operands from stack slots or immediate values.
18pub const TEMP1: u8 = 2;
19
20/// Temporary register 2 (t1).
21/// Used for loading operands from stack slots.
22pub const TEMP2: u8 = 3;
23
24/// Temporary register for computation results (t2).
25/// Holds the result of ALU operations before storing back to stack slot.
26pub const TEMP_RESULT: u8 = 4;
27
28/// Scratch register 1 (s0).
29/// General purpose scratch register.
30pub const SCRATCH1: u8 = 5;
31
32/// Scratch register 2 (s1).
33/// General purpose scratch register.
34pub const SCRATCH2: u8 = 6;
35
36/// Return value register (a0).
37/// Holds the first return value from a function call.
38/// Also used as the pointer to arguments (`args_ptr`) in the entry function.
39pub const RETURN_VALUE_REG: u8 = 7;
40pub const ARGS_PTR_REG: u8 = 7;
41
42/// Arguments length register (a1).
43/// Holds the length of arguments in the entry function.
44/// Also used as the second return value if needed (e.g. for entry function (ptr, len)).
45pub const ARGS_LEN_REG: u8 = 8;
46
47/// First local variable register (l0).
48/// Start of the range of registers used for local variables (callee-saved).
49pub const FIRST_LOCAL_REG: u8 = 9;
50
51/// Number of registers dedicated to local variables (r9-r12).
52pub const MAX_LOCAL_REGS: usize = 4;
53
54/// Maximum number of data registers for `host_call_N` imports (r7-r12).
55/// This is the hard ceiling: r7 (`RETURN_VALUE_REG`) through r12 = 6 registers.
56pub const MAX_HOST_CALL_DATA_ARGS: u8 = 6;
57
58/// Host call variant descriptor parsed from import name.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum HostCallVariant {
61    /// `host_call_N` — exactly N data args (r7..r7+N-1), always returns r7.
62    Typed { data_args: u8 },
63    /// `host_call_Nb` — exactly N data args, returns r7, also captures r8.
64    TypedWithR8 { data_args: u8 },
65    /// `host_call_r8` — no args, returns captured r8 from last `host_call_*b`.
66    GetR8,
67}
68
69/// Parse a host call variant from an import name.
70///
71/// Returns `Some(variant)` for `host_call_0..6`, `host_call_0b..6b`, and `host_call_r8`.
72#[must_use]
73pub fn parse_host_call_variant(name: &str) -> Option<HostCallVariant> {
74    if name == "host_call_r8" {
75        return Some(HostCallVariant::GetR8);
76    }
77    if let Some(suffix) = name.strip_prefix("host_call_") {
78        // host_call_2b → TypedWithR8 { data_args: 2 }
79        if let Some(digits) = suffix.strip_suffix('b')
80            && let Ok(n) = digits.parse::<u8>()
81            && n <= MAX_HOST_CALL_DATA_ARGS
82        {
83            return Some(HostCallVariant::TypedWithR8 { data_args: n });
84        }
85        // host_call_3 → Typed { data_args: 3 }
86        if let Ok(n) = suffix.parse::<u8>()
87            && n <= MAX_HOST_CALL_DATA_ARGS
88        {
89            return Some(HostCallVariant::Typed { data_args: n });
90        }
91    }
92    None
93}
94
95// ── Stack Frame Layout ──
96
97/// Maximum stack frame header size in bytes (used as default when shrink wrapping is disabled).
98///
99/// Layout (all callee-saved registers):
100/// - 0: Saved r0 (ra)
101/// - 8: Saved r9 (l0)
102/// - 16: Saved r10 (l1)
103/// - 24: Saved r11 (l2)
104/// - 32: Saved r12 (l3)
105///
106/// Total: 5 * 8 = 40 bytes.
107///
108/// With shrink wrapping enabled, the actual header size is dynamic:
109/// `8 (ra) + 8 * num_used_callee_regs`. Only registers that are actually
110/// used by the function body are saved/restored.
111pub const FRAME_HEADER_SIZE: i32 = 40;
112
113/// Operand stack spill area base offset (relative to SP, negative direction).
114/// Spilled operand stack values are stored at `SP + OPERAND_SPILL_BASE + slot*8`.
115pub const OPERAND_SPILL_BASE: i32 = -0x100;
116
117/// Stack offset for capturing r8 after `host_call_*b` variants (relative to SP).
118/// Used by `host_call_r8` to retrieve the captured value.
119pub const R8_CAPTURE_SLOT_OFFSET: i32 = -0x108;
120
121// ── Memory Layout ──
122
123// Re-export memory layout constants for convenience.
124pub use crate::memory_layout::*;