Skip to main content

revmc_backend/
traits.rs

1use crate::{Pointer, Result};
2use ruint::aliases::U256;
3use std::{
4    fmt,
5    path::{Path, PathBuf},
6};
7
8/// Backend configuration.
9///
10/// Collects all tuneable settings that [`EvmCompiler`](crate::Backend) forwards to the backend.
11/// Backends receive a full snapshot via [`Backend::apply_config`] whenever the compiler
12/// changes a setting, so they can apply side-effects (e.g. toggling ASM verbosity) in one place.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct BackendConfig {
15    /// Optimization level.
16    pub opt_level: OptimizationLevel,
17    /// Whether IR output is being dumped (enables verbose names, asm comments, etc.).
18    pub is_dumping: bool,
19    /// Whether to enable debug assertions in generated code.
20    pub debug_assertions: bool,
21    /// Whether to enable JIT debug support (GDB/LLDB registration).
22    ///
23    /// Applied once per process on first JIT compilation.
24    pub debug_support: bool,
25    /// Whether to enable JIT profiling support (perf jitdump).
26    ///
27    /// Applied once per process on first JIT compilation.
28    pub profiling_support: bool,
29    /// Whether to enable the simple perf map plugin.
30    ///
31    /// Writes `/tmp/perf-<pid>.map` in the perf map format so that profilers
32    /// can resolve JIT-compiled symbols without the jitdump machinery.
33    ///
34    /// Not suitable for long-running programs. The map file is append-only
35    /// and never cleaned up, so entries for freed JIT code accumulate
36    /// indefinitely. Prefer [`profiling_support`](Self::profiling_support)
37    /// (jitdump) for long-lived processes.
38    ///
39    /// Applied once per process on first JIT compilation.
40    pub simple_perf: bool,
41    /// Debug info source file path. `Some` enables debug info emission.
42    pub debug_file: Option<PathBuf>,
43}
44
45impl Default for BackendConfig {
46    fn default() -> Self {
47        Self {
48            opt_level: OptimizationLevel::Default,
49            is_dumping: false,
50            debug_assertions: cfg!(debug_assertions),
51            debug_support: true,
52            profiling_support: false,
53            simple_perf: true,
54            debug_file: None,
55        }
56    }
57}
58
59/// Optimization level.
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub enum OptimizationLevel {
62    /// No optimizations.
63    None,
64    /// Less optimizations.
65    Less,
66    /// Default optimizations. Highly recommended.
67    #[default]
68    Default,
69    /// Aggressive optimizations.
70    ///
71    /// Not recommended, since it's generally a lot slower and doesn't really produce better code
72    /// than the default level.
73    Aggressive,
74}
75
76impl std::str::FromStr for OptimizationLevel {
77    type Err = String;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        Ok(match s {
81            "0" | "none" => Self::None,
82            "1" | "less" => Self::Less,
83            "2" | "default" => Self::Default,
84            "3" | "aggressive" => Self::Aggressive,
85            _ => return Err(format!("unknown optimization level: {s}")),
86        })
87    }
88}
89
90/// Integer comparison condition.
91#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
92pub enum IntCC {
93    /// `==`.
94    Equal,
95    /// `!=`.
96    NotEqual,
97    /// Signed `<`.
98    SignedLessThan,
99    /// Signed `>=`.
100    SignedGreaterThanOrEqual,
101    /// Signed `>`.
102    SignedGreaterThan,
103    /// Signed `<=`.
104    SignedLessThanOrEqual,
105    /// Unsigned `<`.
106    UnsignedLessThan,
107    /// Unsigned `>=`.
108    UnsignedGreaterThanOrEqual,
109    /// Unsigned `>`.
110    UnsignedGreaterThan,
111    /// Unsigned `<=`.
112    UnsignedLessThanOrEqual,
113}
114
115/// Function or parameter attribute.
116///
117/// Mostly copied from [LLVM](https://llvm.org/docs/LangRef.html).
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119#[non_exhaustive]
120pub enum Attribute {
121    // Function attributes.
122    WillReturn,
123    NoReturn,
124    NoFree,
125    NoRecurse,
126    NoSync,
127    NoUnwind,
128    NonLazyBind,
129    UWTable,
130    AllFramePointers,
131    NativeTargetCpu,
132    Cold,
133    Hot,
134    HintInline,
135    AlwaysInline,
136    NoInline,
137    Speculatable,
138
139    // Parameter attributes.
140    NoAlias,
141    NoCapture,
142    NoUndef,
143    Align(u64),
144    NonNull,
145    Dereferenceable(u64),
146    /// Size of the return type in bytes.
147    SRet(u64),
148    ReadNone,
149    ReadOnly,
150    WriteOnly,
151    Writable,
152    /// `memory(argmem: readwrite)` — function only accesses memory through pointer arguments.
153    ArgMemOnly,
154    /// `initializes((0, N))` — function initializes bytes `[0, N)` through this pointer.
155    Initializes(u64),
156    /// `dead_on_return` — the contents of the pointed-to memory are dead after the function
157    /// returns, allowing the caller to elide stores to the memory.
158    DeadOnReturn,
159    // TODO: Range?
160}
161
162/// Linkage type.
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
164pub enum Linkage {
165    /// Defined outside of the module.
166    Import,
167    /// Defined in the module and visible outside.
168    Public,
169    /// Defined in the module, but not visible outside.
170    Private,
171}
172
173/// Determines where on a function an attribute is assigned to.
174#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175pub enum FunctionAttributeLocation {
176    /// Assign to the function's return type.
177    Return,
178    /// Assign to one of the function's params (0-indexed).
179    Param(u32),
180    /// Assign to the function itself.
181    Function,
182}
183
184/// Calling convention.
185#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub enum CallConv {
187    #[default]
188    Default,
189    /// Preserve most caller registers across the call to reduce callsite register pressure.
190    Cold,
191}
192
193/// Tail call kind.
194#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
195pub enum TailCallKind {
196    #[default]
197    None,
198    Tail,
199    MustTail,
200    NoTail,
201}
202
203pub trait BackendTypes: Sized {
204    type Type: Copy + Eq + fmt::Debug;
205    type Value: Copy + Eq + fmt::Debug;
206    type StackSlot: Copy + Eq + fmt::Debug;
207    type BasicBlock: Copy + Eq + fmt::Debug;
208    type Function: Copy + Eq + fmt::Debug;
209}
210
211#[allow(clippy::missing_safety_doc)]
212pub trait Backend: BackendTypes + TypeMethods {
213    type Builder<'a>: Builder<
214            Type = Self::Type,
215            Value = Self::Value,
216            StackSlot = Self::StackSlot,
217            BasicBlock = Self::BasicBlock,
218            Function = Self::Function,
219        >
220    where
221        Self: 'a;
222    type FuncId: Copy + Eq + std::hash::Hash + fmt::Debug;
223
224    fn ir_extension(&self) -> &'static str;
225
226    fn set_module_name(&mut self, name: &str);
227
228    /// Returns the current backend configuration.
229    fn config(&self) -> &BackendConfig;
230
231    /// Applies the given configuration snapshot.
232    ///
233    /// Backends should apply any side-effects (e.g. toggling ASM verbosity) here.
234    fn apply_config(&mut self, config: BackendConfig);
235
236    /// Finalizes any pending debug info metadata.
237    ///
238    /// Must be called before verification, optimization, or code emission.
239    fn finalize_debug_info(&mut self) -> Result<()> {
240        Ok(())
241    }
242    fn dump_ir(&mut self, path: &Path) -> Result<()>;
243    fn dump_disasm(&mut self, path: &Path) -> Result<()>;
244
245    fn is_aot(&self) -> bool;
246
247    fn function_name_is_unique(&self, name: &str) -> bool;
248
249    fn build_function(
250        &mut self,
251        name: &str,
252        ret: Option<Self::Type>,
253        params: &[Self::Type],
254        param_names: &[&str],
255        linkage: Linkage,
256    ) -> Result<(Self::Builder<'_>, Self::FuncId)>;
257    fn verify_module(&mut self) -> Result<()>;
258    fn optimize_module(&mut self) -> Result<()>;
259    fn write_object<W: std::io::Write>(&mut self, w: W) -> Result<()>;
260    fn jit_function(&mut self, id: Self::FuncId) -> Result<usize>;
261
262    /// Returns the name of a compiled function by its ID.
263    fn function_name(&self, id: Self::FuncId) -> Option<&str>;
264
265    /// Returns the estimated sizes of compiled functions as `(name, size)` pairs.
266    fn function_sizes(&self) -> Vec<(String, usize)> {
267        Vec::new()
268    }
269
270    /// Clears the IR module, freeing memory used by IR representations.
271    ///
272    /// This does **not** free JIT-compiled machine code, so previously obtained function pointers
273    /// remain valid. The module is left in a state where new functions can be translated.
274    fn clear_ir(&mut self) -> Result<()>;
275
276    unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()>;
277    unsafe fn free_all_functions(&mut self) -> Result<()>;
278}
279
280pub trait TypeMethods: BackendTypes {
281    fn type_ptr(&self) -> Self::Type;
282    fn type_ptr_sized_int(&self) -> Self::Type;
283    fn type_int(&self, bits: u32) -> Self::Type;
284    fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type;
285    fn type_bit_width(&self, ty: Self::Type) -> u32;
286}
287
288pub trait Builder: BackendTypes + TypeMethods {
289    fn create_block(&mut self, name: &str) -> Self::BasicBlock;
290    fn create_block_after(&mut self, after: Self::BasicBlock, name: &str) -> Self::BasicBlock;
291    fn switch_to_block(&mut self, block: Self::BasicBlock);
292    fn seal_block(&mut self, block: Self::BasicBlock);
293    fn seal_all_blocks(&mut self);
294    fn set_current_block_cold(&mut self);
295    fn current_block(&mut self) -> Option<Self::BasicBlock>;
296    fn block_addr(&mut self, block: Self::BasicBlock) -> Option<Self::Value>;
297
298    fn add_comment_to_current_inst(&mut self, comment: &str);
299
300    /// Sets the current debug source location for subsequently emitted instructions.
301    fn set_debug_location(&mut self, _line: u32, _col: u32) {}
302
303    /// Clears the current debug source location.
304    fn clear_debug_location(&mut self) {}
305
306    fn fn_param(&mut self, index: usize) -> Self::Value;
307    fn num_fn_params(&self) -> usize;
308
309    fn bool_const(&mut self, value: bool) -> Self::Value;
310    /// Sign-extends negative values to `ty`.
311    fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value;
312    fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value;
313    fn iconst_256(&mut self, value: impl TryInto<U256>) -> Self::Value;
314    fn cstr_const(&mut self, value: &std::ffi::CStr) -> Self::Value {
315        self.str_const(value.to_str().unwrap())
316    }
317    fn str_const(&mut self, value: &str) -> Self::Value;
318    fn nullptr(&mut self) -> Self::Value;
319
320    fn new_stack_slot(&mut self, ty: Self::Type, name: &str) -> Pointer<Self> {
321        Pointer::new_stack_slot(self, ty, name)
322    }
323    fn new_stack_slot_raw(&mut self, ty: Self::Type, name: &str) -> Self::StackSlot;
324    fn stack_load(&mut self, ty: Self::Type, slot: Self::StackSlot, name: &str) -> Self::Value;
325    fn stack_store(&mut self, value: Self::Value, slot: Self::StackSlot);
326    fn stack_addr(&mut self, ty: Self::Type, slot: Self::StackSlot) -> Self::Value;
327
328    /// Loads a value from a pointer, assuming natural alignment.
329    fn load(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value;
330    /// Loads a value from a pointer with an explicit alignment override.
331    fn load_aligned(
332        &mut self,
333        ty: Self::Type,
334        ptr: Self::Value,
335        align: usize,
336        name: &str,
337    ) -> Self::Value;
338    /// Stores a value to a pointer, assuming natural alignment.
339    fn store(&mut self, value: Self::Value, ptr: Self::Value);
340    /// Stores a value to a pointer with an explicit alignment override.
341    fn store_aligned(&mut self, value: Self::Value, ptr: Self::Value, align: usize);
342
343    fn nop(&mut self);
344    fn ret(&mut self, values: &[Self::Value]);
345    fn assume(&mut self, cond: Self::Value) {
346        let _ = cond;
347    }
348
349    fn icmp(&mut self, cond: IntCC, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
350    fn icmp_imm(&mut self, cond: IntCC, lhs: Self::Value, rhs: i64) -> Self::Value;
351    fn is_null(&mut self, ptr: Self::Value) -> Self::Value;
352    fn is_not_null(&mut self, ptr: Self::Value) -> Self::Value;
353
354    fn br(&mut self, dest: Self::BasicBlock);
355    fn brif(
356        &mut self,
357        cond: Self::Value,
358        then_block: Self::BasicBlock,
359        else_block: Self::BasicBlock,
360    );
361    fn brif_cold(
362        &mut self,
363        cond: Self::Value,
364        then_block: Self::BasicBlock,
365        else_block: Self::BasicBlock,
366        then_is_cold: bool,
367    ) {
368        let _ = then_is_cold;
369        self.brif(cond, then_block, else_block)
370    }
371    fn switch(
372        &mut self,
373        index: Self::Value,
374        default: Self::BasicBlock,
375        targets: &[(u64, Self::BasicBlock)],
376        default_is_cold: bool,
377    );
378    fn br_indirect(&mut self, address: Self::Value, destinations: &[Self::BasicBlock]);
379    fn phi(&mut self, ty: Self::Type, incoming: &[(Self::Value, Self::BasicBlock)]) -> Self::Value;
380    fn select(
381        &mut self,
382        cond: Self::Value,
383        then_value: Self::Value,
384        else_value: Self::Value,
385    ) -> Self::Value;
386    fn lazy_select(
387        &mut self,
388        cond: Self::Value,
389        ty: Self::Type,
390        then_value: impl FnOnce(&mut Self) -> Self::Value,
391        else_value: impl FnOnce(&mut Self) -> Self::Value,
392    ) -> Self::Value;
393
394    fn iadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
395    fn isub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
396    fn imul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
397    fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
398    fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
399    fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
400    fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
401
402    fn iadd_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
403    fn isub_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
404    fn imul_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
405
406    // `(result, overflow)`
407    fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
408    fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
409
410    fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
411
412    fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
413    fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
414    fn bswap(&mut self, value: Self::Value) -> Self::Value;
415
416    fn bitor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
417    fn bitand(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
418    fn bitxor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
419    fn bitnot(&mut self, value: Self::Value) -> Self::Value;
420    fn clz(&mut self, value: Self::Value) -> Self::Value;
421
422    fn bitor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
423    fn bitand_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
424    fn bitxor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
425
426    fn ishl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
427    fn ushr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
428    fn sshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
429
430    fn zext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
431    fn sext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
432    #[doc(alias = "trunc")]
433    fn ireduce(&mut self, to: Self::Type, value: Self::Value) -> Self::Value;
434
435    /// Converts an integer value to a pointer.
436    fn inttoptr(&mut self, value: Self::Value, ty: Self::Type) -> Self::Value;
437
438    fn gep(
439        &mut self,
440        ty: Self::Type,
441        ptr: Self::Value,
442        indexes: &[Self::Value],
443        name: &str,
444    ) -> Self::Value;
445
446    #[must_use]
447    fn call(&mut self, function: Self::Function, args: &[Self::Value]) -> Option<Self::Value> {
448        self.tail_call(function, args, TailCallKind::None)
449    }
450    #[must_use]
451    fn tail_call(
452        &mut self,
453        function: Self::Function,
454        args: &[Self::Value],
455        tail_call: TailCallKind,
456    ) -> Option<Self::Value>;
457
458    /// Returns `Some(is_value_compile_time)`, or `None` if unsupported.
459    fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value>;
460
461    fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value);
462    fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
463        let len = self.iconst(self.type_int(64), len);
464        self.memcpy(dst, src, len);
465    }
466
467    fn unreachable(&mut self);
468
469    fn get_or_build_function(
470        &mut self,
471        name: &str,
472        params: &[Self::Type],
473        ret: Option<Self::Type>,
474        linkage: Linkage,
475        build: impl FnOnce(&mut Self),
476    ) -> Self::Function;
477
478    fn get_function(&mut self, name: &str) -> Option<Self::Function>;
479
480    fn get_printf_function(&mut self) -> Self::Function;
481
482    /// Adds a function to the module that's located at `address`.
483    ///
484    /// If `address` is `None`, the function must be built.
485    fn add_function(
486        &mut self,
487        name: &str,
488        params: &[Self::Type],
489        ret: Option<Self::Type>,
490        address: Option<usize>,
491        linkage: Linkage,
492        call_conv: CallConv,
493    ) -> Self::Function;
494
495    /// Adds a local stub with the given calling convention for an existing function.
496    fn add_function_stub(
497        &mut self,
498        function: Self::Function,
499        call_conv: CallConv,
500    ) -> Self::Function {
501        let _ = call_conv;
502        function
503    }
504
505    /// Adds an attribute to a function, one of its parameters, or its return value.
506    ///
507    /// If `function` is `None`, the attribute is added to the current function.
508    fn add_function_attribute(
509        &mut self,
510        function: Option<Self::Function>,
511        attribute: Attribute,
512        loc: FunctionAttributeLocation,
513    );
514}