1use rustc_hash::FxHashMap;
11use sway_types::Ident;
12
13use crate::{
14 asm::{AsmArg, AsmBlock},
15 block::Block,
16 context::Context,
17 function::Function,
18 irtype::Type,
19 pretty::DebugWithContext,
20 value::{Value, ValueDatum},
21 variable::LocalVar,
22 AsmInstruction, ConstantContent, GlobalVar, Module, StorageKey,
23};
24
25#[derive(Debug, Clone, DebugWithContext)]
26pub struct BranchToWithArgs {
27 pub block: Block,
28 pub args: Vec<Value>,
29}
30
31#[derive(Debug, Clone, DebugWithContext)]
32pub struct Instruction {
33 pub parent: Block,
34 pub op: InstOp,
35}
36
37impl Instruction {
38 pub fn get_type(&self, context: &Context) -> Option<Type> {
39 self.op.get_type(context)
40 }
41 pub fn replace_values(&mut self, replace_map: &FxHashMap<Value, Value>) {
43 self.op.replace_values(replace_map)
44 }
45 pub fn get_function(&self, context: &Context) -> Function {
47 context.blocks[self.parent.0].function
48 }
49}
50
51#[derive(Debug, Clone, DebugWithContext)]
52pub enum InstOp {
53 AsmBlock(AsmBlock, Vec<AsmArg>),
55 UnaryOp {
57 op: UnaryOpKind,
58 arg: Value,
59 },
60 BinaryOp {
62 op: BinaryOpKind,
63 arg1: Value,
64 arg2: Value,
65 },
66 BitCast(Value, Type),
68 Branch(BranchToWithArgs),
70 Call(Function, Vec<Value>),
72 CastPtr(Value, Type),
74 Cmp(Predicate, Value, Value),
76 ConditionalBranch {
78 cond_value: Value,
79 true_block: BranchToWithArgs,
80 false_block: BranchToWithArgs,
81 },
82 ContractCall {
84 return_type: Type,
85 name: Option<String>,
86 params: Value,
87 coins: Value,
88 asset_id: Value,
89 gas: Value,
90 },
91 FuelVm(FuelVmInstruction),
93 GetLocal(LocalVar),
95 GetGlobal(GlobalVar),
97 GetConfig(Module, String),
99 GetStorageKey(StorageKey),
100 GetElemPtr {
102 base: Value,
103 elem_ptr_ty: Type,
104 indices: Vec<Value>,
105 },
106 IntToPtr(Value, Type),
108 Load(Value),
110 MemCopyBytes {
112 dst_val_ptr: Value,
113 src_val_ptr: Value,
114 byte_len: u64,
115 },
116 MemCopyVal {
118 dst_val_ptr: Value,
119 src_val_ptr: Value,
120 },
121 MemClearVal {
123 dst_val_ptr: Value,
124 },
125 Nop,
127 PtrToInt(Value, Type),
129 Ret(Value, Type),
131 Store {
133 dst_val_ptr: Value,
134 stored_val: Value,
135 },
136}
137
138#[derive(Debug, Clone, DebugWithContext)]
139pub enum FuelVmInstruction {
140 Gtf {
141 index: Value,
142 tx_field_id: u64,
143 },
144 Log {
146 log_val: Value,
147 log_ty: Type,
148 log_id: Value,
149 },
150 ReadRegister(Register),
152 Revert(Value),
154 Smo {
159 recipient: Value,
160 message: Value,
161 message_size: Value,
162 coins: Value,
163 },
164 StateClear {
166 key: Value,
167 number_of_slots: Value,
168 },
169 StateLoadQuadWord {
172 load_val: Value,
173 key: Value,
174 number_of_slots: Value,
175 },
176 StateLoadWord(Value),
178 StateStoreQuadWord {
181 stored_val: Value,
182 key: Value,
183 number_of_slots: Value,
184 },
185 StateStoreWord {
188 stored_val: Value,
189 key: Value,
190 },
191 WideUnaryOp {
192 op: UnaryOpKind,
193 result: Value,
194 arg: Value,
195 },
196 WideBinaryOp {
197 op: BinaryOpKind,
198 result: Value,
199 arg1: Value,
200 arg2: Value,
201 },
202 WideModularOp {
203 op: BinaryOpKind,
204 result: Value,
205 arg1: Value,
206 arg2: Value,
207 arg3: Value,
208 },
209 WideCmpOp {
210 op: Predicate,
211 arg1: Value,
212 arg2: Value,
213 },
214 JmpMem,
215 Retd {
216 ptr: Value,
217 len: Value,
218 },
219}
220
221#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
223pub enum Predicate {
224 Equal,
225 LessThan,
226 GreaterThan,
227}
228
229#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
230pub enum UnaryOpKind {
231 Not,
232}
233
234#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
235pub enum BinaryOpKind {
236 Add,
237 Sub,
238 Mul,
239 Div,
240 And,
241 Or,
242 Xor,
243 Mod,
244 Rsh,
245 Lsh,
246}
247
248#[derive(Debug, Clone, Copy, Hash)]
250pub enum Register {
251 Of,
253 Pc,
255 Ssp,
257 Sp,
259 Fp,
261 Hp,
263 Error,
265 Ggas,
267 Cgas,
269 Bal,
271 Is,
273 Ret,
275 Retl,
277 Flag,
279}
280
281impl InstOp {
282 pub fn get_type(&self, context: &Context) -> Option<Type> {
287 match self {
288 InstOp::AsmBlock(asm_block, _) => Some(asm_block.return_type),
290 InstOp::UnaryOp { arg, .. } => arg.get_type(context),
291 InstOp::BinaryOp { arg1, .. } => arg1.get_type(context),
292 InstOp::BitCast(_, ty) => Some(*ty),
293 InstOp::Call(function, _) => Some(context.functions[function.0].return_type),
294 InstOp::CastPtr(_val, ty) => Some(*ty),
295 InstOp::Cmp(..) => Some(Type::get_bool(context)),
296 InstOp::ContractCall { return_type, .. } => Some(*return_type),
297 InstOp::FuelVm(FuelVmInstruction::Gtf { .. }) => Some(Type::get_uint64(context)),
298 InstOp::FuelVm(FuelVmInstruction::Log { .. }) => Some(Type::get_unit(context)),
299 InstOp::FuelVm(FuelVmInstruction::ReadRegister(_)) => Some(Type::get_uint64(context)),
300 InstOp::FuelVm(FuelVmInstruction::Smo { .. }) => Some(Type::get_unit(context)),
301
302 InstOp::Load(ptr_val) => match &context.values[ptr_val.0].value {
304 ValueDatum::Argument(arg) => arg.ty.get_pointee_type(context),
305 ValueDatum::Constant(cons) => {
306 cons.get_content(context).ty.get_pointee_type(context)
307 }
308 ValueDatum::Instruction(ins) => ins
309 .get_type(context)
310 .and_then(|ty| ty.get_pointee_type(context)),
311 },
312
313 InstOp::GetElemPtr { elem_ptr_ty, .. } => Some(*elem_ptr_ty),
315 InstOp::GetLocal(local_var) => Some(local_var.get_type(context)),
316 InstOp::GetGlobal(global_var) => Some(global_var.get_type(context)),
317 InstOp::GetConfig(module, name) => Some(match module.get_config(context, name)? {
318 crate::ConfigContent::V0 { ptr_ty, .. } => *ptr_ty,
319 crate::ConfigContent::V1 { ptr_ty, .. } => *ptr_ty,
320 }),
321 InstOp::GetStorageKey(storage_key) => Some(storage_key.get_type(context)),
322
323 InstOp::IntToPtr(_, ptr_ty) => Some(*ptr_ty),
325 InstOp::PtrToInt(_, int_ty) => Some(*int_ty),
326
327 InstOp::Branch(_)
329 | InstOp::ConditionalBranch { .. }
330 | InstOp::FuelVm(
331 FuelVmInstruction::Revert(..)
332 | FuelVmInstruction::JmpMem
333 | FuelVmInstruction::Retd { .. },
334 )
335 | InstOp::Ret(..) => None,
336
337 InstOp::Nop => None,
339
340 InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_)) => Some(Type::get_uint64(context)),
342 InstOp::FuelVm(FuelVmInstruction::StateClear { .. })
343 | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
344 | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
345 | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. }) => {
346 Some(Type::get_bool(context))
347 }
348
349 InstOp::MemCopyBytes { .. }
351 | InstOp::MemCopyVal { .. }
352 | InstOp::MemClearVal { .. }
353 | InstOp::Store { .. } => Some(Type::get_unit(context)),
354
355 InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { result, .. }) => {
357 result.get_type(context)
358 }
359 InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { result, .. }) => {
360 result.get_type(context)
361 }
362 InstOp::FuelVm(FuelVmInstruction::WideCmpOp { .. }) => Some(Type::get_bool(context)),
363 InstOp::FuelVm(FuelVmInstruction::WideModularOp { result, .. }) => {
364 result.get_type(context)
365 }
366 }
367 }
368
369 pub fn get_operands(&self) -> Vec<Value> {
370 match self {
371 InstOp::AsmBlock(_, args) => args.iter().filter_map(|aa| aa.initializer).collect(),
372 InstOp::BitCast(v, _) => vec![*v],
373 InstOp::UnaryOp { op: _, arg } => vec![*arg],
374 InstOp::BinaryOp { op: _, arg1, arg2 } => vec![*arg1, *arg2],
375 InstOp::Branch(BranchToWithArgs { args, .. }) => args.clone(),
376 InstOp::Call(_, vs) => vs.clone(),
377 InstOp::CastPtr(val, _ty) => vec![*val],
378 InstOp::Cmp(_, lhs, rhs) => vec![*lhs, *rhs],
379 InstOp::ConditionalBranch {
380 cond_value,
381 true_block,
382 false_block,
383 } => {
384 let mut v = vec![*cond_value];
385 v.extend_from_slice(&true_block.args);
386 v.extend_from_slice(&false_block.args);
387 v
388 }
389 InstOp::ContractCall {
390 return_type: _,
391 name: _,
392 params,
393 coins,
394 asset_id,
395 gas,
396 } => vec![*params, *coins, *asset_id, *gas],
397 InstOp::GetElemPtr {
398 base,
399 elem_ptr_ty: _,
400 indices,
401 } => {
402 let mut vals = indices.clone();
403 vals.push(*base);
404 vals
405 }
406 InstOp::GetLocal(_local_var) => {
407 vec![]
409 }
410 InstOp::GetGlobal(_global_var) => {
411 vec![]
413 }
414 InstOp::GetConfig(_, _) => {
415 vec![]
417 }
418 InstOp::GetStorageKey(_) => {
419 vec![]
421 }
422 InstOp::IntToPtr(v, _) => vec![*v],
423 InstOp::Load(v) => vec![*v],
424 InstOp::MemCopyBytes {
425 dst_val_ptr,
426 src_val_ptr,
427 byte_len: _,
428 } => {
429 vec![*dst_val_ptr, *src_val_ptr]
430 }
431 InstOp::MemCopyVal {
432 dst_val_ptr,
433 src_val_ptr,
434 } => {
435 vec![*dst_val_ptr, *src_val_ptr]
436 }
437 InstOp::MemClearVal { dst_val_ptr } => {
438 vec![*dst_val_ptr]
439 }
440 InstOp::Nop => vec![],
441 InstOp::PtrToInt(v, _) => vec![*v],
442 InstOp::Ret(v, _) => vec![*v],
443 InstOp::Store {
444 dst_val_ptr,
445 stored_val,
446 } => {
447 vec![*dst_val_ptr, *stored_val]
448 }
449
450 InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
451 FuelVmInstruction::Gtf {
452 index,
453 tx_field_id: _,
454 } => vec![*index],
455 FuelVmInstruction::Log {
456 log_val, log_id, ..
457 } => vec![*log_val, *log_id],
458 FuelVmInstruction::ReadRegister(_) => vec![],
459 FuelVmInstruction::Revert(v) => vec![*v],
460 FuelVmInstruction::JmpMem => vec![],
461 FuelVmInstruction::Smo {
462 recipient,
463 message,
464 message_size,
465 coins,
466 } => vec![*recipient, *message, *message_size, *coins],
467 FuelVmInstruction::StateClear {
468 key,
469 number_of_slots,
470 } => vec![*key, *number_of_slots],
471 FuelVmInstruction::StateLoadQuadWord {
472 load_val,
473 key,
474 number_of_slots,
475 } => vec![*load_val, *key, *number_of_slots],
476 FuelVmInstruction::StateLoadWord(key) => vec![*key],
477 FuelVmInstruction::StateStoreQuadWord {
478 stored_val,
479 key,
480 number_of_slots,
481 } => {
482 vec![*stored_val, *key, *number_of_slots]
483 }
484 FuelVmInstruction::StateStoreWord { stored_val, key } => vec![*stored_val, *key],
485 FuelVmInstruction::WideUnaryOp { arg, result, .. } => vec![*result, *arg],
486 FuelVmInstruction::WideBinaryOp {
487 arg1, arg2, result, ..
488 } => vec![*result, *arg1, *arg2],
489 FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => vec![*arg1, *arg2],
490 FuelVmInstruction::WideModularOp {
491 result,
492 arg1,
493 arg2,
494 arg3,
495 ..
496 } => vec![*result, *arg1, *arg2, *arg3],
497 FuelVmInstruction::Retd { ptr, len } => {
498 vec![*ptr, *len]
499 }
500 },
501 }
502 }
503
504 pub fn set_operand(&mut self, replacement: Value, idx: usize) {
507 match self {
508 InstOp::AsmBlock(_, args) => {
509 let mut cur_idx = 0;
512 for arg in args.iter_mut() {
513 if let Some(_asm_arg) = arg.initializer {
514 if cur_idx == idx {
515 arg.initializer = Some(replacement);
516 return;
517 }
518 cur_idx += 1;
519 }
520 }
521 panic!("Invalid index for AsmBlock");
522 }
523 InstOp::BitCast(v, _) | InstOp::UnaryOp { arg: v, .. } => {
524 if idx == 0 {
525 *v = replacement;
526 } else {
527 panic!("Invalid index for Op");
528 }
529 }
530 InstOp::BinaryOp { op: _, arg1, arg2 } => {
531 if idx == 0 {
532 *arg1 = replacement;
533 } else if idx == 1 {
534 *arg2 = replacement;
535 } else {
536 panic!("Invalid index for BinaryOp");
537 }
538 }
539 InstOp::Branch(BranchToWithArgs { args, .. }) => {
540 if idx < args.len() {
541 args[idx] = replacement;
542 } else {
543 panic!("Invalid index for Branch");
544 }
545 }
546 InstOp::Call(_, vs) => {
547 if idx < vs.len() {
548 vs[idx] = replacement;
549 } else {
550 panic!("Invalid index for Call");
551 }
552 }
553 InstOp::CastPtr(val, _ty) => {
554 if idx == 0 {
555 *val = replacement;
556 } else {
557 panic!("Invalid index for CastPtr");
558 }
559 }
560 InstOp::Cmp(_, lhs, rhs) => {
561 if idx == 0 {
562 *lhs = replacement;
563 } else if idx == 1 {
564 *rhs = replacement;
565 } else {
566 panic!("Invalid index for Cmp");
567 }
568 }
569 InstOp::ConditionalBranch {
570 cond_value,
571 true_block,
572 false_block,
573 } => {
574 if idx == 0 {
575 *cond_value = replacement;
576 } else if idx - 1 < true_block.args.len() {
577 true_block.args[idx - 1] = replacement;
578 } else if idx - 1 - true_block.args.len() < false_block.args.len() {
579 false_block.args[idx - 1 - true_block.args.len()] = replacement;
580 } else {
581 panic!("Invalid index for ConditionalBranch");
582 }
583 }
584 InstOp::ContractCall {
585 return_type: _,
586 name: _,
587 params,
588 coins,
589 asset_id,
590 gas,
591 } => {
592 if idx == 0 {
593 *params = replacement;
594 } else if idx == 1 {
595 *coins = replacement;
596 } else if idx == 2 {
597 *asset_id = replacement;
598 } else if idx == 3 {
599 *gas = replacement;
600 } else {
601 panic!("Invalid index for ContractCall");
602 }
603 }
604 InstOp::GetElemPtr {
605 base,
606 elem_ptr_ty: _,
607 indices,
608 } => {
609 use std::cmp::Ordering;
610 match idx.cmp(&indices.len()) {
611 Ordering::Less => {
612 indices[idx] = replacement;
613 }
614 Ordering::Equal => {
615 *base = replacement;
616 }
617 Ordering::Greater => {
618 panic!("Invalid index for GetElemPtr");
619 }
620 }
621 }
622 InstOp::GetLocal(_local_var) => {
623 panic!("Invalid index for GetLocal");
625 }
626 InstOp::GetGlobal(_global_var) => {
627 panic!("Invalid index for GetGlobal");
629 }
630 InstOp::GetConfig(_, _) => {
631 panic!("Invalid index for GetConfig");
633 }
634 InstOp::GetStorageKey(_) => {
635 panic!("Invalid index for GetStorageKey");
637 }
638 InstOp::IntToPtr(v, _) => {
639 if idx == 0 {
640 *v = replacement;
641 } else {
642 panic!("Invalid index for IntToPtr");
643 }
644 }
645 InstOp::Load(v) => {
646 if idx == 0 {
647 *v = replacement;
648 } else {
649 panic!("Invalid index for Load");
650 }
651 }
652 InstOp::MemCopyBytes {
653 dst_val_ptr,
654 src_val_ptr,
655 byte_len: _,
656 } => {
657 if idx == 0 {
658 *dst_val_ptr = replacement;
659 } else if idx == 1 {
660 *src_val_ptr = replacement;
661 } else {
662 panic!("Invalid index for MemCopyBytes");
663 }
664 }
665 InstOp::MemCopyVal {
666 dst_val_ptr,
667 src_val_ptr,
668 } => {
669 if idx == 0 {
670 *dst_val_ptr = replacement;
671 } else if idx == 1 {
672 *src_val_ptr = replacement;
673 } else {
674 panic!("Invalid index for MemCopyVal");
675 }
676 }
677 InstOp::MemClearVal { dst_val_ptr } => {
678 if idx == 0 {
679 *dst_val_ptr = replacement;
680 } else {
681 panic!("Invalid index for MemClearVal");
682 }
683 }
684 InstOp::Nop => (),
685 InstOp::PtrToInt(v, _) => {
686 if idx == 0 {
687 *v = replacement;
688 } else {
689 panic!("Invalid index for PtrToInt");
690 }
691 }
692 InstOp::Ret(v, _) => {
693 if idx == 0 {
694 *v = replacement;
695 } else {
696 panic!("Invalid index for Ret");
697 }
698 }
699 InstOp::Store {
700 dst_val_ptr,
701 stored_val,
702 } => {
703 if idx == 0 {
704 *dst_val_ptr = replacement;
705 } else if idx == 1 {
706 *stored_val = replacement;
707 } else {
708 panic!("Invalid index for Store");
709 }
710 }
711
712 InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
713 FuelVmInstruction::Gtf {
714 index,
715 tx_field_id: _,
716 } => {
717 if idx == 0 {
718 *index = replacement;
719 } else {
720 panic!("Invalid index for Gtf");
721 }
722 }
723 FuelVmInstruction::Log {
724 log_val, log_id, ..
725 } => {
726 if idx == 0 {
727 *log_val = replacement;
728 } else if idx == 1 {
729 *log_id = replacement;
730 } else {
731 panic!("Invalid index for Log");
732 }
733 }
734 FuelVmInstruction::ReadRegister(_) => {
735 panic!("Invalid index for ReadRegister");
737 }
738 FuelVmInstruction::Revert(v) => {
739 if idx == 0 {
740 *v = replacement;
741 } else {
742 panic!("Invalid index for Revert");
743 }
744 }
745 FuelVmInstruction::JmpMem => {
746 panic!("Invalid index for JmpMem");
748 }
749 FuelVmInstruction::Smo {
750 recipient,
751 message,
752 message_size,
753 coins,
754 } => {
755 if idx == 0 {
756 *recipient = replacement;
757 } else if idx == 1 {
758 *message = replacement;
759 } else if idx == 2 {
760 *message_size = replacement;
761 } else if idx == 3 {
762 *coins = replacement;
763 } else {
764 panic!("Invalid index for Smo");
765 }
766 }
767 FuelVmInstruction::StateClear {
768 key,
769 number_of_slots,
770 } => {
771 if idx == 0 {
772 *key = replacement;
773 } else if idx == 1 {
774 *number_of_slots = replacement;
775 } else {
776 panic!("Invalid index for StateClear");
777 }
778 }
779 FuelVmInstruction::StateLoadQuadWord {
780 load_val,
781 key,
782 number_of_slots,
783 } => {
784 if idx == 0 {
785 *load_val = replacement;
786 } else if idx == 1 {
787 *key = replacement;
788 } else if idx == 2 {
789 *number_of_slots = replacement;
790 } else {
791 panic!("Invalid index for StateLoadQuadWord");
792 }
793 }
794 FuelVmInstruction::StateLoadWord(key) => {
795 if idx == 0 {
796 *key = replacement;
797 } else {
798 panic!("Invalid index for StateLoadWord");
799 }
800 }
801 FuelVmInstruction::StateStoreQuadWord {
802 stored_val,
803 key,
804 number_of_slots,
805 } => {
806 if idx == 0 {
807 *stored_val = replacement;
808 } else if idx == 1 {
809 *key = replacement;
810 } else if idx == 2 {
811 *number_of_slots = replacement;
812 } else {
813 panic!("Invalid index for StateStoreQuadWord");
814 }
815 }
816 FuelVmInstruction::StateStoreWord { stored_val, key } => {
817 if idx == 0 {
818 *stored_val = replacement;
819 } else if idx == 1 {
820 *key = replacement;
821 } else {
822 panic!("Invalid index for StateStoreWord");
823 }
824 }
825 FuelVmInstruction::WideUnaryOp { arg, result, .. } => {
826 if idx == 0 {
827 *result = replacement;
828 } else if idx == 1 {
829 *arg = replacement;
830 } else {
831 panic!("Invalid index for WideUnaryOp");
832 }
833 }
834 FuelVmInstruction::WideBinaryOp {
835 arg1, arg2, result, ..
836 } => {
837 if idx == 0 {
838 *result = replacement;
839 } else if idx == 1 {
840 *arg1 = replacement;
841 } else if idx == 2 {
842 *arg2 = replacement;
843 } else {
844 panic!("Invalid index for WideBinaryOp");
845 }
846 }
847 FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => {
848 if idx == 0 {
849 *arg1 = replacement;
850 } else if idx == 1 {
851 *arg2 = replacement;
852 } else {
853 panic!("Invalid index for WideCmpOp");
854 }
855 }
856 FuelVmInstruction::WideModularOp {
857 result,
858 arg1,
859 arg2,
860 arg3,
861 ..
862 } => {
863 if idx == 0 {
864 *result = replacement;
865 } else if idx == 1 {
866 *arg1 = replacement;
867 } else if idx == 2 {
868 *arg2 = replacement;
869 } else if idx == 3 {
870 *arg3 = replacement;
871 } else {
872 panic!("Invalid index for WideModularOp");
873 }
874 }
875 FuelVmInstruction::Retd { ptr, len } => {
876 if idx == 0 {
877 *ptr = replacement;
878 } else if idx == 1 {
879 *len = replacement;
880 } else {
881 panic!("Invalid index for Retd");
882 }
883 }
884 },
885 }
886 }
887
888 pub fn replace_values(&mut self, replace_map: &FxHashMap<Value, Value>) {
890 let replace = |val: &mut Value| {
891 while let Some(new_val) = replace_map.get(val) {
892 *val = *new_val;
893 }
894 };
895 match self {
896 InstOp::AsmBlock(_, args) => args
897 .iter_mut()
898 .for_each(|asm_arg| asm_arg.initializer.iter_mut().for_each(replace)),
899 InstOp::BitCast(value, _) => replace(value),
900 InstOp::UnaryOp { op: _, arg } => {
901 replace(arg);
902 }
903 InstOp::BinaryOp { op: _, arg1, arg2 } => {
904 replace(arg1);
905 replace(arg2);
906 }
907 InstOp::Branch(block) => {
908 block.args.iter_mut().for_each(replace);
909 }
910 InstOp::Call(_, args) => args.iter_mut().for_each(replace),
911 InstOp::CastPtr(val, _ty) => replace(val),
912 InstOp::Cmp(_, lhs_val, rhs_val) => {
913 replace(lhs_val);
914 replace(rhs_val);
915 }
916 InstOp::ConditionalBranch {
917 cond_value,
918 true_block,
919 false_block,
920 } => {
921 replace(cond_value);
922 true_block.args.iter_mut().for_each(replace);
923 false_block.args.iter_mut().for_each(replace);
924 }
925 InstOp::ContractCall {
926 params,
927 coins,
928 asset_id,
929 gas,
930 ..
931 } => {
932 replace(params);
933 replace(coins);
934 replace(asset_id);
935 replace(gas);
936 }
937 InstOp::GetLocal(_) => (),
938 InstOp::GetGlobal(_) => (),
939 InstOp::GetConfig(_, _) => (),
940 InstOp::GetStorageKey(_) => (),
941 InstOp::GetElemPtr {
942 base,
943 elem_ptr_ty: _,
944 indices,
945 } => {
946 replace(base);
947 indices.iter_mut().for_each(replace);
948 }
949 InstOp::IntToPtr(value, _) => replace(value),
950 InstOp::Load(ptr) => replace(ptr),
951 InstOp::MemCopyBytes {
952 dst_val_ptr,
953 src_val_ptr,
954 ..
955 } => {
956 replace(dst_val_ptr);
957 replace(src_val_ptr);
958 }
959 InstOp::MemCopyVal {
960 dst_val_ptr,
961 src_val_ptr,
962 } => {
963 replace(dst_val_ptr);
964 replace(src_val_ptr);
965 }
966 InstOp::MemClearVal { dst_val_ptr } => {
967 replace(dst_val_ptr);
968 }
969 InstOp::Nop => (),
970 InstOp::PtrToInt(value, _) => replace(value),
971 InstOp::Ret(ret_val, _) => replace(ret_val),
972 InstOp::Store {
973 stored_val,
974 dst_val_ptr,
975 } => {
976 replace(stored_val);
977 replace(dst_val_ptr);
978 }
979
980 InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
981 FuelVmInstruction::Gtf { index, .. } => replace(index),
982 FuelVmInstruction::Log {
983 log_val, log_id, ..
984 } => {
985 replace(log_val);
986 replace(log_id);
987 }
988 FuelVmInstruction::ReadRegister { .. } => (),
989 FuelVmInstruction::Revert(revert_val) => replace(revert_val),
990 FuelVmInstruction::JmpMem => (),
991 FuelVmInstruction::Smo {
992 recipient,
993 message,
994 message_size,
995 coins,
996 } => {
997 replace(recipient);
998 replace(message);
999 replace(message_size);
1000 replace(coins);
1001 }
1002 FuelVmInstruction::StateClear {
1003 key,
1004 number_of_slots,
1005 } => {
1006 replace(key);
1007 replace(number_of_slots);
1008 }
1009 FuelVmInstruction::StateLoadQuadWord {
1010 load_val,
1011 key,
1012 number_of_slots,
1013 } => {
1014 replace(load_val);
1015 replace(key);
1016 replace(number_of_slots);
1017 }
1018 FuelVmInstruction::StateLoadWord(key) => {
1019 replace(key);
1020 }
1021 FuelVmInstruction::StateStoreQuadWord {
1022 stored_val,
1023 key,
1024 number_of_slots,
1025 } => {
1026 replace(key);
1027 replace(stored_val);
1028 replace(number_of_slots);
1029 }
1030 FuelVmInstruction::StateStoreWord { stored_val, key } => {
1031 replace(key);
1032 replace(stored_val);
1033 }
1034 FuelVmInstruction::WideUnaryOp { arg, result, .. } => {
1035 replace(arg);
1036 replace(result);
1037 }
1038 FuelVmInstruction::WideBinaryOp {
1039 arg1, arg2, result, ..
1040 } => {
1041 replace(arg1);
1042 replace(arg2);
1043 replace(result);
1044 }
1045 FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => {
1046 replace(arg1);
1047 replace(arg2);
1048 }
1049 FuelVmInstruction::WideModularOp {
1050 result,
1051 arg1,
1052 arg2,
1053 arg3,
1054 ..
1055 } => {
1056 replace(result);
1057 replace(arg1);
1058 replace(arg2);
1059 replace(arg3);
1060 }
1061 FuelVmInstruction::Retd { ptr, len } => {
1062 replace(ptr);
1063 replace(len);
1064 }
1065 },
1066 }
1067 }
1068
1069 pub fn may_have_side_effect(&self) -> bool {
1070 match self {
1071 InstOp::AsmBlock(asm, _) => !asm.body.is_empty(),
1072 InstOp::Call(..)
1073 | InstOp::ContractCall { .. }
1074 | InstOp::FuelVm(FuelVmInstruction::Log { .. })
1075 | InstOp::FuelVm(FuelVmInstruction::Smo { .. })
1076 | InstOp::FuelVm(FuelVmInstruction::StateClear { .. })
1077 | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
1078 | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
1079 | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. })
1080 | InstOp::FuelVm(FuelVmInstruction::Revert(..))
1081 | InstOp::FuelVm(FuelVmInstruction::JmpMem)
1082 | InstOp::FuelVm(FuelVmInstruction::Retd { .. })
1083 | InstOp::MemCopyBytes { .. }
1084 | InstOp::MemCopyVal { .. }
1085 | InstOp::MemClearVal { .. }
1086 | InstOp::Store { .. }
1087 | InstOp::Ret(..)
1088 | InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { .. })
1089 | InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { .. })
1090 | InstOp::FuelVm(FuelVmInstruction::WideCmpOp { .. })
1091 | InstOp::FuelVm(FuelVmInstruction::WideModularOp { .. }) => true,
1092
1093 InstOp::UnaryOp { .. }
1094 | InstOp::BinaryOp { .. }
1095 | InstOp::BitCast(..)
1096 | InstOp::Branch(_)
1097 | InstOp::CastPtr { .. }
1098 | InstOp::Cmp(..)
1099 | InstOp::ConditionalBranch { .. }
1100 | InstOp::FuelVm(FuelVmInstruction::Gtf { .. })
1101 | InstOp::FuelVm(FuelVmInstruction::ReadRegister(_))
1102 | InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_))
1103 | InstOp::GetElemPtr { .. }
1104 | InstOp::GetLocal(_)
1105 | InstOp::GetGlobal(_)
1106 | InstOp::GetConfig(_, _)
1107 | InstOp::GetStorageKey(_)
1108 | InstOp::IntToPtr(..)
1109 | InstOp::Load(_)
1110 | InstOp::Nop
1111 | InstOp::PtrToInt(..) => false,
1112 }
1113 }
1114
1115 pub fn is_terminator(&self) -> bool {
1116 matches!(
1117 self,
1118 InstOp::Branch(_)
1119 | InstOp::ConditionalBranch { .. }
1120 | InstOp::Ret(..)
1121 | InstOp::FuelVm(
1122 FuelVmInstruction::Revert(..)
1123 | FuelVmInstruction::JmpMem
1124 | FuelVmInstruction::Retd { .. }
1125 )
1126 )
1127 }
1128}
1129
1130pub struct InstructionIterator {
1132 instructions: Vec<slotmap::DefaultKey>,
1133 next: usize,
1134 next_back: isize,
1135}
1136
1137impl InstructionIterator {
1138 pub fn new(context: &Context, block: &Block) -> Self {
1139 InstructionIterator {
1142 instructions: context.blocks[block.0]
1143 .instructions
1144 .iter()
1145 .map(|val| val.0)
1146 .collect(),
1147 next: 0,
1148 next_back: context.blocks[block.0].instructions.len() as isize - 1,
1149 }
1150 }
1151}
1152
1153impl Iterator for InstructionIterator {
1154 type Item = Value;
1155
1156 fn next(&mut self) -> Option<Value> {
1157 if self.next < self.instructions.len() {
1158 let idx = self.next;
1159 self.next += 1;
1160 Some(Value(self.instructions[idx]))
1161 } else {
1162 None
1163 }
1164 }
1165}
1166
1167impl DoubleEndedIterator for InstructionIterator {
1168 fn next_back(&mut self) -> Option<Value> {
1169 if self.next_back >= 0 {
1170 let idx = self.next_back;
1171 self.next_back -= 1;
1172 Some(Value(self.instructions[idx as usize]))
1173 } else {
1174 None
1175 }
1176 }
1177}
1178
1179pub enum InsertionPosition {
1181 Start,
1183 End,
1185 After(Value),
1187 Before(Value),
1189 At(usize),
1191}
1192
1193pub struct InstructionInserter<'a, 'eng> {
1195 context: &'a mut Context<'eng>,
1196 block: Block,
1197 position: InsertionPosition,
1198}
1199
1200macro_rules! insert_instruction {
1201 ($self: ident, $ctor: expr) => {{
1202 let instruction_val = Value::new_instruction($self.context, $self.block, $ctor);
1203 let pos = $self.get_position_index();
1204 let instructions = &mut $self.context.blocks[$self.block.0].instructions;
1205 instructions.insert(pos, instruction_val);
1206 instruction_val
1207 }};
1208}
1209
1210impl<'a, 'eng> InstructionInserter<'a, 'eng> {
1211 pub fn new(
1213 context: &'a mut Context<'eng>,
1214 block: Block,
1215 position: InsertionPosition,
1216 ) -> InstructionInserter<'a, 'eng> {
1217 InstructionInserter {
1218 context,
1219 block,
1220 position,
1221 }
1222 }
1223
1224 fn get_position_index(&self) -> usize {
1226 let instructions = &self.context.blocks[self.block.0].instructions;
1227 match self.position {
1228 InsertionPosition::Start => 0,
1229 InsertionPosition::End => instructions.len(),
1230 InsertionPosition::After(inst) => {
1231 instructions
1232 .iter()
1233 .position(|val| *val == inst)
1234 .expect("Provided position for insertion does not exist")
1235 + 1
1236 }
1237 InsertionPosition::Before(inst) => instructions
1238 .iter()
1239 .position(|val| *val == inst)
1240 .expect("Provided position for insertion does not exist"),
1241 InsertionPosition::At(pos) => pos,
1242 }
1243 }
1244
1245 pub fn insert_slice(&mut self, slice: &[Value]) {
1247 let pos = self.get_position_index();
1248 self.context.blocks[self.block.0]
1249 .instructions
1250 .splice(pos..pos, slice.iter().cloned());
1251 }
1252
1253 pub fn insert(&mut self, inst: Value) {
1255 let pos = self.get_position_index();
1256 self.context.blocks[self.block.0]
1257 .instructions
1258 .insert(pos, inst);
1259 }
1260
1261 pub fn asm_block(
1267 self,
1268 args: Vec<AsmArg>,
1269 body: Vec<AsmInstruction>,
1270 return_type: Type,
1271 return_name: Option<Ident>,
1272 ) -> Value {
1273 let asm = AsmBlock::new(
1274 args.iter().map(|arg| arg.name.clone()).collect(),
1275 body,
1276 return_type,
1277 return_name,
1278 );
1279 self.asm_block_from_asm(asm, args)
1280 }
1281
1282 pub fn asm_block_from_asm(self, asm: AsmBlock, args: Vec<AsmArg>) -> Value {
1283 insert_instruction!(self, InstOp::AsmBlock(asm, args))
1284 }
1285
1286 pub fn bitcast(self, value: Value, ty: Type) -> Value {
1287 insert_instruction!(self, InstOp::BitCast(value, ty))
1288 }
1289
1290 pub fn unary_op(self, op: UnaryOpKind, arg: Value) -> Value {
1291 insert_instruction!(self, InstOp::UnaryOp { op, arg })
1292 }
1293
1294 pub fn wide_unary_op(self, op: UnaryOpKind, arg: Value, result: Value) -> Value {
1295 insert_instruction!(
1296 self,
1297 InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { op, arg, result })
1298 )
1299 }
1300
1301 pub fn wide_binary_op(
1302 self,
1303 op: BinaryOpKind,
1304 arg1: Value,
1305 arg2: Value,
1306 result: Value,
1307 ) -> Value {
1308 insert_instruction!(
1309 self,
1310 InstOp::FuelVm(FuelVmInstruction::WideBinaryOp {
1311 op,
1312 arg1,
1313 arg2,
1314 result
1315 })
1316 )
1317 }
1318
1319 pub fn wide_modular_op(
1320 self,
1321 op: BinaryOpKind,
1322 result: Value,
1323 arg1: Value,
1324 arg2: Value,
1325 arg3: Value,
1326 ) -> Value {
1327 insert_instruction!(
1328 self,
1329 InstOp::FuelVm(FuelVmInstruction::WideModularOp {
1330 op,
1331 result,
1332 arg1,
1333 arg2,
1334 arg3,
1335 })
1336 )
1337 }
1338
1339 pub fn wide_cmp_op(self, op: Predicate, arg1: Value, arg2: Value) -> Value {
1340 insert_instruction!(
1341 self,
1342 InstOp::FuelVm(FuelVmInstruction::WideCmpOp { op, arg1, arg2 })
1343 )
1344 }
1345
1346 pub fn binary_op(self, op: BinaryOpKind, arg1: Value, arg2: Value) -> Value {
1347 insert_instruction!(self, InstOp::BinaryOp { op, arg1, arg2 })
1348 }
1349
1350 pub fn branch(self, to_block: Block, dest_params: Vec<Value>) -> Value {
1351 let br_val = Value::new_instruction(
1352 self.context,
1353 self.block,
1354 InstOp::Branch(BranchToWithArgs {
1355 block: to_block,
1356 args: dest_params,
1357 }),
1358 );
1359 to_block.add_pred(self.context, &self.block);
1360 self.context.blocks[self.block.0].instructions.push(br_val);
1361 br_val
1362 }
1363
1364 pub fn call(self, function: Function, args: &[Value]) -> Value {
1365 insert_instruction!(self, InstOp::Call(function, args.to_vec()))
1366 }
1367
1368 pub fn cast_ptr(self, val: Value, ty: Type) -> Value {
1369 insert_instruction!(self, InstOp::CastPtr(val, ty))
1370 }
1371
1372 pub fn cmp(self, pred: Predicate, lhs_value: Value, rhs_value: Value) -> Value {
1373 insert_instruction!(self, InstOp::Cmp(pred, lhs_value, rhs_value))
1374 }
1375
1376 pub fn conditional_branch(
1377 self,
1378 cond_value: Value,
1379 true_block: Block,
1380 false_block: Block,
1381 true_dest_params: Vec<Value>,
1382 false_dest_params: Vec<Value>,
1383 ) -> Value {
1384 let cbr_val = Value::new_instruction(
1385 self.context,
1386 self.block,
1387 InstOp::ConditionalBranch {
1388 cond_value,
1389 true_block: BranchToWithArgs {
1390 block: true_block,
1391 args: true_dest_params,
1392 },
1393 false_block: BranchToWithArgs {
1394 block: false_block,
1395 args: false_dest_params,
1396 },
1397 },
1398 );
1399 true_block.add_pred(self.context, &self.block);
1400 false_block.add_pred(self.context, &self.block);
1401 self.context.blocks[self.block.0].instructions.push(cbr_val);
1402 cbr_val
1403 }
1404
1405 pub fn contract_call(
1406 self,
1407 return_type: Type,
1408 name: Option<String>,
1409 params: Value,
1410 coins: Value, asset_id: Value, gas: Value, ) -> Value {
1414 insert_instruction!(
1415 self,
1416 InstOp::ContractCall {
1417 return_type,
1418 name,
1419 params,
1420 coins,
1421 asset_id,
1422 gas,
1423 }
1424 )
1425 }
1426
1427 pub fn gtf(self, index: Value, tx_field_id: u64) -> Value {
1428 insert_instruction!(
1429 self,
1430 InstOp::FuelVm(FuelVmInstruction::Gtf { index, tx_field_id })
1431 )
1432 }
1433
1434 pub fn get_elem_ptr(self, base: Value, elem_ty: Type, indices: Vec<Value>) -> Value {
1437 let elem_ptr_ty = Type::new_typed_pointer(self.context, elem_ty);
1438 insert_instruction!(
1439 self,
1440 InstOp::GetElemPtr {
1441 base,
1442 elem_ptr_ty,
1443 indices
1444 }
1445 )
1446 }
1447
1448 pub fn get_elem_ptr_with_idx(self, base: Value, elem_ty: Type, index: u64) -> Value {
1449 let idx_val = ConstantContent::get_uint(self.context, 64, index);
1450 self.get_elem_ptr(base, elem_ty, vec![idx_val])
1451 }
1452
1453 pub fn get_elem_ptr_with_idcs(self, base: Value, elem_ty: Type, indices: &[u64]) -> Value {
1454 let idx_vals = indices
1455 .iter()
1456 .map(|idx| ConstantContent::get_uint(self.context, 64, *idx))
1457 .collect();
1458 self.get_elem_ptr(base, elem_ty, idx_vals)
1459 }
1460
1461 pub fn get_local(self, local_var: LocalVar) -> Value {
1462 insert_instruction!(self, InstOp::GetLocal(local_var))
1463 }
1464
1465 pub fn get_global(self, global_var: GlobalVar) -> Value {
1466 insert_instruction!(self, InstOp::GetGlobal(global_var))
1467 }
1468
1469 pub fn get_config(self, module: Module, name: String) -> Value {
1470 insert_instruction!(self, InstOp::GetConfig(module, name))
1471 }
1472
1473 pub fn get_storage_key(self, storage_key: StorageKey) -> Value {
1474 insert_instruction!(self, InstOp::GetStorageKey(storage_key))
1475 }
1476
1477 pub fn int_to_ptr(self, value: Value, ty: Type) -> Value {
1478 insert_instruction!(self, InstOp::IntToPtr(value, ty))
1479 }
1480
1481 pub fn load(self, src_val: Value) -> Value {
1482 insert_instruction!(self, InstOp::Load(src_val))
1483 }
1484
1485 pub fn log(self, log_val: Value, log_ty: Type, log_id: Value) -> Value {
1486 insert_instruction!(
1487 self,
1488 InstOp::FuelVm(FuelVmInstruction::Log {
1489 log_val,
1490 log_ty,
1491 log_id
1492 })
1493 )
1494 }
1495
1496 pub fn mem_copy_bytes(self, dst_val_ptr: Value, src_val_ptr: Value, byte_len: u64) -> Value {
1497 insert_instruction!(
1498 self,
1499 InstOp::MemCopyBytes {
1500 dst_val_ptr,
1501 src_val_ptr,
1502 byte_len
1503 }
1504 )
1505 }
1506
1507 pub fn mem_copy_val(self, dst_val_ptr: Value, src_val_ptr: Value) -> Value {
1508 insert_instruction!(
1509 self,
1510 InstOp::MemCopyVal {
1511 dst_val_ptr,
1512 src_val_ptr,
1513 }
1514 )
1515 }
1516
1517 pub fn nop(self) -> Value {
1518 insert_instruction!(self, InstOp::Nop)
1519 }
1520
1521 pub fn ptr_to_int(self, value: Value, ty: Type) -> Value {
1522 insert_instruction!(self, InstOp::PtrToInt(value, ty))
1523 }
1524
1525 pub fn read_register(self, reg: Register) -> Value {
1526 insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::ReadRegister(reg)))
1527 }
1528
1529 pub fn ret(self, value: Value, ty: Type) -> Value {
1530 insert_instruction!(self, InstOp::Ret(value, ty))
1531 }
1532
1533 pub fn retd(self, ptr: Value, len: Value) -> Value {
1534 insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::Retd { ptr, len }))
1535 }
1536
1537 pub fn revert(self, value: Value) -> Value {
1538 let revert_val = Value::new_instruction(
1539 self.context,
1540 self.block,
1541 InstOp::FuelVm(FuelVmInstruction::Revert(value)),
1542 );
1543 self.context.blocks[self.block.0]
1544 .instructions
1545 .push(revert_val);
1546 revert_val
1547 }
1548
1549 pub fn jmp_mem(self) -> Value {
1550 let ldc_exec = Value::new_instruction(
1551 self.context,
1552 self.block,
1553 InstOp::FuelVm(FuelVmInstruction::JmpMem),
1554 );
1555 self.context.blocks[self.block.0]
1556 .instructions
1557 .push(ldc_exec);
1558 ldc_exec
1559 }
1560
1561 pub fn smo(self, recipient: Value, message: Value, message_size: Value, coins: Value) -> Value {
1562 insert_instruction!(
1563 self,
1564 InstOp::FuelVm(FuelVmInstruction::Smo {
1565 recipient,
1566 message,
1567 message_size,
1568 coins,
1569 })
1570 )
1571 }
1572
1573 pub fn state_clear(self, key: Value, number_of_slots: Value) -> Value {
1574 insert_instruction!(
1575 self,
1576 InstOp::FuelVm(FuelVmInstruction::StateClear {
1577 key,
1578 number_of_slots
1579 })
1580 )
1581 }
1582
1583 pub fn state_load_quad_word(
1584 self,
1585 load_val: Value,
1586 key: Value,
1587 number_of_slots: Value,
1588 ) -> Value {
1589 insert_instruction!(
1590 self,
1591 InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord {
1592 load_val,
1593 key,
1594 number_of_slots
1595 })
1596 )
1597 }
1598
1599 pub fn state_load_word(self, key: Value) -> Value {
1600 insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::StateLoadWord(key)))
1601 }
1602
1603 pub fn state_store_quad_word(
1604 self,
1605 stored_val: Value,
1606 key: Value,
1607 number_of_slots: Value,
1608 ) -> Value {
1609 insert_instruction!(
1610 self,
1611 InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord {
1612 stored_val,
1613 key,
1614 number_of_slots
1615 })
1616 )
1617 }
1618
1619 pub fn state_store_word(self, stored_val: Value, key: Value) -> Value {
1620 insert_instruction!(
1621 self,
1622 InstOp::FuelVm(FuelVmInstruction::StateStoreWord { stored_val, key })
1623 )
1624 }
1625
1626 pub fn store(self, dst_val_ptr: Value, stored_val: Value) -> Value {
1627 insert_instruction!(
1628 self,
1629 InstOp::Store {
1630 dst_val_ptr,
1631 stored_val,
1632 }
1633 )
1634 }
1635
1636 pub fn mem_clear_val(self, dst_val_ptr: Value) -> Value {
1637 insert_instruction!(self, InstOp::MemClearVal { dst_val_ptr })
1638 }
1639}