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 self.finalize()?;
508 {
509 let _t = self.remarks.time(|r| &r.codegen);
510 self.backend.write_object(w)?;
511 }
512 if let Some(dump_dir) = &self.dump_dir() {
513 self.dump_remarks(dump_dir)?;
514 }
515 Ok(())
516 }
517
518 pub unsafe fn free_function(&mut self, id: B::FuncId) -> Result<()> {
531 unsafe { self.backend.free_function(id) }
532 }
533
534 pub fn clear_ir(&mut self) -> Result<()> {
539 self.builtins.clear();
540 self.remarks.clear();
541 self.compiled_sizes.clear();
542 self.finalized = false;
543 self.backend.clear_ir()
544 }
545
546 pub unsafe fn clear(&mut self) -> Result<()> {
555 self.builtins.clear();
556 self.remarks.clear();
557 self.compiled_sizes.clear();
558 self.finalized = false;
559 unsafe { self.backend.free_all_functions() }
560 }
561
562 #[doc(hidden)] pub fn parse<'a>(
565 &mut self,
566 input: EvmCompilerInput<'a>,
567 spec_id: SpecId,
568 ) -> Result<Bytecode<'a>> {
569 let _t = self.remarks.time(|r| &r.parse);
570 let EvmCompilerInput::Code(bytecode) = input;
571
572 let mut bytecode = Bytecode::new(bytecode, spec_id, self.gas_params.clone());
573 bytecode.compiler_gas_limit = self.compiler_gas_limit;
574 bytecode.config.set(AnalysisConfig::INSPECT_STACK, self.config.inspect_stack);
575 bytecode.config.set(AnalysisConfig::DEDUP, self.dedup);
576 bytecode.config.set(AnalysisConfig::DSE, self.dse);
577 bytecode.analyze()?;
578 if let Some(dump_dir) = &self.dump_dir() {
579 Self::dump_bytecode(dump_dir, &bytecode)?;
580 }
581 Ok(bytecode)
582 }
583
584 #[instrument(name = "translate", level = "debug", skip_all)]
585 #[doc(hidden)] pub fn translate_inner(&mut self, name: &str, bytecode: &Bytecode<'_>) -> Result<B::FuncId> {
587 ensure!(cfg!(target_endian = "little"), "only little-endian is supported");
588 let _t = self.remarks.time(|r| &r.translate);
589 ensure!(self.backend.function_name_is_unique(name), "function name `{name}` is not unique");
590
591 if self.config.debug
593 && let Some(dump_dir) = &self.dump_dir()
594 {
595 let path = dump_dir.join("bytecode.txt");
596 let mut config = self.backend.config().clone();
597 config.debug_file = Some(path);
598 self.backend.apply_config(config);
599 }
600
601 let linkage = Linkage::Public;
602 let (bcx, id) =
603 Self::make_builder(&mut self.backend, &self.config, bytecode, name, linkage)?;
604 FunctionCx::translate(bcx, self.config, &mut self.builtins, bytecode)?;
605 Ok(id)
606 }
607
608 #[instrument(level = "debug", skip_all)]
609 fn finalize(&mut self) -> Result<()> {
610 if self.finalized {
611 return Ok(());
612 }
613
614 let finalize_start = Instant::now();
615
616 self.backend.finalize_debug_info()?;
618
619 let dump_dir = self.dump_dir();
620
621 if let Some(dump_dir) = &dump_dir {
622 let path = dump_dir.join("unopt").with_extension(self.backend.ir_extension());
623 self.dump_ir(&path)?;
624
625 self.verify_module()?;
627
628 if self.dump_assembly && self.dump_unopt_assembly {
629 let path = dump_dir.join("unopt.s");
630 self.dump_disasm(&path)?;
631 if self.config.debug {
632 let src_path = dump_dir.join("bytecode.txt");
633 if src_path.exists() {
634 Self::annotate_asm(&path, &src_path)?;
635 }
636 }
637 }
638 } else {
639 self.verify_module()?;
640 }
641
642 self.optimize_module()?;
643
644 if let Some(dump_dir) = &dump_dir {
645 let path = dump_dir.join("opt").with_extension(self.backend.ir_extension());
646 self.dump_ir(&path)?;
647
648 if self.dump_assembly {
649 let path = dump_dir.join("opt.s");
650 self.dump_disasm(&path)?;
651 if self.config.debug {
652 let src_path = dump_dir.join("bytecode.txt");
653 if src_path.exists() {
654 Self::annotate_asm(&path, &src_path)?;
655 }
656 }
657 }
658 }
659 self.finalized = true;
660
661 let finalize_total = &self.remarks.finalize_total;
662 finalize_total.set(finalize_total.get() + finalize_start.elapsed());
663
664 Ok(())
665 }
666
667 #[instrument(level = "debug", skip_all)]
668 fn make_builder<'a>(
669 backend: &'a mut B,
670 config: &FcxConfig,
671 bytecode: &Bytecode<'_>,
672 name: &str,
673 linkage: Linkage,
674 ) -> Result<(B::Builder<'a>, B::FuncId)> {
675 fn size_align<T>(i: usize) -> (usize, usize, usize) {
676 (i, mem::size_of::<T>(), mem::align_of::<T>())
677 }
678
679 let i8 = backend.type_int(8);
680 let ptr = backend.type_ptr();
681 let (ret, params, param_names, ptr_attrs) = (
682 Some(i8),
683 &[ptr, ptr, ptr],
684 &["arg.ecx.addr", "arg.stack.addr", "arg.stack_len.addr"],
685 &[size_align::<EvmContext<'_>>(0), size_align::<EvmStack>(1), size_align::<usize>(2)],
686 );
687 debug_assert_eq!(params.len(), param_names.len());
688 let (mut bcx, id) = backend.build_function(name, ret, params, param_names, linkage)?;
689
690 let function_attributes = default_attrs::for_fn()
692 .chain(config.frame_pointers.then_some(Attribute::AllFramePointers));
693 for attr in function_attributes {
694 bcx.add_function_attribute(None, attr, FunctionAttributeLocation::Function);
695 }
696
697 for &(i, size, align) in ptr_attrs {
699 let attrs = default_attrs::for_sized_ref((size, align));
700 for attr in attrs {
701 let loc = FunctionAttributeLocation::Param(i as _);
702 bcx.add_function_attribute(None, attr, loc);
703 }
704 }
705
706 if !bytecode.stack_observed() {
709 for param in 1..=2 {
710 bcx.add_function_attribute(
711 None,
712 Attribute::DeadOnReturn,
713 FunctionAttributeLocation::Param(param),
714 );
715 }
716 }
717
718 Ok((bcx, id))
719 }
720
721 #[instrument(level = "debug", skip_all)]
722 fn dump_ir(&mut self, path: &Path) -> Result<()> {
723 self.backend.dump_ir(path)?;
724 if self.config.debug
725 && let Some(dump_dir) = &self.dump_dir()
726 {
727 let src_path = dump_dir.join("bytecode.txt");
728 if src_path.exists() {
729 Self::annotate_ir(path, &src_path)?;
730 }
731 }
732 Ok(())
733 }
734
735 #[instrument(level = "debug", skip_all)]
736 fn dump_disasm(&mut self, path: &Path) -> Result<()> {
737 self.backend.dump_disasm(path)
738 }
739
740 #[instrument(level = "debug", skip_all)]
741 fn verify_module(&mut self) -> Result<()> {
742 if !self.config.debug_assertions {
743 return Ok(());
744 }
745 let _t = self.remarks.time(|r| &r.verify);
746 self.backend.verify_module()
747 }
748
749 #[instrument(level = "debug", skip_all)]
750 fn optimize_module(&mut self) -> Result<()> {
751 let _t = self.remarks.time(|r| &r.optimize);
752 self.backend.optimize_module()
753 }
754
755 fn dump_remarks(&self, dump_dir: &Path) -> Result<()> {
756 let r = &self.remarks;
757 let parse = r.parse.get();
758 let translate = r.translate.get();
759 let finalize = r.finalize_total.get();
760 let verify = r.verify.get();
761 let optimize = r.optimize.get();
762 let codegen = r.codegen.get();
763 let total = parse + translate + finalize + codegen;
764 let file = fs::File::create(dump_dir.join("remarks.txt"))?;
765 let mut w = io::BufWriter::new(file);
766 write!(
767 w,
768 "\
769Compilation remarks
770===================
771
772parse: {parse:>11.3?}
773translate: {translate:>11.3?}
774finalize: {finalize:>11.3?}
775- verify: {verify:>11.3?}
776- optimize: {optimize:>11.3?}
777codegen: {codegen:>11.3?}
778
779total: {total:>11.3?}
780"
781 )?;
782
783 let mut files: Vec<_> = fs::read_dir(dump_dir)?
785 .filter_map(|e| e.ok())
786 .filter(|e| e.file_type().is_ok_and(|t| t.is_file()))
787 .filter(|e| e.file_name() != "remarks.txt")
788 .collect();
789 if !files.is_empty() {
790 files.sort_by_key(|e| e.file_name());
791 writeln!(w)?;
792 writeln!(w, "Generated files")?;
793 writeln!(w, "===============")?;
794 for entry in &files {
795 let name = entry.file_name();
796 let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
797 writeln!(w, "{}: {}", name.to_string_lossy(), format_bytes(size as usize))?;
798 }
799 }
800
801 if !self.compiled_sizes.is_empty() {
803 let total: usize = self.compiled_sizes.iter().map(|(_, s)| *s).sum();
804 writeln!(w)?;
805 writeln!(w, "JIT code sizes (estimated)")?;
806 writeln!(w, "==========================")?;
807 for (name, size) in &self.compiled_sizes {
808 writeln!(w, "{name}: {}", format_bytes(*size))?;
809 }
810 if self.compiled_sizes.len() > 1 {
811 writeln!(w, "total: {}", format_bytes(total))?;
812 }
813 }
814
815 w.flush()?;
816 Ok(())
817 }
818
819 fn record_compiled_sizes(&mut self) {
822 for (name, size) in self.backend.function_sizes() {
823 if let Some(slot) = self.compiled_sizes.iter_mut().find(|(n, _)| *n == name) {
824 slot.1 = size;
825 } else {
826 self.compiled_sizes.push((name, size));
827 }
828 }
829 }
830
831 #[instrument(level = "debug", skip_all)]
832 fn dump_bytecode(dump_dir: &Path, bytecode: &Bytecode<'_>) -> Result<()> {
833 {
834 let file = fs::File::create(dump_dir.join("bytecode.txt"))?;
835 let mut writer = io::BufWriter::new(file);
836 write!(writer, "{bytecode}")?;
837 writer.flush()?;
838 }
839
840 {
841 let file = fs::File::create(dump_dir.join("bytecode.dbg.txt"))?;
842 let mut writer = io::BufWriter::new(file);
843 writeln!(writer, "{bytecode:#?}")?;
844 writer.flush()?;
845 }
846
847 fs::write(dump_dir.join("bytecode.bin"), &*bytecode.code)?;
848
849 {
850 let file = fs::File::create(dump_dir.join("bytecode.dot"))?;
851 let mut writer = io::BufWriter::new(file);
852 let mut dot = String::new();
853 bytecode.write_dot(&mut dot).map_err(|e| revmc_backend::eyre::eyre!("{e}"))?;
854 writer.write_all(dot.as_bytes())?;
855 writer.flush()?;
856 }
857
858 Ok(())
859 }
860
861 fn annotate_ir(ir_path: &Path, src_path: &Path) -> Result<()> {
866 let src = fs::read_to_string(src_path)?;
867 let src_lines: Vec<&str> = src.lines().collect();
868 let ir = fs::read_to_string(ir_path)?;
869
870 let mut di_locs = FxHashMap::default();
872 for line in ir.lines() {
873 let line = line.trim_start();
874 if !line.starts_with('!') {
875 continue;
876 }
877 let Some(rest) = line.strip_prefix('!') else { continue };
879 let Some((id_str, rest)) = rest.split_once(" = !DILocation(line: ") else { continue };
880 let Ok(id) = id_str.parse::<u32>() else { continue };
881 let Some((line_str, _)) = rest.split_once(',') else { continue };
882 let Ok(line_no) = line_str.parse::<u32>() else { continue };
883 di_locs.insert(id, line_no);
884 }
885
886 if di_locs.is_empty() {
887 return Ok(());
888 }
889
890 let annotated: Vec<_> =
892 ir.lines().map(|line| (line, resolve_dbg_line(line, &di_locs, &src_lines))).collect();
893 let comment_col = annotated
894 .iter()
895 .filter_map(|(line, src)| src.is_some().then_some(line.len()))
896 .max()
897 .unwrap_or(0)
898 .min(100);
899
900 let file = fs::File::create(ir_path)?;
902 let mut w = io::BufWriter::new(file);
903 for (line, src_line) in &annotated {
904 if let Some(src_line) = src_line {
905 writeln!(w, "{line:<comment_col$} ; >> {src_line}")?;
906 } else {
907 writeln!(w, "{line}")?;
908 }
909 }
910 w.flush()?;
911 Ok(())
912 }
913
914 fn annotate_asm(asm_path: &Path, src_path: &Path) -> Result<()> {
917 let src = fs::read_to_string(src_path)?;
918 let src_lines: Vec<&str> = src.lines().collect();
919 let asm = fs::read_to_string(asm_path)?;
920
921 let annotated: Vec<_> = asm
922 .lines()
923 .map(|line| {
924 let resolved = resolve_asm_source_line(line, &src_lines);
925 let stripped = if resolved.is_some() {
927 line.find("# bytecode.txt:").map(|pos| line[..pos].trim_end()).unwrap_or(line)
928 } else {
929 line
930 };
931 (stripped, resolved)
932 })
933 .collect();
934 let comment_col = 40;
935
936 let file = fs::File::create(asm_path)?;
937 let mut w = io::BufWriter::new(file);
938 for (line, src_line) in &annotated {
939 if let Some(src_line) = src_line {
940 writeln!(w, "{line:<comment_col$} # {src_line}")?;
941 } else {
942 writeln!(w, "{line}")?;
943 }
944 }
945 w.flush()?;
946 Ok(())
947 }
948
949 #[doc(hidden)]
951 pub fn dump_dir(&self) -> Option<PathBuf> {
952 let mut dump_dir = self.out_dir.clone()?;
953 if let Some(name) = &self.name {
954 dump_dir.push(name.replace(char::is_whitespace, "_"));
955 }
956 if !dump_dir.exists() {
957 let _ = fs::create_dir_all(&dump_dir);
958 }
959 Some(dump_dir)
960 }
961}
962
963#[allow(missing_debug_implementations)]
965pub enum EvmCompilerInput<'a> {
966 Code(&'a [u8]),
968}
969
970impl<'a> From<&'a [u8]> for EvmCompilerInput<'a> {
971 fn from(code: &'a [u8]) -> Self {
972 EvmCompilerInput::Code(code)
973 }
974}
975
976impl<'a> From<&'a Vec<u8>> for EvmCompilerInput<'a> {
977 fn from(code: &'a Vec<u8>) -> Self {
978 EvmCompilerInput::Code(code)
979 }
980}
981
982impl<'a> From<&'a Bytes> for EvmCompilerInput<'a> {
983 fn from(code: &'a Bytes) -> Self {
984 EvmCompilerInput::Code(code)
985 }
986}
987
988#[allow(dead_code)]
989mod default_attrs {
990 use revmc_backend::Attribute;
991
992 pub(crate) fn for_fn() -> impl Iterator<Item = Attribute> {
993 [
994 Attribute::WillReturn, Attribute::NoSync, Attribute::NativeTargetCpu, Attribute::NoRecurse, Attribute::NonLazyBind, Attribute::UWTable, ]
1001 .into_iter()
1002 }
1003
1004 pub(crate) fn for_param() -> impl Iterator<Item = Attribute> {
1005 [Attribute::NoUndef].into_iter()
1006 }
1007
1008 pub(crate) fn for_ptr() -> impl Iterator<Item = Attribute> {
1009 for_param().chain([Attribute::NoCapture])
1010 }
1011
1012 pub(crate) fn for_sized_ptr((size, align): (usize, usize)) -> impl Iterator<Item = Attribute> {
1013 for_ptr().chain([Attribute::Dereferenceable(size as u64), Attribute::Align(align as u64)])
1014 }
1015
1016 pub(crate) fn for_ptr_t<T>() -> impl Iterator<Item = Attribute> {
1017 for_sized_ptr(size_align::<T>())
1018 }
1019
1020 pub(crate) fn for_ref() -> impl Iterator<Item = Attribute> {
1021 for_ptr().chain([Attribute::NonNull, Attribute::NoAlias])
1022 }
1023
1024 pub(crate) fn for_sized_ref((size, align): (usize, usize)) -> impl Iterator<Item = Attribute> {
1025 for_ref().chain([Attribute::Dereferenceable(size as u64), Attribute::Align(align as u64)])
1026 }
1027
1028 pub(crate) fn for_ref_t<T>() -> impl Iterator<Item = Attribute> {
1029 for_sized_ref(size_align::<T>())
1030 }
1031
1032 pub(crate) fn size_align<T>() -> (usize, usize) {
1033 (std::mem::size_of::<T>(), std::mem::align_of::<T>())
1034 }
1035}
1036
1037fn resolve_asm_source_line<'a>(line: &str, src_lines: &[&'a str]) -> Option<&'a str> {
1040 let pos = line.find("# bytecode.txt:")?;
1041 let after = &line[pos + "# bytecode.txt:".len()..];
1042 let num_len = after.find(|c: char| !c.is_ascii_digit()).unwrap_or(after.len());
1043 let line_no: u32 = after[..num_len].parse().ok()?;
1044 let idx = line_no.checked_sub(1)? as usize;
1045 let src_line = src_lines.get(idx)?.trim();
1046 (!src_line.is_empty()).then_some(src_line)
1047}
1048
1049fn resolve_dbg_line<'a>(
1051 ir_line: &str,
1052 di_locs: &FxHashMap<u32, u32>,
1053 src_lines: &[&'a str],
1054) -> Option<&'a str> {
1055 let pos = ir_line.find("!dbg !")?;
1056 let after = &ir_line[pos + 6..];
1057 let id_len = after.find(|c: char| !c.is_ascii_digit()).unwrap_or(after.len());
1058 let id: u32 = after[..id_len].parse().ok()?;
1059 let line_no = *di_locs.get(&id)?;
1060 let idx = line_no.checked_sub(1)? as usize;
1061 let src_line = src_lines.get(idx)?.trim();
1062 (!src_line.is_empty()).then_some(src_line)
1063}