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 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    // Mov is more of a copy. Keeping the name Mov because it is old school and idiomatic.
125    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    // for overlap moves
136    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    /// # Panics
172    ///
173    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    // It takes ownership of the patch position
220    pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
221        self.patch_jump(jump_position, &self.position());
222    }
223
224    /// # Panics
225    ///
226    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    // Collection specific
540    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    /*
587
588        pub fn add_lt_u16(
589        &mut self,
590        dest: FrameMemoryAddress,
591        a: FrameMemoryAddress,
592        b: FrameMemoryAddress,
593        comment: &str,
594    ) {
595        self.add_instruction(OpCode::LtU16, &[dest.0, a.0, b.0], comment);
596    }
597
598
599    pub fn add_stx(
600        &mut self,
601        indirect_target: FrameMemoryAddress,
602        offset: MemoryOffset,
603        source_address: FrameMemoryAddress,
604        size: MemorySize,
605        comment: &str,
606    ) {
607        self.add_instruction(
608            OpCode::Stx,
609            &[indirect_target.0, offset.0, source_address.0, size.0],
610            comment,
611        );
612    }
613    pub fn add_alloc(&mut self, target: FrameMemoryAddress, size: MemorySize, comment: &str) {
614        self.add_instruction(OpCode::Alloc, &[target.0, size.0], comment);
615    }
616
617
618        pub fn add_load_frame_address(
619        &mut self,
620        dest: FrameMemoryAddress,
621        addr: FrameMemoryAddress,
622        comment: &str,
623    ) {
624        self.add_ld_u16(dest, addr.0, comment);
625    }
626
627    */
628}