Skip to main content

revmc_backend/
traits.rs

1use crate::{Pointer, Result};
2use ruint::aliases::U256;
3use std::{fmt, path::Path};
4
5/// Target machine.
6#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub enum Target {
8    /// The host machine.
9    #[default]
10    Native,
11    /// LLVM-style target triple.
12    ///
13    /// Ref: <https://llvm.org/docs/LangRef.html#target-triple>
14    Triple {
15        /// The target triple.
16        triple: String,
17        /// The target CPU.
18        cpu: Option<String>,
19        /// The target features string.
20        features: Option<String>,
21    },
22}
23
24impl std::str::FromStr for Target {
25    type Err = std::convert::Infallible;
26
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        Ok(Self::triple(s))
29    }
30}
31
32impl Target {
33    /// Creates a target from a triple string.
34    ///
35    /// `cpu` and `features` are ignored if `triple` is `native`.
36    pub fn new(
37        triple: impl AsRef<str> + Into<String>,
38        cpu: Option<String>,
39        features: Option<String>,
40    ) -> Self {
41        if triple.as_ref() == "native" {
42            return Self::Native;
43        }
44        Self::Triple { triple: triple.into(), cpu, features }
45    }
46
47    /// Creates a target from a triple string.
48    pub fn triple(triple: impl AsRef<str> + Into<String>) -> Self {
49        Self::new(triple, None, None)
50    }
51}
52
53/// Optimization level.
54#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
55pub enum OptimizationLevel {
56    /// No optimizations.
57    None,
58    /// Less optimizations.
59    Less,
60    /// Default optimizations.
61    Default,
62    /// Aggressive optimizations.
63    Aggressive,
64}
65
66impl std::str::FromStr for OptimizationLevel {
67    type Err = String;
68
69    fn from_str(s: &str) -> Result<Self, Self::Err> {
70        Ok(match s {
71            "0" | "none" => Self::None,
72            "1" | "less" => Self::Less,
73            "2" | "default" => Self::Default,
74            "3" | "aggressive" => Self::Aggressive,
75            _ => return Err(format!("unknown optimization level: {s}")),
76        })
77    }
78}
79
80/// Integer comparison condition.
81#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub enum IntCC {
83    /// `==`.
84    Equal,
85    /// `!=`.
86    NotEqual,
87    /// Signed `<`.
88    SignedLessThan,
89    /// Signed `>=`.
90    SignedGreaterThanOrEqual,
91    /// Signed `>`.
92    SignedGreaterThan,
93    /// Signed `<=`.
94    SignedLessThanOrEqual,
95    /// Unsigned `<`.
96    UnsignedLessThan,
97    /// Unsigned `>=`.
98    UnsignedGreaterThanOrEqual,
99    /// Unsigned `>`.
100    UnsignedGreaterThan,
101    /// Unsigned `<=`.
102    UnsignedLessThanOrEqual,
103}
104
105/// Function or parameter attribute.
106///
107/// Mostly copied from [LLVM](https://llvm.org/docs/LangRef.html).
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109#[non_exhaustive]
110pub enum Attribute {
111    // Function attributes.
112    WillReturn,
113    NoReturn,
114    NoFree,
115    NoRecurse,
116    NoSync,
117    NoUnwind,
118    AllFramePointers,
119    NativeTargetCpu,
120    Cold,
121    Hot,
122    HintInline,
123    AlwaysInline,
124    NoInline,
125    Speculatable,
126
127    // Parameter attributes.
128    NoAlias,
129    NoCapture,
130    NoUndef,
131    Align(u64),
132    NonNull,
133    Dereferenceable(u64),
134    /// Size of the return type in bytes.
135    SRet(u64),
136    ReadNone,
137    ReadOnly,
138    WriteOnly,
139    Writable,
140    // TODO: Range?
141}
142
143/// Linkage type.
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub enum Linkage {
146    /// Defined outside of the module.
147    Import,
148    /// Defined in the module and visible outside.
149    Public,
150    /// Defined in the module, but not visible outside.
151    Private,
152}
153
154/// Determines where on a function an attribute is assigned to.
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156pub enum FunctionAttributeLocation {
157    /// Assign to the function's return type.
158    Return,
159    /// Assign to one of the function's params (0-indexed).
160    Param(u32),
161    /// Assign to the function itself.
162    Function,
163}
164
165/// Tail call kind.
166#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
167pub enum TailCallKind {
168    #[default]
169    None,
170    Tail,
171    MustTail,
172    NoTail,
173}
174
175pub trait BackendTypes: Sized {
176    type Type: Copy + Eq + fmt::Debug;
177    type Value: Copy + Eq + fmt::Debug;
178    type StackSlot: Copy + Eq + fmt::Debug;
179    type BasicBlock: Copy + Eq + fmt::Debug;
180    type Function: Copy + Eq + fmt::Debug;
181}
182
183#[allow(clippy::missing_safety_doc)]
184pub trait Backend: BackendTypes + TypeMethods {
185    type Builder<'a>: Builder<
186            Type = Self::Type,
187            Value = Self::Value,
188            StackSlot = Self::StackSlot,
189            BasicBlock = Self::BasicBlock,
190            Function = Self::Function,
191        >
192    where
193        Self: 'a;
194    type FuncId: Copy + Eq + std::hash::Hash + fmt::Debug;
195
196    fn ir_extension(&self) -> &'static str;
197
198    fn set_module_name(&mut self, name: &str);
199
200    fn set_is_dumping(&mut self, yes: bool);
201    fn set_debug_assertions(&mut self, yes: bool);
202    fn opt_level(&self) -> OptimizationLevel;
203    fn set_opt_level(&mut self, level: OptimizationLevel);
204    fn dump_ir(&mut self, path: &Path) -> Result<()>;
205    fn dump_disasm(&mut self, path: &Path) -> Result<()>;
206
207    fn is_aot(&self) -> bool;
208
209    fn function_name_is_unique(&self, name: &str) -> bool;
210
211    fn build_function(
212        &mut self,
213        name: &str,
214        ret: Option<Self::Type>,
215        params: &[Self::Type],
216        param_names: &[&str],
217        linkage: Linkage,
218    ) -> Result<(Self::Builder<'_>, Self::FuncId)>;
219    fn verify_module(&mut self) -> Result<()>;
220    fn optimize_module(&mut self) -> Result<()>;
221    fn write_object<W: std::io::Write>(&mut self, w: W) -> Result<()>;
222    fn jit_function(&mut self, id: Self::FuncId) -> Result<usize>;
223
224    /// Clears the IR module, freeing memory used by IR representations.
225    ///
226    /// This does **not** free JIT-compiled machine code, so previously obtained function pointers
227    /// remain valid. The module is left in a state where new functions can be translated.
228    fn clear_ir(&mut self) -> Result<()>;
229
230    unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()>;
231    unsafe fn free_all_functions(&mut self) -> Result<()>;
232}
233
234pub trait TypeMethods: BackendTypes {
235    fn type_ptr(&self) -> Self::Type;
236    fn type_ptr_sized_int(&self) -> Self::Type;
237    fn type_int(&self, bits: u32) -> Self::Type;
238    fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type;
239    fn type_bit_width(&self, ty: Self::Type) -> u32;
240}
241
242pub trait Builder: BackendTypes + TypeMethods {
243    fn create_block(&mut self, name: &str) -> Self::BasicBlock;
244    fn create_block_after(&mut self, after: Self::BasicBlock, name: &str) -> Self::BasicBlock;
245    fn switch_to_block(&mut self, block: Self::BasicBlock);
246    fn seal_block(&mut self, block: Self::BasicBlock);
247    fn seal_all_blocks(&mut self);
248    fn set_current_block_cold(&mut self);
249    fn current_block(&mut self) -> Option<Self::BasicBlock>;
250    fn block_addr(&mut self, block: Self::BasicBlock) -> Option<Self::Value>;
251
252    fn add_comment_to_current_inst(&mut self, comment: &str);
253
254    fn fn_param(&mut self, index: usize) -> Self::Value;
255    fn num_fn_params(&self) -> usize;
256
257    fn bool_const(&mut self, value: bool) -> Self::Value;
258    /// Sign-extends negative values to `ty`.
259    fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value;
260    fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value;
261    fn iconst_256(&mut self, value: U256) -> Self::Value;
262    fn cstr_const(&mut self, value: &std::ffi::CStr) -> Self::Value {
263        self.str_const(value.to_str().unwrap())
264    }
265    fn str_const(&mut self, value: &str) -> Self::Value;
266    fn nullptr(&mut self) -> Self::Value;
267
268    fn new_stack_slot(&mut self, ty: Self::Type, name: &str) -> Pointer<Self> {
269        Pointer::new_stack_slot(self, ty, name)
270    }
271    fn new_stack_slot_raw(&mut self, ty: Self::Type, name: &str) -> Self::StackSlot;
272    fn stack_load(&mut self, ty: Self::Type, slot: Self::StackSlot, name: &str) -> Self::Value;
273    fn stack_store(&mut self, value: Self::Value, slot: Self::StackSlot);
274    fn stack_addr(&mut self, ty: Self::Type, slot: Self::StackSlot) -> Self::Value;
275
276    fn load(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value {
277        self.load_unaligned(ty, ptr, name)
278    }
279    fn load_unaligned(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value;
280    fn store(&mut self, value: Self::Value, ptr: Self::Value) {
281        self.store_unaligned(value, ptr);
282    }
283    fn store_unaligned(&mut self, value: Self::Value, ptr: Self::Value);
284
285    fn nop(&mut self);
286    fn ret(&mut self, values: &[Self::Value]);
287
288    fn icmp(&mut self, cond: IntCC, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
289    fn icmp_imm(&mut self, cond: IntCC, lhs: Self::Value, rhs: i64) -> Self::Value;
290    fn is_null(&mut self, ptr: Self::Value) -> Self::Value;
291    fn is_not_null(&mut self, ptr: Self::Value) -> Self::Value;
292
293    fn br(&mut self, dest: Self::BasicBlock);
294    fn brif(
295        &mut self,
296        cond: Self::Value,
297        then_block: Self::BasicBlock,
298        else_block: Self::BasicBlock,
299    );
300    fn brif_cold(
301        &mut self,
302        cond: Self::Value,
303        then_block: Self::BasicBlock,
304        else_block: Self::BasicBlock,
305        then_is_cold: bool,
306    ) {
307        let _ = then_is_cold;
308        self.brif(cond, then_block, else_block)
309    }
310    fn switch(
311        &mut self,
312        index: Self::Value,
313        default: Self::BasicBlock,
314        targets: &[(u64, Self::BasicBlock)],
315        default_is_cold: bool,
316    );
317    fn br_indirect(&mut self, address: Self::Value, destinations: &[Self::BasicBlock]);
318    fn phi(&mut self, ty: Self::Type, incoming: &[(Self::Value, Self::BasicBlock)]) -> Self::Value;
319    fn select(
320        &mut self,
321        cond: Self::Value,
322        then_value: Self::Value,
323        else_value: Self::Value,
324    ) -> Self::Value;
325    fn lazy_select(
326        &mut self,
327        cond: Self::Value,
328        ty: Self::Type,
329        then_value: impl FnOnce(&mut Self) -> Self::Value,
330        else_value: impl FnOnce(&mut Self) -> Self::Value,
331    ) -> Self::Value;
332
333    fn iadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
334    fn isub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
335    fn imul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
336    fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
337    fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
338    fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
339    fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
340
341    fn iadd_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
342    fn isub_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
343    fn imul_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
344
345    // `(result, overflow)`
346    fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
347    fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
348
349    fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
350
351    fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
352    fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
353    fn bswap(&mut self, value: Self::Value) -> Self::Value;
354
355    fn bitor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
356    fn bitand(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
357    fn bitxor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
358    fn bitnot(&mut self, value: Self::Value) -> Self::Value;
359    fn clz(&mut self, value: Self::Value) -> Self::Value;
360
361    fn bitor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
362    fn bitand_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
363    fn bitxor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
364
365    fn ishl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
366    fn ushr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
367    fn sshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
368
369    fn zext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
370    fn sext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
371    #[doc(alias = "trunc")]
372    fn ireduce(&mut self, to: Self::Type, value: Self::Value) -> Self::Value;
373
374    /// Converts an integer value to a pointer.
375    fn inttoptr(&mut self, value: Self::Value, ty: Self::Type) -> Self::Value;
376
377    fn gep(
378        &mut self,
379        ty: Self::Type,
380        ptr: Self::Value,
381        indexes: &[Self::Value],
382        name: &str,
383    ) -> Self::Value;
384
385    #[must_use]
386    fn call(&mut self, function: Self::Function, args: &[Self::Value]) -> Option<Self::Value> {
387        self.tail_call(function, args, TailCallKind::None)
388    }
389    #[must_use]
390    fn tail_call(
391        &mut self,
392        function: Self::Function,
393        args: &[Self::Value],
394        tail_call: TailCallKind,
395    ) -> Option<Self::Value>;
396
397    /// Returns `Some(is_value_compile_time)`, or `None` if unsupported.
398    fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value>;
399
400    fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value);
401    fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
402        let len = self.iconst(self.type_int(64), len);
403        self.memcpy(dst, src, len);
404    }
405
406    fn unreachable(&mut self);
407
408    fn get_or_build_function(
409        &mut self,
410        name: &str,
411        params: &[Self::Type],
412        ret: Option<Self::Type>,
413        linkage: Linkage,
414        build: impl FnOnce(&mut Self),
415    ) -> Self::Function;
416
417    fn get_function(&mut self, name: &str) -> Option<Self::Function>;
418
419    fn get_printf_function(&mut self) -> Self::Function;
420
421    /// Adds a function to the module that's located at `address`.
422    ///
423    /// If `address` is `None`, the function must be built.
424    fn add_function(
425        &mut self,
426        name: &str,
427        params: &[Self::Type],
428        ret: Option<Self::Type>,
429        address: Option<usize>,
430        linkage: Linkage,
431    ) -> Self::Function;
432
433    /// Adds an attribute to a function, one of its parameters, or its return value.
434    ///
435    /// If `function` is `None`, the attribute is added to the current function.
436    fn add_function_attribute(
437        &mut self,
438        function: Option<Self::Function>,
439        attribute: Attribute,
440        loc: FunctionAttributeLocation,
441    );
442}