swamp_vm_instr_build/
lib.rs

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