1use super::builder::ChunkBuilder;
20
21pub use super::builder::Label;
22use super::chunk::Chunk;
23use super::opcode::Opcode;
24use super::value::Value;
25
26pub type FuncId = u32;
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31pub struct Reg(u8);
32
33impl Reg {
34 pub const fn index(self) -> u8 {
36 self.0
37 }
38}
39
40impl From<Reg> for u8 {
41 fn from(r: Reg) -> u8 {
42 r.0
43 }
44}
45
46#[derive(Clone, Copy, Debug)]
48pub struct RegWindow {
49 base: Reg,
50 len: u8,
51}
52
53impl RegWindow {
54 pub fn base(self) -> Reg {
56 self.base
57 }
58
59 pub fn at(self, i: u8) -> Reg {
61 debug_assert!(i < self.len, "RegWindow index out of bounds");
62 Reg(self.base.0.saturating_add(i))
63 }
64}
65
66pub struct Program {
68 inner: ChunkBuilder,
69}
70
71impl Program {
72 pub fn new(name: impl Into<String>) -> Self {
74 Self {
75 inner: ChunkBuilder::new(name),
76 }
77 }
78
79 pub fn function(
83 &mut self,
84 name: impl Into<String>,
85 arity: u8,
86 body: impl FnOnce(&mut Fn<'_>),
87 ) -> FuncId {
88 let mut f = Fn::open(&mut self.inner, name, arity);
89 let id = f.function;
90 body(&mut f);
91 id
92 }
93
94 pub fn function_raw(
99 &mut self,
100 name: impl Into<String>,
101 arity: u8,
102 num_registers: u8,
103 body: impl FnOnce(&mut Fn<'_>),
104 ) -> FuncId {
105 let mut f = Fn::open_with(&mut self.inner, name, arity, num_registers);
106 let id = f.function;
107 body(&mut f);
108 id
109 }
110
111 pub fn function_index(&self, name: &str) -> Option<FuncId> {
113 self.inner.function_index(name)
114 }
115
116 pub fn build(self) -> Chunk {
118 self.inner.finish()
119 }
120}
121
122pub struct Fn<'a> {
124 b: &'a mut ChunkBuilder,
125 function: FuncId,
126 next_reg: u8,
127 scratch: Option<Reg>,
128}
129
130impl<'a> Fn<'a> {
131 fn open(b: &'a mut ChunkBuilder, name: impl Into<String>, arity: u8) -> Self {
132 Self::open_with(b, name, arity, arity.max(4))
133 }
134
135 fn open_with(
136 b: &'a mut ChunkBuilder,
137 name: impl Into<String>,
138 arity: u8,
139 num_registers: u8,
140 ) -> Self {
141 let function = b.begin_function(name, arity, num_registers);
142 Self {
143 b,
144 function,
145 next_reg: num_registers,
146 scratch: None,
147 }
148 }
149
150 pub fn local(&mut self) -> Reg {
152 let reg = Reg(self.next_reg);
153 self.next_reg = self.next_reg.saturating_add(1);
154 self.b.set_num_registers(self.function, self.next_reg);
155 reg
156 }
157
158 pub fn reg(&mut self, index: u8) -> Reg {
160 Reg(index)
161 }
162
163 pub fn reserve(&mut self, count: u8) {
165 if count > self.next_reg {
166 self.next_reg = count;
167 self.b.set_num_registers(self.function, self.next_reg);
168 }
169 }
170
171 pub fn window(&mut self, count: u8) -> RegWindow {
173 let base_idx = self.next_reg;
174 for _ in 0..count {
175 let _ = self.local();
176 }
177 RegWindow {
178 base: Reg(base_idx),
179 len: count,
180 }
181 }
182
183 pub fn load_i32(&mut self, n: i32) -> Reg {
185 let reg = self.local();
186 self.b.emit_load_imm(reg.0, n);
187 reg
188 }
189
190 pub fn load_int(&mut self, n: i64) -> Reg {
192 let konst = self.b.const_(Value::Int(n));
193 let reg = self.local();
194 self.b.emit_load_const(reg.0, konst);
195 reg
196 }
197
198 pub fn set(&mut self, reg: Reg, imm: i32) {
200 self.b.emit_load_imm(reg.0, imm);
201 }
202
203 pub fn mov(&mut self, dst: Reg, src: Reg) {
205 self.b.emit_move(dst.0, src.0);
206 }
207
208 fn binop(&mut self, op: Opcode, lhs: Reg, rhs: Reg) -> Reg {
209 let dst = self.local();
210 self.b.emit_binop(op, dst.0, lhs.0, rhs.0);
211 dst
212 }
213
214 fn binop_imm(&mut self, op: Opcode, lhs: Reg, imm: i32) -> Reg {
215 let rhs = self.temp();
216 self.b.emit_load_imm(rhs.0, imm);
217 self.binop(op, lhs, rhs)
218 }
219
220 pub fn add(&mut self, lhs: Reg, rhs: Reg) -> Reg {
222 self.binop(Opcode::Add, lhs, rhs)
223 }
224
225 pub fn add_imm(&mut self, dst: Reg, imm: i32) {
227 let tmp = self.temp();
228 self.b.emit_load_imm(tmp.0, imm);
229 self.b.emit_binop(Opcode::Add, dst.0, dst.0, tmp.0);
230 }
231
232 pub fn sub(&mut self, lhs: Reg, rhs: Reg) -> Reg {
233 self.binop(Opcode::Sub, lhs, rhs)
234 }
235
236 pub fn mul(&mut self, lhs: Reg, rhs: Reg) -> Reg {
237 self.binop(Opcode::Mul, lhs, rhs)
238 }
239
240 pub fn div(&mut self, lhs: Reg, rhs: Reg) -> Reg {
241 self.binop(Opcode::Div, lhs, rhs)
242 }
243
244 pub fn modulo(&mut self, lhs: Reg, rhs: Reg) -> Reg {
245 self.binop(Opcode::Mod, lhs, rhs)
246 }
247
248 pub fn neg(&mut self, src: Reg) -> Reg {
249 let dst = self.local();
250 self.b.emit_neg(dst.0, src.0);
251 dst
252 }
253
254 pub fn eq(&mut self, lhs: Reg, rhs: Reg) -> Reg {
255 self.binop(Opcode::Eq, lhs, rhs)
256 }
257
258 pub fn eq_imm(&mut self, lhs: Reg, imm: i32) -> Reg {
259 self.binop_imm(Opcode::Eq, lhs, imm)
260 }
261
262 pub fn lt(&mut self, lhs: Reg, rhs: Reg) -> Reg {
263 self.binop(Opcode::Lt, lhs, rhs)
264 }
265
266 pub fn le(&mut self, lhs: Reg, rhs: Reg) -> Reg {
267 self.binop(Opcode::Le, lhs, rhs)
268 }
269
270 pub fn while_lt<F>(&mut self, counter: Reg, limit: Reg, body: F)
272 where
273 F: FnOnce(&mut Fn<'_>),
274 {
275 let head = self.b.new_label();
276 let done = self.b.new_label();
277 let cond = self.local();
278 self.b.bind_label(head);
279 self.b.emit_binop(Opcode::Lt, cond.0, counter.0, limit.0);
280 self.b.emit_branch(cond.0, done);
281 body(self);
282 self.b.emit_jump(head);
283 self.b.bind_label(done);
284 }
285
286 pub fn label(&mut self) -> Label {
287 self.b.new_label()
288 }
289
290 pub fn bind(&mut self, label: Label) {
291 self.b.bind_label(label);
292 }
293
294 pub fn jump(&mut self, label: Label) {
295 self.b.emit_jump(label);
296 }
297
298 pub fn branch_if_falsy(&mut self, cond: Reg, target: Label) {
300 self.b.emit_branch(cond.0, target);
301 }
302
303 pub fn return_(&mut self, value: Reg) {
304 self.b.emit_return(value.0);
305 }
306
307 pub fn call(&mut self, function: FuncId, argc: u8) -> Reg {
308 let dst = self.local();
309 self.b.emit_call(dst.0, function, argc);
310 dst
311 }
312
313 pub fn halt(&mut self) {
314 self.b.emit_halt();
315 }
316
317 pub fn yield_(&mut self) {
318 self.b.emit_yield();
319 }
320
321 pub fn sleep(&mut self, millis: Reg) {
322 self.b.emit_sleep(millis.0);
323 }
324
325 pub fn exit(&mut self, reg: Reg) {
326 self.b.emit_exit(reg.0);
327 }
328
329 pub fn self_cap(&mut self) -> Reg {
331 let cap = self.local();
332 self.b.emit_self_pid(cap.0);
333 cap
334 }
335
336 pub fn spawn(&mut self, function: FuncId, argc: u8) -> Reg {
337 let cap = self.local();
338 self.spawn_at(cap, function, argc);
339 cap
340 }
341
342 pub fn spawn_at(&mut self, dst: Reg, function: FuncId, argc: u8) {
344 self.b.emit_spawn(dst.0, function, argc);
345 }
346
347 pub fn send(&mut self, target_cap: Reg, msg: Reg) {
348 self.b.emit_send(target_cap.0, msg.0);
349 }
350
351 pub fn receive(&mut self) -> Reg {
352 let msg = self.local();
353 self.b.emit_receive(msg.0);
354 msg
355 }
356
357 pub fn receive_timeout(&mut self, millis: Reg) -> Reg {
358 let msg = self.local();
359 self.b.emit_receive_timeout(msg.0, millis.0);
360 msg
361 }
362
363 pub fn receive_match(&mut self, tag: Reg) -> Reg {
364 let msg = self.local();
365 self.b.emit_receive_match(msg.0, tag.0);
366 msg
367 }
368
369 pub fn receive_match_imm(&mut self, tag: u16) -> Reg {
370 let msg = self.local();
371 self.b.emit_receive_match_imm(msg.0, tag);
372 msg
373 }
374
375 pub fn ask(&mut self, target_cap: Reg, msg: Reg) -> Reg {
377 let reply = self.local();
378 self.b.emit_ask(reply.0, target_cap.0, msg.0);
379 reply
380 }
381
382 pub fn trap(&mut self, code: i32) {
383 self.b.emit_trap(code);
384 }
385
386 pub fn native1_from(&mut self, src: Reg, native: u32) -> Reg {
390 let dst = self.local();
391 self.b.emit_native1_from(dst.0, src.0, native);
392 dst
393 }
394
395 pub fn native1_on(&mut self, src: Reg, native: u32) {
397 let tmp = self.local();
398 self.b.emit_native1_from(tmp.0, src.0, native);
399 }
400
401 pub fn native_n(&mut self, base: Reg, native: u32, argc: u8) {
403 self.b.emit_native_n(base.0, native, argc);
404 }
405
406 pub fn call_native(&mut self, base: Reg, native: u32, argc: u8) {
408 self.native_n(base, native, argc);
409 }
410
411 pub fn call_native0(&mut self, native: u32) -> Reg {
413 let dst = self.local();
414 self.b.emit_call_native(dst.0, native, 0);
415 dst
416 }
417
418 pub fn make_msg(
423 &mut self,
424 native_index: u32,
425 sender: Reg,
426 request_id: Reg,
427 tag: i32,
428 payload: Reg,
429 ) -> Reg {
430 let w = self.window(4);
431 self.mov(w.at(0), sender);
432 self.mov(w.at(1), request_id);
433 self.set(w.at(2), tag);
434 self.mov(w.at(3), payload);
435 self.native_n(w.base(), native_index, 4);
436 w.base()
437 }
438
439 fn temp(&mut self) -> Reg {
440 if let Some(scratch) = self.scratch {
441 return scratch;
442 }
443 let scratch = self.local();
444 self.scratch = Some(scratch);
445 scratch
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use crate::{NativeTable, Value, Vm, VmResult};
453
454 type TestResult = Result<(), Box<dyn std::error::Error>>;
455
456 fn count_to(n: i64) -> Chunk {
457 let mut program = Program::new("count");
458 program.function("main", 0, |f| {
459 let limit = f.load_int(n);
460 let counter = f.load_i32(0);
461 f.while_lt(counter, limit, |f| f.add_imm(counter, 1));
462 f.return_(counter);
463 });
464 program.build()
465 }
466
467 #[test]
468 fn function_raw_keeps_declared_register_file_for_verify() -> TestResult {
469 let mut program = Program::new("malformed");
470 program.function_raw("main", 3, 1, |f| {
471 let r0 = f.reg(0);
472 f.return_(r0);
473 });
474 let chunk = program.build();
475 assert_eq!(chunk.functions[0].arity, 3);
476 assert_eq!(chunk.functions[0].num_registers, 1);
477 assert!(matches!(
478 crate::verify(&chunk),
479 Err(crate::VerifyError::ArityExceedsRegisters {
480 function: 0,
481 arity: 3,
482 num_registers: 1,
483 })
484 ));
485 Ok(())
486 }
487
488 #[test]
489 fn count_loop_returns_n() -> TestResult {
490 let chunk = count_to(100);
491 let mut vm = Vm::new(std::sync::Arc::new(chunk), NativeTable::empty(), 0, &[])?;
492 assert!(matches!(
493 vm.run(10_000),
494 VmResult::Complete(Value::Int(100))
495 ));
496 Ok(())
497 }
498
499 #[test]
500 fn ping_pong_shape() -> TestResult {
501 let chunk = crate::samples::ping_pong();
502 crate::verify(&chunk)?;
503 assert!(chunk.functions.iter().any(|f| f.name == "main"));
504 assert!(chunk.functions.iter().any(|f| f.name == "pong"));
505 Ok(())
506 }
507}