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