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 struct ChunkBuilder {
26 name: String,
27 constants: Vec<Value>,
28 code: Vec<Instruction>,
29 functions: Vec<FunctionDef>,
30 next_label: u32,
31 label_targets: HashMap<Label, u32>,
32 pending_jumps: Vec<(usize, Label)>,
34 fn_starts: HashMap<String, u32>,
35}
36
37impl ChunkBuilder {
38 pub fn new(name: impl Into<String>) -> Self {
39 ChunkBuilder {
40 name: name.into(),
41 constants: Vec::new(),
42 code: Vec::new(),
43 functions: Vec::new(),
44 next_label: 0,
45 label_targets: HashMap::new(),
46 pending_jumps: Vec::new(),
47 fn_starts: HashMap::new(),
48 }
49 }
50
51 pub fn const_(&mut self, v: Value) -> u32 {
52 if let Some(pos) = self.constants.iter().position(|c| c == &v) {
55 return pos as u32;
56 }
57 self.constants.push(v);
58 (self.constants.len() - 1) as u32
59 }
60
61 pub fn new_label(&mut self) -> Label {
62 let l = Label(self.next_label);
63 self.next_label += 1;
64 l
65 }
66
67 pub fn bind_label(&mut self, label: Label) {
69 self.label_targets.insert(label, self.code.len() as u32);
70 }
71
72 fn emit(&mut self, instr: Instruction) -> usize {
73 self.code.push(instr);
74 self.code.len() - 1
75 }
76
77 pub fn emit_halt(&mut self) {
78 self.emit(Instruction::nullary(Opcode::Halt));
79 }
80
81 pub fn emit_load_const(&mut self, dst: u8, konst: u32) {
82 self.emit(Instruction::new(Opcode::LoadConst, dst, 0, 0, konst as i32));
83 }
84
85 pub fn emit_load_imm(&mut self, dst: u8, imm: i32) {
86 self.emit(Instruction::a_imm(Opcode::LoadImm, dst, imm));
87 }
88
89 pub fn emit_move(&mut self, dst: u8, src: u8) {
90 self.emit(Instruction::abc(Opcode::Move, dst, src, 0));
91 }
92
93 pub fn emit_binop(&mut self, op: Opcode, dst: u8, lhs: u8, rhs: u8) {
94 debug_assert!(matches!(
95 op,
96 Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Div | Opcode::Mod
97 | Opcode::Eq | Opcode::Lt | Opcode::Le
98 ));
99 self.emit(Instruction::abc(op, dst, lhs, rhs));
100 }
101
102 pub fn emit_neg(&mut self, dst: u8, src: u8) {
103 self.emit(Instruction::abc(Opcode::Neg, dst, src, 0));
104 }
105
106 pub fn emit_jump(&mut self, target: Label) {
107 let idx = self.emit(Instruction::only_imm(Opcode::Jump, 0));
108 self.pending_jumps.push((idx, target));
109 }
110
111 pub fn emit_branch(&mut self, cond: u8, target: Label) {
112 let idx = self.emit(Instruction::a_imm(Opcode::Branch, cond, 0));
113 self.pending_jumps.push((idx, target));
114 }
115
116 pub fn emit_spawn(&mut self, dst: u8, function: u32, argc: u8) {
117 self.emit(Instruction::new(Opcode::Spawn, dst, argc, 0, function as i32));
118 }
119
120 pub fn emit_yield(&mut self) {
121 self.emit(Instruction::nullary(Opcode::Yield));
122 }
123
124 pub fn emit_sleep(&mut self, millis_reg: u8) {
125 self.emit(Instruction::abc(Opcode::Sleep, millis_reg, 0, 0));
126 }
127
128 pub fn emit_exit(&mut self, reg: u8) {
129 self.emit(Instruction::abc(Opcode::Exit, reg, 0, 0));
130 }
131
132 pub fn emit_self_pid(&mut self, dst: u8) {
134 self.emit(Instruction::abc(Opcode::SelfPid, dst, 0, 0));
135 }
136
137 pub fn emit_send(&mut self, target_cap_reg: u8, msg_reg: u8) {
139 self.emit(Instruction::abc(Opcode::Send, target_cap_reg, msg_reg, 0));
140 }
141
142 pub fn emit_receive(&mut self, dst: u8) {
143 self.emit(Instruction::abc(Opcode::Receive, dst, 0, 0));
144 }
145
146 pub fn emit_receive_timeout(&mut self, dst: u8, millis_reg: u8) {
147 self.emit(Instruction::abc(Opcode::ReceiveTimeout, dst, millis_reg, 0));
148 }
149
150 pub fn emit_receive_match(&mut self, dst: u8, tag_reg: u8) {
152 self.emit(Instruction::abc(Opcode::ReceiveMatch, dst, tag_reg, 0));
153 }
154
155 pub fn emit_receive_match_imm(&mut self, dst: u8, tag: u16) {
157 self.emit(Instruction::a_imm(Opcode::ReceiveMatchImm, dst, i32::from(tag)));
158 }
159
160 pub fn emit_ask(&mut self, dest: u8, target_cap_reg: u8, msg_reg: u8) {
168 self.emit(Instruction::abc(Opcode::Ask, dest, target_cap_reg, msg_reg));
169 }
170
171 pub fn emit_trap(&mut self, code: i32) {
172 self.emit(Instruction::only_imm(Opcode::Trap, code));
173 }
174
175 pub fn emit_call(&mut self, dst: u8, function: u32, argc: u8) {
176 self.emit(Instruction::new(Opcode::Call, dst, argc, 0, function as i32));
177 }
178
179 pub fn emit_call_native(&mut self, dst: u8, native_index: u32, argc: u8) {
186 self.emit(Instruction::new(
187 Opcode::CallNative,
188 dst,
189 argc,
190 0,
191 native_index as i32,
192 ));
193 }
194
195 pub fn emit_native1_from(&mut self, dst: u8, src: u8, native_index: u32) {
217 self.emit_move(dst, src);
218 self.emit_call_native(dst, native_index, 1);
219 }
220
221 pub fn emit_native_n(&mut self, base: u8, native_index: u32, argc: u8) {
227 self.emit_call_native(base, native_index, argc);
228 }
229
230 pub fn emit_return(&mut self, reg: u8) {
231 self.emit(Instruction::abc(Opcode::Return, reg, 0, 0));
232 }
233
234 pub fn begin_function(&mut self, name: impl Into<String>, arity: u8, num_registers: u8) -> u32 {
240 let name = name.into();
241 let entry = self.code.len() as u32;
242 let idx = self.functions.len() as u32;
243 self.functions.push(FunctionDef {
244 name: name.clone(),
245 entry,
246 arity,
247 num_registers,
248 });
249 self.fn_starts.insert(name, idx);
250 idx
251 }
252
253 pub fn function_index(&self, name: &str) -> Option<u32> {
254 self.fn_starts.get(name).copied()
255 }
256
257 pub fn set_num_registers(&mut self, function_index: u32, num_registers: u8) {
262 if let Some(def) = self.functions.get_mut(function_index as usize) {
263 def.num_registers = num_registers;
264 }
265 }
266
267 pub fn finish(mut self) -> Chunk {
271 for (idx, label) in self.pending_jumps.drain(..) {
272 let target = match self.label_targets.get(&label) {
273 Some(t) => *t,
274 None => panic!(
275 "byteflow-bytecode: unbound label {label:?} in chunk '{}'",
276 self.name
277 ),
278 };
279 let offset = target as i64 - (idx as i64 + 1);
281 self.code[idx].imm = offset as i32;
282 }
283 Chunk {
284 name: self.name,
285 constants: self.constants,
286 code: self.code,
287 functions: self.functions,
288 }
289 }
290}
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn emit_native1_from_never_clobbers_source_register() {
297 let mut b = ChunkBuilder::new("clobber-test");
298 b.begin_function("main", 0, 8);
299 b.emit_native1_from(1, 0, 10);
300 b.emit_native1_from(2, 0, 11);
301 b.emit_native1_from(3, 0, 12);
302 let chunk = b.finish();
303
304 assert_eq!(chunk.code.len(), 6);
305 for (move_idx, call_idx, expected_native) in
306 [(0usize, 1usize, 10i32), (2, 3, 11), (4, 5, 12)]
307 {
308 assert_eq!(chunk.code[move_idx].op, Opcode::Move);
309 assert_eq!(chunk.code[move_idx].b, 0);
310 assert_eq!(chunk.code[call_idx].op, Opcode::CallNative);
311 assert_ne!(chunk.code[call_idx].a, 0);
312 assert_eq!(chunk.code[call_idx].imm, expected_native);
313 }
314 }
315
316 #[test]
317 fn emit_native_n_is_a_plain_call_native_with_no_extra_instructions() {
318 let mut b = ChunkBuilder::new("native-n-test");
319 b.begin_function("main", 0, 8);
320 b.emit_load_imm(1, 7);
321 b.emit_load_imm(2, 1);
322 b.emit_native_n(1, 99, 2);
323 let chunk = b.finish();
324
325 assert_eq!(chunk.code.len(), 3);
326 assert_eq!(chunk.code[2].op, Opcode::CallNative);
327 assert_eq!(chunk.code[2].a, 1);
328 assert_eq!(chunk.code[2].b, 2);
329 assert_eq!(chunk.code[2].imm, 99);
330 }
331
332 #[test]
333 fn macro_forms_produce_identical_bytecode_to_the_methods() {
334 let mut via_method = ChunkBuilder::new("via-method");
335 via_method.begin_function("main", 0, 8);
336 via_method.emit_native1_from(1, 0, 10);
337 via_method.emit_native_n(1, 99, 2);
338
339 let mut via_macro = ChunkBuilder::new("via-macro");
340 via_macro.begin_function("main", 0, 8);
341 crate::emit_native1_from!(via_macro, 1, 0, 10);
342 crate::emit_native_n!(via_macro, 1, 99, 2);
343
344 assert_eq!(via_method.finish().code, via_macro.finish().code);
345 }
346}