1use std::collections::HashMap;
6
7use std::collections::{HashSet, VecDeque};
8
9#[allow(dead_code)]
11#[derive(Debug, Default)]
12pub struct EvmOptPipeline {
13 pub passes: Vec<EvmOptPass>,
14 pub enabled: bool,
15 pub runs: u32,
16}
17#[allow(dead_code)]
18impl EvmOptPipeline {
19 pub fn new(runs: u32) -> Self {
20 Self {
21 passes: vec![
22 EvmOptPass::ConstantFolding,
23 EvmOptPass::DeadCodeElim,
24 EvmOptPass::CommonSubexprElim,
25 EvmOptPass::Peephole,
26 ],
27 enabled: true,
28 runs,
29 }
30 }
31 pub fn add(&mut self, pass: EvmOptPass) {
32 self.passes.push(pass);
33 }
34 pub fn disable(&mut self) {
35 self.enabled = false;
36 }
37}
38#[allow(dead_code)]
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum EvmDiagLevel {
42 Info,
43 Warning,
44 Error,
45}
46#[allow(dead_code)]
48#[derive(Debug, Default, Clone)]
49pub struct YulObject {
50 pub name: String,
51 pub code: Vec<YulStmt>,
52 pub functions: Vec<YulFunction>,
53 pub sub_objects: Vec<YulObject>,
54}
55#[allow(dead_code)]
56impl YulObject {
57 pub fn new(name: &str) -> Self {
58 Self {
59 name: name.to_string(),
60 ..Default::default()
61 }
62 }
63 pub fn add_function(&mut self, f: YulFunction) {
64 self.functions.push(f);
65 }
66 pub fn add_sub_object(&mut self, o: YulObject) {
67 self.sub_objects.push(o);
68 }
69}
70#[allow(dead_code)]
71#[derive(Debug, Clone)]
72pub struct EvmDiag {
73 pub level: EvmDiagLevel,
74 pub message: String,
75 pub location: Option<String>,
76}
77#[derive(Debug, Clone)]
81pub struct EvmBasicBlock {
82 pub label: String,
84 pub instructions: Vec<EvmInstruction>,
86 pub is_jump_target: bool,
88}
89impl EvmBasicBlock {
90 pub fn new(label: impl Into<String>) -> Self {
92 Self {
93 label: label.into(),
94 instructions: Vec::new(),
95 is_jump_target: false,
96 }
97 }
98 pub fn new_jump_target(label: impl Into<String>) -> Self {
100 Self {
101 label: label.into(),
102 instructions: Vec::new(),
103 is_jump_target: true,
104 }
105 }
106 pub fn push_instr(&mut self, instr: EvmInstruction) {
108 self.instructions.push(instr);
109 }
110 pub fn push_op(&mut self, opcode: EvmOpcode) {
112 self.instructions.push(EvmInstruction::new(opcode));
113 }
114 pub fn byte_len(&self) -> usize {
116 let jumpdest_len = if self.is_jump_target { 1 } else { 0 };
117 jumpdest_len
118 + self
119 .instructions
120 .iter()
121 .map(|i| i.byte_len())
122 .sum::<usize>()
123 }
124 pub fn encode(&self) -> Vec<u8> {
126 let mut out = Vec::new();
127 if self.is_jump_target {
128 out.push(EvmOpcode::Jumpdest.byte());
129 }
130 for instr in &self.instructions {
131 out.extend(instr.encode());
132 }
133 out
134 }
135}
136#[derive(Debug, Clone, Default)]
140pub struct StorageLayout {
141 pub slots: HashMap<String, u64>,
143 pub(super) next_slot: u64,
145}
146impl StorageLayout {
147 pub fn new() -> Self {
149 Self::default()
150 }
151 pub fn allocate(&mut self, name: impl Into<String>) -> u64 {
153 let slot = self.next_slot;
154 self.slots.insert(name.into(), slot);
155 self.next_slot += 1;
156 slot
157 }
158 pub fn slot_of(&self, name: &str) -> Option<u64> {
160 self.slots.get(name).copied()
161 }
162 pub fn len(&self) -> usize {
164 self.slots.len()
165 }
166 pub fn is_empty(&self) -> bool {
168 self.slots.is_empty()
169 }
170}
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173pub enum EvmOpcode {
174 Stop,
176 Add,
178 Mul,
180 Sub,
182 Div,
184 Sdiv,
186 Mod,
188 Smod,
190 Addmod,
192 Mulmod,
194 Exp,
196 Signextend,
198 Lt,
200 Gt,
202 Slt,
204 Sgt,
206 Eq,
208 Iszero,
210 And,
212 Or,
214 Xor,
216 Not,
218 Byte,
220 Shl,
222 Shr,
224 Sar,
226 Sha3,
228 Address,
230 Balance,
232 Origin,
234 Caller,
236 Callvalue,
238 Calldataload,
240 Calldatasize,
242 Calldatacopy,
244 Codesize,
246 Codecopy,
248 Gasprice,
250 Extcodesize,
252 Extcodecopy,
254 Returndatasize,
256 Returndatacopy,
258 Extcodehash,
260 Blockhash,
262 Coinbase,
264 Timestamp,
266 Number,
268 Prevrandao,
270 Gaslimit,
272 Chainid,
274 Selfbalance,
276 Basefee,
278 Blobbasefee,
280 Pop,
282 Mload,
284 Mstore,
286 Mstore8,
288 Sload,
290 Sstore,
292 Jump,
294 Jumpi,
296 Pc,
298 Msize,
300 Gas,
302 Jumpdest,
304 Tload,
306 Tstore,
308 Mcopy,
310 Push1,
312 Push2,
314 Push3,
316 Push4,
318 Push5,
320 Push6,
322 Push7,
324 Push8,
326 Push9,
328 Push10,
330 Push11,
332 Push12,
334 Push13,
336 Push14,
338 Push15,
340 Push16,
342 Push17,
344 Push18,
346 Push19,
348 Push20,
350 Push21,
352 Push22,
354 Push23,
356 Push24,
358 Push25,
360 Push26,
362 Push27,
364 Push28,
366 Push29,
368 Push30,
370 Push31,
372 Push32,
374 Dup1,
376 Dup2,
378 Dup3,
380 Dup4,
382 Dup5,
384 Dup6,
386 Dup7,
388 Dup8,
390 Dup9,
392 Dup10,
394 Dup11,
396 Dup12,
398 Dup13,
400 Dup14,
402 Dup15,
404 Dup16,
406 Swap1,
408 Swap2,
410 Swap3,
412 Swap4,
414 Swap5,
416 Swap6,
418 Swap7,
420 Swap8,
422 Swap9,
424 Swap10,
426 Swap11,
428 Swap12,
430 Swap13,
432 Swap14,
434 Swap15,
436 Swap16,
438 Log0,
440 Log1,
442 Log2,
444 Log3,
446 Log4,
448 Create,
450 Call,
452 Callcode,
454 Return,
456 Delegatecall,
458 Create2,
460 Staticcall,
462 Revert,
464 Invalid,
466 Selfdestruct,
468}
469impl EvmOpcode {
470 pub fn byte(&self) -> u8 {
472 match self {
473 EvmOpcode::Stop => 0x00,
474 EvmOpcode::Add => 0x01,
475 EvmOpcode::Mul => 0x02,
476 EvmOpcode::Sub => 0x03,
477 EvmOpcode::Div => 0x04,
478 EvmOpcode::Sdiv => 0x05,
479 EvmOpcode::Mod => 0x06,
480 EvmOpcode::Smod => 0x07,
481 EvmOpcode::Addmod => 0x08,
482 EvmOpcode::Mulmod => 0x09,
483 EvmOpcode::Exp => 0x0a,
484 EvmOpcode::Signextend => 0x0b,
485 EvmOpcode::Lt => 0x10,
486 EvmOpcode::Gt => 0x11,
487 EvmOpcode::Slt => 0x12,
488 EvmOpcode::Sgt => 0x13,
489 EvmOpcode::Eq => 0x14,
490 EvmOpcode::Iszero => 0x15,
491 EvmOpcode::And => 0x16,
492 EvmOpcode::Or => 0x17,
493 EvmOpcode::Xor => 0x18,
494 EvmOpcode::Not => 0x19,
495 EvmOpcode::Byte => 0x1a,
496 EvmOpcode::Shl => 0x1b,
497 EvmOpcode::Shr => 0x1c,
498 EvmOpcode::Sar => 0x1d,
499 EvmOpcode::Sha3 => 0x20,
500 EvmOpcode::Address => 0x30,
501 EvmOpcode::Balance => 0x31,
502 EvmOpcode::Origin => 0x32,
503 EvmOpcode::Caller => 0x33,
504 EvmOpcode::Callvalue => 0x34,
505 EvmOpcode::Calldataload => 0x35,
506 EvmOpcode::Calldatasize => 0x36,
507 EvmOpcode::Calldatacopy => 0x37,
508 EvmOpcode::Codesize => 0x38,
509 EvmOpcode::Codecopy => 0x39,
510 EvmOpcode::Gasprice => 0x3a,
511 EvmOpcode::Extcodesize => 0x3b,
512 EvmOpcode::Extcodecopy => 0x3c,
513 EvmOpcode::Returndatasize => 0x3d,
514 EvmOpcode::Returndatacopy => 0x3e,
515 EvmOpcode::Extcodehash => 0x3f,
516 EvmOpcode::Blockhash => 0x40,
517 EvmOpcode::Coinbase => 0x41,
518 EvmOpcode::Timestamp => 0x42,
519 EvmOpcode::Number => 0x43,
520 EvmOpcode::Prevrandao => 0x44,
521 EvmOpcode::Gaslimit => 0x45,
522 EvmOpcode::Chainid => 0x46,
523 EvmOpcode::Selfbalance => 0x47,
524 EvmOpcode::Basefee => 0x48,
525 EvmOpcode::Blobbasefee => 0x4a,
526 EvmOpcode::Pop => 0x50,
527 EvmOpcode::Mload => 0x51,
528 EvmOpcode::Mstore => 0x52,
529 EvmOpcode::Mstore8 => 0x53,
530 EvmOpcode::Sload => 0x54,
531 EvmOpcode::Sstore => 0x55,
532 EvmOpcode::Jump => 0x56,
533 EvmOpcode::Jumpi => 0x57,
534 EvmOpcode::Pc => 0x58,
535 EvmOpcode::Msize => 0x59,
536 EvmOpcode::Gas => 0x5a,
537 EvmOpcode::Jumpdest => 0x5b,
538 EvmOpcode::Tload => 0x5c,
539 EvmOpcode::Tstore => 0x5d,
540 EvmOpcode::Mcopy => 0x5e,
541 EvmOpcode::Push1 => 0x60,
542 EvmOpcode::Push2 => 0x61,
543 EvmOpcode::Push3 => 0x62,
544 EvmOpcode::Push4 => 0x63,
545 EvmOpcode::Push5 => 0x64,
546 EvmOpcode::Push6 => 0x65,
547 EvmOpcode::Push7 => 0x66,
548 EvmOpcode::Push8 => 0x67,
549 EvmOpcode::Push9 => 0x68,
550 EvmOpcode::Push10 => 0x69,
551 EvmOpcode::Push11 => 0x6a,
552 EvmOpcode::Push12 => 0x6b,
553 EvmOpcode::Push13 => 0x6c,
554 EvmOpcode::Push14 => 0x6d,
555 EvmOpcode::Push15 => 0x6e,
556 EvmOpcode::Push16 => 0x6f,
557 EvmOpcode::Push17 => 0x70,
558 EvmOpcode::Push18 => 0x71,
559 EvmOpcode::Push19 => 0x72,
560 EvmOpcode::Push20 => 0x73,
561 EvmOpcode::Push21 => 0x74,
562 EvmOpcode::Push22 => 0x75,
563 EvmOpcode::Push23 => 0x76,
564 EvmOpcode::Push24 => 0x77,
565 EvmOpcode::Push25 => 0x78,
566 EvmOpcode::Push26 => 0x79,
567 EvmOpcode::Push27 => 0x7a,
568 EvmOpcode::Push28 => 0x7b,
569 EvmOpcode::Push29 => 0x7c,
570 EvmOpcode::Push30 => 0x7d,
571 EvmOpcode::Push31 => 0x7e,
572 EvmOpcode::Push32 => 0x7f,
573 EvmOpcode::Dup1 => 0x80,
574 EvmOpcode::Dup2 => 0x81,
575 EvmOpcode::Dup3 => 0x82,
576 EvmOpcode::Dup4 => 0x83,
577 EvmOpcode::Dup5 => 0x84,
578 EvmOpcode::Dup6 => 0x85,
579 EvmOpcode::Dup7 => 0x86,
580 EvmOpcode::Dup8 => 0x87,
581 EvmOpcode::Dup9 => 0x88,
582 EvmOpcode::Dup10 => 0x89,
583 EvmOpcode::Dup11 => 0x8a,
584 EvmOpcode::Dup12 => 0x8b,
585 EvmOpcode::Dup13 => 0x8c,
586 EvmOpcode::Dup14 => 0x8d,
587 EvmOpcode::Dup15 => 0x8e,
588 EvmOpcode::Dup16 => 0x8f,
589 EvmOpcode::Swap1 => 0x90,
590 EvmOpcode::Swap2 => 0x91,
591 EvmOpcode::Swap3 => 0x92,
592 EvmOpcode::Swap4 => 0x93,
593 EvmOpcode::Swap5 => 0x94,
594 EvmOpcode::Swap6 => 0x95,
595 EvmOpcode::Swap7 => 0x96,
596 EvmOpcode::Swap8 => 0x97,
597 EvmOpcode::Swap9 => 0x98,
598 EvmOpcode::Swap10 => 0x99,
599 EvmOpcode::Swap11 => 0x9a,
600 EvmOpcode::Swap12 => 0x9b,
601 EvmOpcode::Swap13 => 0x9c,
602 EvmOpcode::Swap14 => 0x9d,
603 EvmOpcode::Swap15 => 0x9e,
604 EvmOpcode::Swap16 => 0x9f,
605 EvmOpcode::Log0 => 0xa0,
606 EvmOpcode::Log1 => 0xa1,
607 EvmOpcode::Log2 => 0xa2,
608 EvmOpcode::Log3 => 0xa3,
609 EvmOpcode::Log4 => 0xa4,
610 EvmOpcode::Create => 0xf0,
611 EvmOpcode::Call => 0xf1,
612 EvmOpcode::Callcode => 0xf2,
613 EvmOpcode::Return => 0xf3,
614 EvmOpcode::Delegatecall => 0xf4,
615 EvmOpcode::Create2 => 0xf5,
616 EvmOpcode::Staticcall => 0xfa,
617 EvmOpcode::Revert => 0xfd,
618 EvmOpcode::Invalid => 0xfe,
619 EvmOpcode::Selfdestruct => 0xff,
620 }
621 }
622 pub fn mnemonic(&self) -> &'static str {
624 match self {
625 EvmOpcode::Stop => "STOP",
626 EvmOpcode::Add => "ADD",
627 EvmOpcode::Mul => "MUL",
628 EvmOpcode::Sub => "SUB",
629 EvmOpcode::Div => "DIV",
630 EvmOpcode::Sdiv => "SDIV",
631 EvmOpcode::Mod => "MOD",
632 EvmOpcode::Smod => "SMOD",
633 EvmOpcode::Addmod => "ADDMOD",
634 EvmOpcode::Mulmod => "MULMOD",
635 EvmOpcode::Exp => "EXP",
636 EvmOpcode::Signextend => "SIGNEXTEND",
637 EvmOpcode::Lt => "LT",
638 EvmOpcode::Gt => "GT",
639 EvmOpcode::Slt => "SLT",
640 EvmOpcode::Sgt => "SGT",
641 EvmOpcode::Eq => "EQ",
642 EvmOpcode::Iszero => "ISZERO",
643 EvmOpcode::And => "AND",
644 EvmOpcode::Or => "OR",
645 EvmOpcode::Xor => "XOR",
646 EvmOpcode::Not => "NOT",
647 EvmOpcode::Byte => "BYTE",
648 EvmOpcode::Shl => "SHL",
649 EvmOpcode::Shr => "SHR",
650 EvmOpcode::Sar => "SAR",
651 EvmOpcode::Sha3 => "SHA3",
652 EvmOpcode::Address => "ADDRESS",
653 EvmOpcode::Balance => "BALANCE",
654 EvmOpcode::Origin => "ORIGIN",
655 EvmOpcode::Caller => "CALLER",
656 EvmOpcode::Callvalue => "CALLVALUE",
657 EvmOpcode::Calldataload => "CALLDATALOAD",
658 EvmOpcode::Calldatasize => "CALLDATASIZE",
659 EvmOpcode::Calldatacopy => "CALLDATACOPY",
660 EvmOpcode::Codesize => "CODESIZE",
661 EvmOpcode::Codecopy => "CODECOPY",
662 EvmOpcode::Gasprice => "GASPRICE",
663 EvmOpcode::Extcodesize => "EXTCODESIZE",
664 EvmOpcode::Extcodecopy => "EXTCODECOPY",
665 EvmOpcode::Returndatasize => "RETURNDATASIZE",
666 EvmOpcode::Returndatacopy => "RETURNDATACOPY",
667 EvmOpcode::Extcodehash => "EXTCODEHASH",
668 EvmOpcode::Blockhash => "BLOCKHASH",
669 EvmOpcode::Coinbase => "COINBASE",
670 EvmOpcode::Timestamp => "TIMESTAMP",
671 EvmOpcode::Number => "NUMBER",
672 EvmOpcode::Prevrandao => "PREVRANDAO",
673 EvmOpcode::Gaslimit => "GASLIMIT",
674 EvmOpcode::Chainid => "CHAINID",
675 EvmOpcode::Selfbalance => "SELFBALANCE",
676 EvmOpcode::Basefee => "BASEFEE",
677 EvmOpcode::Blobbasefee => "BLOBBASEFEE",
678 EvmOpcode::Pop => "POP",
679 EvmOpcode::Mload => "MLOAD",
680 EvmOpcode::Mstore => "MSTORE",
681 EvmOpcode::Mstore8 => "MSTORE8",
682 EvmOpcode::Sload => "SLOAD",
683 EvmOpcode::Sstore => "SSTORE",
684 EvmOpcode::Jump => "JUMP",
685 EvmOpcode::Jumpi => "JUMPI",
686 EvmOpcode::Pc => "PC",
687 EvmOpcode::Msize => "MSIZE",
688 EvmOpcode::Gas => "GAS",
689 EvmOpcode::Jumpdest => "JUMPDEST",
690 EvmOpcode::Tload => "TLOAD",
691 EvmOpcode::Tstore => "TSTORE",
692 EvmOpcode::Mcopy => "MCOPY",
693 EvmOpcode::Push1 => "PUSH1",
694 EvmOpcode::Push2 => "PUSH2",
695 EvmOpcode::Push3 => "PUSH3",
696 EvmOpcode::Push4 => "PUSH4",
697 EvmOpcode::Push5 => "PUSH5",
698 EvmOpcode::Push6 => "PUSH6",
699 EvmOpcode::Push7 => "PUSH7",
700 EvmOpcode::Push8 => "PUSH8",
701 EvmOpcode::Push9 => "PUSH9",
702 EvmOpcode::Push10 => "PUSH10",
703 EvmOpcode::Push11 => "PUSH11",
704 EvmOpcode::Push12 => "PUSH12",
705 EvmOpcode::Push13 => "PUSH13",
706 EvmOpcode::Push14 => "PUSH14",
707 EvmOpcode::Push15 => "PUSH15",
708 EvmOpcode::Push16 => "PUSH16",
709 EvmOpcode::Push17 => "PUSH17",
710 EvmOpcode::Push18 => "PUSH18",
711 EvmOpcode::Push19 => "PUSH19",
712 EvmOpcode::Push20 => "PUSH20",
713 EvmOpcode::Push21 => "PUSH21",
714 EvmOpcode::Push22 => "PUSH22",
715 EvmOpcode::Push23 => "PUSH23",
716 EvmOpcode::Push24 => "PUSH24",
717 EvmOpcode::Push25 => "PUSH25",
718 EvmOpcode::Push26 => "PUSH26",
719 EvmOpcode::Push27 => "PUSH27",
720 EvmOpcode::Push28 => "PUSH28",
721 EvmOpcode::Push29 => "PUSH29",
722 EvmOpcode::Push30 => "PUSH30",
723 EvmOpcode::Push31 => "PUSH31",
724 EvmOpcode::Push32 => "PUSH32",
725 EvmOpcode::Dup1 => "DUP1",
726 EvmOpcode::Dup2 => "DUP2",
727 EvmOpcode::Dup3 => "DUP3",
728 EvmOpcode::Dup4 => "DUP4",
729 EvmOpcode::Dup5 => "DUP5",
730 EvmOpcode::Dup6 => "DUP6",
731 EvmOpcode::Dup7 => "DUP7",
732 EvmOpcode::Dup8 => "DUP8",
733 EvmOpcode::Dup9 => "DUP9",
734 EvmOpcode::Dup10 => "DUP10",
735 EvmOpcode::Dup11 => "DUP11",
736 EvmOpcode::Dup12 => "DUP12",
737 EvmOpcode::Dup13 => "DUP13",
738 EvmOpcode::Dup14 => "DUP14",
739 EvmOpcode::Dup15 => "DUP15",
740 EvmOpcode::Dup16 => "DUP16",
741 EvmOpcode::Swap1 => "SWAP1",
742 EvmOpcode::Swap2 => "SWAP2",
743 EvmOpcode::Swap3 => "SWAP3",
744 EvmOpcode::Swap4 => "SWAP4",
745 EvmOpcode::Swap5 => "SWAP5",
746 EvmOpcode::Swap6 => "SWAP6",
747 EvmOpcode::Swap7 => "SWAP7",
748 EvmOpcode::Swap8 => "SWAP8",
749 EvmOpcode::Swap9 => "SWAP9",
750 EvmOpcode::Swap10 => "SWAP10",
751 EvmOpcode::Swap11 => "SWAP11",
752 EvmOpcode::Swap12 => "SWAP12",
753 EvmOpcode::Swap13 => "SWAP13",
754 EvmOpcode::Swap14 => "SWAP14",
755 EvmOpcode::Swap15 => "SWAP15",
756 EvmOpcode::Swap16 => "SWAP16",
757 EvmOpcode::Log0 => "LOG0",
758 EvmOpcode::Log1 => "LOG1",
759 EvmOpcode::Log2 => "LOG2",
760 EvmOpcode::Log3 => "LOG3",
761 EvmOpcode::Log4 => "LOG4",
762 EvmOpcode::Create => "CREATE",
763 EvmOpcode::Call => "CALL",
764 EvmOpcode::Callcode => "CALLCODE",
765 EvmOpcode::Return => "RETURN",
766 EvmOpcode::Delegatecall => "DELEGATECALL",
767 EvmOpcode::Create2 => "CREATE2",
768 EvmOpcode::Staticcall => "STATICCALL",
769 EvmOpcode::Revert => "REVERT",
770 EvmOpcode::Invalid => "INVALID",
771 EvmOpcode::Selfdestruct => "SELFDESTRUCT",
772 }
773 }
774 pub fn immediate_size(&self) -> usize {
776 match self {
777 EvmOpcode::Push1 => 1,
778 EvmOpcode::Push2 => 2,
779 EvmOpcode::Push3 => 3,
780 EvmOpcode::Push4 => 4,
781 EvmOpcode::Push5 => 5,
782 EvmOpcode::Push6 => 6,
783 EvmOpcode::Push7 => 7,
784 EvmOpcode::Push8 => 8,
785 EvmOpcode::Push9 => 9,
786 EvmOpcode::Push10 => 10,
787 EvmOpcode::Push11 => 11,
788 EvmOpcode::Push12 => 12,
789 EvmOpcode::Push13 => 13,
790 EvmOpcode::Push14 => 14,
791 EvmOpcode::Push15 => 15,
792 EvmOpcode::Push16 => 16,
793 EvmOpcode::Push17 => 17,
794 EvmOpcode::Push18 => 18,
795 EvmOpcode::Push19 => 19,
796 EvmOpcode::Push20 => 20,
797 EvmOpcode::Push21 => 21,
798 EvmOpcode::Push22 => 22,
799 EvmOpcode::Push23 => 23,
800 EvmOpcode::Push24 => 24,
801 EvmOpcode::Push25 => 25,
802 EvmOpcode::Push26 => 26,
803 EvmOpcode::Push27 => 27,
804 EvmOpcode::Push28 => 28,
805 EvmOpcode::Push29 => 29,
806 EvmOpcode::Push30 => 30,
807 EvmOpcode::Push31 => 31,
808 EvmOpcode::Push32 => 32,
809 _ => 0,
810 }
811 }
812 pub fn push_for_size(n: usize) -> Option<EvmOpcode> {
814 match n {
815 1 => Some(EvmOpcode::Push1),
816 2 => Some(EvmOpcode::Push2),
817 3 => Some(EvmOpcode::Push3),
818 4 => Some(EvmOpcode::Push4),
819 8 => Some(EvmOpcode::Push8),
820 20 => Some(EvmOpcode::Push20),
821 32 => Some(EvmOpcode::Push32),
822 _ => None,
823 }
824 }
825}
826#[allow(dead_code)]
827pub struct EVMPassRegistry {
828 pub(super) configs: Vec<EVMPassConfig>,
829 pub(super) stats: std::collections::HashMap<String, EVMPassStats>,
830}
831impl EVMPassRegistry {
832 #[allow(dead_code)]
833 pub fn new() -> Self {
834 EVMPassRegistry {
835 configs: Vec::new(),
836 stats: std::collections::HashMap::new(),
837 }
838 }
839 #[allow(dead_code)]
840 pub fn register(&mut self, config: EVMPassConfig) {
841 self.stats
842 .insert(config.pass_name.clone(), EVMPassStats::new());
843 self.configs.push(config);
844 }
845 #[allow(dead_code)]
846 pub fn enabled_passes(&self) -> Vec<&EVMPassConfig> {
847 self.configs.iter().filter(|c| c.enabled).collect()
848 }
849 #[allow(dead_code)]
850 pub fn get_stats(&self, name: &str) -> Option<&EVMPassStats> {
851 self.stats.get(name)
852 }
853 #[allow(dead_code)]
854 pub fn total_passes(&self) -> usize {
855 self.configs.len()
856 }
857 #[allow(dead_code)]
858 pub fn enabled_count(&self) -> usize {
859 self.enabled_passes().len()
860 }
861 #[allow(dead_code)]
862 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
863 if let Some(stats) = self.stats.get_mut(name) {
864 stats.record_run(changes, time_ms, iter);
865 }
866 }
867}
868#[allow(dead_code)]
869#[derive(Debug, Clone, PartialEq)]
870pub enum EVMPassPhase {
871 Analysis,
872 Transformation,
873 Verification,
874 Cleanup,
875}
876impl EVMPassPhase {
877 #[allow(dead_code)]
878 pub fn name(&self) -> &str {
879 match self {
880 EVMPassPhase::Analysis => "analysis",
881 EVMPassPhase::Transformation => "transformation",
882 EVMPassPhase::Verification => "verification",
883 EVMPassPhase::Cleanup => "cleanup",
884 }
885 }
886 #[allow(dead_code)]
887 pub fn is_modifying(&self) -> bool {
888 matches!(self, EVMPassPhase::Transformation | EVMPassPhase::Cleanup)
889 }
890}
891#[allow(dead_code)]
892#[derive(Debug, Clone)]
893pub struct EVMWorklist {
894 pub(super) items: std::collections::VecDeque<u32>,
895 pub(super) in_worklist: std::collections::HashSet<u32>,
896}
897impl EVMWorklist {
898 #[allow(dead_code)]
899 pub fn new() -> Self {
900 EVMWorklist {
901 items: std::collections::VecDeque::new(),
902 in_worklist: std::collections::HashSet::new(),
903 }
904 }
905 #[allow(dead_code)]
906 pub fn push(&mut self, item: u32) -> bool {
907 if self.in_worklist.insert(item) {
908 self.items.push_back(item);
909 true
910 } else {
911 false
912 }
913 }
914 #[allow(dead_code)]
915 pub fn pop(&mut self) -> Option<u32> {
916 let item = self.items.pop_front()?;
917 self.in_worklist.remove(&item);
918 Some(item)
919 }
920 #[allow(dead_code)]
921 pub fn is_empty(&self) -> bool {
922 self.items.is_empty()
923 }
924 #[allow(dead_code)]
925 pub fn len(&self) -> usize {
926 self.items.len()
927 }
928 #[allow(dead_code)]
929 pub fn contains(&self, item: u32) -> bool {
930 self.in_worklist.contains(&item)
931 }
932}
933#[allow(dead_code)]
934#[derive(Debug, Clone)]
935pub struct EVMLivenessInfo {
936 pub live_in: Vec<std::collections::HashSet<u32>>,
937 pub live_out: Vec<std::collections::HashSet<u32>>,
938 pub defs: Vec<std::collections::HashSet<u32>>,
939 pub uses: Vec<std::collections::HashSet<u32>>,
940}
941impl EVMLivenessInfo {
942 #[allow(dead_code)]
943 pub fn new(block_count: usize) -> Self {
944 EVMLivenessInfo {
945 live_in: vec![std::collections::HashSet::new(); block_count],
946 live_out: vec![std::collections::HashSet::new(); block_count],
947 defs: vec![std::collections::HashSet::new(); block_count],
948 uses: vec![std::collections::HashSet::new(); block_count],
949 }
950 }
951 #[allow(dead_code)]
952 pub fn add_def(&mut self, block: usize, var: u32) {
953 if block < self.defs.len() {
954 self.defs[block].insert(var);
955 }
956 }
957 #[allow(dead_code)]
958 pub fn add_use(&mut self, block: usize, var: u32) {
959 if block < self.uses.len() {
960 self.uses[block].insert(var);
961 }
962 }
963 #[allow(dead_code)]
964 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
965 self.live_in
966 .get(block)
967 .map(|s| s.contains(&var))
968 .unwrap_or(false)
969 }
970 #[allow(dead_code)]
971 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
972 self.live_out
973 .get(block)
974 .map(|s| s.contains(&var))
975 .unwrap_or(false)
976 }
977}
978#[allow(dead_code)]
980#[derive(Debug, Default, Clone)]
981pub struct EvmCodeSizeStats {
982 pub bytecode_size: usize,
983 pub deploy_bytecode_size: usize,
984 pub constructor_size: usize,
985 pub function_sizes: std::collections::HashMap<String, usize>,
986}
987#[allow(dead_code)]
989#[derive(Debug, Default)]
990pub struct EvmExtIdGen {
991 pub(super) counter: u64,
992 pub(super) prefix: String,
993}
994#[allow(dead_code)]
995impl EvmExtIdGen {
996 pub fn new(prefix: &str) -> Self {
997 Self {
998 counter: 0,
999 prefix: prefix.to_string(),
1000 }
1001 }
1002 pub fn next(&mut self) -> String {
1003 let id = self.counter;
1004 self.counter += 1;
1005 format!("{}_{}", self.prefix, id)
1006 }
1007}
1008#[allow(dead_code)]
1010#[derive(Debug, Clone)]
1011pub struct EvmStorageSlot {
1012 pub slot: u64,
1013 pub offset: u8,
1014 pub var_name: String,
1015 pub var_type: EvmAbiType,
1016}
1017#[allow(dead_code)]
1019#[derive(Debug, Default, Clone)]
1020pub struct EvmCodeStats {
1021 pub functions: usize,
1022 pub events: usize,
1023 pub modifiers: usize,
1024 pub storage_vars: usize,
1025 pub bytecode_size: usize,
1026}
1027#[allow(dead_code)]
1028#[derive(Debug, Clone)]
1029pub struct EVMAnalysisCache {
1030 pub(super) entries: std::collections::HashMap<String, EVMCacheEntry>,
1031 pub(super) max_size: usize,
1032 pub(super) hits: u64,
1033 pub(super) misses: u64,
1034}
1035impl EVMAnalysisCache {
1036 #[allow(dead_code)]
1037 pub fn new(max_size: usize) -> Self {
1038 EVMAnalysisCache {
1039 entries: std::collections::HashMap::new(),
1040 max_size,
1041 hits: 0,
1042 misses: 0,
1043 }
1044 }
1045 #[allow(dead_code)]
1046 pub fn get(&mut self, key: &str) -> Option<&EVMCacheEntry> {
1047 if self.entries.contains_key(key) {
1048 self.hits += 1;
1049 self.entries.get(key)
1050 } else {
1051 self.misses += 1;
1052 None
1053 }
1054 }
1055 #[allow(dead_code)]
1056 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1057 if self.entries.len() >= self.max_size {
1058 if let Some(oldest) = self.entries.keys().next().cloned() {
1059 self.entries.remove(&oldest);
1060 }
1061 }
1062 self.entries.insert(
1063 key.clone(),
1064 EVMCacheEntry {
1065 key,
1066 data,
1067 timestamp: 0,
1068 valid: true,
1069 },
1070 );
1071 }
1072 #[allow(dead_code)]
1073 pub fn invalidate(&mut self, key: &str) {
1074 if let Some(entry) = self.entries.get_mut(key) {
1075 entry.valid = false;
1076 }
1077 }
1078 #[allow(dead_code)]
1079 pub fn clear(&mut self) {
1080 self.entries.clear();
1081 }
1082 #[allow(dead_code)]
1083 pub fn hit_rate(&self) -> f64 {
1084 let total = self.hits + self.misses;
1085 if total == 0 {
1086 return 0.0;
1087 }
1088 self.hits as f64 / total as f64
1089 }
1090 #[allow(dead_code)]
1091 pub fn size(&self) -> usize {
1092 self.entries.len()
1093 }
1094}
1095#[allow(dead_code)]
1097#[derive(Debug, Default)]
1098pub struct EvmExtSourceBuffer {
1099 pub sections: Vec<(String, String)>,
1100 pub current: String,
1101 pub indent: usize,
1102}
1103#[allow(dead_code)]
1104impl EvmExtSourceBuffer {
1105 pub fn new() -> Self {
1106 Self::default()
1107 }
1108 pub fn write(&mut self, s: &str) {
1109 let pad = " ".repeat(self.indent);
1110 self.current.push_str(&pad);
1111 self.current.push_str(s);
1112 }
1113 pub fn writeln(&mut self, s: &str) {
1114 let pad = " ".repeat(self.indent);
1115 self.current.push_str(&pad);
1116 self.current.push_str(s);
1117 self.current.push('\n');
1118 }
1119 pub fn indent(&mut self) {
1120 self.indent += 1;
1121 }
1122 pub fn dedent(&mut self) {
1123 if self.indent > 0 {
1124 self.indent -= 1;
1125 }
1126 }
1127 pub fn begin_section(&mut self, name: &str) {
1128 let done = std::mem::take(&mut self.current);
1129 if !done.is_empty() {
1130 self.sections.push(("anon".to_string(), done));
1131 }
1132 self.current = format!("// === {} ===\n", name);
1133 }
1134 pub fn finish(mut self) -> String {
1135 let done = std::mem::take(&mut self.current);
1136 if !done.is_empty() {
1137 self.sections.push(("anon".to_string(), done));
1138 }
1139 self.sections
1140 .iter()
1141 .map(|(_, s)| s.as_str())
1142 .collect::<Vec<_>>()
1143 .join("")
1144 }
1145}
1146#[allow(dead_code)]
1147#[derive(Debug, Default)]
1148pub struct EvmDiagSink {
1149 pub diags: Vec<EvmDiag>,
1150}
1151#[allow(dead_code)]
1152impl EvmDiagSink {
1153 pub fn new() -> Self {
1154 Self::default()
1155 }
1156 pub fn push(&mut self, level: EvmDiagLevel, msg: &str) {
1157 self.diags.push(EvmDiag {
1158 level,
1159 message: msg.to_string(),
1160 location: None,
1161 });
1162 }
1163 pub fn has_errors(&self) -> bool {
1164 self.diags.iter().any(|d| d.level == EvmDiagLevel::Error)
1165 }
1166}
1167#[derive(Debug, Clone)]
1171pub struct EvmContract {
1172 pub name: String,
1174 pub functions: Vec<EvmFunction>,
1176 pub storage_layout: StorageLayout,
1178 pub constructor_code: Vec<EvmInstruction>,
1180 pub metadata: HashMap<String, String>,
1182}
1183impl EvmContract {
1184 pub fn new(name: impl Into<String>) -> Self {
1186 Self {
1187 name: name.into(),
1188 functions: Vec::new(),
1189 storage_layout: StorageLayout::new(),
1190 constructor_code: Vec::new(),
1191 metadata: HashMap::new(),
1192 }
1193 }
1194 pub fn add_function(&mut self, func: EvmFunction) {
1196 self.functions.push(func);
1197 }
1198 pub fn allocate_storage(&mut self, name: impl Into<String>) -> u64 {
1200 self.storage_layout.allocate(name)
1201 }
1202 pub fn set_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
1204 self.metadata.insert(key.into(), value.into());
1205 }
1206}
1207#[allow(dead_code)]
1209#[derive(Debug, Default)]
1210pub struct EvmExtProfiler {
1211 pub timings: Vec<(String, u64)>,
1212}
1213#[allow(dead_code)]
1214impl EvmExtProfiler {
1215 pub fn new() -> Self {
1216 Self::default()
1217 }
1218 pub fn record(&mut self, pass: &str, us: u64) {
1219 self.timings.push((pass.to_string(), us));
1220 }
1221 pub fn total_us(&self) -> u64 {
1222 self.timings.iter().map(|(_, t)| *t).sum()
1223 }
1224}
1225#[allow(dead_code)]
1227#[derive(Debug, Clone)]
1228pub enum YulExpr {
1229 Literal(u64),
1230 Variable(String),
1231 FunctionCall(String, Vec<YulExpr>),
1232}
1233#[allow(dead_code)]
1235#[derive(Debug, Clone)]
1236pub struct YulFunction {
1237 pub name: String,
1238 pub params: Vec<String>,
1239 pub returns: Vec<String>,
1240 pub body: Vec<YulStmt>,
1241}
1242#[allow(dead_code)]
1243#[derive(Debug, Clone)]
1244pub struct EVMCacheEntry {
1245 pub key: String,
1246 pub data: Vec<u8>,
1247 pub timestamp: u64,
1248 pub valid: bool,
1249}
1250#[allow(dead_code)]
1252#[derive(Debug, Clone)]
1253pub struct EvmOpcodeDesc {
1254 pub name: String,
1255 pub opcode: u8,
1256 pub stack_in: u8,
1257 pub stack_out: u8,
1258 pub gas: u64,
1259 pub category: EvmOpcodeCategory,
1260 pub description: String,
1261}
1262#[allow(dead_code)]
1263pub struct EVMConstantFoldingHelper;
1264impl EVMConstantFoldingHelper {
1265 #[allow(dead_code)]
1266 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1267 a.checked_add(b)
1268 }
1269 #[allow(dead_code)]
1270 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1271 a.checked_sub(b)
1272 }
1273 #[allow(dead_code)]
1274 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1275 a.checked_mul(b)
1276 }
1277 #[allow(dead_code)]
1278 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1279 if b == 0 {
1280 None
1281 } else {
1282 a.checked_div(b)
1283 }
1284 }
1285 #[allow(dead_code)]
1286 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1287 a + b
1288 }
1289 #[allow(dead_code)]
1290 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1291 a * b
1292 }
1293 #[allow(dead_code)]
1294 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1295 a.checked_neg()
1296 }
1297 #[allow(dead_code)]
1298 pub fn fold_not_bool(a: bool) -> bool {
1299 !a
1300 }
1301 #[allow(dead_code)]
1302 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1303 a && b
1304 }
1305 #[allow(dead_code)]
1306 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1307 a || b
1308 }
1309 #[allow(dead_code)]
1310 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1311 a.checked_shl(b)
1312 }
1313 #[allow(dead_code)]
1314 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1315 a.checked_shr(b)
1316 }
1317 #[allow(dead_code)]
1318 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1319 if b == 0 {
1320 None
1321 } else {
1322 Some(a % b)
1323 }
1324 }
1325 #[allow(dead_code)]
1326 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1327 a & b
1328 }
1329 #[allow(dead_code)]
1330 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1331 a | b
1332 }
1333 #[allow(dead_code)]
1334 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1335 a ^ b
1336 }
1337 #[allow(dead_code)]
1338 pub fn fold_bitnot_i64(a: i64) -> i64 {
1339 !a
1340 }
1341}
1342#[allow(dead_code)]
1343#[derive(Debug, Clone)]
1344pub struct EVMDepGraph {
1345 pub(super) nodes: Vec<u32>,
1346 pub(super) edges: Vec<(u32, u32)>,
1347}
1348impl EVMDepGraph {
1349 #[allow(dead_code)]
1350 pub fn new() -> Self {
1351 EVMDepGraph {
1352 nodes: Vec::new(),
1353 edges: Vec::new(),
1354 }
1355 }
1356 #[allow(dead_code)]
1357 pub fn add_node(&mut self, id: u32) {
1358 if !self.nodes.contains(&id) {
1359 self.nodes.push(id);
1360 }
1361 }
1362 #[allow(dead_code)]
1363 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1364 self.add_node(dep);
1365 self.add_node(dependent);
1366 self.edges.push((dep, dependent));
1367 }
1368 #[allow(dead_code)]
1369 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1370 self.edges
1371 .iter()
1372 .filter(|(d, _)| *d == node)
1373 .map(|(_, dep)| *dep)
1374 .collect()
1375 }
1376 #[allow(dead_code)]
1377 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1378 self.edges
1379 .iter()
1380 .filter(|(_, dep)| *dep == node)
1381 .map(|(d, _)| *d)
1382 .collect()
1383 }
1384 #[allow(dead_code)]
1385 pub fn topological_sort(&self) -> Vec<u32> {
1386 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1387 for &n in &self.nodes {
1388 in_degree.insert(n, 0);
1389 }
1390 for (_, dep) in &self.edges {
1391 *in_degree.entry(*dep).or_insert(0) += 1;
1392 }
1393 let mut queue: std::collections::VecDeque<u32> = self
1394 .nodes
1395 .iter()
1396 .filter(|&&n| in_degree[&n] == 0)
1397 .copied()
1398 .collect();
1399 let mut result = Vec::new();
1400 while let Some(node) = queue.pop_front() {
1401 result.push(node);
1402 for dep in self.dependents_of(node) {
1403 let cnt = in_degree.entry(dep).or_insert(0);
1404 *cnt = cnt.saturating_sub(1);
1405 if *cnt == 0 {
1406 queue.push_back(dep);
1407 }
1408 }
1409 }
1410 result
1411 }
1412 #[allow(dead_code)]
1413 pub fn has_cycle(&self) -> bool {
1414 self.topological_sort().len() < self.nodes.len()
1415 }
1416}
1417#[allow(dead_code)]
1419#[derive(Debug, Clone, Default)]
1420pub struct EvmFeatureFlags {
1421 pub shanghai: bool,
1422 pub cancun: bool,
1423 pub prague: bool,
1424 pub support_transient_storage: bool,
1425 pub support_push0: bool,
1426}
1427#[allow(dead_code)]
1429#[derive(Debug, Clone)]
1430pub struct EvmSelector {
1431 pub signature: String,
1432 pub selector: [u8; 4],
1433}
1434#[allow(dead_code)]
1435impl EvmSelector {
1436 pub fn from_signature(sig: &str) -> Self {
1437 let bytes = sig.as_bytes();
1438 let selector = [
1439 bytes.get(0).copied().unwrap_or(0),
1440 bytes.get(1).copied().unwrap_or(0),
1441 bytes.get(2).copied().unwrap_or(0),
1442 bytes.get(3).copied().unwrap_or(0),
1443 ];
1444 Self {
1445 signature: sig.to_string(),
1446 selector,
1447 }
1448 }
1449 pub fn hex(&self) -> String {
1450 format!(
1451 "{:02x}{:02x}{:02x}{:02x}",
1452 self.selector[0], self.selector[1], self.selector[2], self.selector[3]
1453 )
1454 }
1455}
1456#[allow(dead_code)]
1458#[derive(Debug, Clone)]
1459pub struct EvmAbiFunction {
1460 pub name: String,
1461 pub inputs: Vec<(String, EvmAbiType)>,
1462 pub outputs: Vec<(String, EvmAbiType)>,
1463 pub state_mutability: String,
1464 pub is_payable: bool,
1465 pub is_view: bool,
1466 pub is_pure: bool,
1467}
1468#[derive(Debug, Default)]
1475pub struct EvmBackend {
1476 pub(super) label_offsets: HashMap<String, usize>,
1478}
1479impl EvmBackend {
1480 pub fn new() -> Self {
1482 Self::default()
1483 }
1484 pub fn compute_selector(signature: &str) -> [u8; 4] {
1489 let mut hash: u32 = 0x811c9dc5;
1490 for &b in signature.as_bytes() {
1491 hash ^= b as u32;
1492 hash = hash.wrapping_mul(0x01000193);
1493 }
1494 hash.to_be_bytes()
1495 }
1496 pub fn emit_dispatcher(&self, contract: &EvmContract) -> Vec<EvmInstruction> {
1504 let mut instrs = vec![
1505 EvmInstruction::new(EvmOpcode::Push1).with_comment("calldata offset 0"),
1506 EvmInstruction::push1(0).with_comment("offset = 0"),
1507 EvmInstruction::new(EvmOpcode::Calldataload)
1508 .with_comment("load 32 bytes from calldata[0]"),
1509 EvmInstruction::push1(0xe0).with_comment("shift 224 bits"),
1510 EvmInstruction::new(EvmOpcode::Shr).with_comment("selector = calldata >> 224"),
1511 ];
1512 for func in &contract.functions {
1513 let sel_val = u32::from_be_bytes(func.selector);
1514 instrs.push(
1515 EvmInstruction::new(EvmOpcode::Dup1).with_comment(format!("check {}", func.name)),
1516 );
1517 instrs.push(
1518 EvmInstruction::push4(sel_val)
1519 .with_comment(format!("selector for {}", func.signature)),
1520 );
1521 instrs.push(EvmInstruction::new(EvmOpcode::Eq));
1522 instrs.push(
1523 EvmInstruction::push(vec![0x00, 0x00])
1524 .expect("push of 2-byte slice is always valid (1..=32 bytes)")
1525 .with_comment(format!("dest: {}", func.name)),
1526 );
1527 instrs.push(EvmInstruction::new(EvmOpcode::Jumpi));
1528 }
1529 instrs.push(EvmInstruction::push1(0).with_comment("revert size 0"));
1530 instrs.push(EvmInstruction::push1(0).with_comment("revert offset 0"));
1531 instrs.push(EvmInstruction::new(EvmOpcode::Revert).with_comment("no matching selector"));
1532 instrs
1533 }
1534 pub fn emit_sload(&self, slot: u64) -> Vec<EvmInstruction> {
1536 let mut instrs = Vec::new();
1537 let bytes = slot.to_be_bytes();
1538 let trimmed: Vec<u8> = {
1539 let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(7);
1540 bytes[first_nonzero..].to_vec()
1541 };
1542 let push_instr = EvmInstruction::push(if trimmed.is_empty() {
1543 vec![0x00]
1544 } else {
1545 trimmed
1546 })
1547 .expect("push of 1..=8 byte slot always valid (within 1..=32 byte range)")
1548 .with_comment(format!("storage slot {}", slot));
1549 instrs.push(push_instr);
1550 instrs.push(
1551 EvmInstruction::new(EvmOpcode::Sload).with_comment(format!("SLOAD slot {}", slot)),
1552 );
1553 instrs
1554 }
1555 pub fn emit_sstore(&self, slot: u64) -> Vec<EvmInstruction> {
1559 let mut instrs = Vec::new();
1560 let bytes = slot.to_be_bytes();
1561 let trimmed: Vec<u8> = {
1562 let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(7);
1563 bytes[first_nonzero..].to_vec()
1564 };
1565 let push_instr = EvmInstruction::push(if trimmed.is_empty() {
1566 vec![0x00]
1567 } else {
1568 trimmed
1569 })
1570 .expect("push of 1..=8 byte slot always valid (within 1..=32 byte range)")
1571 .with_comment(format!("storage slot {}", slot));
1572 instrs.push(push_instr);
1573 instrs.push(
1574 EvmInstruction::new(EvmOpcode::Sstore).with_comment(format!("SSTORE slot {}", slot)),
1575 );
1576 instrs
1577 }
1578 pub fn emit_constructor_bytes(&self, contract: &EvmContract) -> Vec<u8> {
1580 contract
1581 .constructor_code
1582 .iter()
1583 .flat_map(|i| i.encode())
1584 .collect()
1585 }
1586 pub fn emit_runtime_bytes(&self, contract: &EvmContract) -> Vec<u8> {
1590 let mut out = Vec::new();
1591 for instr in self.emit_dispatcher(contract) {
1592 out.extend(instr.encode());
1593 }
1594 for func in &contract.functions {
1595 out.extend(func.encode());
1596 }
1597 out
1598 }
1599 pub fn emit_init_code(&self, contract: &EvmContract) -> Vec<u8> {
1605 let runtime = self.emit_runtime_bytes(contract);
1606 let runtime_len = runtime.len();
1607 let constructor = self.emit_constructor_bytes(contract);
1608 let mut init = Vec::new();
1609 init.extend_from_slice(&constructor);
1610 if runtime_len <= 0xffff {
1611 let len_bytes = (runtime_len as u16).to_be_bytes();
1612 init.extend(
1613 EvmInstruction::push(len_bytes.to_vec())
1614 .expect("2-byte push is always valid")
1615 .encode(),
1616 );
1617 } else {
1618 let len_bytes = (runtime_len as u32).to_be_bytes();
1619 init.extend(
1620 EvmInstruction::push(len_bytes.to_vec())
1621 .expect("4-byte push is always valid")
1622 .encode(),
1623 );
1624 }
1625 init.extend(
1626 EvmInstruction::push(vec![0x00, 0x00])
1627 .expect("2-byte push is always valid")
1628 .encode(),
1629 );
1630 init.extend(EvmInstruction::push1(0x00).encode());
1631 init.push(EvmOpcode::Codecopy.byte());
1632 if runtime_len <= 0xffff {
1633 let len_bytes = (runtime_len as u16).to_be_bytes();
1634 init.extend(
1635 EvmInstruction::push(len_bytes.to_vec())
1636 .expect("2-byte push is always valid")
1637 .encode(),
1638 );
1639 } else {
1640 let len_bytes = (runtime_len as u32).to_be_bytes();
1641 init.extend(
1642 EvmInstruction::push(len_bytes.to_vec())
1643 .expect("4-byte push is always valid")
1644 .encode(),
1645 );
1646 }
1647 init.extend(EvmInstruction::push1(0x00).encode());
1648 init.push(EvmOpcode::Return.byte());
1649 init.extend_from_slice(&runtime);
1650 init
1651 }
1652 pub fn emit_hex(&self, contract: &EvmContract) -> String {
1654 let bytes = self.emit_runtime_bytes(contract);
1655 bytes.iter().map(|b| format!("{:02x}", b)).collect()
1656 }
1657 pub fn emit_hex_prefixed(&self, contract: &EvmContract) -> String {
1659 format!("0x{}", self.emit_hex(contract))
1660 }
1661 pub fn emit_init_hex(&self, contract: &EvmContract) -> String {
1663 let bytes = self.emit_init_code(contract);
1664 format!(
1665 "0x{}",
1666 bytes
1667 .iter()
1668 .map(|b| format!("{:02x}", b))
1669 .collect::<String>()
1670 )
1671 }
1672 pub(super) fn format_instr_line(offset: usize, instr: &EvmInstruction) -> String {
1674 let mut line = format!("{:04x} {}", offset, instr.opcode.mnemonic());
1675 if let Some(ref data) = instr.data {
1676 line.push(' ');
1677 line.push_str("0x");
1678 for b in data {
1679 line.push_str(&format!("{:02x}", b));
1680 }
1681 }
1682 if let Some(ref c) = instr.comment {
1683 while line.len() < 30 {
1684 line.push(' ');
1685 }
1686 line.push_str("; ");
1687 line.push_str(c);
1688 }
1689 line
1690 }
1691 pub fn emit_assembly(&self, contract: &EvmContract) -> String {
1693 let mut out = String::new();
1694 out.push_str(&format!("; Contract: {}\n", contract.name));
1695 for (k, v) in &contract.metadata {
1696 out.push_str(&format!("; {}: {}\n", k, v));
1697 }
1698 out.push('\n');
1699 if !contract.storage_layout.is_empty() {
1700 out.push_str("; Storage Layout:\n");
1701 let mut slots: Vec<_> = contract.storage_layout.slots.iter().collect();
1702 slots.sort_by_key(|(_, &s)| s);
1703 for (name, slot) in &slots {
1704 out.push_str(&format!("; slot {:3}: {}\n", slot, name));
1705 }
1706 out.push('\n');
1707 }
1708 if !contract.constructor_code.is_empty() {
1709 out.push_str("constructor:\n");
1710 let mut offset = 0usize;
1711 for instr in &contract.constructor_code {
1712 out.push_str(&format!(" {}\n", Self::format_instr_line(offset, instr)));
1713 offset += instr.byte_len();
1714 }
1715 out.push('\n');
1716 }
1717 out.push_str("runtime_dispatcher:\n");
1718 let dispatcher = self.emit_dispatcher(contract);
1719 let mut offset = 0usize;
1720 for instr in &dispatcher {
1721 out.push_str(&format!(" {}\n", Self::format_instr_line(offset, instr)));
1722 offset += instr.byte_len();
1723 }
1724 out.push('\n');
1725 for func in &contract.functions {
1726 let sel_hex: String = func.selector.iter().map(|b| format!("{:02x}", b)).collect();
1727 out.push_str(&format!(
1728 "function {} (selector: 0x{}) ; {}\n",
1729 func.name, sel_hex, func.signature
1730 ));
1731 if func.is_payable {
1732 out.push_str("; payable\n");
1733 }
1734 if func.is_view {
1735 out.push_str("; view\n");
1736 }
1737 for block in &func.blocks {
1738 out.push_str(&format!(" .{}:\n", block.label));
1739 if block.is_jump_target {
1740 out.push_str(&format!(" {:04x} JUMPDEST\n", offset));
1741 offset += 1;
1742 }
1743 for instr in &block.instructions {
1744 out.push_str(&format!(" {}\n", Self::format_instr_line(offset, instr)));
1745 offset += instr.byte_len();
1746 }
1747 }
1748 out.push('\n');
1749 }
1750 out
1751 }
1752 #[allow(clippy::too_many_arguments)]
1757 pub fn build_arithmetic_function(
1758 name: &str,
1759 signature: &str,
1760 selector: [u8; 4],
1761 op: EvmOpcode,
1762 ) -> EvmFunction {
1763 let mut func = EvmFunction::new(name, selector, signature);
1764 let mut block = EvmBasicBlock::new_jump_target("entry");
1765 block.push_instr(EvmInstruction::push1(4).with_comment("calldata offset for arg0"));
1766 block.push_op(EvmOpcode::Calldataload);
1767 block.push_instr(EvmInstruction::push1(36).with_comment("calldata offset for arg1"));
1768 block.push_op(EvmOpcode::Calldataload);
1769 block.push_instr(EvmInstruction::new(op).with_comment("arithmetic op"));
1770 block.push_instr(EvmInstruction::push1(0).with_comment("mem offset"));
1771 block.push_op(EvmOpcode::Mstore);
1772 block.push_instr(EvmInstruction::push1(32).with_comment("return size"));
1773 block.push_instr(EvmInstruction::push1(0).with_comment("return offset"));
1774 block.push_op(EvmOpcode::Return);
1775 func.add_block(block);
1776 func
1777 }
1778}
1779#[allow(dead_code)]
1780#[derive(Debug, Clone)]
1781pub struct EVMPassConfig {
1782 pub phase: EVMPassPhase,
1783 pub enabled: bool,
1784 pub max_iterations: u32,
1785 pub debug_output: bool,
1786 pub pass_name: String,
1787}
1788impl EVMPassConfig {
1789 #[allow(dead_code)]
1790 pub fn new(name: impl Into<String>, phase: EVMPassPhase) -> Self {
1791 EVMPassConfig {
1792 phase,
1793 enabled: true,
1794 max_iterations: 10,
1795 debug_output: false,
1796 pass_name: name.into(),
1797 }
1798 }
1799 #[allow(dead_code)]
1800 pub fn disabled(mut self) -> Self {
1801 self.enabled = false;
1802 self
1803 }
1804 #[allow(dead_code)]
1805 pub fn with_debug(mut self) -> Self {
1806 self.debug_output = true;
1807 self
1808 }
1809 #[allow(dead_code)]
1810 pub fn max_iter(mut self, n: u32) -> Self {
1811 self.max_iterations = n;
1812 self
1813 }
1814}
1815#[derive(Debug, Clone, PartialEq)]
1820pub struct EvmInstruction {
1821 pub opcode: EvmOpcode,
1823 pub data: Option<Vec<u8>>,
1825 pub comment: Option<String>,
1827}
1828impl EvmInstruction {
1829 pub fn new(opcode: EvmOpcode) -> Self {
1831 Self {
1832 opcode,
1833 data: None,
1834 comment: None,
1835 }
1836 }
1837 pub fn push(bytes: Vec<u8>) -> Option<Self> {
1841 let len = bytes.len();
1842 if len == 0 || len > 32 {
1843 return None;
1844 }
1845 let opcode = match len {
1846 1 => EvmOpcode::Push1,
1847 2 => EvmOpcode::Push2,
1848 3 => EvmOpcode::Push3,
1849 4 => EvmOpcode::Push4,
1850 5 => EvmOpcode::Push5,
1851 6 => EvmOpcode::Push6,
1852 7 => EvmOpcode::Push7,
1853 8 => EvmOpcode::Push8,
1854 9 => EvmOpcode::Push9,
1855 10 => EvmOpcode::Push10,
1856 11 => EvmOpcode::Push11,
1857 12 => EvmOpcode::Push12,
1858 13 => EvmOpcode::Push13,
1859 14 => EvmOpcode::Push14,
1860 15 => EvmOpcode::Push15,
1861 16 => EvmOpcode::Push16,
1862 17 => EvmOpcode::Push17,
1863 18 => EvmOpcode::Push18,
1864 19 => EvmOpcode::Push19,
1865 20 => EvmOpcode::Push20,
1866 21 => EvmOpcode::Push21,
1867 22 => EvmOpcode::Push22,
1868 23 => EvmOpcode::Push23,
1869 24 => EvmOpcode::Push24,
1870 25 => EvmOpcode::Push25,
1871 26 => EvmOpcode::Push26,
1872 27 => EvmOpcode::Push27,
1873 28 => EvmOpcode::Push28,
1874 29 => EvmOpcode::Push29,
1875 30 => EvmOpcode::Push30,
1876 31 => EvmOpcode::Push31,
1877 32 => EvmOpcode::Push32,
1878 _ => return None,
1879 };
1880 Some(Self {
1881 opcode,
1882 data: Some(bytes),
1883 comment: None,
1884 })
1885 }
1886 pub fn push1(byte: u8) -> Self {
1888 Self {
1889 opcode: EvmOpcode::Push1,
1890 data: Some(vec![byte]),
1891 comment: None,
1892 }
1893 }
1894 pub fn push4(val: u32) -> Self {
1896 let bytes = val.to_be_bytes().to_vec();
1897 Self {
1898 opcode: EvmOpcode::Push4,
1899 data: Some(bytes),
1900 comment: None,
1901 }
1902 }
1903 pub fn push32(val: [u8; 32]) -> Self {
1905 Self {
1906 opcode: EvmOpcode::Push32,
1907 data: Some(val.to_vec()),
1908 comment: None,
1909 }
1910 }
1911 pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
1913 self.comment = Some(comment.into());
1914 self
1915 }
1916 pub fn encode(&self) -> Vec<u8> {
1918 let mut out = vec![self.opcode.byte()];
1919 if let Some(ref data) = self.data {
1920 out.extend_from_slice(data);
1921 }
1922 out
1923 }
1924 pub fn byte_len(&self) -> usize {
1926 1 + self.data.as_ref().map(|d| d.len()).unwrap_or(0)
1927 }
1928}
1929#[allow(dead_code)]
1931#[derive(Debug, Clone)]
1932pub struct EvmGasTable {
1933 pub stop: u64,
1934 pub add: u64,
1935 pub mul: u64,
1936 pub sub: u64,
1937 pub div: u64,
1938 pub sdiv: u64,
1939 pub mload: u64,
1940 pub mstore: u64,
1941 pub sload: u64,
1942 pub sstore_set: u64,
1943 pub sstore_clear: u64,
1944 pub call: u64,
1945 pub create: u64,
1946 pub sha3: u64,
1947 pub sha3_word: u64,
1948 pub log: u64,
1949 pub log_topic: u64,
1950 pub log_byte: u64,
1951}
1952#[allow(dead_code)]
1954#[derive(Debug, Default, Clone)]
1955pub struct EvmStackDepth {
1956 pub current: i32,
1957 pub max: i32,
1958 pub min: i32,
1959}
1960#[allow(dead_code)]
1961impl EvmStackDepth {
1962 pub fn new() -> Self {
1963 Self::default()
1964 }
1965 pub fn push(&mut self, n: i32) {
1966 self.current += n;
1967 self.max = self.max.max(self.current);
1968 }
1969 pub fn pop(&mut self, n: i32) {
1970 self.current -= n;
1971 self.min = self.min.min(self.current);
1972 }
1973 pub fn apply_opcode(&mut self, desc: &EvmOpcodeDesc) {
1974 self.pop(desc.stack_in as i32);
1975 self.push(desc.stack_out as i32);
1976 }
1977 pub fn is_valid(&self) -> bool {
1978 self.current >= 0 && self.current <= 1024
1979 }
1980}
1981#[allow(dead_code)]
1983#[derive(Debug, Default, Clone)]
1984pub struct EvmExtEmitStats {
1985 pub bytes_written: usize,
1986 pub items_emitted: usize,
1987 pub errors: usize,
1988 pub warnings: usize,
1989 pub functions_emitted: usize,
1990 pub events_emitted: usize,
1991}
1992#[allow(dead_code)]
1994#[derive(Debug, Default)]
1995pub struct EvmNameMangler {
1996 pub used: std::collections::HashSet<String>,
1997 pub map: std::collections::HashMap<String, String>,
1998}
1999#[allow(dead_code)]
2000impl EvmNameMangler {
2001 pub fn new() -> Self {
2002 Self::default()
2003 }
2004 pub fn mangle(&mut self, name: &str) -> String {
2005 if let Some(m) = self.map.get(name) {
2006 return m.clone();
2007 }
2008 let mangled: String = name
2009 .chars()
2010 .map(|c| {
2011 if c.is_alphanumeric() || c == '_' {
2012 c
2013 } else {
2014 '_'
2015 }
2016 })
2017 .collect();
2018 let reserved = ["receive", "fallback", "constructor"];
2019 let mut candidate = if reserved.contains(&mangled.as_str()) {
2020 format!("ox_{}", mangled)
2021 } else {
2022 mangled.clone()
2023 };
2024 let base = candidate.clone();
2025 let mut cnt = 0;
2026 while self.used.contains(&candidate) {
2027 cnt += 1;
2028 candidate = format!("{}_{}", base, cnt);
2029 }
2030 self.used.insert(candidate.clone());
2031 self.map.insert(name.to_string(), candidate.clone());
2032 candidate
2033 }
2034}
2035#[allow(dead_code)]
2037#[derive(Debug, Clone)]
2038pub struct EvmAbiError {
2039 pub name: String,
2040 pub inputs: Vec<(String, EvmAbiType)>,
2041}
2042#[allow(dead_code)]
2044#[derive(Debug, Clone)]
2045pub struct EvmExtConfig {
2046 pub evm_version: String,
2047 pub optimize: bool,
2048 pub optimize_runs: u32,
2049 pub emit_ir: bool,
2050 pub emit_asm: bool,
2051 pub via_ir: bool,
2052 pub revert_strings: bool,
2053 pub use_yul: bool,
2054}
2055#[allow(dead_code)]
2057#[derive(Debug, Clone)]
2058pub struct EvmAbiEvent {
2059 pub name: String,
2060 pub inputs: Vec<(String, EvmAbiType, bool)>,
2061 pub is_anonymous: bool,
2062}
2063#[allow(dead_code)]
2065#[derive(Debug, Clone)]
2066pub enum YulStmt {
2067 Let(Vec<String>, Option<YulExpr>),
2068 Assign(Vec<String>, YulExpr),
2069 If(YulExpr, Vec<YulStmt>),
2070 Switch(YulExpr, Vec<(u64, Vec<YulStmt>)>, Option<Vec<YulStmt>>),
2071 For(Vec<YulStmt>, YulExpr, Vec<YulStmt>, Vec<YulStmt>),
2072 Break,
2073 Continue,
2074 Leave,
2075 Return(YulExpr, YulExpr),
2076 Revert(YulExpr, YulExpr),
2077 Pop(YulExpr),
2078 Expr(YulExpr),
2079}
2080#[allow(dead_code)]
2082pub struct EvmContractTemplate {
2083 pub name: String,
2084 pub spdx: String,
2085 pub pragma: String,
2086}
2087#[allow(dead_code)]
2088impl EvmContractTemplate {
2089 pub fn new(name: &str) -> Self {
2090 Self {
2091 name: name.to_string(),
2092 spdx: "MIT".to_string(),
2093 pragma: "^0.8.0".to_string(),
2094 }
2095 }
2096 pub fn emit_header(&self) -> String {
2097 format!(
2098 "// SPDX-License-Identifier: {}\npragma solidity {};\n\ncontract {} {{\n",
2099 self.spdx, self.pragma, self.name
2100 )
2101 }
2102 pub fn emit_footer(&self) -> String {
2103 "}\n".to_string()
2104 }
2105}
2106#[allow(dead_code)]
2108#[derive(Debug, Clone, PartialEq, Eq)]
2109pub enum EvmOpcodeCategory {
2110 Stop,
2111 Arithmetic,
2112 Comparison,
2113 Bitwise,
2114 Sha3,
2115 EnvInfo,
2116 BlockInfo,
2117 MemStack,
2118 Storage,
2119 Control,
2120 Log,
2121 System,
2122 Push,
2123 Dup,
2124 Swap,
2125}
2126#[allow(dead_code)]
2128#[derive(Debug, Clone, Default)]
2129pub struct EvmPassSummary {
2130 pub pass_name: String,
2131 pub functions_compiled: usize,
2132 pub bytecodes_generated: usize,
2133 pub optimizations_applied: usize,
2134 pub duration_us: u64,
2135}
2136#[allow(dead_code)]
2138#[derive(Debug, Clone, PartialEq, Eq)]
2139pub enum EvmOptPass {
2140 DeadCodeElim,
2141 ConstantFolding,
2142 CommonSubexprElim,
2143 InlineFunctions,
2144 JumpElim,
2145 PushPop,
2146 Peephole,
2147}
2148#[derive(Debug, Clone)]
2152pub struct EvmFunction {
2153 pub name: String,
2155 pub selector: [u8; 4],
2157 pub signature: String,
2159 pub blocks: Vec<EvmBasicBlock>,
2161 pub is_payable: bool,
2163 pub is_view: bool,
2165}
2166impl EvmFunction {
2167 pub fn new(name: impl Into<String>, selector: [u8; 4], signature: impl Into<String>) -> Self {
2169 Self {
2170 name: name.into(),
2171 selector,
2172 signature: signature.into(),
2173 blocks: Vec::new(),
2174 is_payable: false,
2175 is_view: false,
2176 }
2177 }
2178 pub fn add_block(&mut self, block: EvmBasicBlock) {
2180 self.blocks.push(block);
2181 }
2182 pub fn byte_len(&self) -> usize {
2184 self.blocks.iter().map(|b| b.byte_len()).sum()
2185 }
2186 pub fn encode(&self) -> Vec<u8> {
2188 self.blocks.iter().flat_map(|b| b.encode()).collect()
2189 }
2190}
2191#[allow(dead_code)]
2192#[derive(Debug, Clone, Default)]
2193pub struct EVMPassStats {
2194 pub total_runs: u32,
2195 pub successful_runs: u32,
2196 pub total_changes: u64,
2197 pub time_ms: u64,
2198 pub iterations_used: u32,
2199}
2200impl EVMPassStats {
2201 #[allow(dead_code)]
2202 pub fn new() -> Self {
2203 Self::default()
2204 }
2205 #[allow(dead_code)]
2206 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
2207 self.total_runs += 1;
2208 self.successful_runs += 1;
2209 self.total_changes += changes;
2210 self.time_ms += time_ms;
2211 self.iterations_used = iterations;
2212 }
2213 #[allow(dead_code)]
2214 pub fn average_changes_per_run(&self) -> f64 {
2215 if self.total_runs == 0 {
2216 return 0.0;
2217 }
2218 self.total_changes as f64 / self.total_runs as f64
2219 }
2220 #[allow(dead_code)]
2221 pub fn success_rate(&self) -> f64 {
2222 if self.total_runs == 0 {
2223 return 0.0;
2224 }
2225 self.successful_runs as f64 / self.total_runs as f64
2226 }
2227 #[allow(dead_code)]
2228 pub fn format_summary(&self) -> String {
2229 format!(
2230 "Runs: {}/{}, Changes: {}, Time: {}ms",
2231 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
2232 )
2233 }
2234}
2235#[allow(dead_code)]
2237#[derive(Debug, Clone)]
2238pub struct EvmMemoryLayout {
2239 pub scratch_space: (u64, u64),
2240 pub free_mem_ptr: u64,
2241 pub zero_slot: u64,
2242 pub initial_free: u64,
2243}
2244#[allow(dead_code)]
2246#[derive(Debug, Clone, PartialEq)]
2247pub enum EvmAbiType {
2248 Uint(u16),
2249 Int(u16),
2250 Address,
2251 Bool,
2252 Bytes(u8),
2253 BytesDyn,
2254 StringDyn,
2255 Tuple(Vec<EvmAbiType>),
2256 Array(Box<EvmAbiType>, Option<u64>),
2257}
2258#[allow(dead_code)]
2259#[derive(Debug, Clone)]
2260pub struct EVMDominatorTree {
2261 pub idom: Vec<Option<u32>>,
2262 pub dom_children: Vec<Vec<u32>>,
2263 pub dom_depth: Vec<u32>,
2264}
2265impl EVMDominatorTree {
2266 #[allow(dead_code)]
2267 pub fn new(size: usize) -> Self {
2268 EVMDominatorTree {
2269 idom: vec![None; size],
2270 dom_children: vec![Vec::new(); size],
2271 dom_depth: vec![0; size],
2272 }
2273 }
2274 #[allow(dead_code)]
2275 pub fn set_idom(&mut self, node: usize, idom: u32) {
2276 self.idom[node] = Some(idom);
2277 }
2278 #[allow(dead_code)]
2279 pub fn dominates(&self, a: usize, b: usize) -> bool {
2280 if a == b {
2281 return true;
2282 }
2283 let mut cur = b;
2284 loop {
2285 match self.idom[cur] {
2286 Some(parent) if parent as usize == a => return true,
2287 Some(parent) if parent as usize == cur => return false,
2288 Some(parent) => cur = parent as usize,
2289 None => return false,
2290 }
2291 }
2292 }
2293 #[allow(dead_code)]
2294 pub fn depth(&self, node: usize) -> u32 {
2295 self.dom_depth.get(node).copied().unwrap_or(0)
2296 }
2297}