llvm_plugin_inkwell/support/
mod.rs

1#[deny(missing_docs)]
2pub mod error_handling;
3
4use libc::c_char;
5#[llvm_versions(16.0)]
6use llvm_sys::core::LLVMGetVersion;
7use llvm_sys::core::{LLVMCreateMessage, LLVMDisposeMessage};
8use llvm_sys::error_handling::LLVMEnablePrettyStackTrace;
9use llvm_sys::support::LLVMLoadLibraryPermanently;
10
11use std::borrow::Cow;
12use std::error::Error;
13use std::ffi::{CStr, CString};
14use std::fmt::{self, Debug, Display, Formatter};
15use std::ops::Deref;
16
17/// An owned LLVM String. Also known as a LLVM Message
18#[derive(Eq)]
19pub struct LLVMString {
20    pub(crate) ptr: *const c_char,
21}
22
23impl LLVMString {
24    pub(crate) unsafe fn new(ptr: *const c_char) -> Self {
25        LLVMString { ptr }
26    }
27
28    /// This is a convenience method for creating a Rust `String`,
29    /// however; it *will* reallocate. `LLVMString` should be used
30    /// as much as possible to save memory since it is allocated by
31    /// LLVM. It's essentially a `CString` with a custom LLVM
32    /// deallocator
33    pub fn to_string(&self) -> String {
34        (*self).to_string_lossy().into_owned()
35    }
36
37    /// This method will allocate a c string through LLVM
38    pub(crate) fn create_from_c_str(string: &CStr) -> LLVMString {
39        unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
40    }
41
42    /// This method will allocate a c string through LLVM
43    pub(crate) fn create_from_str(string: &str) -> LLVMString {
44        debug_assert_eq!(string.as_bytes()[string.as_bytes().len() - 1], 0);
45
46        unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
47    }
48}
49
50impl Deref for LLVMString {
51    type Target = CStr;
52
53    fn deref(&self) -> &Self::Target {
54        unsafe { CStr::from_ptr(self.ptr) }
55    }
56}
57
58impl Debug for LLVMString {
59    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
60        write!(f, "{:?}", self.deref())
61    }
62}
63
64impl Display for LLVMString {
65    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
66        write!(f, "{:?}", self.deref())
67    }
68}
69
70impl PartialEq for LLVMString {
71    fn eq(&self, other: &LLVMString) -> bool {
72        **self == **other
73    }
74}
75
76impl Error for LLVMString {
77    fn description(&self) -> &str {
78        self.to_str()
79            .expect("Could not convert LLVMString to str (likely invalid unicode)")
80    }
81
82    fn cause(&self) -> Option<&dyn Error> {
83        None
84    }
85}
86
87impl Drop for LLVMString {
88    fn drop(&mut self) {
89        unsafe {
90            LLVMDisposeMessage(self.ptr as *mut _);
91        }
92    }
93}
94
95// Similar to Cow; however does not provide ability to clone
96// since memory is allocated by LLVM. Could use a better name
97// too. This is meant to be an internal wrapper only. Maybe
98// belongs in a private utils module.
99#[derive(Eq)]
100pub(crate) enum LLVMStringOrRaw {
101    Owned(LLVMString),
102    Borrowed(*const c_char),
103}
104
105impl LLVMStringOrRaw {
106    pub fn as_str(&self) -> &CStr {
107        match self {
108            LLVMStringOrRaw::Owned(llvm_string) => llvm_string.deref(),
109            LLVMStringOrRaw::Borrowed(ptr) => unsafe { CStr::from_ptr(*ptr) },
110        }
111    }
112}
113
114impl PartialEq for LLVMStringOrRaw {
115    fn eq(&self, other: &LLVMStringOrRaw) -> bool {
116        self.as_str() == other.as_str()
117    }
118}
119
120/// This function is very unsafe. Any reference to LLVM data after this function is called will likey segfault.
121/// Probably only ever useful to call before your program ends. Might not even be absolutely necessary.
122pub unsafe fn shutdown_llvm() {
123    use llvm_sys::core::LLVMShutdown;
124
125    LLVMShutdown()
126}
127
128/// Returns the major, minor, and patch version of the LLVM in use
129#[llvm_versions(16.0..=latest)]
130pub fn get_llvm_version() -> (u32, u32, u32) {
131    let mut major: u32 = 0;
132    let mut minor: u32 = 0;
133    let mut patch: u32 = 0;
134
135    unsafe { LLVMGetVersion(&mut major, &mut minor, &mut patch) };
136
137    return (major, minor, patch);
138}
139
140pub fn load_library_permanently(filename: &str) -> bool {
141    let filename = to_c_str(filename);
142
143    unsafe { LLVMLoadLibraryPermanently(filename.as_ptr()) == 1 }
144}
145
146/// Determines whether or not LLVM has been configured to run in multithreaded mode. (Inkwell currently does
147/// not officially support multithreaded mode)
148pub fn is_multithreaded() -> bool {
149    use llvm_sys::core::LLVMIsMultithreaded;
150
151    unsafe { LLVMIsMultithreaded() == 1 }
152}
153
154pub fn enable_llvm_pretty_stack_trace() {
155    unsafe { LLVMEnablePrettyStackTrace() }
156}
157
158/// This function takes in a Rust string and either:
159///
160/// A) Finds a terminating null byte in the Rust string and can reference it directly like a C string.
161///
162/// B) Finds no null byte and allocates a new C string based on the input Rust string.
163pub(crate) fn to_c_str<'s>(mut s: &'s str) -> Cow<'s, CStr> {
164    if s.is_empty() {
165        s = "\0";
166    }
167
168    // Start from the end of the string as it's the most likely place to find a null byte
169    if !s.chars().rev().any(|ch| ch == '\0') {
170        return Cow::from(CString::new(s).expect("unreachable since null bytes are checked"));
171    }
172
173    unsafe { Cow::from(CStr::from_ptr(s.as_ptr() as *const _)) }
174}
175
176#[test]
177fn test_to_c_str() {
178    assert!(matches!(to_c_str("my string"), Cow::Owned(_)));
179    assert!(matches!(to_c_str("my string\0"), Cow::Borrowed(_)));
180}