1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//! [![github]](https://github.com/jamesmth/llvm-plugin-rs) [![crates-io]](https://crates.io/crates/llvm-plugin)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//!
//! <br>
//!
//! This crate gives the ability to safely implement passes for the [new LLVM pass manager],
//! by leveraging the strongly typed interface  provided by [Inkwell].
//!
//! If you have never developed LLVM passes before, perhaps you should take a look at this
//! [LLVM guide] before carrying on. It will give you a simple overview of the C++ API
//! wrapped by this crate.
//!
//! If you want a deeper understanding of the many concepts surrounding the new LLVM pass manager,
//! you should read the [official LLVM documentation].
//!
//! [Inkwell]: https://github.com/TheDan64/inkwell
//! [new LLVM pass manager]: https://blog.llvm.org/posts/2021-03-26-the-new-pass-manager/
//! [LLVM guide]: https://llvm.org/docs/WritingAnLLVMNewPMPass.html
//! [official LLVM documentation]: https://llvm.org/docs/NewPassManager.html
//!
//! # Example
//!
//! A simple LLVM plugin which defines two passes, one being a transformation pass that queries
//! the result of a second pass, an analysis one:
//!
//! ```
//! // Define an LLVM plugin (a name and a version is required). Only cdylib crates
//! // should define plugins, and only one definition should be done per crate.
//! #[llvm_plugin::plugin(name = "plugin_name", version = "0.1")]
//! mod plugin {
//!     use llvm_plugin::{
//!         LlvmModuleAnalysis, LlvmModulePass, ModuleAnalysisManager, PreservedAnalyses,
//!     };
//!     use llvm_plugin::inkwell::module::Module;
//!
//!     // Must implement the `Default` trait.
//!     #[derive(Default)]
//!     struct Pass1;
//!
//!     // Define a transformation pass (a name is required). Such pass is allowed to
//!     // mutate the LLVM IR. If it does, it should return `PreservedAnalysis::None`
//!     // to notify the pass manager that all analyses are now invalidated.
//!     #[pass(name = "pass_name")]
//!     impl LlvmModulePass for Pass1 {
//!         fn run_pass(
//!             &self,
//!             module: &mut Module,
//!             manager: &ModuleAnalysisManager,
//!         ) -> PreservedAnalyses {
//!             // Ask the pass manager for the result of the analysis pass `Analysis1`
//!             // defined further below. If the result is not in cache, the pass
//!             // manager will call `Analysis1::run_analysis`.
//!             let result = manager.get_result::<Analysis1>(module);
//!
//!             assert_eq!(result, "Hello World!");
//!
//!             // no modification was made on the module, so the pass manager doesn't have
//!             // to recompute any analysis
//!             PreservedAnalyses::All
//!         }
//!     }
//!
//!     // Must implement the `Default` trait.
//!     #[derive(Default)]
//!     struct Analysis1;
//!
//!     // Define an analysis pass. Such pass is not allowed to mutate the LLVM IR. It should
//!     // be used only for inspection of the LLVM IR, and can return some result that will be
//!     // efficiently cached by the pass manager (to prevent recomputing the same analysis
//!     // every time its result is needed).
//!     #[analysis]
//!     impl LlvmModuleAnalysis for Analysis1 {
//!         fn run_analysis(
//!             &self,
//!             module: &Module,
//!             manager: &ModuleAnalysisManager,
//!         ) -> String {
//!             // .. inspect the LLVM IR of the module ..
//!
//!             "Hello World!".to_owned()
//!         }
//!     }
//! }
//! ```

#![deny(missing_docs)]

mod analysis;
pub use analysis::*;

#[doc(hidden)]
pub mod ffi;
#[doc(hidden)]
pub use ffi::*;

pub use inkwell;
use inkwell::module::Module;
use inkwell::values::FunctionValue;

/// Utilities.
pub mod utils;

/// Enum specifying whether analyses on an IR unit are not preserved due
/// to the modification of such unit by a transformation pass.
#[repr(C)]
#[derive(Clone, Copy)]
pub enum PreservedAnalyses {
    /// This variant hints the pass manager that all the analyses are
    /// preserved, so there is no need to re-execute analysis passes.
    ///
    /// Use this variant when a transformation pass doesn't modify some
    /// IR unit.
    All,

