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 {
336 let cap = self.local();
337 self.b.emit_self_pid(cap.0);
338 cap
339 }
340
341 pub fn self_address(&mut self) -> Reg {
343 self.self_cap()
344 }
345
346 pub fn spawn(&mut self, function: FuncId, argc: u8) -> Reg {
347 self.spawn_with_rights(function, argc, crate::bytecode::CapRights::FLOW)
348 }
349
350 pub fn spawn_with_rights(&mut self, function: FuncId, argc: u8, rights: crate::bytecode::CapRights) -> Reg {
352 let cap = self.local();
353 self.b.emit_spawn_with_rights(cap.0, function, argc, rights);
354 cap
355 }
356
357 pub fn spawn_confined(&mut self, function: FuncId, argc: u8) -> Reg {
359 self.spawn_with_rights(function, argc, crate::bytecode::CapRights::NONE)
360 }
361
362 pub fn spawn_at(&mut self, dst: Reg, function: FuncId, argc: u8) {
364 self.b.emit_spawn(dst.0, function, argc);
365 }
366
367 pub fn delegate(&mut self, src: Reg, rights: crate::bytecode::CapRights) -> Reg {
369 let dst = self.local();
370 self.b.emit_delegate(dst.0, src.0, rights);
371 dst
372 }
373
374 pub fn send(&mut self, target_cap: Reg, msg: Reg) {
375 self.b.emit_send(target_cap.0, msg.0);
376 }
377
378 pub fn receive(&mut self) -> Reg {
379 let msg = self.local();
380 self.b.emit_receive(msg.0);
381 msg
382 }
383
384 pub fn receive_timeout(&mut self, millis: Reg) -> Reg {
385 let msg = self.local();
386 self.b.emit_receive_timeout(msg.0, millis.0);
387 msg
388 }
389
390 pub fn receive_match(&mut self, tag: Reg) -> Reg {
391 let msg = self.local();
392 self.b.emit_receive_match(msg.0, tag.0);
393 msg
394 }
395
396 pub fn receive_match_imm(&mut self, tag: u16) -> Reg {
397 let msg = self.local();
398 self.b.emit_receive_match_imm(msg.0, tag);
399 msg
400 }
401
402 pub fn fresh_request_id(&mut self) -> Reg {
404 let dst = self.local();
405 self.b.emit_fresh_request_id(dst.0);
406 dst
407 }
408
409 pub fn receive_match_corr(&mut self, tag: Reg, request_id: Reg) -> Reg {
411 let msg = self.local();
412 self.b.emit_receive_match_corr(msg.0, tag.0, request_id.0);
413 msg
414 }
415
416 pub fn receive_match_corr_imm(&mut self, tag: u16, request_id: Reg) -> Reg {
418 let msg = self.local();
419 self.b.emit_receive_match_corr_imm(msg.0, tag, request_id.0);
420 msg
421 }
422
423 pub fn hop_fresh(&mut self, tag: i32, payload: Reg) -> Reg {
425 let req_id = self.fresh_request_id();
426 self.hop(req_id, tag, payload)
427 }
428
429 pub fn ask(&mut self, target_cap: Reg, msg: Reg) -> Reg {
434 let reply = self.local();
435 self.b.emit_ask(reply.0, target_cap.0, msg.0);
436 reply
437 }
438
439 pub fn ask_timeout(&mut self, target_cap: Reg, msg: Reg, millis: Reg) -> Reg {
441 let reply = self.local();
442 self.b
443 .emit_ask_timeout(reply.0, target_cap.0, msg.0, millis.0);
444 reply
445 }
446
447 pub fn monitor(&mut self, target_cap: Reg) -> Reg {
450 let dst = self.local();
451 self.b.emit_monitor(dst.0, target_cap.0);
452 dst
453 }
454
455 pub fn demonitor(&mut self, monitor: Reg) {
456 self.b.emit_demonitor(monitor.0);
457 }
458
459 pub fn link(&mut self, target_cap: Reg) -> Reg {
461 let dst = self.local();
462 self.b.emit_link(dst.0, target_cap.0);
463 dst
464 }
465
466 pub fn unlink(&mut self, link: Reg) {
467 self.b.emit_unlink(link.0);
468 }
469
470 pub fn load_str(&mut self, s: impl AsRef<str>) -> Reg {
472 let konst = self.b.const_(Value::str(s));
473 let reg = self.local();
474 self.b.emit_load_const(reg.0, konst);
475 reg
476 }
477
478 pub fn register_name(&mut self, name: Reg) {
480 self.b.emit_register_name(name.0);
481 }
482
483 pub fn whereis(&mut self, name: Reg) -> Reg {
485 let dst = self.local();
486 self.b.emit_whereis(dst.0, name.0);
487 dst
488 }
489
490 pub fn trap(&mut self, code: i32) {
491 self.b.emit_trap(code);
492 }
493
494 pub fn native1_from(&mut self, src: Reg, native: u32) -> Reg {
498 let dst = self.local();
499 self.b.emit_native1_from(dst.0, src.0, native);
500 dst
501 }
502
503 pub fn native1_on(&mut self, src: Reg, native: u32) {
505 let tmp = self.local();
506 self.b.emit_native1_from(tmp.0, src.0, native);
507 }
508
509 pub fn native_n(&mut self, base: Reg, native: u32, argc: u8) {
511 self.b.emit_native_n(base.0, native, argc);
512 }
513
514 pub fn call_native(&mut self, base: Reg, native: u32, argc: u8) {
516 self.native_n(base, native, argc);
517 }
518
519 pub fn call_native0(&mut self, native: u32) -> Reg {
521 let dst = self.local();
522 self.b.emit_call_native(dst.0, native, 0);
523 dst
524 }
525
526 pub fn hop(&mut self, request_id: Reg, tag: i32, payload: Reg) -> Reg {
532 self.make_msg(
533 crate::natives::std_native::MAKE_MSG,
534 request_id,
535 tag,
536 payload,
537 )
538 }
539
540 pub fn hop_sender(&mut self, msg: Reg) -> Reg {
542 self.native1_from(msg, crate::natives::std_native::MSG_SENDER)
543 }
544
545 pub fn hop_request_id(&mut self, msg: Reg) -> Reg {
546 self.native1_from(msg, crate::natives::std_native::MSG_REQUEST_ID)
547 }
548
549 pub fn hop_tag(&mut self, msg: Reg) -> Reg {
550 self.native1_from(msg, crate::natives::std_native::MSG_TAG)
551 }
552
553 pub fn hop_payload(&mut self, msg: Reg) -> Reg {
554 self.native1_from(msg, crate::natives::std_native::MSG_PAYLOAD)
555 }
556
557 pub fn hop_reply_cap(&mut self, msg: Reg) -> Reg {
559 self.native1_from(msg, crate::natives::std_native::MSG_REPLY_CAP)
560 }
561
562 pub fn reply_to(&mut self, req: Reg, tag: i32, payload: Reg) -> Reg {
564 let req_id = self.hop_request_id(req);
565 self.hop(req_id, tag, payload)
566 }
567
568 pub fn send_reply(&mut self, req: Reg, tag: i32, payload: Reg) {
570 let reply_cap = self.hop_reply_cap(req);
571 let reply = self.reply_to(req, tag, payload);
572 self.send(reply_cap, reply);
573 }
574
575 pub fn make_msg(
581 &mut self,
582 native_index: u32,
583 request_id: Reg,
584 tag: i32,
585 payload: Reg,
586 ) -> Reg {
587 let w = self.window(3);
588 self.mov(w.at(0), request_id);
589 self.set(w.at(1), tag);
590 self.mov(w.at(2), payload);
591 self.native_n(w.base(), native_index, 3);
592 w.base()
593 }
594
595 pub fn make_msg_legacy_sender(
597 &mut self,
598 native_index: u32,
599 sender: Reg,
600 request_id: Reg,
601 tag: i32,
602 payload: Reg,
603 ) -> Reg {
604 let w = self.window(4);
605 self.mov(w.at(0), sender);
606 self.mov(w.at(1), request_id);
607 self.set(w.at(2), tag);
608 self.mov(w.at(3), payload);
609 self.native_n(w.base(), native_index, 4);
610 w.base()
611 }
612
613 fn temp(&mut self) -> Reg {
614 if let Some(scratch) = self.scratch {
615 return scratch;
616 }
617 let scratch = self.local();
618 self.scratch = Some(scratch);
619 scratch
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626 use crate::{NativeTable, Value, Vm, VmResult};
627
628 type TestResult = Result<(), Box<dyn std::error::Error>>;
629
630 fn count_to(n: i64) -> Chunk {
631 let mut program = Program::new("count");
632 program.function("main", 0, |f| {
633 let limit = f.load_int(n);
634 let counter = f.load_i32(0);
635 f.while_lt(counter, limit, |f| f.add_imm(counter, 1));
636 f.return_(counter);
637 });
638 program.build()
639 }
640
641 #[test]
642 fn function_raw_keeps_declared_register_file_for_verify() -> TestResult {
643 let mut program = Program::new("malformed");
644 program.function_raw("main", 3, 1, |f| {
645 let r0 = f.reg(0);
646 f.return_(r0);
647 });
648 let chunk = program.build();
649 assert_eq!(chunk.functions[0].arity, 3);
650 assert_eq!(chunk.functions[0].num_registers, 1);
651 assert!(matches!(
652 crate::verify(&chunk),
653 Err(crate::VerifyError::ArityExceedsRegisters {
654 function: 0,
655 arity: 3,
656 num_registers: 1,
657 })
658 ));
659 Ok(())
660 }
661
662 #[test]
663 fn count_loop_returns_n() -> TestResult {
664 let chunk = count_to(100);
665 let mut vm = Vm::new(std::sync::Arc::new(chunk), NativeTable::empty(), 0, &[])?;
666 assert!(matches!(
667 vm.run(10_000),
668 VmResult::Complete(Value::Int(100))
669 ));
670 Ok(())
671 }
672
673 #[test]
674 fn ping_pong_shape() -> TestResult {
675 let chunk = crate::samples::ping_pong();
676 crate::verify(&chunk)?;
677 assert!(chunk.functions.iter().any(|f| f.name == "main"));
678 assert!(chunk.functions.iter().any(|f| f.name == "pong"));
679 Ok(())
680 }
681}