Skip to main content

rustc_codegen_llvm/llvm/
tpde_ffi.rs

1use std::path::PathBuf;
2use std::sync::OnceLock;
3
4use rustc_session::config::{self, Sysroot, host_tuple};
5use rustc_session::filesearch;
6
7unsafe extern "C" {
8    type LLVMPassPluginLibraryInfo;
9}
10
11type LLVMGetPassPluginInfoFn = unsafe extern "C" fn() -> LLVMPassPluginLibraryInfo;
12
13/// LLVMRustTpdeMode
14#[repr(C)]
15#[derive(#[automatically_derived]impl ::core::clone::Clone for LLVMRustTpdeMode {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        match self {
            Self::None => LLVMRustTpdeMode::None,
            Self::Only => LLVMRustTpdeMode::Only,
            Self::Try => LLVMRustTpdeMode::Try,
        }
    }
}Clone)]
16pub(crate) enum LLVMRustTpdeMode {
17    None,
18    Only,
19    Try,
20}
21
22/// LLVMRustTpdeOptions
23#[repr(C)]
24#[derive(#[automatically_derived]impl ::core::clone::Clone for LLVMRustTpdeOptions {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        LLVMRustTpdeOptions {
            mode: ::core::clone::Clone::clone(&self.mode),
            plugin: ::core::clone::Clone::clone(&self.plugin),
        }
    }
}Clone)]
25pub(crate) struct LLVMRustTpdeOptions {
26    mode: LLVMRustTpdeMode,
27    // `None` if mode is `LLVMRustTpdeMode::None`
28    plugin: Option<LLVMGetPassPluginInfoFn>,
29}
30
31pub(crate) struct TpdeWrapper {
32    options: LLVMRustTpdeOptions,
33    // Keep the dynamic library loaded while the function pointers are used.
34    _lib: Option<libloading::Library>,
35}
36
37static TPDE_INSTANCE: OnceLock<TpdeWrapper> = OnceLock::new();
38
39#[derive(#[automatically_derived]impl ::core::fmt::Debug for TpdeLibraryError {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::NotFound { err: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "NotFound err", &__self_0),
            Self::LoadFailed { err: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "LoadFailed err", &__self_0),
        }
    }
}Debug)]
40pub(crate) enum TpdeLibraryError {
41    NotFound { err: String },
42    LoadFailed { err: String },
43}
44
45impl From<libloading::Error> for TpdeLibraryError {
46    fn from(err: libloading::Error) -> Self {
47        Self::LoadFailed { err: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", err))
    })format!("{err:?}") }
48    }
49}
50
51impl TpdeWrapper {
52    /// Initialize TpdeWrapper with the given sysroot and mode if not already initialized.
53    /// Safe to call multiple times - subsequent calls are no-ops due to OnceLock.
54    pub(crate) fn get_or_init(
55        sysroot: &rustc_session::config::Sysroot,
56        mode: &Option<config::LlvmTpde>,
57    ) -> Result<&'static Self, TpdeLibraryError> {
58        let wrapper = TPDE_INSTANCE.get_or_try_init(|| {
59            let w = match mode {
60                Some(mode) => Self::call_dynamic(sysroot, mode)?,
61                None => Self {
62                    options: LLVMRustTpdeOptions { mode: LLVMRustTpdeMode::None, plugin: None },
63                    _lib: None,
64                },
65            };
66            Ok::<_, TpdeLibraryError>(w)
67        })?;
68
69        Ok(wrapper)
70    }
71
72    /// Get the TpdeWrapper instance. Panics if not initialized.
73    pub(crate) fn get_instance() -> &'static Self {
74        TPDE_INSTANCE
75            .get()
76            .expect("TpdeWrapper not initialized. Call get_or_init with sysroot first.")
77    }
78
79    pub(crate) fn options(&self) -> &LLVMRustTpdeOptions {
80        &self.options
81    }
82
83    fn call_dynamic(
84        sysroot: &rustc_session::config::Sysroot,
85        mode: &config::LlvmTpde,
86    ) -> Result<Self, TpdeLibraryError> {
87        let mode = match mode {
88            config::LlvmTpde::Only => LLVMRustTpdeMode::Only,
89            config::LlvmTpde::Try => LLVMRustTpdeMode::Try,
90        };
91        let tpde_path = Self::get_tpde_path(sysroot)?;
92        let lib = unsafe { libloading::Library::new(tpde_path)? };
93        let llvm_get_pass_plugin_info =
94            *unsafe { lib.get::<LLVMGetPassPluginInfoFn>(b"llvmGetPassPluginInfo\0")? };
95        let options = LLVMRustTpdeOptions { mode, plugin: Some(llvm_get_pass_plugin_info) };
96        Ok(Self { options, _lib: Some(lib) })
97    }
98
99    fn get_tpde_path(sysroot: &Sysroot) -> Result<PathBuf, TpdeLibraryError> {
100        let path_buf = sysroot
101            .all_paths()
102            .map(|sysroot_path| {
103                filesearch::make_target_lib_path(sysroot_path, host_tuple())
104                    .join("lib")
105                    .with_file_name("tpde-plugin")
106                    .with_extension(std::env::consts::DLL_EXTENSION)
107            })
108            .find(|f| f.exists())
109            .ok_or_else(|| {
110                let candidates = sysroot
111                    .all_paths()
112                    .map(|p| p.join("lib").display().to_string())
113                    .collect::<Vec<String>>()
114                    .join("\n* ");
115                TpdeLibraryError::NotFound {
116                    err: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("failed to find the `tpde-plugin` shared library in the sysroot candidates:\n* {0}",
                candidates))
    })format!(
117                        "failed to find the `tpde-plugin` shared library \
118                    in the sysroot candidates:\n* {candidates}"
119                    ),
120                }
121            })?;
122
123        Ok(path_buf)
124    }
125}