1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(test), warn(unused_extern_crates))]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5#[macro_use]
6extern crate tracing;
7
8use alloy_primitives::map::{FxBuildHasher, HashSet};
9use inkwell::{
10 AddressSpace, IntPredicate,
11 attributes::{Attribute, AttributeLoc},
12 basic_block::BasicBlock,
13 debug_info::{
14 AsDIScope, DICompileUnit, DIFlags, DIFlagsConstants, DISubprogram, DWARFEmissionKind,
15 DWARFSourceLanguage, DebugInfoBuilder,
16 },
17 module::{FlagBehavior, Module},
18 passes::PassBuilderOptions,
19 support::error_handling::install_fatal_error_handler,
20 targets::{
21 CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple,
22 },
23 types::{
24 AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, PointerType,
25 StringRadix, VoidType,
26 },
27 values::{
28 AsValueRef, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue,
29 FunctionValue, InstructionValue, PointerValue,
30 },
31};
32use object::{Object, ObjectSymbol};
33use revmc_backend::{
34 Backend, BackendConfig, BackendTypes, Builder, CallConv, IntCC, OptimizationLevel, Result,
35 TailCallKind, TypeMethods, U256, eyre, format_bytes,
36};
37use std::{
38 cell::Cell,
39 ffi::CString,
40 fmt::{self, Write},
41 iter,
42 mem::ManuallyDrop,
43 path::Path,
44 sync::{
45 Arc, Once, OnceLock,
46 atomic::{AtomicU64, AtomicUsize, Ordering},
47 },
48};
49
50pub use inkwell::{self, context::Context};
51
52mod cpp;
53
54mod dh;
55pub mod orc;
56
57mod utils;
58pub(crate) use utils::*;
59
60const DEFAULT_WEIGHT: u32 = 20000;
62
63#[derive(Clone, Copy, Debug, Default)]
68pub struct JitMemoryUsage {
69 pub code_bytes: usize,
71 pub data_bytes: usize,
73}
74
75impl JitMemoryUsage {
76 pub fn total_bytes(&self) -> usize {
78 self.code_bytes + self.data_bytes
79 }
80}
81
82impl fmt::Display for JitMemoryUsage {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(
85 f,
86 "total: {}, code: {}, data: {}",
87 format_bytes(self.total_bytes()),
88 format_bytes(self.code_bytes),
89 format_bytes(self.data_bytes),
90 )
91 }
92}
93
94struct JitMemoryCounters {
99 code_bytes: AtomicUsize,
100 data_bytes: AtomicUsize,
101}
102
103impl JitMemoryCounters {
104 fn get(&self) -> JitMemoryUsage {
105 JitMemoryUsage {
106 code_bytes: self.code_bytes.load(Ordering::Relaxed),
107 data_bytes: self.data_bytes.load(Ordering::Relaxed),
108 }
109 }
110}
111
112pub fn jit_memory_usage() -> Option<JitMemoryUsage> {
116 GlobalOrcJit::try_get().map(|g| g.memory_counters.get())
117}
118
119pub fn global_gc() {
121 if let Some(orc) = GlobalOrcJit::try_get() {
122 orc.gc();
123 }
124}
125
126type FxHashMap<K, V> = alloy_primitives::map::HashMap<K, V, FxBuildHasher>;
127
128#[derive(Debug)]
130#[must_use]
131pub struct EvmLlvmBackend {
132 cx: &'static Context,
133 _dh: dh::DiagnosticHandlerGuard,
134 bcx: inkwell::builder::Builder<'static>,
135 module: Module<'static>,
136 machine: TargetMachine,
137
138 orc: Option<OrcJitState>,
141 _tscx: Option<orc::ThreadSafeContext>,
143 _cx_handle: Option<Box<ManuallyDrop<Context>>>,
145 _aot_cx: Option<Box<Context>>,
147
148 di_state: Option<DiState>,
150
151 ty_void: VoidType<'static>,
152 ty_ptr: PointerType<'static>,
153 ty_i1: IntType<'static>,
154 ty_i8: IntType<'static>,
155 ty_i32: IntType<'static>,
156 ty_i64: IntType<'static>,
157 ty_i256: IntType<'static>,
158 ty_isize: IntType<'static>,
159
160 aot: bool,
161 backend_config: BackendConfig,
162 scratch: String,
163 function_counter: u32,
165 function_names: FxHashMap<u32, String>,
167}
168
169thread_local! {
172 static OBJ_CAPTURE: Cell<*mut Option<Vec<u8>>> = const { Cell::new(std::ptr::null_mut()) };
173}
174
175struct ScopedObjCapture {
177 prev: *mut Option<Vec<u8>>,
178}
179
180impl ScopedObjCapture {
181 fn install(slot: &mut Option<Vec<u8>>) -> Self {
182 Self { prev: OBJ_CAPTURE.replace(slot as *mut _) }
183 }
184}
185
186impl Drop for ScopedObjCapture {
187 fn drop(&mut self) {
188 OBJ_CAPTURE.set(self.prev);
189 }
190}
191
192fn obj_capture_transform(obj: &[u8]) -> Result<Option<Vec<u8>>, String> {
193 OBJ_CAPTURE.with(|tls| {
194 let ptr = tls.get();
195 if !ptr.is_null() {
196 unsafe { *ptr = Some(obj.to_vec()) };
197 }
198 });
199 Ok(None)
200}
201
202struct GlobalOrcJit {
211 builtins_jd: orc::JITDylibRef,
214
215 builtins_defined: std::sync::Mutex<HashSet<CString>>,
217
218 next_dylib_id: AtomicU64,
219
220 memory_counters: &'static JitMemoryCounters,
222
223 jit: orc::LLJIT,
224}
225
226impl GlobalOrcJit {
227 fn global() -> &'static OnceLock<Result<Self, String>> {
228 static GLOBAL: OnceLock<Result<GlobalOrcJit, String>> = OnceLock::new();
229 &GLOBAL
230 }
231
232 fn try_get() -> Option<&'static Self> {
233 Self::global().get().and_then(|r| r.as_ref().ok())
234 }
235
236 fn get(
237 debug_support: bool,
238 profiling_support: bool,
239 simple_perf: bool,
240 ) -> Result<&'static Self> {
241 let result = Self::global().get_or_init(|| {
242 init().map_err(|e| e.to_string())?;
243 let jit =
244 orc::LLJIT::builder().concurrent_compiler().build().map_err(|e| e.to_string())?;
245 jit.get_execution_session().set_default_error_reporter();
246 jit.get_obj_transform_layer().set_transform(obj_capture_transform);
247
248 if debug_support && let Err(e) = jit.enable_debug_support() {
250 warn!("failed to enable JIT debug support: {e}");
251 }
252 if profiling_support && let Err(e) = jit.enable_perf_support() {
253 warn!("failed to enable JIT perf support: {e}");
254 }
255 if simple_perf && let Err(e) = jit.enable_simple_perf() {
256 warn!("failed to enable simple perf map support: {e}");
257 }
258
259 let memory_counters: &'static JitMemoryCounters =
261 Box::leak(Box::new(JitMemoryCounters {
262 code_bytes: AtomicUsize::new(0),
263 data_bytes: AtomicUsize::new(0),
264 }));
265 orc::cvt(unsafe {
266 cpp::revmc_llvm_lljit_enable_memory_usage(
267 jit.as_inner(),
268 &memory_counters.code_bytes,
269 &memory_counters.data_bytes,
270 )
271 })
272 .map_err(|e| e.to_string())?;
273
274 let builtins_jd = jit.get_execution_session().create_bare_jit_dylib(c"revmc.builtins");
275
276 Ok(Self {
277 jit,
278 builtins_jd,
279 builtins_defined: Default::default(),
280 next_dylib_id: Default::default(),
281 memory_counters,
282 })
283 });
284 match result {
285 Ok(g) => Ok(g),
286 Err(e) => Err(eyre::eyre!("{e}")),
287 }
288 }
289
290 fn create_jit_dylib(&self) -> orc::JITDylibRef {
292 let id = self.next_dylib_id.fetch_add(1, Ordering::Relaxed);
293 let name = CString::new(format!("revmc.compiler.{id}")).unwrap();
294 let es = self.jit.get_execution_session();
295 let jd = es.create_bare_jit_dylib(&name);
296 jd.add_to_link_order(self.builtins_jd);
298 let prefix = self.jit.get_global_prefix();
301 if let Ok(generator) = orc::DefinitionGenerator::for_current_process(prefix) {
302 jd.add_generator(generator);
303 }
304 jd
305 }
306
307 fn remove_jit_dylib(&self, jd: orc::JITDylibRef) {
309 let es = self.jit.get_execution_session();
310 if let Err(e) = es.remove_jit_dylib(jd) {
311 error!("failed to remove JITDylib: {e}");
312 }
313 es.get_symbol_string_pool().clear_dead_entries();
315 }
316
317 fn define_builtins(&self, symbols: &[(CString, usize)]) {
320 if symbols.is_empty() {
321 return;
322 }
323 let mut defined = self.builtins_defined.lock().unwrap();
324 let new_syms: Vec<_> = symbols
325 .iter()
326 .filter(|(name, _)| defined.insert(name.clone()))
327 .map(|(name, addr)| {
328 orc::SymbolMapPair::new(
329 self.jit.mangle_and_intern(name),
330 orc::EvaluatedSymbol::new(
331 *addr as u64,
332 orc::SymbolFlags::none().exported().callable(),
333 ),
334 )
335 })
336 .collect();
337 drop(defined);
338 if !new_syms.is_empty()
339 && let Err((e, _)) =
340 self.builtins_jd.define(orc::MaterializationUnit::absolute_symbols(new_syms))
341 {
342 error!("failed to define builtins: {e}");
343 }
344 }
345
346 fn gc(&self) {
347 self.jit.get_execution_session().get_symbol_string_pool().clear_dead_entries();
348 }
349}
350
351#[allow(missing_debug_implementations)]
357pub struct JitDylibGuard {
358 global: &'static GlobalOrcJit,
359 jd: orc::JITDylibRef,
360}
361
362impl Drop for JitDylibGuard {
363 fn drop(&mut self) {
364 self.global.remove_jit_dylib(self.jd);
365 }
366}
367
368struct OrcJitState {
373 global: &'static GlobalOrcJit,
375 jd_guard: Arc<JitDylibGuard>,
382 staged_functions: FxHashMap<u32, FunctionValue<'static>>,
384 pending_symbols: Vec<(CString, usize)>,
387 loaded_trackers: Vec<orc::ResourceTracker>,
389 committed_functions: FxHashMap<u32, usize>,
391 last_compiled_object: Option<Vec<u8>>,
394 clear_pool_counter: u32,
396}
397
398impl fmt::Debug for OrcJitState {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 f.debug_struct("OrcJitState")
401 .field("staged_functions", &self.staged_functions.len())
402 .field("pending_symbols", &self.pending_symbols.len())
403 .field("loaded_trackers", &self.loaded_trackers.len())
404 .field("committed_functions", &self.committed_functions.len())
405 .finish_non_exhaustive()
406 }
407}
408
409impl OrcJitState {
410 fn new(debug_support: bool, profiling_support: bool, simple_perf: bool) -> Result<Self> {
411 let global = GlobalOrcJit::get(debug_support, profiling_support, simple_perf)?;
412 let jd = global.create_jit_dylib();
413 let jd_guard = Arc::new(JitDylibGuard { global, jd });
414 Ok(Self {
415 global,
416 staged_functions: FxHashMap::default(),
417 pending_symbols: Vec::new(),
418 loaded_trackers: Vec::new(),
419 committed_functions: FxHashMap::default(),
420 last_compiled_object: None,
421 jd_guard,
422 clear_pool_counter: 0,
423 })
424 }
425
426 fn clear(&mut self) -> Result<()> {
428 self.staged_functions.clear();
429 self.pending_symbols.clear();
430 self.committed_functions.clear();
431 self.last_compiled_object = None;
432 self.jd().clear().map_err(error_msg)?;
436 self.loaded_trackers.clear();
437 Ok(())
438 }
439
440 fn maybe_clear_dead_pool_entries(&mut self) {
445 self.clear_pool_counter += 1;
446 if self.clear_pool_counter.is_multiple_of(256) {
447 self.global.jit.get_execution_session().get_symbol_string_pool().clear_dead_entries();
448 }
449 }
450
451 fn jd(&self) -> orc::JITDylibRef {
452 self.jd_guard.jd
453 }
454}
455
456fn create_thread_safe_module(
461 tscx: &orc::ThreadSafeContext,
462 module: Module<'static>,
463) -> orc::ThreadSafeModule {
464 let module = std::mem::ManuallyDrop::new(module);
465 unsafe { orc::ThreadSafeModule::create_in_context(Module::new(module.as_mut_ptr()), tscx) }
467}
468
469fn create_orc_context() -> (&'static Context, orc::ThreadSafeContext, Box<ManuallyDrop<Context>>) {
475 let cx = Context::create();
476 let raw = cx.raw();
477 let tscx = orc::ThreadSafeContext::from_context(cx);
478 let cx_handle = Box::new(ManuallyDrop::new(unsafe { Context::new(raw) }));
481 let cx: &'static Context = unsafe { &*(&**cx_handle as *const Context) };
484 (cx, tscx, cx_handle)
485}
486
487struct DiState {
489 dibuilder: DebugInfoBuilder<'static>,
490 compile_unit: DICompileUnit<'static>,
491 finalized: bool,
492}
493
494impl fmt::Debug for DiState {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 f.debug_struct("DiState").field("finalized", &self.finalized).finish()
497 }
498}
499
500unsafe impl Send for EvmLlvmBackend {}
504
505impl EvmLlvmBackend {
506 #[instrument(name = "new_llvm_backend", level = "debug", skip_all)]
508 pub fn new(aot: bool) -> Result<Self> {
509 let config = BackendConfig::default();
510 init()?;
511
512 let target_info = TargetInfo::new();
513 let target = &target_info.target;
514 let machine = target
515 .create_target_machine(
516 &target_info.triple,
517 &target_info.cpu,
518 &target_info.features,
519 convert_opt_level(config.opt_level),
520 if aot { RelocMode::PIC } else { RelocMode::Static },
521 CodeModel::Default,
522 )
523 .ok_or_else(|| eyre::eyre!("failed to create target machine"))?;
524
525 let (cx, tscx, cx_handle, aot_cx) = if aot {
529 let aot_cx = Box::new(Context::create());
530 let cx: &'static Context = unsafe { &*(&*aot_cx as *const Context) };
533 (cx, None, None, Some(aot_cx))
534 } else {
535 if !target.has_jit() {
536 return Err(eyre::eyre!("target {:?} does not support JIT", target.get_name()));
537 }
538 if !target.has_target_machine() {
539 return Err(eyre::eyre!(
540 "target {:?} does not have target machine",
541 target.get_name()
542 ));
543 }
544
545 let (cx, tscx, cx_handle) = create_orc_context();
546 (cx, Some(tscx), Some(cx_handle), None)
547 };
548
549 let module = create_module(cx, &machine, aot)?;
550 let bcx = cx.create_builder();
551
552 let ty_void = cx.void_type();
553 let ty_i1 = cx.bool_type();
554 let ty_i8 = cx.i8_type();
555 let ty_i32 = cx.i32_type();
556 let ty_i64 = cx.i64_type();
557 let ty_i256 =
558 cx.custom_width_int_type(std::num::NonZeroU32::new(256).unwrap()).expect("i256");
559 let ty_isize = cx.ptr_sized_int_type(&machine.get_target_data(), None);
560 let ty_ptr = cx.ptr_type(AddressSpace::default());
561 Ok(Self {
562 cx,
563 _dh: dh::DiagnosticHandlerGuard::new(cx),
564 bcx,
565 module,
566 machine,
567 orc: None,
568 _tscx: tscx,
569 _cx_handle: cx_handle,
570 _aot_cx: aot_cx,
571 ty_void,
572 ty_i1,
573 ty_i8,
574 ty_i32,
575 ty_i64,
576 ty_i256,
577 ty_isize,
578 ty_ptr,
579 aot,
580 backend_config: config,
581 scratch: String::new(),
582 function_counter: 0,
583 function_names: FxHashMap::default(),
584 di_state: None,
585 })
586 }
587
588 #[inline]
590 pub fn cx(&self) -> &Context {
591 self.cx
592 }
593
594 #[inline]
595 fn module(&self) -> &Module<'static> {
596 &self.module
597 }
598
599 fn ensure_orc(&mut self) -> Result<&mut OrcJitState> {
600 if self.orc.is_none() {
601 self.orc = Some(OrcJitState::new(
602 self.backend_config.debug_support,
603 self.backend_config.profiling_support,
604 self.backend_config.simple_perf,
605 )?);
606 }
607 Ok(self.orc.as_mut().unwrap())
608 }
609
610 fn fn_type(
611 &self,
612 ret: Option<BasicTypeEnum<'static>>,
613 params: &[BasicTypeEnum<'static>],
614 ) -> FunctionType<'static> {
615 let params = params.iter().copied().map(Into::into).collect::<Vec<_>>();
616 match ret {
617 Some(ret) => ret.fn_type(¶ms, false),
618 None => self.ty_void.fn_type(¶ms, false),
619 }
620 }
621
622 #[inline]
626 fn name<'a>(&self, name: &'a str) -> &'a str {
627 if self.backend_config.is_dumping { name } else { "" }
628 }
629
630 fn id_to_name(&self, id: u32) -> &str {
631 &self.function_names[&id]
632 }
633
634 fn ensure_di_state(&mut self) {
636 if self.di_state.is_some() {
637 return;
638 }
639 let Some(debug_file) = &self.backend_config.debug_file else { return };
640
641 let filename =
642 debug_file.file_name().map(|f| f.to_string_lossy()).unwrap_or_default().into_owned();
643 let directory =
644 debug_file.parent().map(|p| p.to_string_lossy()).unwrap_or_default().into_owned();
645
646 self.module().add_basic_value_flag(
648 "Debug Info Version",
649 FlagBehavior::Warning,
650 self.ty_i32.const_int(inkwell::debug_info::debug_metadata_version() as u64, false),
651 );
652 self.module().add_basic_value_flag(
653 "Dwarf Version",
654 FlagBehavior::Warning,
655 self.ty_i32.const_int(5, false),
656 );
657
658 let opt_level = self.backend_config.opt_level;
659 let is_optimized = opt_level != OptimizationLevel::None;
660 let mut flags = Vec::new();
661 flags.push(match opt_level {
662 OptimizationLevel::None => "-O0",
663 OptimizationLevel::Less => "-O1",
664 OptimizationLevel::Default => "-O2",
665 OptimizationLevel::Aggressive => "-O3",
666 });
667 flags.push(if self.aot { "--aot" } else { "--jit" });
668 let flags = flags.join(" ");
669
670 let (dibuilder, compile_unit) = self.module().create_debug_info_builder(
671 true,
672 DWARFSourceLanguage::C,
673 &filename,
674 &directory,
675 "revmc",
676 is_optimized,
677 &flags,
678 0,
679 "",
680 DWARFEmissionKind::Full,
681 0,
682 false,
683 false,
684 "",
685 "",
686 );
687
688 self.di_state = Some(DiState { dibuilder, compile_unit, finalized: false });
689 }
690
691 fn commit_staged_module(&mut self) -> Result<()> {
693 if self.aot || self.orc.as_ref().is_none_or(|o| o.staged_functions.is_empty()) {
694 return Ok(());
695 }
696
697 self.di_state = None;
698
699 let new_module = create_module(self.cx, &self.machine, self.aot)?;
700 let old_module = std::mem::replace(&mut self.module, new_module);
701
702 let tscx = self._tscx.as_ref().expect("missing ThreadSafeContext");
703 let orc = self.orc.as_mut().unwrap();
704
705 let pending = &mut orc.pending_symbols;
707 if !pending.is_empty() {
708 orc.global.define_builtins(pending);
709 pending.clear();
710 }
711
712 let tracker = orc.jd().create_resource_tracker();
713
714 let tsm = create_thread_safe_module(tscx, old_module);
715 orc.global.jit.add_module_with_rt(tsm, &tracker).map_err(error_msg)?;
716
717 let tracker_idx = orc.loaded_trackers.len();
718 for &id in orc.staged_functions.keys() {
719 orc.committed_functions.insert(id, tracker_idx);
720 }
721 orc.loaded_trackers.push(tracker);
722 orc.staged_functions.clear();
723 Ok(())
724 }
725
726 fn reset_jit(&mut self) -> Result<()> {
729 self.function_names.clear();
730 self.di_state = None;
731
732 if let Some(orc) = &mut self.orc {
733 orc.clear()?;
734 }
735
736 self.module = create_module(self.cx, &self.machine, self.aot)?;
737
738 Ok(())
739 }
740
741 pub fn take_last_resource_tracker(&mut self) -> Option<orc::ResourceTracker> {
753 let orc = self.orc.as_mut()?;
754 let tracker = orc.loaded_trackers.pop()?;
755 let tracker_idx = orc.loaded_trackers.len();
756 orc.committed_functions.retain(|_, idx| *idx != tracker_idx);
757 Some(tracker)
758 }
759
760 pub fn jit_dylib_guard(&self) -> Arc<JitDylibGuard> {
766 Arc::clone(&self.orc.as_ref().expect("jit_dylib_guard called in AOT mode").jd_guard)
767 }
768}
769
770impl BackendTypes for EvmLlvmBackend {
771 type Type = BasicTypeEnum<'static>;
772 type Value = BasicValueEnum<'static>;
773 type StackSlot = PointerValue<'static>;
774 type BasicBlock = BasicBlock<'static>;
775 type Function = FunctionValue<'static>;
776}
777
778impl TypeMethods for EvmLlvmBackend {
779 fn type_ptr(&self) -> Self::Type {
780 self.ty_ptr.into()
781 }
782
783 fn type_ptr_sized_int(&self) -> Self::Type {
784 self.ty_isize.into()
785 }
786
787 fn type_int(&self, bits: u32) -> Self::Type {
788 match bits {
789 1 => self.ty_i1,
790 8 => self.ty_i8,
791 16 => self.cx.i16_type(),
792 32 => self.ty_i32,
793 64 => self.ty_i64,
794 128 => self.cx.i128_type(),
795 256 => self.ty_i256,
796 bits => self
797 .cx
798 .custom_width_int_type(std::num::NonZeroU32::new(bits).unwrap())
799 .expect("custom int type"),
800 }
801 .into()
802 }
803
804 fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type {
805 ty.array_type(size).into()
806 }
807
808 fn type_bit_width(&self, ty: Self::Type) -> u32 {
809 ty.into_int_type().get_bit_width()
810 }
811}
812
813impl Backend for EvmLlvmBackend {
814 type Builder<'a>
815 = EvmLlvmBuilder<'a>
816 where
817 Self: 'a;
818 type FuncId = u32;
819
820 fn ir_extension(&self) -> &'static str {
821 "ll"
822 }
823
824 fn set_module_name(&mut self, name: &str) {
825 self.module().set_name(name);
826 }
827
828 fn config(&self) -> &BackendConfig {
829 &self.backend_config
830 }
831
832 fn apply_config(&mut self, config: BackendConfig) {
833 if self.backend_config.is_dumping != config.is_dumping {
834 self.machine.set_asm_verbosity(config.is_dumping);
835 }
836 if self.backend_config.opt_level != config.opt_level {
837 unsafe {
838 cpp::revmc_llvm_target_machine_set_opt_level(
839 self.machine.as_mut_ptr(),
840 convert_opt_level(config.opt_level).into(),
841 );
842 }
843 }
844 self.backend_config = config;
845 }
846
847 fn finalize_debug_info(&mut self) -> Result<()> {
848 if let Some(di) = &mut self.di_state
849 && !di.finalized
850 {
851 di.dibuilder.finalize();
852 di.finalized = true;
853 }
854 Ok(())
855 }
856
857 fn is_aot(&self) -> bool {
858 self.aot
859 }
860
861 fn function_name_is_unique(&self, name: &str) -> bool {
862 self.module().get_function(name).is_none()
863 }
864
865 fn dump_ir(&mut self, path: &Path) -> Result<()> {
866 self.module().print_to_file(path).map_err(error_msg)
867 }
868
869 fn dump_disasm(&mut self, path: &Path) -> Result<()> {
870 self.machine.write_to_file(self.module(), FileType::Assembly, path).map_err(error_msg)
871 }
872
873 fn build_function(
874 &mut self,
875 name: &str,
876 ret: Option<Self::Type>,
877 params: &[Self::Type],
878 param_names: &[&str],
879 linkage: revmc_backend::Linkage,
880 ) -> Result<(Self::Builder<'_>, Self::FuncId)> {
881 if !self.aot {
882 self.ensure_orc()?;
883 }
884 let (id, function) = if let Some((&id, _fname)) =
885 self.function_names.iter().find(|(_k, fname)| fname.as_str() == name)
886 && let Some(orc) = &self.orc
887 && let Some(function) = orc.staged_functions.get(&id).copied()
888 && let Some(function2) = self.module().get_function(name)
889 && function == function2
890 {
891 self.bcx.position_at_end(function.get_first_basic_block().unwrap());
892 (id, function)
893 } else {
894 let fn_type = self.fn_type(ret, params);
895 let function =
896 self.module().add_function(name, fn_type, Some(convert_linkage(linkage)));
897 cpp::set_dso_local(function);
898 if self.backend_config.is_dumping {
899 for (i, &name) in param_names.iter().enumerate() {
900 function.get_nth_param(i as u32).expect(name).set_name(self.name(name));
901 }
902 }
903
904 let entry = self.cx.append_basic_block(function, self.name("entry"));
905 self.bcx.position_at_end(entry);
906
907 let id = self.function_counter;
908 self.function_counter += 1;
909 self.function_names.insert(id, name.to_string());
910 if let Some(orc) = &mut self.orc {
911 orc.staged_functions.insert(id, function);
912 }
913 (id, function)
914 };
915
916 self.ensure_di_state();
918 let debug_scope = if let Some(di) = &self.di_state {
919 let file = di.compile_unit.get_file();
920 let subroutine_type =
921 di.dibuilder.create_subroutine_type(file, None, &[], DIFlags::PUBLIC);
922 let subprogram = di.dibuilder.create_function(
923 di.compile_unit.as_debug_info_scope(),
924 name,
925 None,
926 file,
927 0,
928 subroutine_type,
929 true,
930 true,
931 0,
932 DIFlags::PUBLIC,
933 self.backend_config.opt_level != OptimizationLevel::None,
934 );
935 function.set_subprogram(subprogram);
936 Some(subprogram)
937 } else {
938 None
939 };
940
941 let builder = EvmLlvmBuilder { backend: self, function, debug_scope };
942 Ok((builder, id))
943 }
944
945 fn verify_module(&mut self) -> Result<()> {
946 self.module().verify().map_err(error_msg)
947 }
948
949 fn optimize_module(&mut self) -> Result<()> {
950 static PASSES_OVERRIDE: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
969 static PASSES: std::sync::OnceLock<String> = std::sync::OnceLock::new();
970 static PASSES_WITH_LICM: std::sync::OnceLock<String> = std::sync::OnceLock::new();
971
972 let passes = PASSES_OVERRIDE.get_or_init(|| std::env::var("REVMC_PASSES").ok());
973 let passes = passes.as_deref().unwrap_or_else(|| match self.backend_config.opt_level {
974 OptimizationLevel::None => "default<O0>",
975 OptimizationLevel::Less | OptimizationLevel::Default => {
976 let total_bbs: u32 =
977 self.module.get_functions().map(|f| f.count_basic_blocks()).sum();
978 let with_licm = total_bbs <= 4000;
979 let passes = if with_licm { &PASSES_WITH_LICM } else { &PASSES };
980 passes.get_or_init(|| build_pass_pipeline(with_licm))
981 }
982 OptimizationLevel::Aggressive => "default<O3>",
983 });
984 let opts = PassBuilderOptions::create();
985 self.module().run_passes(passes, &self.machine, opts).map_err(error_msg)
986 }
987
988 fn write_object<W: std::io::Write>(&mut self, mut w: W) -> Result<()> {
989 let buffer = self
990 .machine
991 .write_to_memory_buffer(self.module(), FileType::Object)
992 .map_err(error_msg)?;
993 w.write_all(buffer.as_slice())?;
994 Ok(())
995 }
996
997 fn jit_function(&mut self, id: Self::FuncId) -> Result<usize> {
998 self.ensure_orc()?;
999 self.commit_staged_module()?;
1000 let name_str = self.id_to_name(id);
1001 let name = CString::new(name_str).unwrap();
1002 let orc = self.orc.as_mut().unwrap();
1003 let mut captured = None;
1007 let _guard = ScopedObjCapture::install(&mut captured);
1008 let addr = orc.global.jit.lookup_in(orc.jd(), &name).map_err(error_msg)?;
1009 drop(_guard);
1010 if captured.is_some() {
1011 orc.last_compiled_object = captured;
1012 }
1013 Ok(addr)
1014 }
1015
1016 fn function_name(&self, id: Self::FuncId) -> Option<&str> {
1017 self.function_names.get(&id).map(|s| s.as_str())
1018 }
1019
1020 fn function_sizes(&self) -> Vec<(String, usize)> {
1021 let Some(orc) = &self.orc else { return Vec::new() };
1022 let Some(data) = orc.last_compiled_object.as_deref() else { return Vec::new() };
1023 let Ok(obj) = object::File::parse(data) else { return Vec::new() };
1024
1025 let mut result: Vec<_> = obj
1026 .symbols()
1027 .filter(|sym| sym.is_definition())
1028 .filter_map(|sym| {
1029 let name = sym.name().ok()?;
1030 self.function_names.values().any(|n| n == name).then_some(())?;
1031 Some((name.to_string(), sym.size() as usize))
1032 })
1033 .collect();
1034 result.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
1035 result
1036 }
1037
1038 fn clear_ir(&mut self) -> Result<()> {
1039 self.di_state = None;
1040 self.function_names.clear();
1041 self.module = create_module(self.cx, &self.machine, self.aot)?;
1042 if let Some(orc) = &mut self.orc {
1043 orc.staged_functions.clear();
1044 orc.pending_symbols.clear();
1045 orc.maybe_clear_dead_pool_entries();
1046 }
1047 Ok(())
1048 }
1049
1050 unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()> {
1051 if let Some(orc) = &mut self.orc {
1052 if let Some(function) = orc.staged_functions.remove(&id) {
1054 unsafe { function.delete() };
1055 }
1056
1057 if let Some(tracker_idx) = orc.committed_functions.remove(&id) {
1059 orc.loaded_trackers[tracker_idx].remove().map_err(error_msg)?;
1060 orc.committed_functions.retain(|co_id, idx| {
1063 if *idx == tracker_idx {
1064 self.function_names.remove(co_id);
1065 false
1066 } else {
1067 true
1068 }
1069 });
1070 }
1073 }
1074 self.function_names.remove(&id);
1075 Ok(())
1076 }
1077
1078 unsafe fn free_all_functions(&mut self) -> Result<()> {
1079 self.reset_jit()
1080 }
1081}
1082
1083#[derive(Debug)]
1085struct TargetInfo {
1086 triple: TargetTriple,
1087 target: Target,
1088 cpu: String,
1089 features: String,
1090}
1091
1092unsafe impl std::marker::Send for TargetInfo {}
1094unsafe impl std::marker::Sync for TargetInfo {}
1095
1096impl Clone for TargetInfo {
1097 fn clone(&self) -> Self {
1098 let triple = TargetTriple::create(self.triple.as_str().to_str().unwrap());
1099 Self {
1100 target: Target::from_triple(&triple).unwrap(),
1101 triple,
1102 cpu: self.cpu.clone(),
1103 features: self.features.clone(),
1104 }
1105 }
1106}
1107
1108impl TargetInfo {
1109 fn new() -> &'static Self {
1110 static HOST_TARGET_INFO: OnceLock<TargetInfo> = OnceLock::new();
1111 HOST_TARGET_INFO.get_or_init(|| {
1112 let triple = TargetMachine::get_default_triple();
1113 let target = Target::from_triple(&triple).unwrap();
1114 let cpu = TargetMachine::get_host_cpu_name().to_string_lossy().into_owned();
1115 let features = TargetMachine::get_host_cpu_features().to_string_lossy().into_owned();
1116 Self { target, triple, cpu, features }
1117 })
1118 }
1119}
1120
1121#[derive(Debug)]
1123#[must_use]
1124pub struct EvmLlvmBuilder<'a> {
1125 backend: &'a mut EvmLlvmBackend,
1126 function: FunctionValue<'static>,
1127 debug_scope: Option<DISubprogram<'static>>,
1128}
1129
1130impl std::ops::Deref for EvmLlvmBuilder<'_> {
1131 type Target = EvmLlvmBackend;
1132
1133 #[inline]
1134 fn deref(&self) -> &Self::Target {
1135 self.backend
1136 }
1137}
1138
1139impl std::ops::DerefMut for EvmLlvmBuilder<'_> {
1140 #[inline]
1141 fn deref_mut(&mut self) -> &mut Self::Target {
1142 self.backend
1143 }
1144}
1145
1146impl EvmLlvmBuilder<'_> {
1147 #[allow(dead_code)]
1148 fn extract_value(
1149 &mut self,
1150 value: BasicValueEnum<'static>,
1151 index: u32,
1152 name: &str,
1153 ) -> BasicValueEnum<'static> {
1154 self.bcx.build_extract_value(value.into_struct_value(), index, self.name(name)).unwrap()
1155 }
1156
1157 fn memcpy_inner(
1158 &mut self,
1159 dst: BasicValueEnum<'static>,
1160 src: BasicValueEnum<'static>,
1161 len: BasicValueEnum<'static>,
1162 inline: bool,
1163 ) {
1164 let dst = dst.into_pointer_value();
1165 let src = src.into_pointer_value();
1166 let len = len.into_int_value();
1167 let volatile = self.bool_const(false);
1168 let name = format!(
1169 "llvm.memcpy{}.p0.p0.{}",
1170 if inline { ".inline" } else { "" },
1171 fmt_ty(len.get_type().into()),
1172 );
1173 let memcpy = self.get_or_add_function(&name, |this| {
1174 this.ty_void.fn_type(
1175 &[this.ty_ptr.into(), this.ty_ptr.into(), this.ty_i64.into(), this.ty_i1.into()],
1176 false,
1177 )
1178 });
1179 self.bcx
1180 .build_call(memcpy, &[dst.into(), src.into(), len.into(), volatile.into()], "")
1181 .unwrap();
1182 }
1183
1184 #[allow(dead_code)]
1185 fn call_overflow_function(
1186 &mut self,
1187 name: &str,
1188 lhs: BasicValueEnum<'static>,
1189 rhs: BasicValueEnum<'static>,
1190 ) -> (BasicValueEnum<'static>, BasicValueEnum<'static>) {
1191 let f = self.get_overflow_function(name, lhs.get_type());
1192 let result = self.call(f, &[lhs, rhs]).unwrap();
1193 (self.extract_value(result, 0, "result"), self.extract_value(result, 1, "overflow"))
1194 }
1195
1196 #[allow(dead_code)]
1197 fn get_overflow_function(
1198 &mut self,
1199 name: &str,
1200 ty: BasicTypeEnum<'static>,
1201 ) -> FunctionValue<'static> {
1202 let name = format!("llvm.{name}.with.overflow.{}", fmt_ty(ty));
1203 self.get_or_add_function(&name, |this| {
1204 this.fn_type(
1205 Some(this.cx.struct_type(&[ty, this.ty_i1.into()], false).into()),
1206 &[ty, ty],
1207 )
1208 })
1209 }
1210
1211 fn get_sat_function(
1212 &mut self,
1213 name: &str,
1214 ty: BasicTypeEnum<'static>,
1215 ) -> FunctionValue<'static> {
1216 let name = format!("llvm.{name}.sat.{}", fmt_ty(ty));
1217 self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]))
1218 }
1219
1220 fn get_or_add_function(
1221 &mut self,
1222 name: &str,
1223 mk_ty: impl FnOnce(&mut Self) -> FunctionType<'static>,
1224 ) -> FunctionValue<'static> {
1225 match self.module().get_function(name) {
1226 Some(function) => function,
1227 None => {
1228 let ty = mk_ty(self);
1229 self.module().add_function(name, ty, None)
1230 }
1231 }
1232 }
1233
1234 fn set_branch_weights(
1235 &self,
1236 inst: InstructionValue<'static>,
1237 weights: impl IntoIterator<Item = u32>,
1238 ) {
1239 let weights = weights.into_iter();
1240 let mut values = Vec::<BasicMetadataValueEnum<'static>>::with_capacity(
1241 1 + weights.size_hint().1.unwrap(),
1242 );
1243 values.push(self.cx.metadata_string("branch_weights").into());
1244 for weight in weights {
1245 values.push(self.ty_i32.const_int(weight as u64, false).into());
1246 }
1247 let metadata = self.cx.metadata_node(&values);
1248 let kind_id = self.cx.get_kind_id("prof");
1249 inst.set_metadata(metadata, kind_id).unwrap();
1250 }
1251
1252 fn assume_inner(&mut self, cond: BasicValueEnum<'static>) -> CallSiteValue<'static> {
1253 let function = self.get_or_add_function("llvm.assume", |this| {
1254 this.ty_void.fn_type(&[this.ty_i1.into()], false)
1255 });
1256 self.bcx.build_call(function, &[cond.into()], "").unwrap()
1257 }
1258}
1259
1260impl BackendTypes for EvmLlvmBuilder<'_> {
1261 type Type = <EvmLlvmBackend as BackendTypes>::Type;
1262 type Value = <EvmLlvmBackend as BackendTypes>::Value;
1263 type StackSlot = <EvmLlvmBackend as BackendTypes>::StackSlot;
1264 type BasicBlock = <EvmLlvmBackend as BackendTypes>::BasicBlock;
1265 type Function = <EvmLlvmBackend as BackendTypes>::Function;
1266}
1267
1268impl TypeMethods for EvmLlvmBuilder<'_> {
1269 fn type_ptr(&self) -> Self::Type {
1270 self.backend.type_ptr()
1271 }
1272
1273 fn type_ptr_sized_int(&self) -> Self::Type {
1274 self.backend.type_ptr_sized_int()
1275 }
1276
1277 fn type_int(&self, bits: u32) -> Self::Type {
1278 self.backend.type_int(bits)
1279 }
1280
1281 fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type {
1282 self.backend.type_array(ty, size)
1283 }
1284
1285 fn type_bit_width(&self, ty: Self::Type) -> u32 {
1286 self.backend.type_bit_width(ty)
1287 }
1288}
1289
1290impl Builder for EvmLlvmBuilder<'_> {
1291 fn create_block(&mut self, name: &str) -> Self::BasicBlock {
1292 self.cx.append_basic_block(self.function, self.name(name))
1293 }
1294
1295 fn create_block_after(&mut self, after: Self::BasicBlock, name: &str) -> Self::BasicBlock {
1296 self.cx.insert_basic_block_after(after, self.name(name))
1297 }
1298
1299 fn switch_to_block(&mut self, block: Self::BasicBlock) {
1300 self.bcx.position_at_end(block);
1301 }
1302
1303 fn seal_block(&mut self, block: Self::BasicBlock) {
1304 let _ = block;
1305 }
1307
1308 fn seal_all_blocks(&mut self) {
1309 }
1311
1312 fn set_current_block_cold(&mut self) {
1313 let true_ = self.bool_const(true);
1314 let callsite = self.assume_inner(true_);
1315 let cold = self.cx.create_enum_attribute(Attribute::get_named_enum_kind_id("cold"), 0);
1316 callsite.add_attribute(AttributeLoc::Function, cold);
1317 }
1318
1319 fn current_block(&mut self) -> Option<Self::BasicBlock> {
1320 self.bcx.get_insert_block()
1321 }
1322
1323 fn block_addr(&mut self, block: Self::BasicBlock) -> Option<Self::Value> {
1324 unsafe { block.get_address().map(Into::into) }
1325 }
1326
1327 fn add_comment_to_current_inst(&mut self, comment: &str) {
1328 if !self.backend_config.is_dumping {
1329 return;
1330 }
1331 let Some(block) = self.current_block() else { return };
1332 let Some(ins) = block.get_last_instruction() else { return };
1333 let metadata = self.cx.metadata_string(comment);
1334 let metadata = self.cx.metadata_node(&[metadata.into()]);
1335 ins.set_metadata(metadata, self.cx.get_kind_id("annotation")).unwrap();
1336 }
1337
1338 fn set_debug_location(&mut self, line: u32, col: u32) {
1339 let Some(scope) = self.debug_scope else { return };
1340 let Some(di) = &self.di_state else { return };
1341 let loc = di.dibuilder.create_debug_location(
1342 self.cx,
1343 line,
1344 col,
1345 scope.as_debug_info_scope(),
1346 None,
1347 );
1348 self.bcx.set_current_debug_location(loc);
1349 }
1350
1351 fn clear_debug_location(&mut self) {
1352 if self.debug_scope.is_some() {
1353 self.bcx.unset_current_debug_location();
1354 }
1355 }
1356
1357 fn fn_param(&mut self, index: usize) -> Self::Value {
1358 self.function.get_nth_param(index as _).unwrap()
1359 }
1360
1361 fn num_fn_params(&self) -> usize {
1362 self.function.count_params() as usize
1363 }
1364
1365 fn bool_const(&mut self, value: bool) -> Self::Value {
1366 self.ty_i1.const_int(value as u64, false).into()
1367 }
1368
1369 fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value {
1370 ty.into_int_type().const_int(value as u64, value.is_negative()).into()
1371 }
1372
1373 fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value {
1374 ty.into_int_type().const_int(value, false).into()
1375 }
1376
1377 fn iconst_256(&mut self, value: impl TryInto<U256>) -> Self::Value {
1378 let value = value.try_into().ok().expect("invalid U256 value");
1379 if let Ok(low) = value.try_into() {
1380 return self.ty_i256.const_int(low, false).into();
1381 }
1382
1383 self.scratch.clear();
1384 write!(self.scratch, "{value:x}").unwrap();
1385 self.ty_i256.const_int_from_string(&self.scratch, StringRadix::Hexadecimal).unwrap().into()
1386 }
1387
1388 fn str_const(&mut self, value: &str) -> Self::Value {
1389 self.bcx.build_global_string_ptr(value, "").unwrap().as_pointer_value().into()
1390 }
1391
1392 fn nullptr(&mut self) -> Self::Value {
1393 self.ty_ptr.const_null().into()
1394 }
1395
1396 fn new_stack_slot_raw(&mut self, ty: Self::Type, name: &str) -> Self::StackSlot {
1397 self.bcx.build_alloca(ty, self.name(name)).unwrap()
1402 }
1403
1404 fn stack_load(&mut self, ty: Self::Type, slot: Self::StackSlot, name: &str) -> Self::Value {
1405 self.load(ty, slot.into(), name)
1406 }
1407
1408 fn stack_store(&mut self, value: Self::Value, slot: Self::StackSlot) {
1409 self.store(value, slot.into())
1410 }
1411
1412 fn stack_addr(&mut self, ty: Self::Type, slot: Self::StackSlot) -> Self::Value {
1413 let _ = ty;
1414 slot.into()
1415 }
1416
1417 fn load(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value {
1418 let value = self.bcx.build_load(ty, ptr.into_pointer_value(), self.name(name)).unwrap();
1419 if ty == self.ty_i256.into() {
1420 self.current_block().unwrap().get_last_instruction().unwrap().set_alignment(8).unwrap();
1421 }
1422 value
1423 }
1424
1425 fn load_aligned(
1426 &mut self,
1427 ty: Self::Type,
1428 ptr: Self::Value,
1429 align: usize,
1430 name: &str,
1431 ) -> Self::Value {
1432 let value = self.bcx.build_load(ty, ptr.into_pointer_value(), self.name(name)).unwrap();
1433 self.current_block()
1434 .unwrap()
1435 .get_last_instruction()
1436 .unwrap()
1437 .set_alignment(align as u32)
1438 .unwrap();
1439 value
1440 }
1441
1442 fn store(&mut self, value: Self::Value, ptr: Self::Value) {
1443 let inst = self.bcx.build_store(ptr.into_pointer_value(), value).unwrap();
1444 if value.get_type() == self.ty_i256.into() {
1445 inst.set_alignment(8).unwrap();
1446 }
1447 }
1448
1449 fn store_aligned(&mut self, value: Self::Value, ptr: Self::Value, align: usize) {
1450 let inst = self.bcx.build_store(ptr.into_pointer_value(), value).unwrap();
1451 inst.set_alignment(align as u32).unwrap();
1452 }
1453
1454 fn nop(&mut self) {
1455 }
1457
1458 fn ret(&mut self, values: &[Self::Value]) {
1459 match values {
1460 [] => self.bcx.build_return(None),
1461 [value] => self.bcx.build_return(Some(value)),
1462 values => self.bcx.build_aggregate_return(values),
1463 }
1464 .unwrap();
1465 }
1466
1467 fn assume(&mut self, cond: Self::Value) {
1468 self.assume_inner(cond);
1469 }
1470
1471 fn icmp(&mut self, cond: IntCC, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1472 self.bcx
1473 .build_int_compare(convert_intcc(cond), lhs.into_int_value(), rhs.into_int_value(), "")
1474 .unwrap()
1475 .into()
1476 }
1477
1478 fn icmp_imm(&mut self, cond: IntCC, lhs: Self::Value, rhs: i64) -> Self::Value {
1479 let rhs = self.iconst(lhs.get_type(), rhs);
1480 self.icmp(cond, lhs, rhs)
1481 }
1482
1483 fn is_null(&mut self, ptr: Self::Value) -> Self::Value {
1484 self.bcx.build_is_null(ptr.into_pointer_value(), "").unwrap().into()
1485 }
1486
1487 fn is_not_null(&mut self, ptr: Self::Value) -> Self::Value {
1488 self.bcx.build_is_not_null(ptr.into_pointer_value(), "").unwrap().into()
1489 }
1490
1491 fn br(&mut self, dest: Self::BasicBlock) {
1492 self.bcx.build_unconditional_branch(dest).unwrap();
1493 }
1494
1495 fn brif(
1496 &mut self,
1497 cond: Self::Value,
1498 then_block: Self::BasicBlock,
1499 else_block: Self::BasicBlock,
1500 ) {
1501 self.bcx.build_conditional_branch(cond.into_int_value(), then_block, else_block).unwrap();
1502 }
1503
1504 fn brif_cold(
1505 &mut self,
1506 cond: Self::Value,
1507 then_block: Self::BasicBlock,
1508 else_block: Self::BasicBlock,
1509 then_is_cold: bool,
1510 ) {
1511 let inst = self
1512 .bcx
1513 .build_conditional_branch(cond.into_int_value(), then_block, else_block)
1514 .unwrap();
1515 let weights = if then_is_cold { [1, DEFAULT_WEIGHT] } else { [DEFAULT_WEIGHT, 1] };
1516 self.set_branch_weights(inst, weights);
1517 }
1518
1519 fn switch(
1520 &mut self,
1521 index: Self::Value,
1522 default: Self::BasicBlock,
1523 targets: &[(u64, Self::BasicBlock)],
1524 default_is_cold: bool,
1525 ) {
1526 let ty = index.get_type().into_int_type();
1527 let targets =
1528 targets.iter().map(|(v, b)| (ty.const_int(*v, false), *b)).collect::<Vec<_>>();
1529 let inst = self.bcx.build_switch(index.into_int_value(), default, &targets).unwrap();
1530 if default_is_cold {
1531 let weights = iter::once(1).chain(iter::repeat_n(DEFAULT_WEIGHT, targets.len()));
1532 self.set_branch_weights(inst, weights);
1533 }
1534 }
1535
1536 fn br_indirect(&mut self, address: Self::Value, destinations: &[Self::BasicBlock]) {
1537 let _ = self.bcx.build_indirect_branch(address, destinations).unwrap();
1538 }
1539
1540 fn phi(&mut self, ty: Self::Type, incoming: &[(Self::Value, Self::BasicBlock)]) -> Self::Value {
1541 let incoming = incoming
1542 .iter()
1543 .map(|(value, block)| (value as &dyn BasicValue<'_>, *block))
1544 .collect::<Vec<_>>();
1545 let phi = self.bcx.build_phi(ty, "").unwrap();
1546 phi.add_incoming(&incoming);
1547 phi.as_basic_value()
1548 }
1549
1550 fn select(
1551 &mut self,
1552 cond: Self::Value,
1553 then_value: Self::Value,
1554 else_value: Self::Value,
1555 ) -> Self::Value {
1556 self.bcx.build_select(cond.into_int_value(), then_value, else_value, "").unwrap()
1557 }
1558
1559 fn lazy_select(
1560 &mut self,
1561 cond: Self::Value,
1562 ty: Self::Type,
1563 then_value: impl FnOnce(&mut Self) -> Self::Value,
1564 else_value: impl FnOnce(&mut Self) -> Self::Value,
1565 ) -> Self::Value {
1566 let then_block = if let Some(current) = self.current_block() {
1567 self.create_block_after(current, "then")
1568 } else {
1569 self.create_block("then")
1570 };
1571 let else_block = self.create_block_after(then_block, "else");
1572 let done_block = self.create_block_after(else_block, "contd");
1573
1574 self.brif(cond, then_block, else_block);
1575
1576 self.switch_to_block(then_block);
1577 let then_value = then_value(self);
1578 self.br(done_block);
1579
1580 self.switch_to_block(else_block);
1581 let else_value = else_value(self);
1582 self.br(done_block);
1583
1584 self.switch_to_block(done_block);
1585 let phi = self.bcx.build_phi(ty, "").unwrap();
1586 phi.add_incoming(&[(&then_value, then_block), (&else_value, else_block)]);
1587 phi.as_basic_value()
1588 }
1589
1590 fn iadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1591 self.bcx.build_int_add(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1592 }
1593
1594 fn isub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1595 self.bcx.build_int_sub(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1596 }
1597
1598 fn imul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1599 self.bcx.build_int_mul(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1600 }
1601
1602 fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1603 self.bcx
1604 .build_int_unsigned_div(lhs.into_int_value(), rhs.into_int_value(), "")
1605 .unwrap()
1606 .into()
1607 }
1608
1609 fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1610 self.bcx
1611 .build_int_signed_div(lhs.into_int_value(), rhs.into_int_value(), "")
1612 .unwrap()
1613 .into()
1614 }
1615
1616 fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1617 self.bcx
1618 .build_int_unsigned_rem(lhs.into_int_value(), rhs.into_int_value(), "")
1619 .unwrap()
1620 .into()
1621 }
1622
1623 fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1624 self.bcx
1625 .build_int_signed_rem(lhs.into_int_value(), rhs.into_int_value(), "")
1626 .unwrap()
1627 .into()
1628 }
1629
1630 fn iadd_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1631 let rhs = self.iconst(lhs.get_type(), rhs);
1632 self.iadd(lhs, rhs)
1633 }
1634
1635 fn isub_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1636 let rhs = self.iconst(lhs.get_type(), rhs);
1637 self.isub(lhs, rhs)
1638 }
1639
1640 fn imul_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1641 let rhs = self.iconst(lhs.get_type(), rhs);
1642 self.imul(lhs, rhs)
1643 }
1644
1645 fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
1649 let result = self.iadd(lhs, rhs);
1650 let overflow = self.icmp(IntCC::UnsignedLessThan, result, rhs);
1651 (result, overflow)
1652 }
1653
1654 fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
1655 let result = self.isub(lhs, rhs);
1656 let overflow = self.icmp(IntCC::UnsignedLessThan, lhs, rhs);
1657 (result, overflow)
1658 }
1659
1660 fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1661 let f = self.get_sat_function("uadd", lhs.get_type());
1662 self.call(f, &[lhs, rhs]).unwrap()
1663 }
1664
1665 fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1666 let ty = lhs.get_type();
1667 let name = format!("llvm.umax.{}", fmt_ty(ty));
1668 let max = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]));
1669 self.call(max, &[lhs, rhs]).unwrap()
1670 }
1671
1672 fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1673 let ty = lhs.get_type();
1674 let name = format!("llvm.umin.{}", fmt_ty(ty));
1675 let min = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]));
1676 self.call(min, &[lhs, rhs]).unwrap()
1677 }
1678
1679 fn bswap(&mut self, value: Self::Value) -> Self::Value {
1680 let ty = value.get_type();
1681 let name = format!("llvm.bswap.{}", fmt_ty(ty));
1682 let bswap = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty]));
1683 self.call(bswap, &[value]).unwrap()
1684 }
1685
1686 fn bitor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1687 self.bcx.build_or(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1688 }
1689
1690 fn bitand(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1691 self.bcx.build_and(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1692 }
1693
1694 fn bitxor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1695 self.bcx.build_xor(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1696 }
1697
1698 fn bitnot(&mut self, value: Self::Value) -> Self::Value {
1699 self.bcx.build_not(value.into_int_value(), "").unwrap().into()
1700 }
1701
1702 fn clz(&mut self, value: Self::Value) -> Self::Value {
1703 let ty = value.get_type();
1704 let i1_ty = self.type_int(1);
1705 let name = format!("llvm.ctlz.{}", fmt_ty(ty));
1706 let ctlz = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, i1_ty]));
1707 let is_poison_on_zero = self.bool_const(false);
1708 self.call(ctlz, &[value, is_poison_on_zero]).unwrap()
1709 }
1710
1711 fn bitor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1712 let rhs = self.iconst(lhs.get_type(), rhs);
1713 self.bitor(lhs, rhs)
1714 }
1715
1716 fn bitand_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1717 let rhs = self.iconst(lhs.get_type(), rhs);
1718 self.bitand(lhs, rhs)
1719 }
1720
1721 fn bitxor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1722 let rhs = self.iconst(lhs.get_type(), rhs);
1723 self.bitxor(lhs, rhs)
1724 }
1725
1726 fn ishl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1727 self.bcx.build_left_shift(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1728 }
1729
1730 fn ushr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1731 self.bcx
1732 .build_right_shift(lhs.into_int_value(), rhs.into_int_value(), false, "")
1733 .unwrap()
1734 .into()
1735 }
1736
1737 fn sshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1738 self.bcx
1739 .build_right_shift(lhs.into_int_value(), rhs.into_int_value(), true, "")
1740 .unwrap()
1741 .into()
1742 }
1743
1744 fn zext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value {
1745 self.bcx.build_int_z_extend(value.into_int_value(), ty.into_int_type(), "").unwrap().into()
1746 }
1747
1748 fn sext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value {
1749 self.bcx.build_int_s_extend(value.into_int_value(), ty.into_int_type(), "").unwrap().into()
1750 }
1751
1752 fn ireduce(&mut self, to: Self::Type, value: Self::Value) -> Self::Value {
1753 self.bcx.build_int_truncate(value.into_int_value(), to.into_int_type(), "").unwrap().into()
1754 }
1755
1756 fn inttoptr(&mut self, value: Self::Value, ty: Self::Type) -> Self::Value {
1757 self.bcx
1758 .build_int_to_ptr(value.into_int_value(), ty.into_pointer_type(), "")
1759 .unwrap()
1760 .into()
1761 }
1762
1763 fn gep(
1764 &mut self,
1765 elem_ty: Self::Type,
1766 ptr: Self::Value,
1767 indexes: &[Self::Value],
1768 name: &str,
1769 ) -> Self::Value {
1770 let indexes = indexes.iter().map(|idx| idx.into_int_value()).collect::<Vec<_>>();
1771 unsafe {
1772 self.bcx.build_in_bounds_gep(
1773 elem_ty,
1774 ptr.into_pointer_value(),
1775 &indexes,
1776 self.name(name),
1777 )
1778 }
1779 .unwrap()
1780 .into()
1781 }
1782
1783 fn tail_call(
1784 &mut self,
1785 function: Self::Function,
1786 args: &[Self::Value],
1787 tail_call: TailCallKind,
1788 ) -> Option<Self::Value> {
1789 let args = args.iter().copied().map(Into::into).collect::<Vec<_>>();
1790 let callsite = self.bcx.build_call(function, &args, "").unwrap();
1791 if let Some(call_conv) = function_call_conv(function) {
1792 unsafe {
1793 inkwell::llvm_sys::core::LLVMSetInstructionCallConv(
1794 callsite.as_value_ref(),
1795 call_conv as _,
1796 );
1797 }
1798 }
1799 if tail_call != TailCallKind::None {
1800 callsite.set_tail_call_kind(convert_tail_call_kind(tail_call));
1801 }
1802 callsite.try_as_basic_value().basic()
1803 }
1804
1805 fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value> {
1806 let ty = value.get_type();
1807 let name = format!("llvm.is.constant.{}", fmt_ty(ty));
1808 let f =
1809 self.get_or_add_function(&name, |this| this.fn_type(Some(this.ty_i1.into()), &[ty]));
1810 Some(self.call(f, &[value]).unwrap())
1811 }
1812
1813 fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value) {
1814 self.memcpy_inner(dst, src, len, false);
1815 }
1816
1817 fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
1818 let len = self.iconst(self.ty_i64.into(), len);
1819 self.memcpy_inner(dst, src, len, true);
1820 }
1821
1822 fn unreachable(&mut self) {
1823 self.bcx.build_unreachable().unwrap();
1824 }
1825
1826 fn get_or_build_function(
1827 &mut self,
1828 name: &str,
1829 params: &[Self::Type],
1830 ret: Option<Self::Type>,
1831 linkage: revmc_backend::Linkage,
1832 build: impl FnOnce(&mut Self),
1833 ) -> Self::Function {
1834 if let Some(function) = self.module().get_function(name) {
1835 return function;
1836 }
1837
1838 let before = self.current_block();
1839
1840 let func_ty = self.fn_type(ret, params);
1841 let function = self.module().add_function(name, func_ty, Some(convert_linkage(linkage)));
1842 cpp::set_dso_local(function);
1843 let prev_function = std::mem::replace(&mut self.function, function);
1844
1845 let entry = self.cx.append_basic_block(function, self.name("entry"));
1846 self.bcx.position_at_end(entry);
1847 build(self);
1848 if let Some(before) = before {
1849 self.bcx.position_at_end(before);
1850 }
1851
1852 self.function = prev_function;
1853
1854 function
1855 }
1856
1857 fn get_function(&mut self, name: &str) -> Option<Self::Function> {
1858 self.module().get_function(name)
1859 }
1860
1861 fn get_printf_function(&mut self) -> Self::Function {
1862 let name = "printf";
1863 if let Some(function) = self.module().get_function(name) {
1864 return function;
1865 }
1866
1867 let ty = self.cx.void_type().fn_type(&[self.ty_ptr.into()], true);
1868 let function =
1869 self.module().add_function(name, ty, Some(inkwell::module::Linkage::External));
1870 cpp::set_dso_local(function);
1871 function
1872 }
1873
1874 fn add_function(
1875 &mut self,
1876 name: &str,
1877 params: &[Self::Type],
1878 ret: Option<Self::Type>,
1879 address: Option<usize>,
1880 linkage: revmc_backend::Linkage,
1881 call_conv: CallConv,
1882 ) -> Self::Function {
1883 let func_ty = self.fn_type(ret, params);
1884 let function = self.module().add_function(name, func_ty, Some(convert_linkage(linkage)));
1885 set_function_call_conv(function, call_conv);
1886 cpp::set_dso_local(function);
1887 if let Some(address) = address
1888 && let Some(orc) = &mut self.orc
1889 {
1890 orc.pending_symbols.push((CString::new(name).unwrap(), address));
1891 }
1892 function
1893 }
1894
1895 fn add_function_stub(
1896 &mut self,
1897 function: Self::Function,
1898 call_conv: CallConv,
1899 ) -> Self::Function {
1900 let name = function.get_name().to_string_lossy();
1901 let stub_name = format!("{name}.stub");
1902 if let Some(stub) = self.module().get_function(&stub_name) {
1903 return stub;
1904 }
1905
1906 let func_ty = function.get_type();
1907 let stub = self.module().add_function(
1908 &stub_name,
1909 func_ty,
1910 Some(inkwell::module::Linkage::Internal),
1911 );
1912 set_function_call_conv(stub, call_conv);
1913 cpp::set_dso_local(stub);
1914 self.add_function_attribute(
1915 Some(stub),
1916 revmc_backend::Attribute::NoInline,
1917 revmc_backend::FunctionAttributeLocation::Function,
1918 );
1919
1920 let before = self.bcx.get_insert_block();
1921 let debug_location = self.debug_scope.and_then(|_| self.bcx.get_current_debug_location());
1922 self.bcx.unset_current_debug_location();
1923
1924 let entry = self.cx.append_basic_block(stub, self.name("stub"));
1925 self.bcx.position_at_end(entry);
1926 let args =
1927 (0..stub.count_params()).map(|i| stub.get_nth_param(i).unwrap()).collect::<Vec<_>>();
1928 if let Some(value) = self.call(function, &args) {
1929 self.bcx.build_return(Some(&value)).unwrap();
1930 } else {
1931 self.bcx.build_return(None).unwrap();
1932 }
1933
1934 if let Some(before) = before {
1935 self.bcx.position_at_end(before);
1936 }
1937 if let Some(debug_location) = debug_location {
1938 self.bcx.set_current_debug_location(debug_location);
1939 }
1940
1941 stub
1942 }
1943
1944 fn add_function_attribute(
1945 &mut self,
1946 function: Option<Self::Function>,
1947 attribute: revmc_backend::Attribute,
1948 loc: revmc_backend::FunctionAttributeLocation,
1949 ) {
1950 let func = function.unwrap_or(self.function);
1951 let loc = convert_attribute_loc(loc);
1952 let attr = convert_attribute(self, attribute);
1953 func.add_attribute(loc, attr);
1954 }
1955}
1956
1957fn build_pass_pipeline(with_licm: bool) -> String {
1959 let mut passes = String::from("module(globalopt,cgscc(inline),globalopt,function(");
1960 let function_passes: &[&str] = &[
1961 "simplifycfg",
1962 "sroa",
1963 "early-cse",
1964 "jump-threading",
1965 "correlated-propagation",
1966 "simplifycfg",
1967 "instcombine<no-verify-fixpoint>",
1968 ];
1969 let licm_passes: &[&str] =
1970 &["loop-mssa(licm,loop-rotate,licm)", "simplifycfg", "instcombine<no-verify-fixpoint>"];
1971 let post_passes: &[&str] = &[
1972 "sroa",
1973 "early-cse",
1974 "sccp",
1975 "instcombine<no-verify-fixpoint>",
1976 "adce",
1977 "dse",
1978 "simplifycfg",
1979 ];
1980
1981 let iter =
1982 function_passes.iter().chain(if with_licm { licm_passes } else { &[] }).chain(post_passes);
1983 for (i, pass) in iter.enumerate() {
1984 if i > 0 {
1985 passes.push(',');
1986 }
1987 passes.push_str(pass);
1988 }
1989 passes.push_str("),globaldce)");
1990 passes
1991}
1992
1993fn init() -> Result<()> {
1994 let mut init_result = Ok(());
1995 static INIT: Once = Once::new();
1996 INIT.call_once(|| init_result = init_());
1997 init_result
1998}
1999
2000fn init_() -> Result<()> {
2001 extern "C" fn report_fatal_error(msg: *const std::ffi::c_char) {
2006 let msg_cstr = unsafe { std::ffi::CStr::from_ptr(msg) };
2007 let msg = msg_cstr.to_string_lossy();
2008 error!(target: "llvm", "LLVM fatal error: {msg}");
2009 }
2010
2011 unsafe {
2012 install_fatal_error_handler(report_fatal_error);
2013 }
2014
2015 let extra: Vec<CString> = std::env::var("REVMC_LLVM_ARGS")
2017 .ok()
2018 .iter()
2019 .flat_map(|s| s.split_whitespace().filter_map(|s| CString::new(s).ok()).collect::<Vec<_>>())
2020 .collect();
2021 if !extra.is_empty() {
2022 debug!(extra = ?&extra, "passing extra LLVM args");
2023 }
2024
2025 let mut args = vec![
2027 c"revmc-llvm".as_ptr(),
2028 c"-x86-asm-syntax=intel".as_ptr(),
2029 c"--cgpp-huge-func=1000".as_ptr(),
2036 c"--huge-size-for-split=64".as_ptr(),
2041 ];
2042 args.extend(extra.iter().map(|s| s.as_ptr()));
2043 unsafe {
2044 inkwell::llvm_sys::support::LLVMParseCommandLineOptions(
2045 args.len() as i32,
2046 args.as_ptr(),
2047 std::ptr::null(),
2048 )
2049 }
2050
2051 let config = InitializationConfig {
2052 asm_parser: false,
2053 asm_printer: true,
2054 base: true,
2055 disassembler: true,
2056 info: true,
2057 machine_code: true,
2058 };
2059 Target::initialize_all(&config);
2060
2061 Ok(())
2062}
2063
2064fn create_module<'ctx>(
2065 cx: &'ctx Context,
2066 machine: &TargetMachine,
2067 aot: bool,
2068) -> Result<Module<'ctx>> {
2069 let module_name = "evm";
2070 let module = cx.create_module(module_name);
2071 module.set_source_file_name(module_name);
2072 module.set_data_layout(&machine.get_target_data().get_data_layout());
2073 module.set_triple(&machine.get_triple());
2074 if aot {
2075 module.add_basic_value_flag(
2076 "PIC Level",
2077 FlagBehavior::Error, cx.i32_type().const_int(2, false),
2079 );
2080 module.add_basic_value_flag(
2081 "RtLibUseGOT",
2082 FlagBehavior::Warning,
2083 cx.i32_type().const_int(1, false),
2084 );
2085 }
2086 Ok(module)
2087}
2088
2089fn convert_intcc(cond: IntCC) -> IntPredicate {
2090 match cond {
2091 IntCC::Equal => IntPredicate::EQ,
2092 IntCC::NotEqual => IntPredicate::NE,
2093 IntCC::SignedLessThan => IntPredicate::SLT,
2094 IntCC::SignedGreaterThanOrEqual => IntPredicate::SGE,
2095 IntCC::SignedGreaterThan => IntPredicate::SGT,
2096 IntCC::SignedLessThanOrEqual => IntPredicate::SLE,
2097 IntCC::UnsignedLessThan => IntPredicate::ULT,
2098 IntCC::UnsignedGreaterThanOrEqual => IntPredicate::UGE,
2099 IntCC::UnsignedGreaterThan => IntPredicate::UGT,
2100 IntCC::UnsignedLessThanOrEqual => IntPredicate::ULE,
2101 }
2102}
2103
2104fn convert_opt_level(level: OptimizationLevel) -> inkwell::OptimizationLevel {
2105 match level {
2106 OptimizationLevel::None => inkwell::OptimizationLevel::None,
2107 OptimizationLevel::Less => inkwell::OptimizationLevel::Less,
2108 OptimizationLevel::Default => inkwell::OptimizationLevel::Default,
2109 OptimizationLevel::Aggressive => inkwell::OptimizationLevel::Aggressive,
2110 }
2111}
2112
2113fn convert_attribute(bcx: &EvmLlvmBuilder<'_>, attr: revmc_backend::Attribute) -> Attribute {
2114 use revmc_backend::Attribute as OurAttr;
2115
2116 enum AttrValue<'a> {
2117 String(&'a str),
2118 Enum(u64),
2119 Type(AnyTypeEnum<'a>),
2120 }
2121
2122 let cpu;
2123 let (key, value) = match attr {
2124 OurAttr::WillReturn => ("willreturn", AttrValue::Enum(0)),
2125 OurAttr::NoReturn => ("noreturn", AttrValue::Enum(0)),
2126 OurAttr::NoFree => ("nofree", AttrValue::Enum(0)),
2127 OurAttr::NoRecurse => ("norecurse", AttrValue::Enum(0)),
2128 OurAttr::NoSync => ("nosync", AttrValue::Enum(0)),
2129 OurAttr::NoUnwind => ("nounwind", AttrValue::Enum(0)),
2130 OurAttr::NonLazyBind => ("nonlazybind", AttrValue::Enum(0)),
2131 OurAttr::UWTable => ("uwtable", AttrValue::Enum(2)),
2132 OurAttr::AllFramePointers => ("frame-pointer", AttrValue::String("all")),
2133 OurAttr::NativeTargetCpu => (
2134 "target-cpu",
2135 AttrValue::String({
2136 cpu = bcx.machine.get_cpu();
2137 cpu.to_str().unwrap()
2138 }),
2139 ),
2140 OurAttr::Cold => ("cold", AttrValue::Enum(0)),
2141 OurAttr::Hot => ("hot", AttrValue::Enum(0)),
2142 OurAttr::HintInline => ("inlinehint", AttrValue::Enum(0)),
2143 OurAttr::AlwaysInline => ("alwaysinline", AttrValue::Enum(0)),
2144 OurAttr::NoInline => ("noinline", AttrValue::Enum(0)),
2145 OurAttr::Speculatable => ("speculatable", AttrValue::Enum(0)),
2146
2147 OurAttr::NoAlias => ("noalias", AttrValue::Enum(0)),
2148 OurAttr::NoCapture => ("captures", AttrValue::Enum(0)), OurAttr::NoUndef => ("noundef", AttrValue::Enum(0)),
2150 OurAttr::Align(n) => ("align", AttrValue::Enum(n)),
2151 OurAttr::NonNull => ("nonnull", AttrValue::Enum(0)),
2152 OurAttr::Dereferenceable(n) => ("dereferenceable", AttrValue::Enum(n)),
2153 OurAttr::SRet(n) => {
2154 ("sret", AttrValue::Type(bcx.type_array(bcx.ty_i8.into(), n as _).as_any_type_enum()))
2155 }
2156 OurAttr::ReadNone => ("readnone", AttrValue::Enum(0)),
2157 OurAttr::ReadOnly => ("readonly", AttrValue::Enum(0)),
2158 OurAttr::WriteOnly => ("writeonly", AttrValue::Enum(0)),
2159 OurAttr::Writable => ("writable", AttrValue::Enum(0)),
2160 OurAttr::ArgMemOnly => ("memory", AttrValue::Enum(3)),
2162 OurAttr::DeadOnReturn => ("dead_on_return", AttrValue::Enum(0)),
2163
2164 OurAttr::Initializes(size) => {
2165 return cpp::create_initializes_attr(bcx.cx, 0, size as i64);
2166 }
2167
2168 attr => unimplemented!("llvm attribute conversion: {attr:?}"),
2169 };
2170 match value {
2171 AttrValue::String(value) => bcx.cx.create_string_attribute(key, value),
2172 AttrValue::Enum(value) => {
2173 let id = Attribute::get_named_enum_kind_id(key);
2174 bcx.cx.create_enum_attribute(id, value)
2175 }
2176 AttrValue::Type(ty) => {
2177 let id = Attribute::get_named_enum_kind_id(key);
2178 bcx.cx.create_type_attribute(id, ty)
2179 }
2180 }
2181}
2182
2183fn convert_attribute_loc(loc: revmc_backend::FunctionAttributeLocation) -> AttributeLoc {
2184 match loc {
2185 revmc_backend::FunctionAttributeLocation::Return => AttributeLoc::Return,
2186 revmc_backend::FunctionAttributeLocation::Param(i) => AttributeLoc::Param(i),
2187 revmc_backend::FunctionAttributeLocation::Function => AttributeLoc::Function,
2188 }
2189}
2190
2191fn function_call_conv(function: FunctionValue<'_>) -> Option<u32> {
2192 let call_conv =
2193 unsafe { inkwell::llvm_sys::core::LLVMGetFunctionCallConv(function.as_value_ref()) };
2194 (call_conv != inkwell::llvm_sys::LLVMCallConv::LLVMCCallConv as u32).then_some(call_conv)
2195}
2196
2197fn set_function_call_conv(function: FunctionValue<'_>, call_conv: CallConv) {
2198 let Some(call_conv) = convert_call_conv(call_conv) else { return };
2199 unsafe {
2200 inkwell::llvm_sys::core::LLVMSetFunctionCallConv(function.as_value_ref(), call_conv as _);
2201 }
2202}
2203
2204fn convert_call_conv(call_conv: CallConv) -> Option<inkwell::llvm_sys::LLVMCallConv> {
2205 match call_conv {
2206 CallConv::Default => None,
2207 CallConv::Cold => Some(inkwell::llvm_sys::LLVMCallConv::LLVMPreserveMostCallConv),
2208 }
2209}
2210
2211fn convert_linkage(linkage: revmc_backend::Linkage) -> inkwell::module::Linkage {
2212 match linkage {
2213 revmc_backend::Linkage::Public => inkwell::module::Linkage::External,
2214 revmc_backend::Linkage::Import => inkwell::module::Linkage::External,
2215 revmc_backend::Linkage::Private => inkwell::module::Linkage::Private,
2216 }
2217}
2218
2219fn convert_tail_call_kind(kind: TailCallKind) -> inkwell::llvm_sys::LLVMTailCallKind {
2220 match kind {
2221 TailCallKind::None => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindNone,
2222 TailCallKind::Tail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindTail,
2223 TailCallKind::MustTail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindMustTail,
2224 TailCallKind::NoTail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindNoTail,
2225 }
2226}
2227
2228fn error_msg(msg: inkwell::support::LLVMString) -> revmc_backend::Error {
2230 revmc_backend::Error::msg(msg.to_string_lossy().trim_end().to_string())
2231}
2232
2233fn fmt_ty(ty: BasicTypeEnum<'_>) -> impl std::fmt::Display {
2234 ty.print_to_string().to_str().unwrap().trim_matches('"').to_string()
2235}