1use swamp_vm_types::opcode::OpCode;
2use swamp_vm_types::{
3 BinaryInstruction, FrameMemoryAddress, FrameMemorySize, InstructionPosition, MemoryAddress,
4 MemoryOffset, 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 add_alloc(&mut self, target: FrameMemoryAddress, size: MemorySize, comment: &str) {
97 self.add_instruction(OpCode::Alloc, &[target.0, size.0], comment);
98 }
99
100 pub fn patch_jump(
103 &mut self,
104 patch_position: PatchPosition,
105 target_position: &InstructionPosition,
106 ) {
107 const JMP_IF_NOT: u8 = OpCode::Bz as u8;
108 const JMP: u8 = OpCode::Jmp as u8;
109
110 let instruction = &mut self.instructions[patch_position.0.0 as usize];
111
112 match instruction.opcode {
113 JMP_IF_NOT => {
114 instruction.operands[1] = target_position.0 as u16 - 1;
116 }
117 JMP => {
118 instruction.operands[0] = target_position.0 as u16 - 1;
121 }
122 _ => panic!("Attempted to patch a non-jump instruction at position {patch_position:?}"),
123 }
124 }
125
126 pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
128 self.patch_jump(jump_position, &self.position());
129 }
130
131 pub fn patch_call(&mut self, patch_position: PatchPosition, ip: &InstructionPosition) {
134 const CALL: u8 = OpCode::Call as u8;
135
136 let instruction = &mut self.instructions[patch_position.0.0 as usize];
137
138 match instruction.opcode {
139 CALL => {
140 instruction.operands[0] = ip.0 as u16 - 1;
141 }
142 _ => panic!("Attempted to patch a non-call instruction at position {patch_position:?}"),
143 }
144 }
145
146 pub fn add_jmp(&mut self, ip: InstructionPosition, comment: &str) {
147 self.add_instruction(OpCode::Jmp, &[ip.0 - 1], comment);
148 }
149
150 pub fn add_ld32(&mut self, dst_offset: FrameMemoryAddress, value: i32, comment: &str) {
151 let value_u32 = value as u32;
152
153 let lower_bits = (value_u32 & 0xFFFF) as u16;
154 let upper_bits = (value_u32 >> 16) as u16;
155
156 self.add_instruction(
157 OpCode::Ld32,
158 &[dst_offset.0, lower_bits, upper_bits],
159 comment,
160 );
161 }
162
163 pub fn add_ld8(&mut self, dst_offset: FrameMemoryAddress, value: u8, comment: &str) {
164 self.add_instruction(OpCode::Ld8, &[dst_offset.0, value as u16], comment);
165 }
166
167 pub fn add_load_frame_address(
168 &mut self,
169 dest: FrameMemoryAddress,
170 addr: FrameMemoryAddress,
171 comment: &str,
172 ) {
173 self.add_ld_u16(dest, addr.0, comment);
174 }
175
176 pub fn add_ld_u16(&mut self, dest: FrameMemoryAddress, data: u16, comment: &str) {
177 self.add_instruction(OpCode::Ld16, &[dest.0, data], comment);
178 }
179
180 pub fn add_stx(
181 &mut self,
182 indirect_target: FrameMemoryAddress,
183 offset: MemoryOffset,
184 source_address: FrameMemoryAddress,
185 size: MemorySize,
186 comment: &str,
187 ) {
188 self.add_instruction(
189 OpCode::Stx,
190 &[indirect_target.0, offset.0, source_address.0, size.0],
191 comment,
192 );
193 }
194
195 pub fn add_add_i32(
196 &mut self,
197 dst_offset: FrameMemoryAddress,
198 lhs_offset: FrameMemoryAddress,
199 rhs_offset: FrameMemoryAddress,
200 comment: &str,
201 ) {
202 self.add_instruction(
203 OpCode::AddI32,
204 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
205 comment,
206 );
207 }
208
209 pub fn add_jmp_if(
210 &mut self,
211 condition_offset: FrameMemoryAddress,
212 jmp_target: &InstructionPosition,
213 comment: &str,
214 ) {
215 self.add_instruction(OpCode::Bnz, &[condition_offset.0, jmp_target.0], comment);
216 }
217
218 pub fn add_jmp_if_not(
219 &mut self,
220 condition_offset: MemoryAddress,
221 jmp_target: InstructionPosition,
222 comment: &str,
223 ) {
224 self.add_instruction(OpCode::Bz, &[condition_offset.0, jmp_target.0], comment);
225 }
226
227 pub fn add_lt_i32(
228 &mut self,
229 dst_offset: FrameMemoryAddress,
230 lhs_offset: FrameMemoryAddress,
231 rhs_offset: FrameMemoryAddress,
232 comment: &str,
233 ) {
234 self.add_instruction(
235 OpCode::LtI32,
236 &[dst_offset.0, lhs_offset.0, rhs_offset.0],
237 comment,
238 );
239 }
240
241 pub fn add_lt_u16(
242 &mut self,
243 dest: FrameMemoryAddress,
244 a: FrameMemoryAddress,
245 b: FrameMemoryAddress,
246 comment: &str,
247 ) {
248 self.add_instruction(OpCode::LtU16, &[dest.0, a.0, b.0], comment);
249 }
250
251 fn add_instruction(&mut self, op_code: OpCode, operands: &[u16], comment: &str) {
252 let mut array: [u16; 4] = [0; 4];
253 let len = operands.len().min(4);
254 array[..len].copy_from_slice(&operands[..len]);
255 self.instructions.push(BinaryInstruction {
256 opcode: op_code as u8,
257 opcode_count: operands.len() as u8,
258 operands: array,
259 });
260 self.comments.push(comment.to_string());
261 }
262}