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 let cap = self.local();
348 self.spawn_at(cap, function, argc);
349 cap
350 }
351
352 pub fn spawn_at(&mut self, dst: Reg, function: FuncId, argc: u8) {
354 self.b.emit_spawn(dst.0, function, argc);
355 }
356
357 pub fn send(&mut self, target_cap: Reg, msg: Reg) {
358 self.b.emit_send(target_cap.0, msg.0);
359 }
360
361 pub fn receive(&mut self) -> Reg {
362 let msg = self.local();
363 self.b.emit_receive(msg.0);
364 msg
365 }
366
367 pub fn receive_timeout(&mut self, millis: Reg) -> Reg {
368 let msg = self.local();
369 self.b.emit_receive_timeout(msg.0, millis.0);
370 msg
371 }
372
373 pub fn receive_match(&mut self, tag: Reg) -> Reg {
374 let msg = self.local();
375 self.b.emit_receive_match(msg.0, tag.0);
376 msg
377 }
378
379 pub fn receive_match_imm(&mut self, tag: u16) -> Reg {
380 let msg = self.local();
381 self.b.emit_receive_match_imm(msg.0, tag);
382 msg
383 }
384
385 pub fn ask(&mut self, target_cap: Reg, msg: Reg) -> Reg {
390 let reply = self.local();
391 self.b.emit_ask(reply.0, target_cap.0, msg.0);
392 reply
393 }
394
395 pub fn ask_timeout(&mut self, target_cap: Reg, msg: Reg, millis: Reg) -> Reg {
397 let reply = self.local();
398 self.b
399 .emit_ask_timeout(reply.0, target_cap.0, msg.0, millis.0);
400 reply
401 }
402
403 pub fn monitor(&mut self, target_cap: Reg) -> Reg {
406 let dst = self.local();
407 self.b.emit_monitor(dst.0, target_cap.0);
408 dst
409 }
410
411 pub fn demonitor(&mut self, monitor: Reg) {
412 self.b.emit_demonitor(monitor.0);
413 }
414
415 pub fn link(&mut self, target_cap: Reg) -> Reg {
417 let dst = self.local();
418 self.b.emit_link(dst.0, target_cap.0);
419 dst
420 }
421
422 pub fn unlink(&mut self, link: Reg) {
423 self.b.emit_unlink(link.0);
424 }
425
426 pub fn trap(&mut self, code: i32) {
427 self.b.emit_trap(code);
428 }
429
430 pub fn native1_from(&mut self, src: Reg, native: u32) -> Reg {
434 let dst = self.local();
435 self.b.emit_native1_from(dst.0, src.0, native);
436 dst
437 }
438
439 pub fn native1_on(&mut self, src: Reg, native: u32) {
441 let tmp = self.local();
442 self.b.emit_native1_from(tmp.0, src.0, native);
443 }
444
445 pub fn native_n(&mut self, base: Reg, native: u32, argc: u8) {
447 self.b.emit_native_n(base.0, native, argc);
448 }
449
450 pub fn call_native(&mut self, base: Reg, native: u32, argc: u8) {
452 self.native_n(base, native, argc);
453 }
454
455 pub fn call_native0(&mut self, native: u32) -> Reg {
457 let dst = self.local();
458 self.b.emit_call_native(dst.0, native, 0);
459 dst
460 }
461
462 pub fn hop(&mut self, request_id: Reg, tag: i32, payload: Reg) -> Reg {
467 let placeholder = self.load_i32(0);
468 self.make_msg(
469 crate::natives::std_native::MAKE_MSG,
470 placeholder,
471 request_id,
472 tag,
473 payload,
474 )
475 }
476
477 pub fn hop_sender(&mut self, msg: Reg) -> Reg {
479 self.native1_from(msg, crate::natives::std_native::MSG_SENDER)
480 }
481
482 pub fn hop_request_id(&mut self, msg: Reg) -> Reg {
483 self.native1_from(msg, crate::natives::std_native::MSG_REQUEST_ID)
484 }
485
486 pub fn hop_tag(&mut self, msg: Reg) -> Reg {
487 self.native1_from(msg, crate::natives::std_native::MSG_TAG)
488 }
489
490 pub fn hop_payload(&mut self, msg: Reg) -> Reg {
491 self.native1_from(msg, crate::natives::std_native::MSG_PAYLOAD)
492 }
493
494 pub fn hop_reply_cap(&mut self, msg: Reg) -> Reg {
496 self.native1_from(msg, crate::natives::std_native::MSG_REPLY_CAP)
497 }
498
499 pub fn reply_to(&mut self, req: Reg, tag: i32, payload: Reg) -> Reg {
501 let req_id = self.hop_request_id(req);
502 self.hop(req_id, tag, payload)
503 }
504
505 pub fn send_reply(&mut self, req: Reg, tag: i32, payload: Reg) {
507 let reply_cap = self.hop_reply_cap(req);
508 let reply = self.reply_to(req, tag, payload);
509 self.send(reply_cap, reply);
510 }
511
512 pub fn make_msg(
519 &mut self,
520 native_index: u32,
521 sender: Reg,
522 request_id: Reg,
523 tag: i32,
524 payload: Reg,
525 ) -> Reg {
526 let w = self.window(4);
527 self.mov(w.at(0), sender);
528 self.mov(w.at(1), request_id);
529 self.set(w.at(2), tag);
530 self.mov(w.at(3), payload);
531 self.native_n(w.base(), native_index, 4);
532 w.base()
533 }
534
535 fn temp(&mut self) -> Reg {
536 if let Some(scratch) = self.scratch {
537 return scratch;
538 }
539 let scratch = self.local();
540 self.scratch = Some(scratch);
541 scratch
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548 use crate::{NativeTable, Value, Vm, VmResult};
549
550 type TestResult = Result<(), Box<dyn std::error::Error>>;
551
552 fn count_to(n: i64) -> Chunk {
553 let mut program = Program::new("count");
554 program.function("main", 0, |f| {
555 let limit = f.load_int(n);
556 let counter = f.load_i32(0);
557 f.while_lt(counter, limit, |f| f.add_imm(counter, 1));
558 f.return_(counter);
559 });
560 program.build()
561 }
562
563 #[test]
564 fn function_raw_keeps_declared_register_file_for_verify() -> TestResult {
565 let mut program = Program::new("malformed");
566 program.function_raw("main", 3, 1, |f| {
567 let r0 = f.reg(0);
568 f.return_(r0);
569 });
570 let chunk = program.build();
571 assert_eq!(chunk.functions[0].arity, 3);
572 assert_eq!(chunk.functions[0].num_registers, 1);
573 assert!(matches!(
574 crate::verify(&chunk),
575 Err(crate::VerifyError::ArityExceedsRegisters {
576 function: 0,
577 arity: 3,
578 num_registers: 1,
579 })
580 ));
581 Ok(())
582 }
583
584 #[test]
585 fn count_loop_returns_n() -> TestResult {
586 let chunk = count_to(100);
587 let mut vm = Vm::new(std::sync::Arc::new(chunk), NativeTable::empty(), 0, &[])?;
588 assert!(matches!(
589 vm.run(10_000),
590 VmResult::Complete(Value::Int(100))
591 ));
592 Ok(())
593 }
594
595 #[test]
596 fn ping_pong_shape() -> TestResult {
597 let chunk = crate::samples::ping_pong();
598 crate::verify(&chunk)?;
599 assert!(chunk.functions.iter().any(|f| f.name == "main"));
600 assert!(chunk.functions.iter().any(|f| f.name == "pong"));
601 Ok(())
602 }
603}