1use super::{BlockId, Function, FunctionId, MirType, Value, ValueId};
4use alloy_primitives::U256;
5use smallvec::SmallVec;
6use solar_interface::Span;
7use solar_sema::hir;
8use std::fmt;
9
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
12pub struct InstructionMetadata {
13 storage_alias: Option<Box<StorageAlias>>,
15 source_span: Span,
17 hir_expr: Option<hir::ExprId>,
19 pub loop_depth: u16,
21 flags: MetadataFlags,
23}
24
25impl InstructionMetadata {
26 pub const EMPTY: Self = Self {
28 storage_alias: None,
29 hir_expr: None,
30 source_span: Span::DUMMY,
31 loop_depth: 0,
32 flags: MetadataFlags::EMPTY,
33 };
34
35 #[must_use]
37 pub fn storage_alias(&self) -> Option<StorageAlias> {
38 self.storage_alias.as_deref().copied()
39 }
40
41 pub fn set_storage_alias(&mut self, alias: Option<StorageAlias>) {
43 self.storage_alias = alias.map(Box::new);
44 }
45
46 #[must_use]
48 pub fn hir_expr(&self) -> Option<hir::ExprId> {
49 self.hir_expr
50 }
51
52 pub fn set_hir_expr(&mut self, expr: Option<hir::ExprId>) {
54 self.hir_expr = expr;
55 }
56
57 #[must_use]
59 pub fn source_span(&self) -> Option<Span> {
60 (!self.source_span.is_dummy()).then_some(self.source_span)
61 }
62
63 pub fn set_source_span(&mut self, span: Option<Span>) {
65 self.source_span = span.unwrap_or(Span::DUMMY);
66 }
67
68 #[must_use]
70 pub fn memory_region(&self) -> Option<MemoryRegion> {
71 self.flags.memory_region()
72 }
73
74 pub fn set_memory_region(&mut self, region: Option<MemoryRegion>) {
76 self.flags.set_memory_region(region);
77 }
78
79 #[must_use]
81 pub fn unchecked(&self) -> bool {
82 self.flags.unchecked()
83 }
84
85 pub fn set_unchecked(&mut self, unchecked: bool) {
87 self.flags.set_unchecked(unchecked);
88 }
89
90 #[must_use]
92 pub fn effect(&self) -> Option<EffectKind> {
93 self.flags.effect()
94 }
95
96 pub fn set_effect(&mut self, effect: Option<EffectKind>) {
98 self.flags.set_effect(effect);
99 }
100}
101
102#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
103struct MetadataFlags(u8);
104
105impl MetadataFlags {
106 const EMPTY: Self = Self(0);
107 const MEMORY_MASK: u8 = 0b0000_0111;
108 const EFFECT_MASK: u8 = 0b0111_1000;
109 const EFFECT_SHIFT: u8 = 3;
110 const UNCHECKED: u8 = 0b1000_0000;
111
112 fn memory_region(self) -> Option<MemoryRegion> {
113 match self.0 & Self::MEMORY_MASK {
114 0 => None,
115 1 => Some(MemoryRegion::Scratch),
116 2 => Some(MemoryRegion::AbiReturn),
117 3 => Some(MemoryRegion::Heap),
118 4 => Some(MemoryRegion::InternalFrame),
119 5 => Some(MemoryRegion::Unknown),
120 _ => unreachable!("invalid packed memory region"),
121 }
122 }
123
124 fn set_memory_region(&mut self, region: Option<MemoryRegion>) {
125 let bits = match region {
126 None => 0,
127 Some(MemoryRegion::Scratch) => 1,
128 Some(MemoryRegion::AbiReturn) => 2,
129 Some(MemoryRegion::Heap) => 3,
130 Some(MemoryRegion::InternalFrame) => 4,
131 Some(MemoryRegion::Unknown) => 5,
132 };
133 self.0 = (self.0 & !Self::MEMORY_MASK) | bits;
134 }
135
136 fn unchecked(self) -> bool {
137 self.0 & Self::UNCHECKED != 0
138 }
139
140 fn set_unchecked(&mut self, unchecked: bool) {
141 if unchecked {
142 self.0 |= Self::UNCHECKED;
143 } else {
144 self.0 &= !Self::UNCHECKED;
145 }
146 }
147
148 fn effect(self) -> Option<EffectKind> {
149 match (self.0 & Self::EFFECT_MASK) >> Self::EFFECT_SHIFT {
150 0 => None,
151 1 => Some(EffectKind::Pure),
152 2 => Some(EffectKind::MemoryRead),
153 3 => Some(EffectKind::MemoryWrite),
154 4 => Some(EffectKind::StorageRead),
155 5 => Some(EffectKind::StorageWrite),
156 6 => Some(EffectKind::TransientRead),
157 7 => Some(EffectKind::TransientWrite),
158 8 => Some(EffectKind::EnvironmentRead),
159 9 => Some(EffectKind::ExternalCall),
160 10 => Some(EffectKind::InternalCall),
161 11 => Some(EffectKind::Create),
162 12 => Some(EffectKind::Log),
163 _ => unreachable!("invalid packed effect kind"),
164 }
165 }
166
167 fn set_effect(&mut self, effect: Option<EffectKind>) {
168 let bits = match effect {
169 None => 0,
170 Some(EffectKind::Pure) => 1,
171 Some(EffectKind::MemoryRead) => 2,
172 Some(EffectKind::MemoryWrite) => 3,
173 Some(EffectKind::StorageRead) => 4,
174 Some(EffectKind::StorageWrite) => 5,
175 Some(EffectKind::TransientRead) => 6,
176 Some(EffectKind::TransientWrite) => 7,
177 Some(EffectKind::EnvironmentRead) => 8,
178 Some(EffectKind::ExternalCall) => 9,
179 Some(EffectKind::InternalCall) => 10,
180 Some(EffectKind::Create) => 11,
181 Some(EffectKind::Log) => 12,
182 } << Self::EFFECT_SHIFT;
183 self.0 = (self.0 & !Self::EFFECT_MASK) | bits;
184 }
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
189pub enum StorageAlias {
190 Slot(U256),
192 Symbolic(ValueId),
194 Offset {
196 base: ValueId,
198 offset: U256,
200 },
201}
202
203impl StorageAlias {
204 #[must_use]
206 pub fn for_value(func: &Function, value: ValueId) -> Self {
207 match func.value(value) {
208 Value::Immediate(imm) => imm.as_u256().map_or(Self::Symbolic(value), Self::Slot),
209 Value::Inst(inst_id) => match func.instructions[*inst_id].kind {
210 InstKind::Add(lhs, rhs) => {
211 if let Some(offset) = Self::immediate_u256(func, rhs) {
212 Self::add_offset(func, lhs, offset)
213 } else if let Some(offset) = Self::immediate_u256(func, lhs) {
214 Self::add_offset(func, rhs, offset)
215 } else {
216 Self::Symbolic(value)
217 }
218 }
219 InstKind::Sub(lhs, rhs) => {
220 if let Some(offset) = Self::immediate_u256(func, rhs) {
221 Self::add_offset(func, lhs, U256::ZERO.wrapping_sub(offset))
222 } else {
223 Self::Symbolic(value)
224 }
225 }
226 _ => Self::Symbolic(value),
227 },
228 Value::Arg { .. } | Value::Undef(_) | Value::Error(_) => Self::Symbolic(value),
229 }
230 }
231
232 #[must_use]
234 pub fn may_alias(self, other: Self) -> bool {
235 match (self, other) {
236 (Self::Slot(a), Self::Slot(b)) => a == b,
237 (
238 Self::Offset { base: a, offset: a_offset },
239 Self::Offset { base: b, offset: b_offset },
240 ) if a == b => a_offset == b_offset,
241 (Self::Symbolic(_), Self::Symbolic(_)) => true,
242 (Self::Symbolic(a), Self::Offset { base, offset })
243 | (Self::Offset { base, offset }, Self::Symbolic(a))
244 if a == base =>
245 {
246 offset.is_zero()
247 }
248 _ => true,
249 }
250 }
251
252 #[must_use]
254 pub const fn symbolic_base(self) -> Option<ValueId> {
255 match self {
256 Self::Symbolic(value) | Self::Offset { base: value, .. } => Some(value),
257 Self::Slot(_) => None,
258 }
259 }
260
261 fn add_offset(func: &Function, value: ValueId, offset: U256) -> Self {
262 match Self::for_value(func, value) {
263 Self::Slot(slot) => Self::Slot(slot.wrapping_add(offset)),
264 Self::Symbolic(base) if offset.is_zero() => Self::Symbolic(base),
265 Self::Symbolic(base) => Self::Offset { base, offset },
266 Self::Offset { base, offset: existing } => {
267 let offset = existing.wrapping_add(offset);
268 if offset.is_zero() { Self::Symbolic(base) } else { Self::Offset { base, offset } }
269 }
270 }
271 }
272
273 fn immediate_u256(func: &Function, value: ValueId) -> Option<U256> {
274 match func.value(value) {
275 Value::Immediate(imm) => imm.as_u256(),
276 _ => None,
277 }
278 }
279}
280
281#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
283pub enum MemoryRegion {
284 Scratch,
286 AbiReturn,
288 Heap,
290 InternalFrame,
292 Unknown,
294}
295
296impl MemoryRegion {
297 #[must_use]
299 pub const fn name(&self) -> &'static str {
300 match self {
301 Self::Scratch => "scratch",
302 Self::AbiReturn => "abi_return",
303 Self::Heap => "heap",
304 Self::InternalFrame => "internal_frame",
305 Self::Unknown => "unknown",
306 }
307 }
308}
309
310#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
312pub enum EffectKind {
313 Pure,
315 MemoryRead,
317 MemoryWrite,
319 StorageRead,
321 StorageWrite,
323 TransientRead,
325 TransientWrite,
327 EnvironmentRead,
329 ExternalCall,
331 InternalCall,
333 Create,
335 Log,
337}
338
339impl EffectKind {
340 #[must_use]
342 pub const fn name(&self) -> &'static str {
343 match self {
344 Self::Pure => "pure",
345 Self::MemoryRead => "memory_read",
346 Self::MemoryWrite => "memory_write",
347 Self::StorageRead => "storage_read",
348 Self::StorageWrite => "storage_write",
349 Self::TransientRead => "transient_read",
350 Self::TransientWrite => "transient_write",
351 Self::EnvironmentRead => "environment_read",
352 Self::ExternalCall => "external_call",
353 Self::InternalCall => "internal_call",
354 Self::Create => "create",
355 Self::Log => "log",
356 }
357 }
358}
359
360#[derive(Clone, Debug)]
362pub struct Instruction {
363 pub kind: InstKind,
365 pub result_ty: Option<MirType>,
367 pub metadata: InstructionMetadata,
369}
370
371impl Instruction {
372 #[must_use]
374 pub const fn new(kind: InstKind, result_ty: Option<MirType>) -> Self {
375 Self { kind, result_ty, metadata: InstructionMetadata::EMPTY }
376 }
377
378 #[must_use]
380 pub fn operands(&self) -> SmallVec<[ValueId; 8]> {
381 self.kind.operands()
382 }
383
384 #[must_use]
386 pub fn effect_kind(&self) -> EffectKind {
387 self.metadata.effect().unwrap_or_else(|| self.kind.effect_kind())
388 }
389}
390
391#[derive(Clone, Debug)]
397pub enum InstKind {
398 Add(ValueId, ValueId),
401 Sub(ValueId, ValueId),
403 Mul(ValueId, ValueId),
405 Div(ValueId, ValueId),
407 SDiv(ValueId, ValueId),
409 Mod(ValueId, ValueId),
411 SMod(ValueId, ValueId),
413 Exp(ValueId, ValueId),
415 AddMod(ValueId, ValueId, ValueId),
417 MulMod(ValueId, ValueId, ValueId),
419
420 And(ValueId, ValueId),
423 Or(ValueId, ValueId),
425 Xor(ValueId, ValueId),
427 Not(ValueId),
429 Shl(ValueId, ValueId),
431 Shr(ValueId, ValueId),
433 Sar(ValueId, ValueId),
435 Byte(ValueId, ValueId),
437
438 Lt(ValueId, ValueId),
441 Gt(ValueId, ValueId),
443 SLt(ValueId, ValueId),
445 SGt(ValueId, ValueId),
447 Eq(ValueId, ValueId),
449 IsZero(ValueId),
451
452 MLoad(ValueId),
455 MStore(ValueId, ValueId),
457 MStore8(ValueId, ValueId),
459 MSize,
461 MCopy(ValueId, ValueId, ValueId),
463
464 SLoad(ValueId),
467 SStore(ValueId, ValueId),
469 TLoad(ValueId),
471 TStore(ValueId, ValueId),
473
474 CalldataLoad(ValueId),
477 CalldataCopy(ValueId, ValueId, ValueId),
479 CalldataSize,
481 InternalFrameAddr(u64),
483
484 CodeSize,
487 CodeCopy(ValueId, ValueId, ValueId),
489 ExtCodeSize(ValueId),
491 ExtCodeCopy(ValueId, ValueId, ValueId, ValueId),
493 ExtCodeHash(ValueId),
495 LoadImmutable(u32),
501
502 ReturnDataSize,
505 ReturnDataCopy(ValueId, ValueId, ValueId),
507
508 Caller,
511 CallValue,
513 Origin,
515 GasPrice,
517 BlockHash(ValueId),
519 Coinbase,
521 Timestamp,
523 BlockNumber,
525 PrevRandao,
527 GasLimit,
529 ChainId,
531 Address,
533 Balance(ValueId),
535 SelfBalance,
537 Gas,
539 BaseFee,
541 BlobBaseFee,
543 BlobHash(ValueId),
545
546 Keccak256(ValueId, ValueId),
549
550 Call {
555 gas: ValueId,
556 addr: ValueId,
557 value: ValueId,
558 args_offset: ValueId,
559 args_size: ValueId,
560 ret_offset: ValueId,
561 ret_size: ValueId,
562 },
563 StaticCall {
565 gas: ValueId,
566 addr: ValueId,
567 args_offset: ValueId,
568 args_size: ValueId,
569 ret_offset: ValueId,
570 ret_size: ValueId,
571 },
572 DelegateCall {
574 gas: ValueId,
575 addr: ValueId,
576 args_offset: ValueId,
577 args_size: ValueId,
578 ret_offset: ValueId,
579 ret_size: ValueId,
580 },
581 InternalCall { function: FunctionId, args: Box<[ValueId]>, returns: u32 },
583
584 Create(ValueId, ValueId, ValueId),
587 Create2(ValueId, ValueId, ValueId, ValueId),
589
590 Log0(ValueId, ValueId),
594 Log1(ValueId, ValueId, ValueId),
596 Log2(ValueId, ValueId, ValueId, ValueId),
598 Log3(ValueId, ValueId, ValueId, ValueId, ValueId),
600 Log4(ValueId, ValueId, ValueId, ValueId, ValueId, ValueId),
602
603 Phi(Vec<(BlockId, ValueId)>),
606 Select(ValueId, ValueId, ValueId),
608
609 SignExtend(ValueId, ValueId),
612}
613
614impl InstKind {
615 pub fn collect_operands(&self, out: &mut SmallVec<[ValueId; 8]>) {
618 match self {
619 Self::Add(a, b)
621 | Self::Sub(a, b)
622 | Self::Mul(a, b)
623 | Self::Div(a, b)
624 | Self::SDiv(a, b)
625 | Self::Mod(a, b)
626 | Self::SMod(a, b)
627 | Self::Exp(a, b)
628 | Self::And(a, b)
629 | Self::Or(a, b)
630 | Self::Xor(a, b)
631 | Self::Shl(a, b)
632 | Self::Shr(a, b)
633 | Self::Sar(a, b)
634 | Self::Byte(a, b)
635 | Self::Lt(a, b)
636 | Self::Gt(a, b)
637 | Self::SLt(a, b)
638 | Self::SGt(a, b)
639 | Self::Eq(a, b)
640 | Self::MStore(a, b)
641 | Self::MStore8(a, b)
642 | Self::SStore(a, b)
643 | Self::TStore(a, b)
644 | Self::Keccak256(a, b)
645 | Self::Log0(a, b)
646 | Self::SignExtend(a, b) => {
647 out.push(*a);
648 out.push(*b);
649 }
650
651 Self::Not(a)
653 | Self::IsZero(a)
654 | Self::MLoad(a)
655 | Self::SLoad(a)
656 | Self::TLoad(a)
657 | Self::CalldataLoad(a)
658 | Self::ExtCodeSize(a)
659 | Self::ExtCodeHash(a)
660 | Self::Balance(a)
661 | Self::BlockHash(a)
662 | Self::BlobHash(a) => {
663 out.push(*a);
664 }
665
666 Self::MCopy(a, b, c)
668 | Self::CalldataCopy(a, b, c)
669 | Self::CodeCopy(a, b, c)
670 | Self::ReturnDataCopy(a, b, c)
671 | Self::AddMod(a, b, c)
672 | Self::MulMod(a, b, c)
673 | Self::Create(a, b, c)
674 | Self::Log1(a, b, c)
675 | Self::Select(a, b, c) => {
676 out.push(*a);
677 out.push(*b);
678 out.push(*c);
679 }
680
681 Self::ExtCodeCopy(a, b, c, d) | Self::Create2(a, b, c, d) | Self::Log2(a, b, c, d) => {
683 out.push(*a);
684 out.push(*b);
685 out.push(*c);
686 out.push(*d);
687 }
688
689 Self::Log3(a, b, c, d, e) => {
691 out.push(*a);
692 out.push(*b);
693 out.push(*c);
694 out.push(*d);
695 out.push(*e);
696 }
697
698 Self::Log4(a, b, c, d, e, f) => {
700 out.push(*a);
701 out.push(*b);
702 out.push(*c);
703 out.push(*d);
704 out.push(*e);
705 out.push(*f);
706 }
707
708 Self::Call { gas, addr, value, args_offset, args_size, ret_offset, ret_size } => {
710 out.push(*gas);
711 out.push(*addr);
712 out.push(*value);
713 out.push(*args_offset);
714 out.push(*args_size);
715 out.push(*ret_offset);
716 out.push(*ret_size);
717 }
718 Self::StaticCall { gas, addr, args_offset, args_size, ret_offset, ret_size } => {
719 out.push(*gas);
720 out.push(*addr);
721 out.push(*args_offset);
722 out.push(*args_size);
723 out.push(*ret_offset);
724 out.push(*ret_size);
725 }
726 Self::DelegateCall { gas, addr, args_offset, args_size, ret_offset, ret_size } => {
727 out.push(*gas);
728 out.push(*addr);
729 out.push(*args_offset);
730 out.push(*args_size);
731 out.push(*ret_offset);
732 out.push(*ret_size);
733 }
734 Self::InternalCall { args, .. } => {
735 out.extend(args.iter().copied());
736 }
737
738 Self::Phi(incoming) => {
740 for (_, val) in incoming {
741 out.push(*val);
742 }
743 }
744
745 Self::MSize
747 | Self::CalldataSize
748 | Self::InternalFrameAddr(_)
749 | Self::CodeSize
750 | Self::LoadImmutable(_)
751 | Self::ReturnDataSize
752 | Self::Caller
753 | Self::CallValue
754 | Self::Origin
755 | Self::GasPrice
756 | Self::Coinbase
757 | Self::Timestamp
758 | Self::BlockNumber
759 | Self::PrevRandao
760 | Self::GasLimit
761 | Self::ChainId
762 | Self::Address
763 | Self::SelfBalance
764 | Self::Gas
765 | Self::BaseFee
766 | Self::BlobBaseFee => {}
767 }
768 }
769
770 #[must_use]
772 pub fn operands(&self) -> SmallVec<[ValueId; 8]> {
773 let mut out = SmallVec::new();
774 self.collect_operands(&mut out);
775 out
776 }
777
778 pub fn visit_operands_mut(&mut self, mut f: impl FnMut(&mut ValueId)) {
780 match self {
781 Self::Add(a, b)
782 | Self::Sub(a, b)
783 | Self::Mul(a, b)
784 | Self::Div(a, b)
785 | Self::SDiv(a, b)
786 | Self::Mod(a, b)
787 | Self::SMod(a, b)
788 | Self::Exp(a, b)
789 | Self::And(a, b)
790 | Self::Or(a, b)
791 | Self::Xor(a, b)
792 | Self::Shl(a, b)
793 | Self::Shr(a, b)
794 | Self::Sar(a, b)
795 | Self::Byte(a, b)
796 | Self::Lt(a, b)
797 | Self::Gt(a, b)
798 | Self::SLt(a, b)
799 | Self::SGt(a, b)
800 | Self::Eq(a, b)
801 | Self::MStore(a, b)
802 | Self::MStore8(a, b)
803 | Self::SStore(a, b)
804 | Self::TStore(a, b)
805 | Self::Keccak256(a, b)
806 | Self::Log0(a, b)
807 | Self::SignExtend(a, b) => {
808 f(a);
809 f(b);
810 }
811
812 Self::Not(a)
813 | Self::IsZero(a)
814 | Self::MLoad(a)
815 | Self::SLoad(a)
816 | Self::TLoad(a)
817 | Self::CalldataLoad(a)
818 | Self::ExtCodeSize(a)
819 | Self::ExtCodeHash(a)
820 | Self::Balance(a)
821 | Self::BlockHash(a)
822 | Self::BlobHash(a) => f(a),
823
824 Self::MCopy(a, b, c)
825 | Self::CalldataCopy(a, b, c)
826 | Self::CodeCopy(a, b, c)
827 | Self::ReturnDataCopy(a, b, c)
828 | Self::AddMod(a, b, c)
829 | Self::MulMod(a, b, c)
830 | Self::Create(a, b, c)
831 | Self::Log1(a, b, c)
832 | Self::Select(a, b, c) => {
833 f(a);
834 f(b);
835 f(c);
836 }
837
838 Self::ExtCodeCopy(a, b, c, d) | Self::Create2(a, b, c, d) | Self::Log2(a, b, c, d) => {
839 f(a);
840 f(b);
841 f(c);
842 f(d);
843 }
844
845 Self::Log3(a, b, c, d, e) => {
846 f(a);
847 f(b);
848 f(c);
849 f(d);
850 f(e);
851 }
852
853 Self::Log4(a, b, c, d, e, g) => {
854 f(a);
855 f(b);
856 f(c);
857 f(d);
858 f(e);
859 f(g);
860 }
861
862 Self::Call { gas, addr, value, args_offset, args_size, ret_offset, ret_size } => {
863 f(gas);
864 f(addr);
865 f(value);
866 f(args_offset);
867 f(args_size);
868 f(ret_offset);
869 f(ret_size);
870 }
871 Self::StaticCall { gas, addr, args_offset, args_size, ret_offset, ret_size }
872 | Self::DelegateCall { gas, addr, args_offset, args_size, ret_offset, ret_size } => {
873 f(gas);
874 f(addr);
875 f(args_offset);
876 f(args_size);
877 f(ret_offset);
878 f(ret_size);
879 }
880 Self::InternalCall { args, .. } => {
881 for arg in args {
882 f(arg);
883 }
884 }
885
886 Self::Phi(incoming) => {
887 for (_, value) in incoming {
888 f(value);
889 }
890 }
891
892 Self::MSize
893 | Self::CalldataSize
894 | Self::InternalFrameAddr(_)
895 | Self::CodeSize
896 | Self::LoadImmutable(_)
897 | Self::ReturnDataSize
898 | Self::Caller
899 | Self::CallValue
900 | Self::Origin
901 | Self::GasPrice
902 | Self::Coinbase
903 | Self::Timestamp
904 | Self::BlockNumber
905 | Self::PrevRandao
906 | Self::GasLimit
907 | Self::ChainId
908 | Self::Address
909 | Self::SelfBalance
910 | Self::Gas
911 | Self::BaseFee
912 | Self::BlobBaseFee => {}
913 }
914 }
915
916 #[must_use]
918 pub const fn may_mutate_storage(&self) -> bool {
919 matches!(
920 self,
921 Self::SStore(_, _)
922 | Self::Call { .. }
923 | Self::DelegateCall { .. }
924 | Self::InternalCall { .. }
925 | Self::Create(_, _, _)
926 | Self::Create2(_, _, _, _)
927 )
928 }
929
930 #[must_use]
932 pub const fn may_mutate_transient_storage(&self) -> bool {
933 matches!(
934 self,
935 Self::TStore(_, _)
936 | Self::Call { .. }
937 | Self::DelegateCall { .. }
938 | Self::InternalCall { .. }
939 | Self::Create(_, _, _)
940 | Self::Create2(_, _, _, _)
941 )
942 }
943
944 #[must_use]
946 pub const fn may_mutate_memory(&self) -> bool {
947 matches!(
948 self,
949 Self::MStore(_, _)
950 | Self::MStore8(_, _)
951 | Self::MCopy(_, _, _)
952 | Self::CalldataCopy(_, _, _)
953 | Self::CodeCopy(_, _, _)
954 | Self::ReturnDataCopy(_, _, _)
955 | Self::ExtCodeCopy(_, _, _, _)
956 | Self::Call { .. }
957 | Self::StaticCall { .. }
958 | Self::DelegateCall { .. }
959 | Self::InternalCall { .. }
960 | Self::Create(_, _, _)
961 | Self::Create2(_, _, _, _)
962 )
963 }
964
965 #[must_use]
967 pub const fn mnemonic(&self) -> &'static str {
968 match self {
969 Self::Add(_, _) => "add",
970 Self::Sub(_, _) => "sub",
971 Self::Mul(_, _) => "mul",
972 Self::Div(_, _) => "div",
973 Self::SDiv(_, _) => "sdiv",
974 Self::Mod(_, _) => "mod",
975 Self::SMod(_, _) => "smod",
976 Self::Exp(_, _) => "exp",
977 Self::AddMod(_, _, _) => "addmod",
978 Self::MulMod(_, _, _) => "mulmod",
979 Self::And(_, _) => "and",
980 Self::Or(_, _) => "or",
981 Self::Xor(_, _) => "xor",
982 Self::Not(_) => "not",
983 Self::Shl(_, _) => "shl",
984 Self::Shr(_, _) => "shr",
985 Self::Sar(_, _) => "sar",
986 Self::Byte(_, _) => "byte",
987 Self::Lt(_, _) => "lt",
988 Self::Gt(_, _) => "gt",
989 Self::SLt(_, _) => "slt",
990 Self::SGt(_, _) => "sgt",
991 Self::Eq(_, _) => "eq",
992 Self::IsZero(_) => "iszero",
993 Self::MLoad(_) => "mload",
994 Self::MStore(_, _) => "mstore",
995 Self::MStore8(_, _) => "mstore8",
996 Self::MSize => "msize",
997 Self::MCopy(_, _, _) => "mcopy",
998 Self::SLoad(_) => "sload",
999 Self::SStore(_, _) => "sstore",
1000 Self::TLoad(_) => "tload",
1001 Self::TStore(_, _) => "tstore",
1002 Self::CalldataLoad(_) => "calldataload",
1003 Self::CalldataCopy(_, _, _) => "calldatacopy",
1004 Self::CalldataSize => "calldatasize",
1005 Self::CodeSize => "codesize",
1006 Self::CodeCopy(_, _, _) => "codecopy",
1007 Self::LoadImmutable(_) => "loadimmutable",
1008 Self::ExtCodeSize(_) => "extcodesize",
1009 Self::ExtCodeCopy(_, _, _, _) => "extcodecopy",
1010 Self::ExtCodeHash(_) => "extcodehash",
1011 Self::ReturnDataSize => "returndatasize",
1012 Self::ReturnDataCopy(_, _, _) => "returndatacopy",
1013 Self::InternalFrameAddr(_) => "internal_frame_addr",
1014 Self::Caller => "caller",
1015 Self::CallValue => "callvalue",
1016 Self::Origin => "origin",
1017 Self::GasPrice => "gasprice",
1018 Self::BlockHash(_) => "blockhash",
1019 Self::Coinbase => "coinbase",
1020 Self::Timestamp => "timestamp",
1021 Self::BlockNumber => "number",
1022 Self::PrevRandao => "prevrandao",
1023 Self::GasLimit => "gaslimit",
1024 Self::ChainId => "chainid",
1025 Self::Address => "address",
1026 Self::Balance(_) => "balance",
1027 Self::SelfBalance => "selfbalance",
1028 Self::Gas => "gas",
1029 Self::BaseFee => "basefee",
1030 Self::BlobBaseFee => "blobbasefee",
1031 Self::BlobHash(_) => "blobhash",
1032 Self::Keccak256(_, _) => "keccak256",
1033 Self::Call { .. } => "call",
1034 Self::StaticCall { .. } => "staticcall",
1035 Self::DelegateCall { .. } => "delegatecall",
1036 Self::InternalCall { .. } => "internal_call",
1037 Self::Create(_, _, _) => "create",
1038 Self::Create2(_, _, _, _) => "create2",
1039 Self::Log0(_, _) => "log0",
1040 Self::Log1(_, _, _) => "log1",
1041 Self::Log2(_, _, _, _) => "log2",
1042 Self::Log3(_, _, _, _, _) => "log3",
1043 Self::Log4(_, _, _, _, _, _) => "log4",
1044 Self::Phi(_) => "phi",
1045 Self::Select(_, _, _) => "select",
1046 Self::SignExtend(_, _) => "signextend",
1047 }
1048 }
1049
1050 #[must_use]
1053 pub const fn has_side_effects(&self) -> bool {
1054 matches!(
1055 self,
1056 Self::SStore(_, _)
1058 | Self::TStore(_, _)
1059 | Self::MStore(_, _)
1061 | Self::MStore8(_, _)
1062 | Self::MCopy(_, _, _)
1063 | Self::Call { .. }
1065 | Self::StaticCall { .. }
1066 | Self::DelegateCall { .. }
1067 | Self::InternalCall { .. }
1068 | Self::Create(_, _, _)
1070 | Self::Create2(_, _, _, _)
1071 | Self::Log0(_, _)
1073 | Self::Log1(_, _, _)
1074 | Self::Log2(_, _, _, _)
1075 | Self::Log3(_, _, _, _, _)
1076 | Self::Log4(_, _, _, _, _, _)
1077 | Self::CalldataCopy(_, _, _)
1079 | Self::CodeCopy(_, _, _)
1080 | Self::ExtCodeCopy(_, _, _, _)
1081 | Self::ReturnDataCopy(_, _, _)
1082 )
1083 }
1084
1085 #[must_use]
1087 pub const fn effect_kind(&self) -> EffectKind {
1088 match self {
1089 Self::MStore(_, _)
1090 | Self::MStore8(_, _)
1091 | Self::MCopy(_, _, _)
1092 | Self::CalldataCopy(_, _, _)
1093 | Self::CodeCopy(_, _, _)
1094 | Self::ExtCodeCopy(_, _, _, _)
1095 | Self::ReturnDataCopy(_, _, _) => EffectKind::MemoryWrite,
1096 Self::MLoad(_) | Self::MSize | Self::Keccak256(_, _) => EffectKind::MemoryRead,
1097 Self::SLoad(_) => EffectKind::StorageRead,
1098 Self::SStore(_, _) => EffectKind::StorageWrite,
1099 Self::TLoad(_) => EffectKind::TransientRead,
1100 Self::TStore(_, _) => EffectKind::TransientWrite,
1101 Self::Call { .. } | Self::StaticCall { .. } | Self::DelegateCall { .. } => {
1102 EffectKind::ExternalCall
1103 }
1104 Self::InternalCall { .. } => EffectKind::InternalCall,
1105 Self::Create(_, _, _) | Self::Create2(_, _, _, _) => EffectKind::Create,
1106 Self::Log0(_, _)
1107 | Self::Log1(_, _, _)
1108 | Self::Log2(_, _, _, _)
1109 | Self::Log3(_, _, _, _, _)
1110 | Self::Log4(_, _, _, _, _, _) => EffectKind::Log,
1111 Self::CalldataLoad(_)
1112 | Self::CalldataSize
1113 | Self::CodeSize
1114 | Self::LoadImmutable(_)
1115 | Self::ExtCodeSize(_)
1116 | Self::ExtCodeHash(_)
1117 | Self::ReturnDataSize
1118 | Self::Caller
1119 | Self::CallValue
1120 | Self::Origin
1121 | Self::GasPrice
1122 | Self::BlockHash(_)
1123 | Self::Coinbase
1124 | Self::Timestamp
1125 | Self::BlockNumber
1126 | Self::PrevRandao
1127 | Self::GasLimit
1128 | Self::ChainId
1129 | Self::Address
1130 | Self::Balance(_)
1131 | Self::SelfBalance
1132 | Self::Gas
1133 | Self::BaseFee
1134 | Self::BlobBaseFee
1135 | Self::BlobHash(_) => EffectKind::EnvironmentRead,
1136 Self::Add(_, _)
1137 | Self::Sub(_, _)
1138 | Self::Mul(_, _)
1139 | Self::Div(_, _)
1140 | Self::SDiv(_, _)
1141 | Self::Mod(_, _)
1142 | Self::SMod(_, _)
1143 | Self::Exp(_, _)
1144 | Self::AddMod(_, _, _)
1145 | Self::MulMod(_, _, _)
1146 | Self::And(_, _)
1147 | Self::Or(_, _)
1148 | Self::Xor(_, _)
1149 | Self::Not(_)
1150 | Self::Shl(_, _)
1151 | Self::Shr(_, _)
1152 | Self::Sar(_, _)
1153 | Self::Byte(_, _)
1154 | Self::Lt(_, _)
1155 | Self::Gt(_, _)
1156 | Self::SLt(_, _)
1157 | Self::SGt(_, _)
1158 | Self::Eq(_, _)
1159 | Self::IsZero(_)
1160 | Self::InternalFrameAddr(_)
1161 | Self::Phi(_)
1162 | Self::Select(_, _, _)
1163 | Self::SignExtend(_, _) => EffectKind::Pure,
1164 }
1165 }
1166}
1167
1168impl fmt::Display for InstKind {
1169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1170 write!(f, "{}", self.mnemonic())
1171 }
1172}
1173
1174#[cfg(test)]
1175mod tests {
1176 use super::*;
1177 use crate::mir::{Function, Immediate, Value};
1178 use alloy_primitives::U256;
1179 use solar_interface::Ident;
1180
1181 #[test]
1182 fn phi_operands_include_incoming_values() {
1183 let mut func = Function::new(Ident::DUMMY);
1184 let pred_a = func.entry_block;
1185 let pred_b = func.alloc_block();
1186 let a = func.alloc_value(Value::Immediate(Immediate::uint256(U256::from(1))));
1187 let b = func.alloc_value(Value::Immediate(Immediate::uint256(U256::from(2))));
1188
1189 let phi = InstKind::Phi(vec![(pred_a, a), (pred_b, b)]);
1190
1191 assert_eq!(phi.operands().as_slice(), &[a, b]);
1192 }
1193
1194 #[test]
1195 #[cfg_attr(not(target_pointer_width = "64"), ignore = "64-bit only")]
1196 #[cfg_attr(feature = "nightly", ignore = "stable only")]
1197 fn instruction_layout_sizes() {
1198 use snapbox::{assert_data_eq, str};
1199
1200 #[track_caller]
1201 fn assert_size<T>(size: impl snapbox::IntoData) {
1202 assert_size_(std::mem::size_of::<T>(), size.into_data());
1203 }
1204
1205 #[track_caller]
1206 fn assert_size_(actual: usize, expected: snapbox::Data) {
1207 assert_data_eq!(actual.to_string(), expected);
1208 }
1209
1210 assert_size::<InstKind>(str!["32"]);
1211 assert_size::<InstructionMetadata>(str!["24"]);
1212 assert_size::<Instruction>(str!["64"]);
1213 }
1214}