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