pliron_llvm/llvm_sys/mod.rs
1//! Safe(r) wrappers around [llvm_sys].
2//!
3//! The wrappers provide (some) safety by asserting that the concrete C++
4//! types of arguments match the expected C++ type. This minimizes
5//! undefined behavior / invalid memory accesses. For example:
6//! 1. `core::llvm_add_incoming(phi_node: LLVMValueRef, ...)`
7//! checks that `phi_node` is indeed a C++ `PHINode`.
8//! 2. `core::llvm_count_param_types(ty: LLVMTypeRef)`
9//! checks that `ty` is a function type.
10//!
11//! We do not check for invalid IR that is caught by the verifier.
12//! As a general guideline, ensure that the C-Types (which are C++ base classes)
13//! match the C++ derived class expected by the callee, via assertions;
14//! such as the PHINode: LLVMValueRef example above.
15//! Type-checking the LLVM-IR itself is left to the verifier, with exceptions.
16//! Exceptions include constraints on `LLVMTypeRef`, for example `ArrayType`'s
17//! element must satisfy `llvm_is_valid_array_element_type`. This isn't verified
18//! by the verifier.
19//!
20//! Note that these wrappers do not provide full memory safety.
21//! Values returned by LLVM are not lifetime-managed / bound.
22//! So you can easily create use-after-free scenarios by deleting / erasing
23//! a value / basic-block etc and then using it later.
24//!
25//! This inherent "unsafety" exists in `inkwell` too, even though Inkwell binds
26//! values returned by LLVM to a context, which isn't sufficient. For example,
27//! use-after-free / undefined behavior:
28//! ```unknown
29//! let instruction = builder.build_int_add(...);
30//! instruction.erase_from_basic_block();
31//! instruction.get_opcode(); // use-after-free / undefined behavior!
32//! ```
33//! As another example, `BasicBlock::delete` is marked `unsafe`, but it isn't
34//! the `delete` that is unsafe, but a subsequent use of the deleted block,
35//! (which need not be marked `unsafe`) that is unsafe: an illusion of safety.
36
37pub mod core;
38pub mod execution_engine;
39pub mod lljit;
40pub mod target;
41
42use llvm_sys::prelude::LLVMBool;
43use std::{
44 borrow::Cow,
45 ffi::{CStr, CString},
46 mem::MaybeUninit,
47};
48
49/// Create an uninitialized vector with given length.
50unsafe fn uninitialized_vec<T>(len: usize) -> MaybeUninit<Vec<T>> {
51 let mut v = MaybeUninit::new(Vec::with_capacity(len));
52 unsafe {
53 v.assume_init_mut().set_len(len);
54 }
55 v
56}
57
58/// Convert a null-terminated, possibly null, C string to [String].
59fn cstr_to_string(ptr: *const ::core::ffi::c_char) -> Option<String> {
60 if ptr.is_null() {
61 return None;
62 }
63 Some(
64 unsafe { CStr::from_ptr(ptr) }
65 .to_str()
66 .expect("CStr not UTF-8")
67 .to_owned(),
68 )
69}
70
71/// Convert a non-null-terminated, possibly null C string to [String]
72fn sized_cstr_to_string(ptr: *const ::core::ffi::c_char, len: usize) -> Option<String> {
73 if ptr.is_null() {
74 return None;
75 }
76
77 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
78 Some(
79 std::str::from_utf8(slice)
80 .expect("CStr not UTF-8")
81 .to_owned(),
82 )
83}
84
85/// Convert a C array to a Rust vec.
86fn c_array_to_vec<T: Clone>(ptr: *const T, len: usize) -> Vec<T> {
87 unsafe { std::slice::from_raw_parts(ptr, len).to_vec() }
88}
89
90/// Convert a value to `bool`
91trait ToBool {
92 fn to_bool(&self) -> bool;
93}
94
95impl ToBool for LLVMBool {
96 fn to_bool(&self) -> bool {
97 *self != 0
98 }
99}
100
101/// This function takes in a Rust string and either:
102///
103/// A) Finds a terminating null byte in the Rust string and can reference it directly like a C string.
104///
105/// B) Finds no null byte and allocates a new C string based on the input Rust string.
106///
107/// This function and its test are taken from the [inkwell](https://github.com/thedan64/inkwell/) project
108fn to_c_str(mut s: &str) -> Cow<'_, CStr> {
109 if s.is_empty() {
110 s = "\0";
111 }
112
113 // Start from the end of the string as it's the most likely place to find a null byte
114 if !s.chars().rev().any(|ch| ch == '\0') {
115 return Cow::from(CString::new(s).expect("unreachable since null bytes are checked"));
116 }
117
118 unsafe { Cow::from(CStr::from_ptr(s.as_ptr() as *const _)) }
119}
120
121#[cfg(test)]
122pub(crate) mod tests {
123 use std::borrow::Cow;
124
125 use crate::llvm_sys::to_c_str;
126
127 #[test]
128 fn test_to_c_str() {
129 assert!(matches!(to_c_str("my string"), Cow::Owned(_)));
130 assert!(matches!(to_c_str("my string\0"), Cow::Borrowed(_)));
131 }
132}