1use crate::{
4 Backend, Builder, Bytecode, EvmCompilerFn, EvmContext, EvmStack, FxHashMap, GasParams, Result,
5 bytecode::AnalysisConfig,
6};
7use revm_primitives::{Bytes, hardfork::SpecId};
8use revmc_backend::{
9 Attribute, BackendConfig, FunctionAttributeLocation, Linkage, OptimizationLevel, eyre::ensure,
10 format_bytes,
11};
12use revmc_builtins::Builtins;
13use revmc_context::RawEvmCompilerFn;
14use std::{
15 cell::Cell,
16 fs,
17 io::{self, Write},
18 mem,
19 path::{Path, PathBuf},
20 time::{Duration, Instant},
21};
22
23mod translate;
24use translate::{FcxConfig, FunctionCx};
25
26#[derive(Default)]
28struct Remarks {
29 parse: Cell<Duration>,
30 translate: Cell<Duration>,
31 verify: Cell<Duration>,
32 optimize: Cell<Duration>,
33 codegen: Cell<Duration>,
34 finalize_total: Cell<Duration>,
35}
36
37#[derive(Clone, Copy, Debug, Default)]
39pub struct CompileTimings {
40 pub parse: Duration,
42 pub translate: Duration,
44 pub optimize: Duration,
46 pub codegen: Duration,
48}
49
50impl Remarks {
51 fn clear(&mut self) {
52 *self = Self::default();
53 }
54
55 fn time(&self, field: impl FnOnce(&Self) -> &Cell<Duration>) -> TimingGuard<'_> {
56 TimingGuard { target: field(self), start: Instant::now() }
57 }
58}
59
60struct TimingGuard<'a> {
61 target: &'a Cell<Duration>,
62 start: Instant,
63}
64
65impl Drop for TimingGuard<'_> {
66 fn drop(&mut self) {
67 self.target.set(self.target.get() + self.start.elapsed());
68 }
69}
70
71#[derive(derive_more::Debug)]
87pub struct EvmCompiler<B: Backend> {
88 name: Option<String>,
89 #[debug(skip)]
90 backend: B,
91 out_dir: Option<PathBuf>,
92 config: FcxConfig,
93 #[debug(skip)]
94 builtins: Builtins<B>,
95 #[debug(skip)]
96 gas_params: Option<GasParams>,
97
98 dedup: bool,
99 dse: bool,
100
101 dump_assembly: bool,
102 dump_unopt_assembly: bool,
103
104 compiler_gas_limit: u64,
105
106 #[debug(skip)]
107 remarks: Remarks,
108 compiled_sizes: Vec<(String, usize)>,
111 finalized: bool,
112}
113
114#[cfg(feature = "llvm")]
115impl EvmCompiler<revmc_llvm::EvmLlvmBackend> {
116 pub fn new_llvm(aot: bool) -> Result<Self> {
118 revmc_llvm::EvmLlvmBackend::new(aot).map(Self::new)
119 }
120}
121
122impl<B: Backend> EvmCompiler<B> {
123 pub fn new(backend: B) -> Self {
125 Self {
126 name: None,
127 backend,
128 out_dir: None,
129 config: FcxConfig::default(),
130 builtins: Builtins::new(),
131 gas_params: None,
132 dedup: true,
133 dse: true,
134 dump_assembly: true,
135 dump_unopt_assembly: false,
136 compiler_gas_limit: crate::bytecode::DEFAULT_COMPILER_GAS_LIMIT,
137 remarks: Remarks::default(),
138 compiled_sizes: Vec::new(),
139 finalized: false,
140 }
141 }
142
143 pub fn set_module_name(&mut self, name: impl Into<String>) {
145 let name = name.into();
146 self.backend.set_module_name(&name);
147 self.name = Some(name);
148 }
149
150 fn is_aot(&self) -> bool {
151 self.backend.is_aot()
152 }
153
154 fn is_jit(&self) -> bool {
155 !self.is_aot()
156 }
157
158 #[doc(hidden)]
160 #[inline]
161 pub fn backend(&self) -> &B {
162 &self.backend
163 }
164
165 #[doc(hidden)]
167 #[inline]
168 pub fn backend_mut(&mut self) -> &mut B {
169 &mut self.backend
170 }
171
172 pub fn take_timings(&self) -> CompileTimings {
174 let r = &self.remarks;
175 let timings = CompileTimings {
176 parse: r.parse.get(),
177 translate: r.translate.get(),
178 optimize: r.optimize.get(),
179 codegen: r.codegen.get(),
180 };
181 r.parse.set(Duration::ZERO);
182 r.translate.set(Duration::ZERO);
183 r.optimize.set(Duration::ZERO);
184 r.codegen.set(Duration::ZERO);
185 timings
186 }
187
188 fn update_backend_config(&mut self, f: impl FnOnce(&mut BackendConfig)) {
190 let mut config = self.backend.config().clone();
191 f(&mut config);
192 self.backend.apply_config(config);
193 }
194
195 pub fn out_dir(&self) -> Option<&Path> {
197 self.out_dir.as_deref()
198 }
199
200 pub fn set_dump_to(&mut self, output_dir: Option<PathBuf>) {
204 self.update_backend_config(|c| c.is_dumping = output_dir.is_some());
205 self.config.comments = output_dir.is_some();
206 self.config.debug = output_dir.is_some();
207 if output_dir.is_some() {
208 self.config.frame_pointers = true;
209 }
210 self.out_dir = output_dir;
211 }
212
213 pub fn dump_assembly(&mut self, yes: bool) {
219 self.dump_assembly = yes;
220 }
221
222 pub fn dump_unopt_assembly(&mut self, yes: bool) {
228 self.dump_unopt_assembly = yes;
229 }
230
231 pub fn opt_level(&self) -> OptimizationLevel {
233 self.backend.config().opt_level
234 }
235
236 pub fn set_opt_level(&mut self, level: OptimizationLevel) {
242 self.update_backend_config(|c| c.opt_level = level);
243 }
244
245 pub fn debug_assertions(&mut self, yes: bool) {
252 self.update_backend_config(|c| c.debug_assertions = yes);
253 self.config.debug_assertions = yes;
254 }
255
256 pub fn set_dedup(&mut self, yes: bool) {
260 self.dedup = yes;
261 }
262
263 pub fn set_dse(&mut self, yes: bool) {
267 self.dse = yes;
268 }
269
270 pub fn debug_support(&self) -> bool {
280 self.backend.config().debug_support
281 }
282
283 pub fn set_debug_support(&mut self, yes: bool) {
288 self.update_backend_config(|c| c.debug_support = yes);
289 }
290
291 pub fn profiling_support(&self) -> bool {
301 self.backend.config().profiling_support
302 }
303
304 pub fn set_profiling_support(&mut self, yes: bool) {
309 self.update_backend_config(|c| c.profiling_support = yes);
310 }
311
312 pub fn simple_perf(&self) -> bool {
327 self.backend.config().simple_perf
328 }
329
330 pub fn set_simple_perf(&mut self, yes: bool) {
335 self.update_backend_config(|c| c.simple_perf = yes);
336 }
337
338 pub fn set_debug_info(&mut self, yes: bool) {
347 self.config.debug = yes;
348 }
349
350 pub fn frame_pointers(&mut self, yes: bool) {
357 self.config.frame_pointers = yes;
358 }
359
360 pub fn inspect_stack(&mut self, yes: bool) {
369 self.config.inspect_stack = yes;
370 }
371
372 pub unsafe fn stack_bound_checks(&mut self, yes: bool) {
385 self.config.stack_bound_checks = yes;
386 }
387
388 pub fn set_compiler_gas_limit(&mut self, limit: u64) {
399 self.compiler_gas_limit = limit;
400 }
401
402 pub fn gas_metering(&mut self, yes: bool) {
414 self.config.gas_metering = yes;
415 }
416
417 pub fn single_error(&mut self, yes: bool) {
429 self.config.single_error = yes;
430 }
431
432 pub fn set_gas_params(&mut self, gas_params: GasParams) {
439 self.gas_params = Some(gas_params);
440 }
441
442 pub fn translate<'a>(
447 &mut self,
448 name: &str,
449 input: impl Into<EvmCompilerInput<'a>>,
450 spec_id: SpecId,
451 ) -> Result<B::FuncId> {
452 ensure!(!self.finalized, "cannot compile more functions after finalizing the module");
453 let bytecode = self.parse(input.into(), spec_id)?;
454 self.translate_inner(name, &bytecode)
455 }
456
457 pub unsafe fn jit<'a>(
466 &mut self,
467 name: &str,
468 bytecode: impl Into<EvmCompilerInput<'a>>,
469 spec_id: SpecId,
470 ) -> Result<EvmCompilerFn> {
471 let id = self.translate(name, bytecode.into(), spec_id)?;
472 unsafe { self.jit_function(id) }
473 }
474
475 pub unsafe fn jit_function(&mut self, id: B::FuncId) -> Result<EvmCompilerFn> {
482 ensure!(self.is_jit(), "cannot JIT functions during AOT compilation");
483 self.finalize()?;
484 let addr = {
485 let _t = self.remarks.time(|r| &r.codegen);
486 self.backend.jit_function(id)?
487 };
488 debug_assert!(addr != 0);
489 self.record_compiled_sizes();
490 if let Some(dump_dir) = &self.dump_dir() {
491 self.dump_remarks(dump_dir)?;
492 }
493 Ok(EvmCompilerFn::new(unsafe { std::mem::transmute::<usize, RawEvmCompilerFn>(addr) }))
494 }
495
496 pub fn write_object_to_file(&mut self, path: &Path) -> Result<()> {
498 let file = fs::File::create(path)?;
499 let mut writer = io::BufWriter::new(file);
500 self.write_object(&mut writer)?;
501 writer.flush()?;
502 Ok(())
503 }
504
505 pub fn write_object<W: io::Write>(&mut self, w: W) -> Result<()> {
507 ensure!(self.is_aot(), "cannot write AOT object during JIT compilation");
508 self.finalize()?;
509 {
510 let _t = self.remarks.time(|r| &r.codegen);
511 self.backend.write_object(w)?;
512 }
513 if let Some(dump_dir) = &self.dump_dir() {
514 self.dump_remarks(dump_dir)?;
515 }
516 Ok(())
517 }
518
519 pub unsafe fn free_function(&mut self, id: B::FuncId) -> Result<()> {
532 unsafe { self.backend.free_function(id) }
533 }
534
535 pub fn clear_ir(&mut self) -> Result<()> {
540 self.builtins.clear();
541 self.remarks.clear();
542 self.compiled_sizes.clear();
543 self.finalized = false;
544 self.backend.clear_ir()
545 }
546
547 pub unsafe fn clear(&mut self) -> Result<()> {
556 self.builtins.clear();
557 self.remarks.clear();
558 self.compiled_sizes.clear();
559 self.finalized = false;
560 unsafe { self.backend.free_all_functions() }
561 }
562
563 #[doc(hidden)] pub fn parse<'a>(
566 &mut self,
567 input: EvmCompilerInput<'a>,
568 spec_id: SpecId,
569 ) -> Result<Bytecode<'a>> {
570 let _t = self.remarks.time(|r| &r.parse);
571 let EvmCompilerInput::Code(bytecode) = input;
572
573 let mut bytecode = Bytecode::new(bytecode, spec_id, self.gas_params.clone());
574 bytecode.compiler_gas_limit = self.compiler_gas_limit;
575 bytecode.config.set(AnalysisConfig::INSPECT_STACK, self.config.inspect_stack);
576 bytecode.config.set(AnalysisConfig::DEDUP, self.dedup);
577 bytecode.config.set(AnalysisConfig::DSE, self.dse);
578 bytecode.analyze()?;
579 if let Some(dump_dir) = &self.dump_dir() {
580 Self::dump_bytecode(dump_dir, &bytecode)?;
581 }
582 Ok(bytecode)
583 }
584
585 #[instrument(name = "translate", level = "debug", skip_all)]
586 #[doc(hidden)] pub fn translate_inner(&mut self, name: &str, bytecode: &Bytecode<'_>) -> Result<B::FuncId> {
588 ensure!(cfg!(target_endian = "little"), "only little-endian is supported");
589 let _t = self.remarks.time(|r| &r.translate);
590 ensure!(self.backend.function_name_is_unique(name), "function name `{name}` is not unique");
591
592 if self.config.debug
594 && let Some(dump_dir) = &self.dump_dir()
595 {
596 let path = dump_dir.join("bytecode.txt");
597 let mut config = self.backend.config().clone();
598 config.debug_file = Some(path);
599 self.backend.apply_config(config);
600 }
601
602 let linkage = Linkage::Public;
603 let (bcx, id) =
604 Self::make_builder(&mut self.backend, &self.config, bytecode, name, linkage)?;
605 FunctionCx::translate(bcx, self.config, &mut self.builtins, bytecode)?;
606 Ok(id)
607 }
608
609 #[instrument(level = "debug", skip_all)]
610 fn finalize(&mut self) -> Result<()> {
611 if self.finalized {
612 return Ok(());
613 }
614
615 let finalize_start = Instant::now();
616
617 self.backend.finalize_debug_info()?;
619
620 let dump_dir = self.dump_dir();
621
622 if let Some(dump_dir) = &dump_dir {
623 let path = dump_dir.join("unopt").with_extension(self.backend.ir_extension());
624 self.dump_ir(&path)?;
625
626 self.verify_module()?;
628
629 if self.dump_assembly && self.dump_unopt_assembly {
630 let path = dump_dir.join("unopt.s");
631 self.dump_disasm(&path)?;
632 if self.config.debug {
633 let src_path = dump_dir.join("bytecode.txt");
634 if src_path.exists() {
635 Self::annotate_asm(&path, &src_path)?;
636 }
637 }
638 }
639 } else {
640 self.verify_module()?;
641 }
642
643 self.optimize_module()?;
644
645 if let Some(dump_dir) = &dump_dir {
646 let path = dump_dir.join("opt").with_extension(self.backend.ir_extension());
647 self.dump_ir(&path)?;
648
649 if self.dump_assembly {
650 let path = dump_dir.join("opt.s");
651 self.dump_disasm(&path)?;
652 if self.config.debug {
653 let src_path = dump_dir.join("bytecode.txt");
654 if src_path.exists() {
655 Self::annotate_asm(&path, &src_path)?;
656 }
657 }
658 }
659 }
660 self.finalized = true;
661
662 let finalize_total = &self.remarks.finalize_total;
663 finalize_total.set(finalize_total.get() + finalize_start.elapsed());
664
665 Ok(())
666 }
667
668 #[instrument(level = "debug", skip_all)]
669 fn make_builder<'a>(
670 backend: &'a mut B,
671 config: &FcxConfig,
672 bytecode: &Bytecode<'_>,
673 name: &str,
674 linkage: Linkage,
675 ) -> Result<(B::Builder<'a>, B::FuncId)> {
676 fn size_align<T>(i: usize) -> (usize, usize, usize) {
677 (i, mem::size_of::<T>(), mem::align_of::<T>())
678 }
679
680 let i8 = backend.type_int(8);
681 let ptr = backend.type_ptr();
682 let (ret, params, param_names, ptr_attrs) = (
683 Some(i8),
684 &[ptr, ptr, ptr],
685 &["arg.ecx.addr", "arg.stack.addr", "arg.stack_len.addr"],
686 &[size_align::<EvmContext<'_>>(0), size_align::<EvmStack>(1), size_align::<usize>(2)],
687 );
688 debug_assert_eq!(params.len(), param_names.len());
689 let (mut bcx, id) = backend.build_function(name, ret, params, param_names, linkage)?;
690
691 let function_attributes = default_attrs::for_fn()
693 .chain(config.frame_pointers.then_some(Attribute::AllFramePointers));
694 for attr in function_attributes {
695 bcx.add_function_attribute(None, attr, FunctionAttributeLocation::Function);
696 }
697
698 for &(i, size, align) in ptr_attrs {
700 let attrs = default_attrs::for_sized_ref((size, align));
701 for attr in attrs {
702 let loc = FunctionAttributeLocation::Param(i as _);
703 bcx.add_function_attribute(None, attr, loc);
704 }
705 }
706
707 if !bytecode.stack_observed() {
710 for param in 1..=2 {
711 bcx.add_function_attribute(
712 None,
713 Attribute::DeadOnReturn,
714 FunctionAttributeLocation::Param(param),
715 );
716 }
717 }
718
719 Ok((bcx, id))
720 }
721
722 #[instrument(level = "debug", skip_all)]
723 fn dump_ir(&mut self, path: &Path) -> Result<()> {
724 self.backend.dump_ir(path)?;
725 if self.config.debug
726 && let Some(dump_dir) = &self.dump_dir()
727 {
728 let src_path = dump_dir.join("bytecode.txt");
729 if src_path.exists() {
730 Self::annotate_ir(path, &src_path)?;
731 }
732 }
733 Ok(())
734 }
735
736 #[instrument(level = "debug", skip_all)]
737 fn dump_disasm(&mut self, path: &Path) -> Result<()> {
738 self.backend.dump_disasm(path)
739 }
740
741 #[instrument(level = "debug", skip_all)]
742 fn verify_module(&mut self) -> Result<()> {
743 if !self.config.debug_assertions {
744 return Ok(());
745 }
746 let _t = self.remarks.time(|r| &r.verify);
747 self.backend.verify_module()
748 }
749
750 #[instrument(level = "debug", skip_all)]
751 fn optimize_module(&mut self) -> Result<()> {
752 let _t = self.remarks.time(|r| &r.optimize);
753 self.backend.optimize_module()
754 }
755
756 fn dump_remarks(&self, dump_dir: &Path) -> Result<()> {
757 let r = &self.remarks;
758 let parse = r.parse.get();
759 let translate = r.translate.get();
760 let finalize = r.finalize_total.get();
761 let verify = r.verify.get();
762 let optimize = r.optimize.get();
763 let codegen = r.codegen.get();
764 let total = parse + translate + finalize + codegen;
765 let file = fs::File::create(dump_dir.join("remarks.txt"))?;
766 let mut w = io::BufWriter::new(file);
767 write!(
768 w,
769 "\
770Compilation remarks
771===================
772
773parse: {parse:>11.3?}
774translate: {translate:>11.3?}
775finalize: {finalize:>11.3?}
776- verify: {verify:>11.3?}
777- optimize: {optimize:>11.3?}
778codegen: {codegen:>11.3?}
779
780total: {total:>11.3?}
781"
782 )?;
783
784 let mut files: Vec<_> = fs::read_dir(dump_dir)?
786 .filter_map(|e| e.ok())
787 .filter(|e| e.file_type().is_ok_and(|t| t.is_file()))
788 .filter(|e| e.file_name() != "remarks.txt")
789 .collect();
790 if !files.is_empty() {
791 files.sort_by_key(|e| e.file_name());
792 writeln!(w)?;
793 writeln!(w, "Generated files")?;
794 writeln!(w, "===============")?;
795 for entry in &files {
796 let name = entry.file_name();
797 let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
798 writeln!(w, "{}: {}", name.to_string_lossy(), format_bytes(size as usize))?;
799 }
800 }
801
802 if !self.compiled_sizes.is_empty() {
804 let total: usize = self.compiled_sizes.iter().map(|(_, s)| *s).sum();
805 writeln!(w)?;
806 writeln!(w, "JIT code sizes (estimated)")?;
807 writeln!(w, "==========================")?;
808 for (name, size) in &self.compiled_sizes {
809 writeln!(w, "{name}: {}", format_bytes(*size))?;
810 }
811 if self.compiled_sizes.len() > 1 {
812 writeln!(w, "total: {}", format_bytes(total))?;
813 }
814 }
815
816 w.flush()?;
817 Ok(())
818 }
819
820 fn record_compiled_sizes(&mut self) {
823 for (name, size) in self.backend.function_sizes() {
824 if let Some(slot) = self.compiled_sizes.iter_mut().find(|(n, _)| *n == name) {
825 slot.1 = size;
826 } else {
827 self.compiled_sizes.push((name, size));
828 }
829 }
830 }
831
832 #[instrument(level = "debug", skip_all)]
833 fn dump_bytecode(dump_dir: &Path, bytecode: &Bytecode<'_>) -> Result<()> {
834 {
835 let file = fs::File::create(dump_dir.join("bytecode.txt"))?;
836 let mut writer = io::BufWriter::new(file);
837 write!(writer, "{bytecode}")?;
838 writer.flush()?;
839 }
840
841 {
842 let file = fs::File::create(dump_dir.join("bytecode.dbg.txt"))?;
843 let mut writer = io::BufWriter::new(file);
844 writeln!(writer, "{bytecode:#?}")?;
845 writer.flush()?;
846 }
847
848 fs::write(dump_dir.join("bytecode.bin"), &*bytecode.code)?;
849
850 {
851 let file = fs::File::create(dump_dir.join("bytecode.dot"))?;
852 let mut writer = io::BufWriter::new(file);
853 let mut dot = String::new();
854 bytecode.write_dot(&mut dot).map_err(|e| revmc_backend::eyre::eyre!("{e}"))?;
855 writer.write_all(dot.as_bytes())?;
856 writer.flush()?;
857 }
858
859 Ok(())
860 }
861
862 fn annotate_ir(ir_path: &Path, src_path: &Path) -> Result<()> {
867 let src = fs::read_to_string(src_path)?;
868 let src_lines: Vec<&str> = src.lines().collect();
869 let ir = fs::read_to_string(ir_path)?;
870
871 let mut di_locs = FxHashMap::default();
873 for line in ir.lines() {
874 let line = line.trim_start();
875 if !line.starts_with('!') {
876 continue;
877 }
878 let Some(rest) = line.strip_prefix('!') else { continue };
880 let Some((id_str, rest)) = rest.split_once(" = !DILocation(line: ") else { continue };
881 let Ok(id) = id_str.parse::<u32>() else { continue };
882 let Some((line_str, _)) = rest.split_once(',') else { continue };
883 let Ok(line_no) = line_str.parse::<u32>() else { continue };
884 di_locs.insert(id, line_no);
885 }
886
887 if di_locs.is_empty() {
888 return Ok(());
889 }
890
891 let annotated: Vec<_> =
893 ir.lines().map(|line| (line, resolve_dbg_line(line, &di_locs, &src_lines))).collect();
894 let comment_col = annotated
895 .iter()
896 .filter_map(|(line, src)| src.is_some().then_some(line.len()))
897 .max()
898 .unwrap_or(0)
899 .min(100);
900
901 let file = fs::File::create(ir_path)?;
903 let mut w = io::BufWriter::new(file);
904 for (line, src_line) in &annotated {
905 if let Some(src_line) = src_line {
906 writeln!(w, "{line:<comment_col$} ; >> {src_line}")?;
907 } else {
908 writeln!(w, "{line}")?;
909 }
910 }
911 w.flush()?;
912 Ok(())
913 }
914
915 fn annotate_asm(asm_path: &Path, src_path: &Path) -> Result<()> {
918 let src = fs::read_to_string(src_path)?;
919 let src_lines: Vec<&str> = src.lines().collect();
920 let asm = fs::read_to_string(asm_path)?;
921
922 let annotated: Vec<_> = asm
923 .lines()
924 .map(|line| {
925 let resolved = resolve_asm_source_line(line, &src_lines);
926 let stripped = if resolved.is_some() {
928 line.find("# bytecode.txt:").map(|pos| line[..pos].trim_end()).unwrap_or(line)
929 } else {
930 line
931 };
932 (stripped, resolved)
933 })
934 .collect();
935 let comment_col = 40;
936
937 let file = fs::File::create(asm_path)?;
938 let mut w = io::BufWriter::new(file);
939 for (line, src_line) in &annotated {
940 if let Some(src_line) = src_line {
941 writeln!(w, "{line:<comment_col$} # {src_line}")?;
942 } else {
943 writeln!(w, "{line}")?;
944 }
945 }
946 w.flush()?;
947 Ok(())
948 }
949
950 #[doc(hidden)]
952 pub fn dump_dir(&self) -> Option<PathBuf> {
953 let mut dump_dir = self.out_dir.clone()?;
954 if let Some(name) = &self.name {
955 dump_dir.push(name.replace(char::is_whitespace, "_"));
956 }
957 if !dump_dir.exists() {
958 let _ = fs::create_dir_all(&dump_dir);
959 }
960 Some(dump_dir)
961 }
962}
963
964#[allow(missing_debug_implementations)]
966pub enum EvmCompilerInput<'a> {
967 Code(&'a [u8]),
969}
970
971impl<'a> From<&'a [u8]> for EvmCompilerInput<'a> {
972 fn from(code: &'a [u8]) -> Self {
973 EvmCompilerInput::Code(code)
974 }
975}
976
977impl<'a> From<&'a Vec<u8>> for EvmCompilerInput<'a> {
978 fn from(code: &'a Vec<u8>) -> Self {
979 EvmCompilerInput::Code(code)
980 }
981}
982
983impl<'a> From<&'a Bytes> for EvmCompilerInput<'a> {
984 fn from(code: &'a Bytes) -> Self {
985 EvmCompilerInput::Code(code)
986 }
987}
988
989#[allow(dead_code)]
990mod default_attrs {
991 use revmc_backend::Attribute;
992
993 pub(crate) fn for_fn() -> impl Iterator<Item = Attribute> {
994 [
995 Attribute::WillReturn, Attribute::NoSync, Attribute::NativeTargetCpu, Attribute::NoRecurse, Attribute::NonLazyBind, Attribute::UWTable, ]
1002 .into_iter()
1003 }
1004
1005 pub(crate) fn for_param() -> impl Iterator<Item = Attribute> {
1006 [Attribute::NoUndef].into_iter()
1007 }
1008
1009 pub(crate) fn for_ptr() -> impl Iterator<Item = Attribute> {
1010 for_param().chain([Attribute::NoCapture])
1011 }
1012
1013 pub(crate) fn for_sized_ptr((size, align): (usize, usize)) -> impl Iterator<Item = Attribute> {
1014 for_ptr().chain([Attribute::Dereferenceable(size as u64), Attribute::Align(align as u64)])
1015 }
1016
1017 pub(crate) fn for_ptr_t<T>() -> impl Iterator<Item = Attribute> {
1018 for_sized_ptr(size_align::<T>())
1019 }
1020
1021 pub(crate) fn for_ref() -> impl Iterator<Item = Attribute> {
1022 for_ptr().chain([Attribute::NonNull, Attribute::NoAlias])
1023 }
1024
1025 pub(crate) fn for_sized_ref((size, align): (usize, usize)) -> impl Iterator<Item = Attribute> {
1026 for_ref().chain([Attribute::Dereferenceable(size as u64), Attribute::Align(align as u64)])
1027 }
1028
1029 pub(crate) fn for_ref_t<T>() -> impl Iterator<Item = Attribute> {
1030 for_sized_ref(size_align::<T>())
1031 }
1032
1033 pub(crate) fn size_align<T>() -> (usize, usize) {
1034 (std::mem::size_of::<T>(), std::mem::align_of::<T>())
1035 }
1036}
1037
1038fn resolve_asm_source_line<'a>(line: &str, src_lines: &[&'a str]) -> Option<&'a str> {
1041 let pos = line.find("# bytecode.txt:")?;
1042 let after = &line[pos + "# bytecode.txt:".len()..];
1043 let num_len = after.find(|c: char| !c.is_ascii_digit()).unwrap_or(after.len());
1044 let line_no: u32 = after[..num_len].parse().ok()?;
1045 let idx = line_no.checked_sub(1)? as usize;
1046 let src_line = src_lines.get(idx)?.trim();
1047 (!src_line.is_empty()).then_some(src_line)
1048}
1049
1050fn resolve_dbg_line<'a>(
1052 ir_line: &str,
1053 di_locs: &FxHashMap<u32, u32>,
1054 src_lines: &[&'a str],
1055) -> Option<&'a str> {
1056 let pos = ir_line.find("!dbg !")?;
1057 let after = &ir_line[pos + 6..];
1058 let id_len = after.find(|c: char| !c.is_ascii_digit()).unwrap_or(after.len());
1059 let id: u32 = after[..id_len].parse().ok()?;
1060 let line_no = *di_locs.get(&id)?;
1061 let idx = line_no.checked_sub(1)? as usize;
1062 let src_line = src_lines.get(idx)?.trim();
1063 (!src_line.is_empty()).then_some(src_line)
1064}