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 ask(&mut self, target_cap: Reg, msg: Reg) -> Reg {
407 let reply = self.local();
408 self.b.emit_ask(reply.0, target_cap.0, msg.0);
409 reply
410 }
411
412 pub fn ask_timeout(&mut self, target_cap: Reg, msg: Reg, millis: Reg) -> Reg {
414 let reply = self.local();
415 self.b
416 .emit_ask_timeout(reply.0, target_cap.0, msg.0, millis.0);
417 reply
418 }
419
420 pub fn monitor(&mut self, target_cap: Reg) -> Reg {
423 let dst = self.local();
424 self.b.emit_monitor(dst.0, target_cap.0);
425 dst
426 }
427
428 pub fn demonitor(&mut self, monitor: Reg) {
429 self.b.emit_demonitor(monitor.0);
430 }
431
432 pub fn link(&mut self, target_cap: Reg) -> Reg {
434 let dst = self.local();
435 self.b.emit_link(dst.0, target_cap.0);
436 dst
437 }
438
439 pub fn unlink(&mut self, link: Reg) {
440 self.b.emit_unlink(link.0);
441 }
442
443 pub fn trap(&mut self, code: i32) {
444 self.b.emit_trap(code);
445 }
446
447 pub fn native1_from(&mut self, src: Reg, native: u32) -> Reg {
451 let dst = self.local();
452 self.b.emit_native1_from(dst.0, src.0, native);
453 dst
454 }
455
456 pub fn native1_on(&mut self, src: Reg, native: u32) {
458 let tmp = self.local();
459 self.b.emit_native1_from(tmp.0, src.0, native);
460 }
461
462 pub fn native_n(&mut self, base: Reg, native: u32, argc: u8) {
464 self.b.emit_native_n(base.0, native, argc);
465 }
466
467 pub fn call_native(&mut self, base: Reg, native: u32, argc: u8) {
469 self.native_n(base, native, argc);
470 }
471
472 pub fn call_native0(&mut self, native: u32) -> Reg {
474 let dst = self.local();
475 self.b.emit_call_native(dst.0, native, 0);
476 dst
477 }
478
479 pub fn hop(&mut self, request_id: Reg, tag: i32, payload: Reg) -> Reg {
484 self.make_msg(
485 crate::natives::std_native::MAKE_MSG,
486 request_id,
487 tag,
488 payload,
489 )
490 }
491
492 pub fn hop_sender(&mut self, msg: Reg) -> Reg {
494 self.native1_from(msg, crate::natives::std_native::MSG_SENDER)
495 }
496
497 pub fn hop_request_id(&mut self, msg: Reg) -> Reg {
498 self.native1_from(msg, crate::natives::std_native::MSG_REQUEST_ID)
499 }
500
501 pub fn hop_tag(&mut self, msg: Reg) -> Reg {
502 self.native1_from(msg, crate::natives::std_native::MSG_TAG)
503 }
504
505 pub fn hop_payload(&mut self, msg: Reg) -> Reg {
506 self.native1_from(msg, crate::natives::std_native::MSG_PAYLOAD)
507 }
508
509 pub fn hop_reply_cap(&mut self, msg: Reg) -> Reg {
511 self.native1_from(msg, crate::natives::std_native::MSG_REPLY_CAP)
512 }
513
514 pub fn reply_to(&mut self, req: Reg, tag: i32, payload: Reg) -> Reg {
516 let req_id = self.hop_request_id(req);
517 self.hop(req_id, tag, payload)
518 }
519
520 pub fn send_reply(&mut self, req: Reg, tag: i32, payload: Reg) {
522 let reply_cap = self.hop_reply_cap(req);
523 let reply = self.reply_to(req, tag, payload);
524 self.send(reply_cap, reply);
525 }
526
527 pub fn make_msg(
533 &mut self,
534 native_index: u32,
535 request_id: Reg,
536 tag: i32,
537 payload: Reg,
538 ) -> Reg {
539 let w = self.window(3);
540 self.mov(w.at(0), request_id);
541 self.set(w.at(1), tag);
542 self.mov(w.at(2), payload);
543 self.native_n(w.base(), native_index, 3);
544 w.base()
545 }
546
547 pub fn make_msg_legacy_sender(
549 &mut self,
550 native_index: u32,
551 sender: Reg,
552 request_id: Reg,
553 tag: i32,
554 payload: Reg,
555 ) -> Reg {
556 let w = self.window(4);
557 self.mov(w.at(0), sender);
558 self.mov(w.at(1), request_id);
559 self.set(w.at(2), tag);
560 self.mov(w.at(3), payload);
561 self.native_n(w.base(), native_index, 4);
562 w.base()
563 }
564
565 fn temp(&mut self) -> Reg {
566 if let Some(scratch) = self.scratch {
567 return scratch;
568 }
569 let scratch = self.local();
570 self.scratch = Some(scratch);
571 scratch
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578 use crate::{NativeTable, Value, Vm, VmResult};
579
580 type TestResult = Result<(), Box<dyn std::error::Error>>;
581
582 fn count_to(n: i64) -> Chunk {
583 let mut program = Program::new("count");
584 program.function("main", 0, |f| {
585 let limit = f.load_int(n);
586 let counter = f.load_i32(0);
587 f.while_lt(counter, limit, |f| f.add_imm(counter, 1));
588 f.return_(counter);
589 });
590 program.build()
591 }
592
593 #[test]
594 fn function_raw_keeps_declared_register_file_for_verify() -> TestResult {
595 let mut program = Program::new("malformed");
596 program.function_raw("main", 3, 1, |f| {
597 let r0 = f.reg(0);
598 f.return_(r0);
599 });
600 let chunk = program.build();
601 assert_eq!(chunk.functions[0].arity, 3);
602 assert_eq!(chunk.functions[0].num_registers, 1);
603 assert!(matches!(
604 crate::verify(&chunk),
605 Err(crate::VerifyError::ArityExceedsRegisters {
606 function: 0,
607 arity: 3,
608 num_registers: 1,
609 })
610 ));
611 Ok(())
612 }
613
614 #[test]
615 fn count_loop_returns_n() -> TestResult {
616 let chunk = count_to(100);
617 let mut vm = Vm::new(std::sync::Arc::new(chunk), NativeTable::empty(), 0, &[])?;
618 assert!(matches!(
619 vm.run(10_000),
620 VmResult::Complete(Value::Int(100))
621 ));
622 Ok(())
623 }
624
625 #[test]
626 fn ping_pong_shape() -> TestResult {
627 let chunk = crate::samples::ping_pong();
628 crate::verify(&chunk)?;
629 assert!(chunk.functions.iter().any(|f| f.name == "main"));
630 assert!(chunk.functions.iter().any(|f| f.name == "pong"));
631 Ok(())
632 }
633}