1use 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 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 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 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 pub fn patch_jump_here(&mut self, jump_position: PatchPosition) {
226 self.patch_jump(jump_position, &self.position());
227 }
228
229 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 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 }