Skip to main content

pliron_llvm/llvm_sys/
mod.rs

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