swamp_vm_instr_build/
lib.rs

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