Skip to main content

polka/
lib.rs

1// OpCode design frozen; type-agnostic bytecode, type from OpCode + masks.
2#![no_std]
3
4extern crate alloc;
5
6use alloc::{string::String, vec::Vec};
7
8pub mod value;
9pub mod cartridge;
10
11pub use value::{Value, HANDLE_NONE, HANDLE_SLOT_MAX};
12
13pub const FRAME_REGS: usize = 128;
14pub const FRAME_MASK_WORDS: usize = (FRAME_REGS + 63) / 64;
15
16pub const DISPATCH_ID: u8 = 0xE0;
17pub const DISPATCH_PORT_LOOKUP: u8 = 0x00;
18pub const DISPATCH_PORT_POP_HANDLER: u8 = 0x01;
19pub const DISPATCH_PORT_ENV: u8 = 0x02;
20pub const DISPATCH_PORT_RETURN_FN: u8 = 0x03;
21pub const DISPATCH_PORT_RETURN_ENV: u8 = 0x04;
22pub const DISPATCH_NO_MATCH: u16 = 0xFFFF;
23// Dispatch-table fn_id entries may carry this bit: the arm is tail-resumptive
24// (compiled to end in Ret), so raise may call it plainly — no cont cell, no
25// register snapshot.
26pub const DISPATCH_TAIL_FLAG: u64 = 1 << 16;
27
28pub const REGION_ID: u8 = 0xE1;
29pub const REGION_PORT_PUSH: u8 = 0x00;
30pub const REGION_PORT_POP: u8 = 0x01;
31pub const REGION_PORT_FORGET: u8 = 0x02;
32
33pub const MODULE_ID: u8 = 0xE2;
34pub const MODULE_PORT_TABLE: u8 = 0x00;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct Register(pub u8);
38
39impl Register {
40    pub fn to_usize(self) -> usize {
41        self.0 as usize
42    }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum OpCode {
47    Add(Register, Register, Register),
48    Sub(Register, Register, Register),
49    Mul(Register, Register, Register),
50    Div(Register, Register, Register),
51    Mod(Register, Register, Register),
52    Neg(Register, Register),
53
54    FAdd(Register, Register, Register),
55    FSub(Register, Register, Register),
56    FMul(Register, Register, Register),
57    FDiv(Register, Register, Register),
58    FNeg(Register, Register),
59    FLt(Register, Register, Register),
60    FEq(Register, Register, Register),
61
62    Eq(Register, Register, Register),
63    Neq(Register, Register, Register),
64    Lt(Register, Register, Register),
65    Gt(Register, Register, Register),
66    Lte(Register, Register, Register),
67    Gte(Register, Register, Register),
68
69    And(Register, Register, Register),
70    Or(Register, Register, Register),
71    Xor(Register, Register, Register),
72    Shl(Register, Register, Register),
73    Shr(Register, Register, Register),
74
75    Jmp(i16),
76    Jz(Register, i16),
77    Jnz(Register, i16),
78    Call(Register, u16),
79    CallReg(Register, Register),
80    Ret(Register),
81
82    PushConst(Register, u16),
83    Copy(Register, Register),
84    Move(Register, Register),
85
86    Ld(Register, Register, u16),
87    St(Register, Register, u16),
88    LdIdx(Register, Register, Register),
89    StIdx(Register, Register, Register),
90
91    AddImm(Register, Register, i8),
92    SubImm(Register, Register, i8),
93
94    Alloc(Register, u16),
95    Drop(Register),
96
97    Dei(Register, Register),
98    Deo(Register, Register),
99
100    Handle(Register, u16),
101    Resume(Register, Register),
102
103    Raise(Register, Register, Register),
104}
105
106#[derive(Clone, Default, Debug)]
107pub struct BytecodeChunk {
108    pub code: Vec<OpCode>,
109    pub constants: Vec<u64>,
110    // Handle-bit constants store string_constants index; loader replaces with real heap handle.
111    pub const_mask: Vec<u64>,
112    pub string_constants: Vec<String>,
113    pub reg_count: usize,
114    pub param_count: usize,
115    // Debug info: source line per op (parallel to code). Empty = stripped.
116    pub lines: Vec<u32>,
117    // Debug info: source file of this fn. Empty = unknown/stripped.
118    pub src_file: String,
119}
120
121impl BytecodeChunk {
122    #[inline]
123    pub fn const_is_handle(&self, idx: u16) -> bool {
124        let i = idx as usize;
125        let word = i / 64;
126        let bit = i % 64;
127        self.const_mask.get(word).map_or(false, |w| (w >> bit) & 1 == 1)
128    }
129}
130
131#[derive(Clone, Debug)]
132pub struct NativeChunk {
133    pub name: String,
134    pub param_count: usize,
135}
136
137#[derive(Clone, Debug)]
138pub enum Chunk {
139    Bytecode(BytecodeChunk),
140    Native(NativeChunk),
141}
142
143impl Chunk {
144    pub fn param_count(&self) -> usize {
145        match self {
146            Chunk::Bytecode(b) => b.param_count,
147            Chunk::Native(n) => n.param_count,
148        }
149    }
150
151    pub fn as_bytecode(&self) -> Option<&BytecodeChunk> {
152        if let Chunk::Bytecode(b) = self { Some(b) } else { None }
153    }
154}
155
156#[derive(Debug, Default)]
157pub struct Module {
158    pub functions: Vec<Chunk>,
159    pub entry: usize,
160    pub flags: u16,
161    pub exports: Vec<Export>,
162}
163
164#[derive(Debug, Clone)]
165pub struct Export {
166    pub name: String,
167    pub fn_id: u16,
168}
169
170pub const CART_FLAG_INT32_SAFE: u16 = 0x0001;
171
172#[inline(always)]
173pub fn mask_bit_set(mask: &[u64], idx: usize) -> bool {
174    let word = idx / 64;
175    let bit = idx % 64;
176    mask.get(word).map_or(false, |w| (w >> bit) & 1 == 1)
177}
178
179#[inline(always)]
180pub fn mask_set(mask: &mut [u64], idx: usize, on: bool) {
181    let word = idx / 64;
182    let bit = idx % 64;
183    if let Some(w) = mask.get_mut(word) {
184        if on { *w |= 1u64 << bit; } else { *w &= !(1u64 << bit); }
185    }
186}