1use std::collections::HashMap;
2
3use super::chunk::{Chunk, FunctionDef};
4use super::instruction::Instruction;
5use super::opcode::Opcode;
6use super::value::Value;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct Label(u32);
12
13pub(crate) struct ChunkBuilder {
18 name: String,
19 constants: Vec<Value>,
20 code: Vec<Instruction>,
21 functions: Vec<FunctionDef>,
22 next_label: u32,
23 label_targets: HashMap<Label, u32>,
24 pending_jumps: Vec<(usize, Label)>,
26 fn_starts: HashMap<String, u32>,
27}
28
29impl ChunkBuilder {
30 pub fn new(name: impl Into<String>) -> Self {
31 ChunkBuilder {
32 name: name.into(),
33 constants: Vec::new(),
34 code: Vec::new(),
35 functions: Vec::new(),
36 next_label: 0,
37 label_targets: HashMap::new(),
38 pending_jumps: Vec::new(),
39 fn_starts: HashMap::new(),
40 }
41 }
42
43 pub fn const_(&mut self, v: Value) -> u32 {
44 if let Some(pos) = self.constants.iter().position(|c| c == &v) {
47 return pos as u32;
48 }
49 self.constants.push(v);
50 (self.constants.len() - 1) as u32
51 }
52
53 pub fn new_label(&mut self) -> Label {
54 let l = Label(self.next_label);
55 self.next_label += 1;
56 l
57 }
58
59 pub fn bind_label(&mut self, label: Label) {
61 self.label_targets.insert(label, self.code.len() as u32);
62 }
63
64 fn emit(&mut self, instr: Instruction) -> usize {
65 self.code.push(instr);
66 self.code.len() - 1
67 }
68
69 pub fn emit_halt(&mut self) {
70 self.emit(Instruction::nullary(Opcode::Halt));
71 }
72
73 pub fn emit_load_const(&mut self, dst: u8, konst: u32) {
74 self.emit(Instruction::new(Opcode::LoadConst, dst, 0, 0, konst as i32));
75 }
76
77 pub fn emit_load_imm(&mut self, dst: u8, imm: i32) {
78 self.emit(Instruction::a_imm(Opcode::LoadImm, dst, imm));
79 }
80
81 pub fn emit_move(&mut self, dst: u8, src: u8) {
82 self.emit(Instruction::abc(Opcode::Move, dst, src, 0));
83 }
84
85 pub fn emit_binop(&mut self, op: Opcode, dst: u8, lhs: u8, rhs: u8) {
86 debug_assert!(matches!(
87 op,
88 Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Div | Opcode::Mod
89 | Opcode::Eq | Opcode::Lt | Opcode::Le
90 ));
91 self.emit(Instruction::abc(op, dst, lhs, rhs));
92 }
93
94 pub fn emit_neg(&mut self, dst: u8, src: u8) {
95 self.emit(Instruction::abc(Opcode::Neg, dst, src, 0));
96 }
97
98 pub fn emit_jump(&mut self, target: Label) {
99 let idx = self.emit(Instruction::only_imm(Opcode::Jump, 0));
100 self.pending_jumps.push((idx, target));
101 }
102
103 pub fn emit_branch(&mut self, cond: u8, target: Label) {
104 let idx = self.emit(Instruction::a_imm(Opcode::Branch, cond, 0));
105 self.pending_jumps.push((idx, target));
106 }
107
108 pub fn emit_spawn(&mut self, dst: u8, function: u32, argc: u8) {
109 self.emit_spawn_with_rights(dst, function, argc, crate::bytecode::CapRights::FLOW);
110 }
111
112 pub fn emit_spawn_with_rights(
115 &mut self,
116 dst: u8,
117 function: u32,
118 argc: u8,
119 rights: crate::bytecode::CapRights,
120 ) {
121 self.emit(Instruction::new(
122 Opcode::Spawn,
123 dst,
124 argc,
125 rights.bits_u8(),
126 function as i32,
127 ));
128 }
129
130 pub fn emit_delegate(&mut self, dst: u8, src_cap: u8, rights: crate::bytecode::CapRights) {
131 self.emit(Instruction::new(
132 Opcode::Delegate,
133 dst,
134 src_cap,
135 255,
136 rights.bits() as i32,
137 ));
138 }
139
140 pub fn emit_yield(&mut self) {
141 self.emit(Instruction::nullary(Opcode::Yield));
142 }
143
144 pub fn emit_sleep(&mut self, millis_reg: u8) {
145 self.emit(Instruction::abc(Opcode::Sleep, millis_reg, 0, 0));
146 }
147
148 pub fn emit_exit(&mut self, reg: u8) {
149 self.emit(Instruction::abc(Opcode::Exit, reg, 0, 0));
150 }
151
152 pub fn emit_self_pid(&mut self, dst: u8) {
154 self.emit(Instruction::abc(Opcode::SelfPid, dst, 0, 0));
155 }
156
157 pub fn emit_fresh_request_id(&mut self, dst: u8) {
159 self.emit(Instruction::abc(Opcode::FreshRequestId, dst, 0, 0));
160 }
161
162 pub fn emit_send(&mut self, target_cap_reg: u8, msg_reg: u8) {
164 self.emit(Instruction::abc(Opcode::Send, target_cap_reg, msg_reg, 0));
165 }
166
167 pub fn emit_receive(&mut self, dst: u8) {
168 self.emit(Instruction::abc(Opcode::Receive, dst, 0, 0));
169 }
170
171 pub fn emit_receive_timeout(&mut self, dst: u8, millis_reg: u8) {
172 self.emit(Instruction::abc(Opcode::ReceiveTimeout, dst, millis_reg, 0));
173 }
174
175 pub fn emit_receive_match(&mut self, dst: u8, tag_reg: u8) {
177 self.emit(Instruction::abc(Opcode::ReceiveMatch, dst, tag_reg, 0));
178 }
179
180 pub fn emit_receive_match_imm(&mut self, dst: u8, tag: u16) {
182 self.emit(Instruction::a_imm(Opcode::ReceiveMatchImm, dst, i32::from(tag)));
183 }
184
185 pub fn emit_receive_match_corr(&mut self, dst: u8, tag_reg: u8, id_reg: u8) {
187 self.emit(Instruction::abc(Opcode::ReceiveMatchCorr, dst, tag_reg, id_reg));
188 }
189
190 pub fn emit_receive_match_corr_imm(&mut self, dst: u8, tag: u16, id_reg: u8) {
192 self.emit(Instruction::new(
193 Opcode::ReceiveMatchCorrImm,
194 dst,
195 id_reg,
196 0,
197 i32::from(tag),
198 ));
199 }
200
201 pub fn emit_ask(&mut self, dest: u8, target_cap_reg: u8, msg_reg: u8) {
209 self.emit(Instruction::abc(Opcode::Ask, dest, target_cap_reg, msg_reg));
210 }
211
212 pub fn emit_ask_timeout(
214 &mut self,
215 dest: u8,
216 target_cap_reg: u8,
217 msg_reg: u8,
218 millis_reg: u8,
219 ) {
220 self.emit(Instruction::new(
221 Opcode::AskTimeout,
222 dest,
223 target_cap_reg,
224 msg_reg,
225 i32::from(millis_reg),
226 ));
227 }
228
229 pub fn emit_monitor(&mut self, dest: u8, target_cap_reg: u8) {
230 self.emit(Instruction::abc(Opcode::Monitor, dest, target_cap_reg, 0));
231 }
232
233 pub fn emit_demonitor(&mut self, monitor_reg: u8) {
234 self.emit(Instruction::abc(Opcode::Demonitor, monitor_reg, 0, 0));
235 }
236
237 pub fn emit_link(&mut self, dest: u8, target_cap_reg: u8) {
238 self.emit(Instruction::abc(Opcode::Link, dest, target_cap_reg, 0));
239 }
240
241 pub fn emit_unlink(&mut self, link_reg: u8) {
242 self.emit(Instruction::abc(Opcode::Unlink, link_reg, 0, 0));
243 }
244
245 pub fn emit_register_name(&mut self, name_reg: u8) {
246 self.emit(Instruction::abc(Opcode::RegisterName, name_reg, 0, 0));
247 }
248
249 pub fn emit_whereis(&mut self, dst: u8, name_reg: u8) {
250 self.emit(Instruction::abc(Opcode::Whereis, dst, name_reg, 0));
251 }
252
253 pub fn emit_trap(&mut self, code: i32) {
254 self.emit(Instruction::only_imm(Opcode::Trap, code));
255 }
256
257 pub fn emit_call(&mut self, dst: u8, function: u32, argc: u8) {
258 self.emit(Instruction::new(Opcode::Call, dst, argc, 0, function as i32));
259 }
260
261 pub fn emit_call_native(&mut self, dst: u8, native_index: u32, argc: u8) {
268 self.emit(Instruction::new(
269 Opcode::CallNative,
270 dst,
271 argc,
272 0,
273 native_index as i32,
274 ));
275 }
276
277 pub fn emit_native1_from(&mut self, dst: u8, src: u8, native_index: u32) {
299 self.emit_move(dst, src);
300 self.emit_call_native(dst, native_index, 1);
301 }
302
303 pub fn emit_native_n(&mut self, base: u8, native_index: u32, argc: u8) {
309 self.emit_call_native(base, native_index, argc);
310 }
311
312 pub fn emit_return(&mut self, reg: u8) {
313 self.emit(Instruction::abc(Opcode::Return, reg, 0, 0));
314 }
315
316 pub fn begin_function(&mut self, name: impl Into<String>, arity: u8, num_registers: u8) -> u32 {
322 let name = name.into();
323 let entry = self.code.len() as u32;
324 let idx = self.functions.len() as u32;
325 self.functions.push(FunctionDef {
326 name: name.clone(),
327 entry,
328 arity,
329 num_registers,
330 });
331 self.fn_starts.insert(name, idx);
332 idx
333 }
334
335 pub fn function_index(&self, name: &str) -> Option<u32> {
336 self.fn_starts.get(name).copied()
337 }
338
339 pub fn set_num_registers(&mut self, function_index: u32, num_registers: u8) {
344 if let Some(def) = self.functions.get_mut(function_index as usize) {
345 def.num_registers = num_registers;
346 }
347 }
348
349 pub fn finish(mut self) -> Chunk {
357 for (idx, label) in self.pending_jumps.drain(..) {
358 match self.label_targets.get(&label) {
359 Some(target) => {
360 let offset = *target as i64 - (idx as i64 + 1);
362 self.code[idx].imm = offset as i32;
363 }
364 None => {
365 self.code[idx].imm = 0;
367 }
368 }
369 }
370 Chunk {
371 name: self.name,
372 constants: self.constants,
373 code: self.code,
374 functions: self.functions,
375 }
376 }
377}
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn emit_native1_from_never_clobbers_source_register() {
384 let mut b = ChunkBuilder::new("clobber-test");
385 b.begin_function("main", 0, 8);
386 b.emit_native1_from(1, 0, 10);
387 b.emit_native1_from(2, 0, 11);
388 b.emit_native1_from(3, 0, 12);
389 let chunk = b.finish();
390
391 assert_eq!(chunk.code.len(), 6);
392 for (move_idx, call_idx, expected_native) in
393 [(0usize, 1usize, 10i32), (2, 3, 11), (4, 5, 12)]
394 {
395 assert_eq!(chunk.code[move_idx].op, Opcode::Move);
396 assert_eq!(chunk.code[move_idx].b, 0);
397 assert_eq!(chunk.code[call_idx].op, Opcode::CallNative);
398 assert_ne!(chunk.code[call_idx].a, 0);
399 assert_eq!(chunk.code[call_idx].imm, expected_native);
400 }
401 }
402
403 #[test]
404 fn emit_native_n_is_a_plain_call_native_with_no_extra_instructions() {
405 let mut b = ChunkBuilder::new("native-n-test");
406 b.begin_function("main", 0, 8);
407 b.emit_load_imm(1, 7);
408 b.emit_load_imm(2, 1);
409 b.emit_native_n(1, 99, 2);
410 let chunk = b.finish();
411
412 assert_eq!(chunk.code.len(), 3);
413 assert_eq!(chunk.code[2].op, Opcode::CallNative);
414 assert_eq!(chunk.code[2].a, 1);
415 assert_eq!(chunk.code[2].b, 2);
416 assert_eq!(chunk.code[2].imm, 99);
417 }
418
419 #[test]
420 fn macro_forms_produce_identical_bytecode_to_the_methods() {
421 let mut via_method = ChunkBuilder::new("via-method");
422 via_method.begin_function("main", 0, 8);
423 via_method.emit_native1_from(1, 0, 10);
424 via_method.emit_native_n(1, 99, 2);
425
426 let mut via_macro = ChunkBuilder::new("via-macro");
427 via_macro.begin_function("main", 0, 8);
428 crate::emit_native1_from!(via_macro, 1, 0, 10);
429 crate::emit_native_n!(via_macro, 1, 99, 2);
430
431 assert_eq!(via_method.finish().code, via_macro.finish().code);
432 }
433}