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; pub 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 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 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 instruction.operands[1] = target_position.0 as u16 - 1;
112 }
113 JMP => {
114 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 pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
124 self.patch_jump(jump_position, &self.position());
125 }
126
127 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}