Skip to main content

inkwell/
intrinsics.rs

1use llvm_sys::core::{LLVMGetIntrinsicDeclaration, LLVMIntrinsicIsOverloaded, LLVMLookupIntrinsicID};
2use llvm_sys::prelude::LLVMTypeRef;
3
4use crate::module::Module;
5use crate::types::{AsTypeRef, BasicTypeEnum};
6use crate::values::FunctionValue;
7
8#[derive(Debug, Clone, Copy, Eq, PartialEq)]
9pub struct Intrinsic {
10    id: u32,
11}
12
13/// A wrapper around LLVM intrinsic id
14///
15/// To call it you would need to create a declaration inside a module using [`Self::get_declaration()`].
16impl Intrinsic {
17    /// Create an Intrinsic object from raw LLVM intrinsic id
18    ///
19    /// SAFETY: the id is a valid LLVM intrinsic ID
20    pub(crate) unsafe fn new(id: u32) -> Self {
21        Self { id }
22    }
23
24    /// Find llvm intrinsic id from name
25    ///
26    /// # Example
27    /// ```no_run
28    /// use inkwell::{intrinsics::Intrinsic, context::Context};
29    ///
30    /// let trap_intrinsic = Intrinsic::find("llvm.trap").unwrap();
31    ///
32    /// let context = Context::create();
33    /// let module = context.create_module("trap");
34    /// let builder = context.create_builder();
35    /// let void_type = context.void_type();
36    /// let fn_type = void_type.fn_type(&[], false);
37    /// let fn_value = module.add_function("trap", fn_type, None);
38    /// let entry = context.append_basic_block(fn_value, "entry");
39    ///
40    /// let trap_function = trap_intrinsic.get_declaration(&module, &[]).unwrap();
41    ///
42    /// builder.position_at_end(entry);
43    /// builder.build_call(trap_function, &[], "trap_call");
44    /// ```
45    pub fn find(name: &str) -> Option<Self> {
46        let id = unsafe { LLVMLookupIntrinsicID(name.as_ptr() as *const ::libc::c_char, name.len()) };
47
48        if id == 0 {
49            return None;
50        }
51
52        Some(unsafe { Intrinsic::new(id) })
53    }
54
55    /// Check if specified intrinsic is overloaded
56    ///
57    /// Overloaded intrinsics need some argument types to be specified to declare them
58    pub fn is_overloaded(&self) -> bool {
59        unsafe { LLVMIntrinsicIsOverloaded(self.id) != 0 }
60    }
61
62    /// Create or insert the declaration of an intrinsic.
63    ///
64    /// For overloaded intrinsics, parameter types must be provided to uniquely identify an overload.
65    ///
66    /// # Example
67    /// ```no_run
68    /// use inkwell::{intrinsics::Intrinsic, context::Context};
69    ///
70    /// let trap_intrinsic = Intrinsic::find("llvm.trap").unwrap();
71    ///
72    /// let context = Context::create();
73    /// let module = context.create_module("trap");
74    /// let builder = context.create_builder();
75    /// let void_type = context.void_type();
76    /// let fn_type = void_type.fn_type(&[], false);
77    /// let fn_value = module.add_function("trap", fn_type, None);
78    /// let entry = context.append_basic_block(fn_value, "entry");
79    ///
80    /// let trap_function = trap_intrinsic.get_declaration(&module, &[]).unwrap();
81    ///
82    /// builder.position_at_end(entry);
83    /// builder.build_call(trap_function, &[], "trap_call");
84    /// ```
85    pub fn get_declaration<'ctx>(
86        &self,
87        module: &Module<'ctx>,
88        param_types: &[BasicTypeEnum],
89    ) -> Option<FunctionValue<'ctx>> {
90        let mut param_types: Vec<LLVMTypeRef> = param_types.iter().map(|val| val.as_type_ref()).collect();
91
92        // param_types should be empty for non-overloaded intrinsics (I think?)
93        // for overloaded intrinsics they determine the overload used
94
95        if self.is_overloaded() && param_types.is_empty() {
96            // LLVM crashes otherwise
97            return None;
98        }
99
100        unsafe {
101            FunctionValue::new(LLVMGetIntrinsicDeclaration(
102                module.as_mut_ptr(),
103                self.id,
104                param_types.as_mut_ptr(),
105                param_types.len(),
106            ))
107        }
108    }
109}