Skip to main content

pliron_llvm/llvm_sys/
target.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Safe(r) wrappers around llvm_sys::target
5
6use crate::llvm_sys::{
7    ToBool,
8    core::{LLVMType, llvm_count_struct_element_types, llvm_get_type_kind, llvm_type_is_sized},
9    cstr_to_string, to_c_str,
10};
11use llvm_sys::{
12    LLVMTypeKind,
13    core::LLVMDisposeMessage,
14    target::{
15        LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters,
16        LLVM_InitializeAllDisassemblers, LLVM_InitializeAllTargetInfos,
17        LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, LLVM_InitializeNativeAsmParser,
18        LLVM_InitializeNativeAsmPrinter, LLVM_InitializeNativeDisassembler,
19        LLVM_InitializeNativeTarget, LLVMABIAlignmentOfType, LLVMABISizeOfType,
20        LLVMCopyStringRepOfTargetData, LLVMCreateTargetData, LLVMDisposeTargetData,
21        LLVMOffsetOfElement, LLVMPointerSizeForAS, LLVMPreferredAlignmentOfType,
22        LLVMSizeOfTypeInBits, LLVMStoreSizeOfType, LLVMTargetDataRef,
23    },
24    target_machine::{
25        LLVMCodeGenOptLevel, LLVMCodeModel, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine,
26        LLVMDisposeTargetMachine, LLVMGetDefaultTargetTriple, LLVMGetTargetFromTriple,
27        LLVMRelocMode,
28    },
29};
30use std::{
31    mem::MaybeUninit,
32    sync::{Mutex, PoisonError},
33};
34
35/// Exclusive access to LLVM's target registry.
36static REGISTRY_LOCK: Mutex<()> = Mutex::new(());
37
38/// Do `f` with exclusive access to LLVM's target registry.
39fn with_registry_lock<R>(f: impl FnOnce() -> R) -> R {
40    // The lock protects LLVM's registry, not Rust data. A panic in another
41    // thread thus leaves no invalid state here. Ignore the poison flag.
42    let _guard = REGISTRY_LOCK.lock().unwrap_or_else(PoisonError::into_inner);
43    f()
44}
45
46/// LLVM_InitializeAllTargetInfos
47pub fn llvm_initialize_all_target_infos() {
48    with_registry_lock(|| unsafe {
49        LLVM_InitializeAllTargetInfos();
50    });
51}
52
53/// LLVM_InitializeAllTargets
54pub fn llvm_initialize_all_targets() {
55    with_registry_lock(|| unsafe {
56        LLVM_InitializeAllTargets();
57    });
58}
59
60/// LLVM_InitializeAllTargetMCs
61pub fn llvm_initialize_all_target_mcs() {
62    with_registry_lock(|| unsafe {
63        LLVM_InitializeAllTargetMCs();
64    });
65}
66
67/// LLVM_InitializeAllAsmPrinters
68pub fn llvm_initialize_all_asm_printers() {
69    with_registry_lock(|| unsafe {
70        LLVM_InitializeAllAsmPrinters();
71    });
72}
73
74/// LLVM_InitializeAllAsmParsers
75pub fn llvm_initialize_all_asm_parsers() {
76    with_registry_lock(|| unsafe {
77        LLVM_InitializeAllAsmParsers();
78    });
79}
80
81/// LLVM_InitializeAllDisassemblers
82pub fn llvm_initialize_all_disassemblers() {
83    with_registry_lock(|| unsafe {
84        LLVM_InitializeAllDisassemblers();
85    });
86}
87
88/// LLVM_InitializeNativeTarget
89pub fn llvm_initialize_native_target() -> Result<(), String> {
90    if !with_registry_lock(|| unsafe { LLVM_InitializeNativeTarget().to_bool() }) {
91        Ok(())
92    } else {
93        Err("Failed to initialize native target".to_string())
94    }
95}
96
97/// LLVM_InitializeNativeAsmParser
98pub fn llvm_initialize_native_asm_parser() -> Result<(), String> {
99    if !with_registry_lock(|| unsafe { LLVM_InitializeNativeAsmParser().to_bool() }) {
100        Ok(())
101    } else {
102        Err("Failed to initialize native asm parser".to_string())
103    }
104}
105
106/// LLVM_InitializeNativeAsmParser
107pub fn llvm_initialize_native_asm_printer() -> Result<(), String> {
108    if !with_registry_lock(|| unsafe { LLVM_InitializeNativeAsmPrinter().to_bool() }) {
109        Ok(())
110    } else {
111        Err("Failed to initialize native asm printer".to_string())
112    }
113}
114
115/// LLVM_InitializeNativeDisassembler
116pub fn llvm_initialize_native_disassembler() -> Result<(), String> {
117    if !with_registry_lock(|| unsafe { LLVM_InitializeNativeDisassembler().to_bool() }) {
118        Ok(())
119    } else {
120        Err("Failed to initialize native disassembler".to_string())
121    }
122}
123
124/// Initialize native everything.
125pub fn initialize_native() -> Result<(), String> {
126    llvm_initialize_native_target()?;
127    llvm_initialize_native_asm_printer()?;
128    llvm_initialize_native_asm_parser()?;
129    llvm_initialize_native_disassembler()?;
130    Ok(())
131}
132
133/// Initialize all targets everything.
134pub fn initialize_all() {
135    llvm_initialize_all_target_infos();
136    llvm_initialize_all_targets();
137    llvm_initialize_all_target_mcs();
138    llvm_initialize_all_asm_printers();
139    llvm_initialize_all_asm_parsers();
140    llvm_initialize_all_disassemblers();
141}
142
143// We wrap LLVMTargetData in a module to limit its visibility for constructing
144mod llvm_target_data {
145    use super::*;
146
147    /// RAII wrapper around LLVMTargetDataReff
148    pub struct LLVMTargetData(LLVMTargetDataRef);
149
150    impl Drop for LLVMTargetData {
151        fn drop(&mut self) {
152            unsafe { LLVMDisposeTargetData(self.0) }
153        }
154    }
155
156    impl LLVMTargetData {
157        /// LLVMCreateTargetData
158        ///
159        /// **Note**: `layout` is not validated (the C-API gives no way to do that).
160        pub fn new(layout: &str) -> Self {
161            LLVMTargetData(unsafe { LLVMCreateTargetData(to_c_str(layout).as_ptr()) })
162        }
163
164        /// Build the layout of the machine that this program runs on.
165        pub fn host() -> Result<Self, String> {
166            // The layout comes from a target machine
167            llvm_initialize_native_target()?;
168
169            let triple_ptr = unsafe { LLVMGetDefaultTargetTriple() };
170            let triple = cstr_to_string(triple_ptr).unwrap_or_default();
171            unsafe { LLVMDisposeMessage(triple_ptr) };
172
173            let mut target = MaybeUninit::uninit();
174            let mut err_string = MaybeUninit::uninit();
175            let failed = unsafe {
176                LLVMGetTargetFromTriple(
177                    to_c_str(&triple).as_ptr(),
178                    target.as_mut_ptr(),
179                    err_string.as_mut_ptr(),
180                )
181                .to_bool()
182            };
183            if failed {
184                unsafe {
185                    let err_ptr = err_string.assume_init();
186                    let err = cstr_to_string(err_ptr).unwrap_or_default();
187                    LLVMDisposeMessage(err_ptr);
188                    return Err(err);
189                }
190            }
191
192            let machine = unsafe {
193                LLVMCreateTargetMachine(
194                    target.assume_init(),
195                    to_c_str(&triple).as_ptr(),
196                    // CPU and its features do not affect the layout
197                    to_c_str("").as_ptr(),
198                    to_c_str("").as_ptr(),
199                    LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,
200                    LLVMRelocMode::LLVMRelocDefault,
201                    LLVMCodeModel::LLVMCodeModelDefault,
202                )
203            };
204            if machine.is_null() {
205                return Err(format!("Failed to create a target machine for {triple}"));
206            }
207
208            let target_data = unsafe { LLVMCreateTargetDataLayout(machine) };
209            unsafe { LLVMDisposeTargetMachine(machine) };
210            Ok(LLVMTargetData(target_data))
211        }
212
213        /// LLVMCopyStringRepOfTargetData
214        pub fn copy_string_rep_of_target_data(&self) -> String {
215            let buf_ptr = unsafe { LLVMCopyStringRepOfTargetData(self.0) };
216            let layout = cstr_to_string(buf_ptr).unwrap_or_default();
217            unsafe { LLVMDisposeMessage(buf_ptr) };
218            layout
219        }
220
221        /// LLVMSizeOfTypeInBits
222        pub fn size_of_type_in_bits(&self, ty: LLVMType) -> u64 {
223            assert!(llvm_type_is_sized(ty));
224            unsafe { LLVMSizeOfTypeInBits(self.0, ty.into()) }
225        }
226
227        /// LLVMStoreSizeOfType
228        pub fn store_size_of_type(&self, ty: LLVMType) -> u64 {
229            assert!(llvm_type_is_sized(ty));
230            unsafe { LLVMStoreSizeOfType(self.0, ty.into()) }
231        }
232
233        /// LLVMABISizeOfType: Misnomer. It actually is `llvm::DataLayout::getTypeAllocSize.
234        pub fn abi_size_of_type(&self, ty: LLVMType) -> u64 {
235            assert!(llvm_type_is_sized(ty));
236            unsafe { LLVMABISizeOfType(self.0, ty.into()) }
237        }
238
239        /// LLVMABIAlignmentOfType
240        pub fn abi_alignment_of_type(&self, ty: LLVMType) -> u32 {
241            assert!(llvm_type_is_sized(ty));
242            unsafe { LLVMABIAlignmentOfType(self.0, ty.into()) }
243        }
244
245        /// LLVMPreferredAlignmentOfType
246        pub fn preferred_alignment_of_type(&self, ty: LLVMType) -> u32 {
247            assert!(llvm_type_is_sized(ty));
248            unsafe { LLVMPreferredAlignmentOfType(self.0, ty.into()) }
249        }
250
251        /// LLVMOffsetOfElement
252        pub fn offset_of_element(&self, struct_ty: LLVMType, index: u32) -> u64 {
253            assert!(llvm_get_type_kind(struct_ty) == LLVMTypeKind::LLVMStructTypeKind);
254            assert!(llvm_type_is_sized(struct_ty));
255            assert!(index < llvm_count_struct_element_types(struct_ty));
256            unsafe { LLVMOffsetOfElement(self.0, struct_ty.into(), index) }
257        }
258
259        /// LLVMPointerSizeForAS
260        pub fn pointer_size_for_as(&self, addr_space: u32) -> u32 {
261            unsafe { LLVMPointerSizeForAS(self.0, addr_space) }
262        }
263    }
264}
265pub use llvm_target_data::LLVMTargetData;