    /// This variant hints the pass manager that all the analyses are
    /// should be re-executed.
    ///
    /// Use this variant when a transformation pass modifies some IR unit.
    None,
}

/// Trait to use for implementing a transformation pass on an LLVM module.
///
/// A transformation pass is allowed to mutate the LLVM IR.
pub trait LlvmModulePass: Default {
    /// Entrypoint for the pass.
    ///
    /// The given analysis manager allows the pass to query the pass
    /// manager for the result of specific analysis passes.
    ///
    /// If this function makes modifications on the given module IR, it
    /// should return `PreservedAnalyses::None` to indicate to the
    /// pass manager that all analyses are now invalidated.
    fn run_pass<'a>(
        &self,
        module: &mut Module<'a>,
        manager: &ModuleAnalysisManager,
    ) -> PreservedAnalyses;
}

/// Trait to use for implementing a transformation pass on an LLVM function.
///
/// A transformation pass is allowed to mutate the LLVM IR.
pub trait LlvmFunctionPass: Default {
    /// Entrypoint for the pass.
    ///
    /// The given analysis manager allows the pass to query the pass
    /// manager for the result of specific analysis passes.
    ///
    /// If this function makes modifications on the given function IR, it
    /// should return `PreservedAnalyses::None` to indicate to the
    /// pass manager that all analyses are now invalidated.
    fn run_pass<'a>(
        &self,
        function: &mut FunctionValue<'a>,
        manager: &FunctionAnalysisManager,
    ) -> PreservedAnalyses;
}

/// Trait to use for implementing an analysis pass on an LLVM module.
///
/// An analysis pass is not allowed to mutate the LLVM IR.
pub trait LlvmModuleAnalysis: Default {
    /// Result of the successful execution of this pass by the pass manager.
    ///
    /// This data can be queried by passes through a [`ModuleAnalysisManager`].
    type Result;

    /// Entrypoint for the pass.
    ///
    /// The given analysis manager allows the pass to query the pass
    /// manager for the result of specific analysis passes.
    ///
    /// The returned result will be moved into a [`Box`](`std::boxed::Box`)
    /// before being given to the pass manager. This one will then add it to
    /// its internal cache, to avoid unnecessary calls to this entrypoint.
    fn run_analysis<'a>(
        &self,
        module: &Module<'a>,
        manager: &ModuleAnalysisManager,
    ) -> Self::Result;

    #[doc(hidden)]
    fn id() -> AnalysisKey;
}

/// Trait to use for implementing an analysis pass on an LLVM function.
///
/// An analysis pass is not allowed to mutate the LLVM IR.
pub trait LlvmFunctionAnalysis: Default {
    /// Result of the successful execution of this pass by the pass manager.
    ///
    /// This data can be queried by passes through a [`FunctionAnalysisManager`].
    type Result;

    /// Entrypoint for the pass.
    ///
    /// The given analysis manager allows the pass to query the pass
    /// manager for the result of specific analysis passes.
    ///
    /// The returned result will be moved into a [`Box`](`std::boxed::Box`)
    /// before being given to the pass manager. This one will then add it to
    /// its internal cache, to avoid unnecessary calls to this entrypoint.
    fn run_analysis<'a>(
        &self,
        module: &FunctionValue<'a>,
        manager: &FunctionAnalysisManager,
    ) -> Self::Result;

    #[doc(hidden)]
    fn id() -> AnalysisKey;
}

#[doc(hidden)]
#[repr(C)]
pub struct PassPluginLibraryInfo {
    pub api_version: u32,
    pub plugin_name: *const u8,
    pub plugin_version: *const u8,
    pub plugin_registrar: unsafe extern "C" fn(*mut std::ffi::c_void),
}

#[cfg(feature = "macros")]
pub use llvm_plugin_macros::*;

// See https://github.com/jamesmth/llvm-plugin-rs/issues/1
#[cfg(all(target_os = "windows", feature = "llvm10-0"))]
compile_error!("LLVM 10 not supported on Windows");

// Taken from llvm-sys source code.
//
// Since we use `llvm-no-linking`, `llvm-sys` won't trigger that error
// for us, so we need to take care of it ourselves.
#[cfg(all(not(doc), LLVM_NOT_FOUND))]
compile_error!(
    "No suitable version of LLVM was found system-wide or pointed
       to by LLVM_PLUGIN_PREFIX."
);