1use swamp_vm_types::opcode::OpCode;
2use swamp_vm_types::{
3 BinaryInstruction, ConstantMemoryAddress, CountU16, FrameMemoryAddress,
4 FrameMemoryAddressIndirectPointer, FrameMemorySize, InstructionPosition, MemoryAddress,
5 MemorySize,
6};
7
8#[derive(Debug)]
9pub struct PatchPosition(pub InstructionPosition);
10
11pub struct InstructionBuilder {
12 pub instructions: Vec<BinaryInstruction>,
13 pub comments: Vec<String>,
14}
15
16impl Default for InstructionBuilder {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl InstructionBuilder {
23 #[must_use]
24 pub const fn new() -> Self {
25 Self {
26 instructions: Vec::new(),
27 comments: Vec::new(),
28 }
29 }
30
31 #[must_use]
32 pub fn position(&self) -> InstructionPosition {
33 InstructionPosition(self.instructions.len() as u16)
34 }
35
36 pub fn add_jmp_if_equal_placeholder(&mut self, comment: &str) -> PatchPosition {
37 let position = self.position();
38
39 self.add_instruction(OpCode::Bz, &[0], comment);
40
41 PatchPosition(position)
42 }
43
44 pub fn add_jmp_if_not_equal_placeholder(&mut self, comment: &str) -> PatchPosition {
45 let position = self.position();
46
47 self.add_instruction(OpCode::Bnz, &[0], comment);
48
49 PatchPosition(position)
50 }
51
52 pub fn add_vec_iter_next_placeholder(
53 &mut self,
54 iterator_target: FrameMemoryAddress,
55 closure_variable: FrameMemoryAddress,
56 comment: &str,
57 ) -> PatchPosition {
58 let position = self.position();
59 self.add_instruction(
60 OpCode::VecIterNext,
61 &[iterator_target.0, closure_variable.0, 0],
62 comment,
63 );
64 PatchPosition(position)
65 }
66
67 pub fn add_vec_iter_next_pair_placeholder(
68 &mut self,
69 iterator_target: FrameMemoryAddress,
70 closure_variable: FrameMemoryAddress,
71 closure_variable_b: FrameMemoryAddress,
72 comment: &str,
73 ) -> PatchPosition {
74 let position = self.position();
75 self.add_instruction(
76 OpCode::VecIterNextPair,
77 &[
78 iterator_target.0,
79 closure_variable.0,
80 closure_variable_b.0,
81 0,
82 ],
83 comment,
84 );
85 PatchPosition(position)
86 }
87
88 pub fn add_eq_u8_immediate(
89 &mut self,
90 source_addr: FrameMemoryAddress,
91 immediate: u8,
92 comment: &str,
93 ) {
94 self.add_instruction(OpCode::Eq8Imm, &[source_addr.0, immediate as u16], comment);
95 }
96
97 pub fn add_eq_32(
98 &mut self,
99 addr_a: FrameMemoryAddress,
100 addr_b: FrameMemoryAddress,
101 comment: &str,
102 ) {
103 self.add_instruction(OpCode::Eq32, &[addr_a.0, addr_b.0], comment);
104 }
105
106 pub fn add_call_placeholder(&mut self, comment: &str) -> PatchPosition {
107 let position = self.position();
108 self.add_instruction(OpCode::Call, &[0], comment);
109 PatchPosition(position)
110 }
111
112 pub fn add_jump_placeholder(&mut self, comment: &str) -> PatchPosition {
113 let position = self.position();
114
115 self.add_instruction(OpCode::Jmp, &[0], comment);
116
117 PatchPosition(position)
118 }
119
120 pub fn add_enter(&mut self, size: FrameMemorySize, comment: &str) {
121 self.add_instruction(OpCode::Enter, &[size.0], comment);
122 }
123
124 pub fn add_mov(
126 &mut self,
127 target: FrameMemoryAddress,
128 source: FrameMemoryAddress,
129 size: MemorySize,
130 comment: &str,
131 ) {
132 self.add_instruction(OpCode::Mov, &[target.0, source.0, size.0], comment);
133 }
134
135 pub fn add_movlp(
137 &mut self,
138 target: FrameMemoryAddress,
139 source: FrameMemoryAddress,
140 size: MemorySize,
141 comment: &str,
142 ) {
143 self.add_instruction(OpCode::MovLp, &[target.0, source.0, size.0], comment);
144 }
145
146 pub fn add_ret(&mut self, comment: &str) {
147 self.add_instruction(OpCode::Ret, &[], comment);
148 }
149
150 pub fn add_hlt(&mut self, comment: &str) {
151 self.add_instruction(OpCode::Hlt, &[], comment);
152 }
153
154 pub fn add_call(&mut self, function_ip: &InstructionPosition, comment: &str) {
155 self.add_instruction(OpCode::Call, &[function_ip.0], comment);
156 }
157
158 pub fn add_host_call(
159 &mut self,
160 host_function_id: u16,
161 arguments_size: MemorySize,
162 comment: &str,
163 ) {
164 self.add_instruction(
165 OpCode::HostCall,
166 &[host_function_id, arguments_size.0],
167 comment,
168 );
169 }
170
171 pub fn patch_jump(
174 &mut self,
175 patch_position: PatchPosition,
176 target_position: &InstructionPosition,
177 ) {
178 const JMP_IF_NOT: u8 = OpCode::Bz as u8;
179 const JMP_IF: u8 = OpCode::Bnz as u8;
180 const JMP: u8 = OpCode::Jmp as u8;
181
182 const VEC_ITER_NEXT: u8 = OpCode::VecIterNext as u8;
183 const VEC_ITER_NEXT_PAIR: u8 = OpCode::VecIterNextPair as u8;
184 const MAP_ITER_NEXT: u8 = OpCode::MapIterNext as u8;
185 const MAP_ITER_NEXT_PAIR: u8 = OpCode::MapIterNextPair as u8;
186
187 let instruction = &mut self.instructions[patch_position.0.0 as usize];
188
189 match instruction.opcode {
190 JMP_IF_NOT => {
191 instruction.operands[0] = target_position.0 as u16 - 1;
192 }
193 JMP_IF => {
194 instruction.operands[0] = target_position.0 as u16 - 1;
195 }
196 JMP => {
197 instruction.operands[0] = target_position.0 as u16 - 1;
198 }
199
200 VEC_ITER_NEXT => {
201 instruction.operands[2] = target_position.0 as u16 - 1;
202 }
203
204 MAP_ITER_NEXT => {
205 instruction.operands[2] = target_position.0 as u16 - 1;
206 }
207
208 VEC_ITER_NEXT_PAIR => {
209 instruction.operands[3] = target_position.0 as u16 - 1;
210 }
211
212 MAP_ITER_NEXT_PAIR => {
213 instruction.operands[3] = target_position.0 as u16 - 1;
214 }
215 _ => panic!("Attempted to patch a non-jump instruction at position {patch_position:?}"),
216 }
217 }
218
219 pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
221 self.patch_jump(jump_position, &self.position());
222 }
223
224 pub fn patch_call(&mut self, patch_position: PatchPosition, ip: &InstructionPosition) {
227 const CALL: u8 = OpCode::Call as u8;
228
229 let instruction = &mut self.instructions[patch_position.0.0 as usize];
230
231 match instruction.opcode {
232 CALL => {
233 instruction.operands[0] = ip.0 as u16 - 1;
234 }
235 _ => panic!("Attempted to patch a non-call instruction at position {patch_position:?}"),
236 }
237 }
238
239 pub fn add_jmp(&mut self, ip: InstructionPosition, comment: &str) {
240 self.add_instruction(OpCode::Jmp, &[ip.0 - 1], comment);
241 }
242
243 pub fn add_map_iter_init(
244 &mut self,
245 iterator_target: FrameMemoryAddress,
246 pointer_to_map: FrameMemoryAddressIndirectPointer,
247 comment: &str,
248 ) {
249 self.add_instruction(
250 OpCode::MapIterInit,
251 &[iterator_target.0, pointer_to_map.0.0],
252 comment,
253 );
254 }
255
256 pub fn add_map_iter_next(
257 &mut self,
258 iterator_target: FrameMemoryAddress,
259 closure_variable: FrameMemoryAddress,
260 instruction_position: InstructionPosition,
261 comment: &str,
262 ) {
263 self.add_instruction(
264 OpCode::MapIterNext,
265 &[
266 iterator_target.0,
267 closure_variable.0,
268 instruction_position.0,
269 ],
270 comment,
271 );
272 }
273
274 pub fn add_map_iter_next_pair(
275 &mut self,
276 iterator_target: FrameMemoryAddress,
277 closure_variable_key: FrameMemoryAddress,
278 closure_variable_value: FrameMemoryAddress,
279 instruction_position: InstructionPosition,
280 comment: &str,
281 ) {
282 self.add_instruction(
283 OpCode::MapIterNextPair,
284 &[
285 iterator_target.0,
286 closure_variable_key.0,
287 closure_variable_value.0,
288 instruction_position.0,
289 ],
290 comment,
291 );
292 }
293
294 pub fn add_string_from_constant_slice(
295 &mut self,
296 target_string: FrameMemoryAddress,
297 constant_addr: ConstantMemoryAddress,
298 byte_count: MemorySize,
299 comment: &str,
300 ) {
301 let (lower_bits, upper_bits) = Self::convert_to_lower_and_upper(constant_addr.0);
302
303 self.add_instruction(
304 OpCode::StringFromConstantSlice,
305 &[target_string.0, lower_bits, upper_bits, byte_count.0],
306 comment,
307 );
308 }
309
310 pub fn add_string_append(
311 &mut self,
312 dst_offset: FrameMemoryAddress,
313 lhs_offset: FrameMemoryAddress,
314 rhs_offset: FrameMemoryAddress,
315 comment: &str,
316 ) {
317 self.add_instruction(
318 OpCode::StringAppend,
319 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
320 comment,
321 );
322 }
323
324 pub fn add_string_len(
325 &mut self,
326 len_target: FrameMemoryAddress,
327 indirect: FrameMemoryAddressIndirectPointer,
328 comment: &str,
329 ) {
330 self.add_instruction(OpCode::StringLen, &[len_target.0, indirect.0.0], comment);
331 }
332
333 pub fn add_vec_from_slice(
334 &mut self,
335 target: FrameMemoryAddress,
336 source_slice: FrameMemoryAddress,
337 element_size: MemorySize,
338 element_count: CountU16,
339 comment: &str,
340 ) {
341 self.add_instruction(
342 OpCode::VecFromSlice,
343 &[target.0, source_slice.0, element_size.0, element_count.0],
344 comment,
345 );
346 }
347
348 pub fn add_vec_iter_init(
349 &mut self,
350 iterator_target: FrameMemoryAddress,
351 pointer_to_vec: FrameMemoryAddressIndirectPointer,
352 comment: &str,
353 ) {
354 self.add_instruction(
355 OpCode::VecIterInit,
356 &[iterator_target.0, pointer_to_vec.0.0],
357 comment,
358 );
359 }
360
361 pub fn add_vec_iter_next(
362 &mut self,
363 iterator_target: FrameMemoryAddress,
364 closure_variable: FrameMemoryAddress,
365 instruction_position: InstructionPosition,
366 comment: &str,
367 ) {
368 self.add_instruction(
369 OpCode::VecIterNext,
370 &[
371 iterator_target.0,
372 closure_variable.0,
373 instruction_position.0,
374 ],
375 comment,
376 );
377 }
378
379 pub fn add_vec_iter_next_pair(
380 &mut self,
381 iterator_target: FrameMemoryAddress,
382 closure_variable_key: FrameMemoryAddress,
383 closure_variable_value: FrameMemoryAddress,
384 instruction_position: InstructionPosition,
385 comment: &str,
386 ) {
387 self.add_instruction(
388 OpCode::VecIterNextPair,
389 &[
390 iterator_target.0,
391 closure_variable_key.0,
392 closure_variable_value.0,
393 instruction_position.0,
394 ],
395 comment,
396 );
397 }
398
399 fn convert_to_lower_and_upper(data: u32) -> (u16, u16) {
400 let lower_bits = (data & 0xFFFF) as u16;
401 let upper_bits = (data >> 16) as u16;
402
403 (lower_bits, upper_bits)
404 }
405
406 pub fn add_ld32(&mut self, dst_offset: FrameMemoryAddress, value: i32, comment: &str) {
407 let (lower_bits, upper_bits) = Self::convert_to_lower_and_upper(value as u32);
408
409 self.add_instruction(
410 OpCode::Ld32,
411 &[dst_offset.0, lower_bits, upper_bits],
412 comment,
413 );
414 }
415
416 pub fn add_ld_constant(
417 &mut self,
418 target_addr: FrameMemoryAddress,
419 constant_addr: ConstantMemoryAddress,
420 size: MemorySize,
421 comment: &str,
422 ) {
423 let value_u32 = constant_addr.0;
424
425 let lower_bits = (value_u32 & 0xFFFF) as u16;
426 let upper_bits = (value_u32 >> 16) as u16;
427
428 self.add_instruction(
429 OpCode::LdConst,
430 &[target_addr.0, lower_bits, upper_bits, size.0],
431 comment,
432 );
433 }
434
435 pub fn add_ld8(&mut self, dst_offset: FrameMemoryAddress, value: u8, comment: &str) {
436 self.add_instruction(OpCode::Ld8, &[dst_offset.0, value as u16], comment);
437 }
438
439 pub fn add_add_i32(
440 &mut self,
441 dst_offset: FrameMemoryAddress,
442 lhs_offset: FrameMemoryAddress,
443 rhs_offset: FrameMemoryAddress,
444 comment: &str,
445 ) {
446 self.add_instruction(
447 OpCode::AddI32,
448 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
449 comment,
450 );
451 }
452
453 pub fn add_add_f32(
454 &mut self,
455 dst_offset: FrameMemoryAddress,
456 lhs_offset: FrameMemoryAddress,
457 rhs_offset: FrameMemoryAddress,
458 comment: &str,
459 ) {
460 self.add_instruction(
461 OpCode::AddF32,
462 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
463 comment,
464 );
465 }
466
467 pub fn add_mul_i32(
468 &mut self,
469 dst_offset: FrameMemoryAddress,
470 lhs_offset: FrameMemoryAddress,
471 rhs_offset: FrameMemoryAddress,
472 comment: &str,
473 ) {
474 self.add_instruction(
475 OpCode::MulI32,
476 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
477 comment,
478 );
479 }
480
481 pub fn add_neg_i32(
482 &mut self,
483 target: FrameMemoryAddress,
484 source: FrameMemoryAddress,
485 comment: &str,
486 ) {
487 self.add_instruction(OpCode::NegI32, &[target.0, source.0], comment);
488 }
489
490 pub fn add_neg_f32(
491 &mut self,
492 target: FrameMemoryAddress,
493 source: FrameMemoryAddress,
494 comment: &str,
495 ) {
496 self.add_instruction(OpCode::NegF32, &[target.0, source.0], comment);
497 }
498
499 pub fn add_jmp_if(
500 &mut self,
501 condition_offset: FrameMemoryAddress,
502 jmp_target: &InstructionPosition,
503 comment: &str,
504 ) {
505 self.add_instruction(OpCode::Bnz, &[condition_offset.0, jmp_target.0], comment);
506 }
507
508 pub fn add_jmp_if_not(
509 &mut self,
510 condition_offset: MemoryAddress,
511 jmp_target: InstructionPosition,
512 comment: &str,
513 ) {
514 self.add_instruction(OpCode::Bz, &[condition_offset.0, jmp_target.0], comment);
515 }
516
517 pub fn add_lt_i32(
518 &mut self,
519 lhs_offset: FrameMemoryAddress,
520 rhs_offset: FrameMemoryAddress,
521 comment: &str,
522 ) {
523 self.add_instruction(OpCode::LtI32, &[lhs_offset.0, rhs_offset.0], comment);
524 }
525
526 pub fn add_gt_i32(
527 &mut self,
528 lhs_offset: FrameMemoryAddress,
529 rhs_offset: FrameMemoryAddress,
530 comment: &str,
531 ) {
532 self.add_instruction(OpCode::GtI32, &[lhs_offset.0, rhs_offset.0], comment);
533 }
534
535 pub fn add_tst8(&mut self, addr: FrameMemoryAddress, comment: &str) {
536 self.add_instruction(OpCode::Tst8, &[addr.0], comment);
537 }
538
539 pub fn add_map_new_from_slice(
541 &mut self,
542 map_target_addr: FrameMemoryAddress,
543 slice_source_addr: FrameMemoryAddress,
544 key_size: MemorySize,
545 value_size: MemorySize,
546 count: CountU16,
547 comment: &str,
548 ) {
549 self.add_instruction(
550 OpCode::MapNewFromPairs,
551 &[
552 map_target_addr.0,
553 slice_source_addr.0,
554 key_size.0,
555 value_size.0,
556 count.0,
557 ],
558 comment,
559 );
560 }
561
562 pub fn add_map_remove(
563 &mut self,
564 map_target_addr: FrameMemoryAddress,
565 key_addr: FrameMemoryAddress,
566 comment: &str,
567 ) {
568 self.add_instruction(OpCode::MapRemove, &[map_target_addr.0, key_addr.0], comment);
569 }
570
571 fn add_instruction(&mut self, op_code: OpCode, operands: &[u16], comment: &str) {
572 let mut array: [u16; 5] = [0; 5];
573 assert!(operands.len() <= 5);
574 let len = operands.len();
575 array[..len].copy_from_slice(&operands[..len]);
576 self.instructions.push(BinaryInstruction {
577 opcode: op_code as u8,
578 operands: array,
579 });
580 self.comments.push(comment.to_string());
581 }
582 pub fn add_ld_u16(&mut self, dest: FrameMemoryAddress, data: u16, comment: &str) {
583 self.add_instruction(OpCode::Ld16, &[dest.0, data], comment);
584 }
585
586 }