1use std::fmt;
2
3use fe_analyzer::namespace::items::ContractId;
4use id_arena::Id;
5
6use super::{basic_block::BasicBlockId, function::FunctionId, value::ValueId, SourceInfo, TypeId};
7
8pub type InstId = Id<Inst>;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Inst {
12 pub kind: InstKind,
13 pub source: SourceInfo,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub enum InstKind {
18 Declare {
21 local: ValueId,
22 },
23
24 Unary {
26 op: UnOp,
27 value: ValueId,
28 },
29
30 Binary {
32 op: BinOp,
33 lhs: ValueId,
34 rhs: ValueId,
35 },
36
37 Cast {
38 kind: CastKind,
39 value: ValueId,
40 to: TypeId,
41 },
42
43 AggregateConstruct {
45 ty: TypeId,
46 args: Vec<ValueId>,
47 },
48
49 Bind {
50 src: ValueId,
51 },
52
53 MemCopy {
54 src: ValueId,
55 },
56
57 Load {
59 src: ValueId,
60 },
61
62 AggregateAccess {
72 value: ValueId,
73 indices: Vec<ValueId>,
74 },
75
76 MapAccess {
77 key: ValueId,
78 value: ValueId,
79 },
80
81 Call {
82 func: FunctionId,
83 args: Vec<ValueId>,
84 call_type: CallType,
85 },
86
87 Jump {
89 dest: BasicBlockId,
90 },
91
92 Branch {
94 cond: ValueId,
95 then: BasicBlockId,
96 else_: BasicBlockId,
97 },
98
99 Switch {
100 disc: ValueId,
101 table: SwitchTable,
102 default: Option<BasicBlockId>,
103 },
104
105 Revert {
106 arg: Option<ValueId>,
107 },
108
109 Emit {
110 arg: ValueId,
111 },
112
113 Return {
114 arg: Option<ValueId>,
115 },
116
117 Keccak256 {
118 arg: ValueId,
119 },
120
121 AbiEncode {
122 arg: ValueId,
123 },
124
125 Nop,
126
127 Create {
128 value: ValueId,
129 contract: ContractId,
130 },
131
132 Create2 {
133 value: ValueId,
134 salt: ValueId,
135 contract: ContractId,
136 },
137
138 YulIntrinsic {
139 op: YulIntrinsicOp,
140 args: Vec<ValueId>,
141 },
142}
143
144#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
145pub struct SwitchTable {
146 values: Vec<ValueId>,
147 blocks: Vec<BasicBlockId>,
148}
149
150impl SwitchTable {
151 pub fn iter(&self) -> impl Iterator<Item = (ValueId, BasicBlockId)> + '_ {
152 self.values.iter().copied().zip(self.blocks.iter().copied())
153 }
154
155 pub fn len(&self) -> usize {
156 debug_assert!(self.values.len() == self.blocks.len());
157 self.values.len()
158 }
159
160 pub fn is_empty(&self) -> bool {
161 debug_assert!(self.values.len() == self.blocks.len());
162 self.values.is_empty()
163 }
164
165 pub fn add_arm(&mut self, value: ValueId, block: BasicBlockId) {
166 self.values.push(value);
167 self.blocks.push(block);
168 }
169}
170
171impl Inst {
172 pub fn new(kind: InstKind, source: SourceInfo) -> Self {
173 Self { kind, source }
174 }
175
176 pub fn unary(op: UnOp, value: ValueId, source: SourceInfo) -> Self {
177 let kind = InstKind::Unary { op, value };
178 Self::new(kind, source)
179 }
180
181 pub fn binary(op: BinOp, lhs: ValueId, rhs: ValueId, source: SourceInfo) -> Self {
182 let kind = InstKind::Binary { op, lhs, rhs };
183 Self::new(kind, source)
184 }
185
186 pub fn intrinsic(op: YulIntrinsicOp, args: Vec<ValueId>, source: SourceInfo) -> Self {
187 let kind = InstKind::YulIntrinsic { op, args };
188 Self::new(kind, source)
189 }
190
191 pub fn nop() -> Self {
192 Self {
193 kind: InstKind::Nop,
194 source: SourceInfo::dummy(),
195 }
196 }
197
198 pub fn is_terminator(&self) -> bool {
199 match self.kind {
200 InstKind::Jump { .. }
201 | InstKind::Branch { .. }
202 | InstKind::Switch { .. }
203 | InstKind::Revert { .. }
204 | InstKind::Return { .. } => true,
205 InstKind::YulIntrinsic { op, .. } => op.is_terminator(),
206 _ => false,
207 }
208 }
209
210 pub fn branch_info(&self) -> BranchInfo {
211 match self.kind {
212 InstKind::Jump { dest } => BranchInfo::Jump(dest),
213 InstKind::Branch { cond, then, else_ } => BranchInfo::Branch(cond, then, else_),
214 InstKind::Switch {
215 disc,
216 ref table,
217 default,
218 } => BranchInfo::Switch(disc, table, default),
219 _ => BranchInfo::NotBranch,
220 }
221 }
222
223 pub fn args(&self) -> ValueIter {
224 use InstKind::*;
225 match &self.kind {
226 Declare { local: arg }
227 | Bind { src: arg }
228 | MemCopy { src: arg }
229 | Load { src: arg }
230 | Unary { value: arg, .. }
231 | Cast { value: arg, .. }
232 | Emit { arg }
233 | Keccak256 { arg }
234 | AbiEncode { arg }
235 | Create { value: arg, .. }
236 | Branch { cond: arg, .. } => ValueIter::one(*arg),
237
238 Switch { disc, table, .. } => {
239 ValueIter::one(*disc).chain(ValueIter::Slice(table.values.iter()))
240 }
241
242 Binary { lhs, rhs, .. }
243 | MapAccess {
244 value: lhs,
245 key: rhs,
246 }
247 | Create2 {
248 value: lhs,
249 salt: rhs,
250 ..
251 } => ValueIter::one(*lhs).chain(ValueIter::one(*rhs)),
252
253 Revert { arg } | Return { arg } => ValueIter::One(*arg),
254
255 Nop | Jump { .. } => ValueIter::Zero,
256
257 AggregateAccess { value, indices } => {
258 ValueIter::one(*value).chain(ValueIter::Slice(indices.iter()))
259 }
260
261 AggregateConstruct { args, .. } | Call { args, .. } | YulIntrinsic { args, .. } => {
262 ValueIter::Slice(args.iter())
263 }
264 }
265 }
266
267 pub fn args_mut(&mut self) -> ValueIterMut {
268 use InstKind::*;
269 match &mut self.kind {
270 Declare { local: arg }
271 | Bind { src: arg }
272 | MemCopy { src: arg }
273 | Load { src: arg }
274 | Unary { value: arg, .. }
275 | Cast { value: arg, .. }
276 | Emit { arg }
277 | Keccak256 { arg }
278 | AbiEncode { arg }
279 | Create { value: arg, .. }
280 | Branch { cond: arg, .. } => ValueIterMut::one(arg),
281
282 Switch { disc, table, .. } => {
283 ValueIterMut::one(disc).chain(ValueIterMut::Slice(table.values.iter_mut()))
284 }
285
286 Binary { lhs, rhs, .. }
287 | MapAccess {
288 value: lhs,
289 key: rhs,
290 }
291 | Create2 {
292 value: lhs,
293 salt: rhs,
294 ..
295 } => ValueIterMut::one(lhs).chain(ValueIterMut::one(rhs)),
296
297 Revert { arg } | Return { arg } => ValueIterMut::One(arg.as_mut()),
298
299 Nop | Jump { .. } => ValueIterMut::Zero,
300
301 AggregateAccess { value, indices } => {
302 ValueIterMut::one(value).chain(ValueIterMut::Slice(indices.iter_mut()))
303 }
304
305 AggregateConstruct { args, .. } | Call { args, .. } | YulIntrinsic { args, .. } => {
306 ValueIterMut::Slice(args.iter_mut())
307 }
308 }
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
313pub enum UnOp {
314 Not,
316 Neg,
318 Inv,
320}
321
322impl fmt::Display for UnOp {
323 fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
324 match self {
325 Self::Not => write!(w, "not"),
326 Self::Neg => write!(w, "-"),
327 Self::Inv => write!(w, "~"),
328 }
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333pub enum BinOp {
334 Add,
335 Sub,
336 Mul,
337 Div,
338 Mod,
339 Pow,
340 Shl,
341 Shr,
342 BitOr,
343 BitXor,
344 BitAnd,
345 LogicalAnd,
346 LogicalOr,
347 Eq,
348 Ne,
349 Ge,
350 Gt,
351 Le,
352 Lt,
353}
354
355impl fmt::Display for BinOp {
356 fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
357 match self {
358 Self::Add => write!(w, "+"),
359 Self::Sub => write!(w, "-"),
360 Self::Mul => write!(w, "*"),
361 Self::Div => write!(w, "/"),
362 Self::Mod => write!(w, "%"),
363 Self::Pow => write!(w, "**"),
364 Self::Shl => write!(w, "<<"),
365 Self::Shr => write!(w, ">>"),
366 Self::BitOr => write!(w, "|"),
367 Self::BitXor => write!(w, "^"),
368 Self::BitAnd => write!(w, "&"),
369 Self::LogicalAnd => write!(w, "and"),
370 Self::LogicalOr => write!(w, "or"),
371 Self::Eq => write!(w, "=="),
372 Self::Ne => write!(w, "!="),
373 Self::Ge => write!(w, ">="),
374 Self::Gt => write!(w, ">"),
375 Self::Le => write!(w, "<="),
376 Self::Lt => write!(w, "<"),
377 }
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
382pub enum CallType {
383 Internal,
384 External,
385}
386
387impl fmt::Display for CallType {
388 fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
389 match self {
390 Self::Internal => write!(w, "internal"),
391 Self::External => write!(w, "external"),
392 }
393 }
394}
395
396#[derive(Debug, Clone, PartialEq, Eq, Hash)]
397pub enum CastKind {
398 Primitive,
400
401 Untag,
403}
404
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
407pub enum YulIntrinsicOp {
408 Stop,
409 Add,
410 Sub,
411 Mul,
412 Div,
413 Sdiv,
414 Mod,
415 Smod,
416 Exp,
417 Not,
418 Lt,
419 Gt,
420 Slt,
421 Sgt,
422 Eq,
423 Iszero,
424 And,
425 Or,
426 Xor,
427 Byte,
428 Shl,
429 Shr,
430 Sar,
431 Addmod,
432 Mulmod,
433 Signextend,
434 Keccak256,
435 Pc,
436 Pop,
437 Mload,
438 Mstore,
439 Mstore8,
440 Sload,
441 Sstore,
442 Msize,
443 Gas,
444 Address,
445 Balance,
446 Selfbalance,
447 Caller,
448 Callvalue,
449 Calldataload,
450 Calldatasize,
451 Calldatacopy,
452 Codesize,
453 Codecopy,
454 Extcodesize,
455 Extcodecopy,
456 Returndatasize,
457 Returndatacopy,
458 Extcodehash,
459 Create,
460 Create2,
461 Call,
462 Callcode,
463 Delegatecall,
464 Staticcall,
465 Return,
466 Revert,
467 Selfdestruct,
468 Invalid,
469 Log0,
470 Log1,
471 Log2,
472 Log3,
473 Log4,
474 Chainid,
475 Basefee,
476 Origin,
477 Gasprice,
478 Blockhash,
479 Coinbase,
480 Timestamp,
481 Number,
482 Prevrandao,
483 Gaslimit,
484}
485impl YulIntrinsicOp {
486 pub fn is_terminator(self) -> bool {
487 matches!(
488 self,
489 Self::Return | Self::Revert | Self::Selfdestruct | Self::Invalid
490 )
491 }
492}
493
494impl fmt::Display for YulIntrinsicOp {
495 fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
496 let op = match self {
497 Self::Stop => "__stop",
498 Self::Add => "__add",
499 Self::Sub => "__sub",
500 Self::Mul => "__mul",
501 Self::Div => "__div",
502 Self::Sdiv => "__sdiv",
503 Self::Mod => "__mod",
504 Self::Smod => "__smod",
505 Self::Exp => "__exp",
506 Self::Not => "__not",
507 Self::Lt => "__lt",
508 Self::Gt => "__gt",
509 Self::Slt => "__slt",
510 Self::Sgt => "__sgt",
511 Self::Eq => "__eq",
512 Self::Iszero => "__iszero",
513 Self::And => "__and",
514 Self::Or => "__or",
515 Self::Xor => "__xor",
516 Self::Byte => "__byte",
517 Self::Shl => "__shl",
518 Self::Shr => "__shr",
519 Self::Sar => "__sar",
520 Self::Addmod => "__addmod",
521 Self::Mulmod => "__mulmod",
522 Self::Signextend => "__signextend",
523 Self::Keccak256 => "__keccak256",
524 Self::Pc => "__pc",
525 Self::Pop => "__pop",
526 Self::Mload => "__mload",
527 Self::Mstore => "__mstore",
528 Self::Mstore8 => "__mstore8",
529 Self::Sload => "__sload",
530 Self::Sstore => "__sstore",
531 Self::Msize => "__msize",
532 Self::Gas => "__gas",
533 Self::Address => "__address",
534 Self::Balance => "__balance",
535 Self::Selfbalance => "__selfbalance",
536 Self::Caller => "__caller",
537 Self::Callvalue => "__callvalue",
538 Self::Calldataload => "__calldataload",
539 Self::Calldatasize => "__calldatasize",
540 Self::Calldatacopy => "__calldatacopy",
541 Self::Codesize => "__codesize",
542 Self::Codecopy => "__codecopy",
543 Self::Extcodesize => "__extcodesize",
544 Self::Extcodecopy => "__extcodecopy",
545 Self::Returndatasize => "__returndatasize",
546 Self::Returndatacopy => "__returndatacopy",
547 Self::Extcodehash => "__extcodehash",
548 Self::Create => "__create",
549 Self::Create2 => "__create2",
550 Self::Call => "__call",
551 Self::Callcode => "__callcode",
552 Self::Delegatecall => "__delegatecall",
553 Self::Staticcall => "__staticcall",
554 Self::Return => "__return",
555 Self::Revert => "__revert",
556 Self::Selfdestruct => "__selfdestruct",
557 Self::Invalid => "__invalid",
558 Self::Log0 => "__log0",
559 Self::Log1 => "__log1",
560 Self::Log2 => "__log2",
561 Self::Log3 => "__log3",
562 Self::Log4 => "__log4",
563 Self::Chainid => "__chainid",
564 Self::Basefee => "__basefee",
565 Self::Origin => "__origin",
566 Self::Gasprice => "__gasprice",
567 Self::Blockhash => "__blockhash",
568 Self::Coinbase => "__coinbase",
569 Self::Timestamp => "__timestamp",
570 Self::Number => "__number",
571 Self::Prevrandao => "__prevrandao",
572 Self::Gaslimit => "__gaslimit",
573 };
574
575 write!(w, "{op}")
576 }
577}
578
579impl From<fe_analyzer::builtins::Intrinsic> for YulIntrinsicOp {
580 fn from(val: fe_analyzer::builtins::Intrinsic) -> Self {
581 use fe_analyzer::builtins::Intrinsic;
582 match val {
583 Intrinsic::__stop => Self::Stop,
584 Intrinsic::__add => Self::Add,
585 Intrinsic::__sub => Self::Sub,
586 Intrinsic::__mul => Self::Mul,
587 Intrinsic::__div => Self::Div,
588 Intrinsic::__sdiv => Self::Sdiv,
589 Intrinsic::__mod => Self::Mod,
590 Intrinsic::__smod => Self::Smod,
591 Intrinsic::__exp => Self::Exp,
592 Intrinsic::__not => Self::Not,
593 Intrinsic::__lt => Self::Lt,
594 Intrinsic::__gt => Self::Gt,
595 Intrinsic::__slt => Self::Slt,
596 Intrinsic::__sgt => Self::Sgt,
597 Intrinsic::__eq => Self::Eq,
598 Intrinsic::__iszero => Self::Iszero,
599 Intrinsic::__and => Self::And,
600 Intrinsic::__or => Self::Or,
601 Intrinsic::__xor => Self::Xor,
602 Intrinsic::__byte => Self::Byte,
603 Intrinsic::__shl => Self::Shl,
604 Intrinsic::__shr => Self::Shr,
605 Intrinsic::__sar => Self::Sar,
606 Intrinsic::__addmod => Self::Addmod,
607 Intrinsic::__mulmod => Self::Mulmod,
608 Intrinsic::__signextend => Self::Signextend,
609 Intrinsic::__keccak256 => Self::Keccak256,
610 Intrinsic::__pc => Self::Pc,
611 Intrinsic::__pop => Self::Pop,
612 Intrinsic::__mload => Self::Mload,
613 Intrinsic::__mstore => Self::Mstore,
614 Intrinsic::__mstore8 => Self::Mstore8,
615 Intrinsic::__sload => Self::Sload,
616 Intrinsic::__sstore => Self::Sstore,
617 Intrinsic::__msize => Self::Msize,
618 Intrinsic::__gas => Self::Gas,
619 Intrinsic::__address => Self::Address,
620 Intrinsic::__balance => Self::Balance,
621 Intrinsic::__selfbalance => Self::Selfbalance,
622 Intrinsic::__caller => Self::Caller,
623 Intrinsic::__callvalue => Self::Callvalue,
624 Intrinsic::__calldataload => Self::Calldataload,
625 Intrinsic::__calldatasize => Self::Calldatasize,
626 Intrinsic::__calldatacopy => Self::Calldatacopy,
627 Intrinsic::__codesize => Self::Codesize,
628 Intrinsic::__codecopy => Self::Codecopy,
629 Intrinsic::__extcodesize => Self::Extcodesize,
630 Intrinsic::__extcodecopy => Self::Extcodecopy,
631 Intrinsic::__returndatasize => Self::Returndatasize,
632 Intrinsic::__returndatacopy => Self::Returndatacopy,
633 Intrinsic::__extcodehash => Self::Extcodehash,
634 Intrinsic::__create => Self::Create,
635 Intrinsic::__create2 => Self::Create2,
636 Intrinsic::__call => Self::Call,
637 Intrinsic::__callcode => Self::Callcode,
638 Intrinsic::__delegatecall => Self::Delegatecall,
639 Intrinsic::__staticcall => Self::Staticcall,
640 Intrinsic::__return => Self::Return,
641 Intrinsic::__revert => Self::Revert,
642 Intrinsic::__selfdestruct => Self::Selfdestruct,
643 Intrinsic::__invalid => Self::Invalid,
644 Intrinsic::__log0 => Self::Log0,
645 Intrinsic::__log1 => Self::Log1,
646 Intrinsic::__log2 => Self::Log2,
647 Intrinsic::__log3 => Self::Log3,
648 Intrinsic::__log4 => Self::Log4,
649 Intrinsic::__chainid => Self::Chainid,
650 Intrinsic::__basefee => Self::Basefee,
651 Intrinsic::__origin => Self::Origin,
652 Intrinsic::__gasprice => Self::Gasprice,
653 Intrinsic::__blockhash => Self::Blockhash,
654 Intrinsic::__coinbase => Self::Coinbase,
655 Intrinsic::__timestamp => Self::Timestamp,
656 Intrinsic::__number => Self::Number,
657 Intrinsic::__prevrandao => Self::Prevrandao,
658 Intrinsic::__gaslimit => Self::Gaslimit,
659 }
660 }
661}
662
663pub enum BranchInfo<'a> {
664 NotBranch,
665 Jump(BasicBlockId),
666 Branch(ValueId, BasicBlockId, BasicBlockId),
667 Switch(ValueId, &'a SwitchTable, Option<BasicBlockId>),
668}
669
670impl<'a> BranchInfo<'a> {
671 pub fn is_not_a_branch(&self) -> bool {
672 matches!(self, BranchInfo::NotBranch)
673 }
674
675 pub fn block_iter(&self) -> BlockIter {
676 match self {
677 Self::NotBranch => BlockIter::Zero,
678 Self::Jump(block) => BlockIter::one(*block),
679 Self::Branch(_, then, else_) => BlockIter::one(*then).chain(BlockIter::one(*else_)),
680 Self::Switch(_, table, default) => {
681 BlockIter::Slice(table.blocks.iter()).chain(BlockIter::One(*default))
682 }
683 }
684 }
685}
686
687pub type BlockIter<'a> = IterBase<'a, BasicBlockId>;
688pub type ValueIter<'a> = IterBase<'a, ValueId>;
689pub type ValueIterMut<'a> = IterMutBase<'a, ValueId>;
690
691pub enum IterBase<'a, T> {
692 Zero,
693 One(Option<T>),
694 Slice(std::slice::Iter<'a, T>),
695 Chain(Box<IterBase<'a, T>>, Box<IterBase<'a, T>>),
696}
697
698impl<'a, T> IterBase<'a, T> {
699 fn one(value: T) -> Self {
700 Self::One(Some(value))
701 }
702
703 fn chain(self, rhs: Self) -> Self {
704 Self::Chain(self.into(), rhs.into())
705 }
706}
707
708impl<'a, T> Iterator for IterBase<'a, T>
709where
710 T: Copy,
711{
712 type Item = T;
713
714 fn next(&mut self) -> Option<Self::Item> {
715 match self {
716 Self::Zero => None,
717 Self::One(value) => value.take(),
718 Self::Slice(s) => s.next().copied(),
719 Self::Chain(first, second) => {
720 if let Some(value) = first.next() {
721 Some(value)
722 } else {
723 second.next()
724 }
725 }
726 }
727 }
728}
729
730pub enum IterMutBase<'a, T> {
731 Zero,
732 One(Option<&'a mut T>),
733 Slice(std::slice::IterMut<'a, T>),
734 Chain(Box<IterMutBase<'a, T>>, Box<IterMutBase<'a, T>>),
735}
736
737impl<'a, T> IterMutBase<'a, T> {
738 fn one(value: &'a mut T) -> Self {
739 Self::One(Some(value))
740 }
741
742 fn chain(self, rhs: Self) -> Self {
743 Self::Chain(self.into(), rhs.into())
744 }
745}
746
747impl<'a, T> Iterator for IterMutBase<'a, T> {
748 type Item = &'a mut T;
749
750 fn next(&mut self) -> Option<Self::Item> {
751 match self {
752 Self::Zero => None,
753 Self::One(value) => value.take(),
754 Self::Slice(s) => s.next(),
755 Self::Chain(first, second) => {
756 if let Some(value) = first.next() {
757 Some(value)
758 } else {
759 second.next()
760 }
761 }
762 }
763 }
764}