just_engine/runner/jit/reg_bytecode.rs
1//! Register-based bytecode instruction set and chunk structure.
2//!
3//! Defines a flat, register-based bytecode IR that the register VM will execute.
4
5use crate::runner::ds::value::JsValue;
6
7/// Bytecode opcodes for the register-based VM.
8#[derive(Debug, Clone, Copy, PartialEq)]
9#[repr(u8)]
10pub enum RegOpCode {
11 // ── Constants & Literals ──────────────────────────────────
12 /// dst = constant pool value.
13 LoadConst,
14 /// dst = undefined.
15 LoadUndefined,
16 /// dst = null.
17 LoadNull,
18 /// dst = true.
19 LoadTrue,
20 /// dst = false.
21 LoadFalse,
22
23 // ── Register moves ───────────────────────────────────────
24 /// dst = src1.
25 Move,
26
27 // ── Arithmetic ───────────────────────────────────────────
28 /// dst = src1 + src2.
29 Add,
30 /// dst = src1 - src2.
31 Sub,
32 /// dst = src1 * src2.
33 Mul,
34 /// dst = src1 / src2.
35 Div,
36 /// dst = src1 % src2.
37 Mod,
38 /// dst = -src1.
39 Negate,
40
41 // ── Bitwise ──────────────────────────────────────────────
42 /// dst = src1 & src2.
43 BitAnd,
44 /// dst = src1 | src2.
45 BitOr,
46 /// dst = src1 ^ src2.
47 BitXor,
48 /// dst = ~src1.
49 BitNot,
50 /// dst = src1 << src2.
51 ShiftLeft,
52 /// dst = src1 >> src2.
53 ShiftRight,
54 /// dst = src1 >>> src2.
55 UShiftRight,
56
57 // ── Comparison ───────────────────────────────────────────
58 /// dst = (src1 === src2).
59 StrictEqual,
60 /// dst = (src1 !== src2).
61 StrictNotEqual,
62 /// dst = (src1 == src2).
63 Equal,
64 /// dst = (src1 != src2).
65 NotEqual,
66 /// dst = (src1 < src2).
67 LessThan,
68 /// dst = (src1 <= src2).
69 LessEqual,
70 /// dst = (src1 > src2).
71 GreaterThan,
72 /// dst = (src1 >= src2).
73 GreaterEqual,
74
75 // ── Logical / Unary ──────────────────────────────────────
76 /// dst = !src1.
77 Not,
78 /// dst = typeof src1.
79 TypeOf,
80 /// dst = +src1.
81 UnaryPlus,
82
83 // ── Variables / Bindings ─────────────────────────────────
84 /// dst = get var by name (imm = name index).
85 GetVar,
86 /// set var by name (src1 = value, imm = name index).
87 SetVar,
88 /// declare var (imm = name index).
89 DeclareVar,
90 /// declare let (imm = name index).
91 DeclareLet,
92 /// declare const (imm = name index).
93 DeclareConst,
94 /// init var (src1 = value, imm = name index).
95 InitVar,
96 /// init let/const (src1 = value, imm = name index).
97 InitBinding,
98
99 // ── Objects & Properties ─────────────────────────────────
100 /// dst = obj.prop (src1 = obj, imm = name index).
101 GetProp,
102 /// obj.prop = value (src1 = obj, src2 = value, imm = name index).
103 SetProp,
104 /// dst = obj[key] (src1 = obj, src2 = key).
105 GetElem,
106 /// obj[key] = value (dst = value, src1 = obj, src2 = key).
107 SetElem,
108
109 // ── Control Flow ─────────────────────────────────────────
110 /// jump to imm (absolute index).
111 Jump,
112 /// if !src1 jump to imm.
113 JumpIfFalse,
114 /// if src1 jump to imm.
115 JumpIfTrue,
116
117 // ── Function calls ───────────────────────────────────────
118 /// dst = call callee (src1 = callee, imm = arg count).
119 Call,
120 /// dst = call method (src1 = object, imm = arg count, src2 = name index).
121 CallMethod,
122
123 // ── Misc ─────────────────────────────────────────────────
124 /// return src1.
125 Return,
126 /// halt (optional src1 as result).
127 Halt,
128}
129
130/// A single register bytecode instruction.
131#[derive(Debug, Clone)]
132pub struct RegInstruction {
133 pub op: RegOpCode,
134 /// Destination register (when applicable).
135 pub dst: u32,
136 /// First source register.
137 pub src1: u32,
138 /// Second source register.
139 pub src2: u32,
140 /// Immediate operand (constant index, name index, jump target, arg count).
141 pub imm: u32,
142}
143
144impl RegInstruction {
145 pub fn new(op: RegOpCode, dst: u32, src1: u32, src2: u32, imm: u32) -> Self {
146 RegInstruction { op, dst, src1, src2, imm }
147 }
148
149 pub fn simple(op: RegOpCode) -> Self {
150 RegInstruction::new(op, 0, 0, 0, 0)
151 }
152
153 pub fn with_dst(op: RegOpCode, dst: u32) -> Self {
154 RegInstruction::new(op, dst, 0, 0, 0)
155 }
156
157 pub fn with_dst_src(op: RegOpCode, dst: u32, src1: u32) -> Self {
158 RegInstruction::new(op, dst, src1, 0, 0)
159 }
160
161 pub fn with_dst_srcs(op: RegOpCode, dst: u32, src1: u32, src2: u32) -> Self {
162 RegInstruction::new(op, dst, src1, src2, 0)
163 }
164
165 pub fn with_dst_imm(op: RegOpCode, dst: u32, imm: u32) -> Self {
166 RegInstruction::new(op, dst, 0, 0, imm)
167 }
168
169 pub fn with_src_imm(op: RegOpCode, src1: u32, imm: u32) -> Self {
170 RegInstruction::new(op, 0, src1, 0, imm)
171 }
172
173 pub fn with_srcs_imm(op: RegOpCode, src1: u32, src2: u32, imm: u32) -> Self {
174 RegInstruction::new(op, 0, src1, src2, imm)
175 }
176}
177
178/// A compiled chunk of register bytecode with its constant pool.
179#[derive(Debug, Clone)]
180pub struct RegChunk {
181 /// The register bytecode instructions.
182 pub code: Vec<RegInstruction>,
183 /// Constant pool — holds literal values.
184 pub constants: Vec<JsValue>,
185 /// Deduplicated name table for variable/property names.
186 pub names: Vec<String>,
187 /// Local register table (name index -> register index).
188 pub locals: Vec<RegLocal>,
189 /// Register count required by this chunk.
190 pub register_count: u32,
191}
192
193impl RegChunk {
194 pub fn new() -> Self {
195 RegChunk {
196 code: Vec::new(),
197 constants: Vec::new(),
198 names: Vec::new(),
199 locals: Vec::new(),
200 register_count: 0,
201 }
202 }
203
204 /// Emit an instruction and return its index.
205 pub fn emit(&mut self, instr: RegInstruction) -> usize {
206 let idx = self.code.len();
207 self.code.push(instr);
208 idx
209 }
210
211 /// Emit a simple (no-operand) instruction.
212 pub fn emit_op(&mut self, op: RegOpCode) -> usize {
213 self.emit(RegInstruction::simple(op))
214 }
215
216 /// Add a constant to the pool and return its index.
217 pub fn add_constant(&mut self, value: JsValue) -> u32 {
218 let idx = self.constants.len();
219 self.constants.push(value);
220 idx as u32
221 }
222
223 /// Add a name to the deduplicated name table and return its index.
224 pub fn add_name(&mut self, s: &str) -> u32 {
225 for (i, existing) in self.names.iter().enumerate() {
226 if existing == s {
227 return i as u32;
228 }
229 }
230 let idx = self.names.len();
231 self.names.push(s.to_string());
232 idx as u32
233 }
234
235 /// Get a name by index (zero-copy reference).
236 #[inline]
237 pub fn get_name(&self, idx: u32) -> &str {
238 &self.names[idx as usize]
239 }
240
241 /// Add a local register for a name index and return its local index.
242 pub fn add_local(&mut self, name_idx: u32, reg: u32) -> u32 {
243 let idx = self.locals.len();
244 self.locals.push(RegLocal { name_idx, reg });
245 idx as u32
246 }
247
248 /// Get a local register's name.
249 #[inline]
250 pub fn get_local_name(&self, local_idx: u32) -> &str {
251 let name_idx = self.locals[local_idx as usize].name_idx;
252 self.get_name(name_idx)
253 }
254
255 /// Update the register count for this chunk.
256 pub fn set_register_count(&mut self, count: u32) {
257 self.register_count = count;
258 }
259
260 /// Disassemble the chunk for debugging.
261 pub fn disassemble(&self, name: &str) -> String {
262 let mut out = format!("== {} ==\n", name);
263 for (i, instr) in self.code.iter().enumerate() {
264 out.push_str(&format!("{:04} {:?} dst={} src1={} src2={} imm={}", i, instr.op, instr.dst, instr.src1, instr.src2, instr.imm));
265 match instr.op {
266 RegOpCode::LoadConst => {
267 if let Some(val) = self.constants.get(instr.imm as usize) {
268 out.push_str(&format!(" const={}", val));
269 }
270 }
271 RegOpCode::GetVar | RegOpCode::SetVar
272 | RegOpCode::DeclareVar | RegOpCode::DeclareLet
273 | RegOpCode::DeclareConst | RegOpCode::InitVar
274 | RegOpCode::InitBinding | RegOpCode::GetProp
275 | RegOpCode::SetProp => {
276 if let Some(name) = self.names.get(instr.imm as usize) {
277 out.push_str(&format!(" name=\"{}\"", name));
278 }
279 }
280 RegOpCode::CallMethod => {
281 if let Some(name) = self.names.get(instr.src2 as usize) {
282 out.push_str(&format!(" method=\"{}\"", name));
283 }
284 }
285 RegOpCode::Jump | RegOpCode::JumpIfFalse | RegOpCode::JumpIfTrue => {
286 out.push_str(&format!(" -> {:04}", instr.imm));
287 }
288 _ => {}
289 }
290 out.push('\n');
291 }
292 out
293 }
294}
295
296/// Local register metadata for name sync.
297#[derive(Debug, Clone)]
298pub struct RegLocal {
299 pub name_idx: u32,
300 pub reg: u32,
301}