swamp_vm_instr_build/
lib.rs

1use swamp_vm_types::opcode::OpCode;
2use swamp_vm_types::{
3    BinaryInstruction, FrameMemoryAddress, FrameMemorySize, InstructionPosition, MemoryAddress,
4    MemorySize,
5};
6pub const INT_SIZE: u16 = 4;
7pub const FLOAT_SIZE: u16 = 4;
8pub const BOOL_SIZE: u16 = 1;
9pub const PTR_SIZE: u16 = 2;
10pub const STR_SIZE: u16 = VEC_SIZE; // TODO: FIX THIS
11pub const VEC_SIZE: u16 = 2 + 2 + 2;
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 InstructionBuilder {}
22
23impl Default for InstructionBuilder {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl InstructionBuilder {
30    #[must_use]
31    pub const fn new() -> Self {
32        Self {
33            instructions: Vec::new(),
34            comments: Vec::new(),
35        }
36    }
37
38    #[must_use]
39    pub fn position(&self) -> InstructionPosition {
40        InstructionPosition(self.instructions.len() as u16)
41    }
42
43    pub fn add_conditional_jump_placeholder(
44        &mut self,
45        condition_addr: FrameMemoryAddress,
46        comment: &str,
47    ) -> PatchPosition {
48        let position = self.position();
49
50        self.add_instruction(OpCode::Bz, &[condition_addr.0, 0], comment);
51
52        PatchPosition(position)
53    }
54
55    pub fn add_call_placeholder(&mut self, comment: &str) -> PatchPosition {
56        let position = self.position();
57        self.add_instruction(OpCode::Call, &[0], comment);
58        PatchPosition(position)
59    }
60
61    pub fn add_jump_placeholder(&mut self, comment: &str) -> PatchPosition {
62        let position = self.position();
63
64        self.add_instruction(OpCode::Jmp, &[0], comment);
65
66        PatchPosition(position)
67    }
68
69    pub fn add_enter(&mut self, size: FrameMemorySize, comment: &str) {
70        self.add_instruction(OpCode::Enter, &[size.0], comment);
71    }
72
73    // Mov is more of a copy. Keeping the name Mov because it is old school and idiomatic.
74    pub fn add_mov(
75        &mut self,
76        target: FrameMemoryAddress,
77        source: FrameMemoryAddress,
78        size: MemorySize,
79        comment: &str,
80    ) {
81        self.add_instruction(OpCode::Mov, &[target.0, source.0, size.0], comment);
82    }
83
84    pub fn add_ret(&mut self, comment: &str) {
85        self.add_instruction(OpCode::Ret, &[], comment);
86    }
87
88    pub fn add_hlt(&mut self, comment: &str) {
89        self.add_instruction(OpCode::Hlt, &[], comment);
90    }
91
92    pub fn add_call(&mut self, function_ip: &InstructionPosition, comment: &str) {
93        self.add_instruction(OpCode::Call, &[function_ip.0], comment);
94    }
95
96    /// # Panics
97    ///
98    pub fn patch_jump(
99        &mut self,
100        patch_position: PatchPosition,
101        target_position: &InstructionPosition,
102    ) {
103        const JMP_IF_NOT: u8 = OpCode::Bz as u8;
104        const JMP: u8 = OpCode::Jmp as u8;
105
106        let instruction = &mut self.instructions[patch_position.0.0 as usize];
107
108        match instruction.opcode {
109            JMP_IF_NOT => {
110                // For conditional jump, target ip addr is the second operand
111                instruction.operands[1] = target_position.0 as u16 - 1;
112            }
113            JMP => {
114                // For conditional jump, target ip addr is the first operand
115                // TODO: maybe have them both at the first operand?
116                instruction.operands[0] = target_position.0 as u16 - 1;
117            }
118            _ => panic!("Attempted to patch a non-jump instruction at position {patch_position:?}"),
119        }
120    }
121
122    // It takes ownership of the patch position
123    pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
124        self.patch_jump(jump_position, &self.position());
125    }
126
127    /// # Panics
128    ///
129    pub fn patch_call(&mut self, patch_position: PatchPosition, ip: &InstructionPosition) {
130        const CALL: u8 = OpCode::Call as u8;
131
132        let instruction = &mut self.instructions[patch_position.0.0 as usize];
133
134        match instruction.opcode {
135            CALL => {
136                instruction.operands[0] = ip.0 as u16 - 1;
137            }
138            _ => panic!("Attempted to patch a non-call instruction at position {patch_position:?}"),
139        }
140    }
141
142    pub fn add_jmp(&mut self, ip: InstructionPosition, comment: &str) {
143        self.add_instruction(OpCode::Jmp, &[ip.0 - 1], comment);
144    }
145
146    pub fn add_ld_local(&mut self, dst_offset: FrameMemoryAddress, src_offset: u16, comment: &str) {
147        self.add_instruction(OpCode::Ld, &[dst_offset.0, src_offset], comment);
148    }
149    pub fn add_st_local(&mut self, dst_offset: FrameMemoryAddress, src_offset: u16, comment: &str) {
150        self.add_instruction(OpCode::St, &[dst_offset.0, src_offset], comment);
151    }
152
153    pub fn add_ld_imm_i32(&mut self, dst_offset: FrameMemoryAddress, value: i32, comment: &str) {
154        let value_u32 = value as u32;
155
156        let lower_bits = (value_u32 & 0xFFFF) as u16;
157        let upper_bits = (value_u32 >> 16) as u16;
158
159        self.add_instruction(
160            OpCode::Ld32,
161            &[dst_offset.0, lower_bits, upper_bits],
162            comment,
163        );
164    }
165
166    pub fn add_ld_imm_u8(&mut self, dst_offset: FrameMemoryAddress, value: u8, comment: &str) {
167        self.add_instruction(OpCode::Ld8, &[dst_offset.0, value as u16], comment);
168    }
169
170    pub fn add_load_frame_address(
171        &mut self,
172        dest: FrameMemoryAddress,
173        addr: FrameMemoryAddress,
174        comment: &str,
175    ) {
176        self.add_ld_u16(dest, addr.0, comment);
177    }
178
179    pub fn add_ld_u16(&mut self, dest: FrameMemoryAddress, data: u16, comment: &str) {
180        self.add_instruction(OpCode::Ld16, &[dest.0, data], comment);
181    }
182
183    pub fn add_add_i32(
184        &mut self,
185        dst_offset: FrameMemoryAddress,
186        lhs_offset: FrameMemoryAddress,
187        rhs_offset: FrameMemoryAddress,
188        comment: &str,
189    ) {
190        self.add_instruction(
191            OpCode::AddI32,
192            &[dst_offset.0, lhs_offset.0, rhs_offset.0],
193            comment,
194        );
195    }
196
197    pub fn add_jmp_if(
198        &mut self,
199        condition_offset: FrameMemoryAddress,
200        jmp_target: &InstructionPosition,
201        comment: &str,
202    ) {
203        self.add_instruction(OpCode::Bnz, &[condition_offset.0, jmp_target.0], comment);
204    }
205
206    pub fn add_jmp_if_not(
207        &mut self,
208        condition_offset: MemoryAddress,
209        jmp_target: InstructionPosition,
210        comment: &str,
211    ) {
212        self.add_instruction(OpCode::Bz, &[condition_offset.0, jmp_target.0], comment);
213    }
214
215    pub fn add_lt_i32(
216        &mut self,
217        dst_offset: FrameMemoryAddress,
218        lhs_offset: FrameMemoryAddress,
219        rhs_offset: FrameMemoryAddress,
220        comment: &str,
221    ) {
222        self.add_instruction(
223            OpCode::LtI32,
224            &[dst_offset.0, lhs_offset.0, rhs_offset.0],
225            comment,
226        );
227    }
228
229    pub fn add_lt_u16(
230        &mut self,
231        dest: FrameMemoryAddress,
232        a: FrameMemoryAddress,
233        b: FrameMemoryAddress,
234        comment: &str,
235    ) {
236        self.add_instruction(OpCode::LtU16, &[dest.0, a.0, b.0], comment);
237    }
238
239    pub fn add_ld_indirect(
240        &mut self,
241        dest: FrameMemoryAddress,
242        base_ptr: FrameMemoryAddress,
243        offset: FrameMemoryAddress,
244        size: u16,
245        comment: &str,
246    ) {
247        self.add_instruction(
248            OpCode::LdIndirect,
249            &[dest.0, base_ptr.0, offset.0, size],
250            comment,
251        );
252    }
253
254    fn add_instruction(&mut self, op_code: OpCode, operands: &[u16], comment: &str) {
255        let mut array: [u16; 4] = [0; 4];
256        let len = operands.len().min(4);
257        array[..len].copy_from_slice(&operands[..len]);
258        self.instructions.push(BinaryInstruction {
259            opcode: op_code as u8,
260            opcode_count: operands.len() as u8,
261            operands: array,
262        });
263        self.comments.push(comment.to_string());
264    }
265}