inkwell/
intrinsics.rs

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