llvm_plugin/lib.rs
1//! [![github]](https://github.com/jamesmth/llvm-plugin-rs) [![crates-io]](https://crates.io/crates/llvm-plugin)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//!
6//! <br>
7//!
8//! This crate gives the ability to safely implement passes for the [new LLVM pass manager],
9//! by leveraging the strongly typed interface provided by [Inkwell].
10//!
11//! If you have never developed LLVM passes before, you can take a look at the available
12//! [examples]. They will (hopefully) give you a better idea of how to use this crate.
13//!
14//! If you want a deeper understanding of the many concepts surrounding the new LLVM
15//! pass manager, you should read the [official LLVM documentation].
16//!
17//! [Inkwell]: https://github.com/TheDan64/inkwell
18//! [new LLVM pass manager]: https://blog.llvm.org/posts/2021-03-26-the-new-pass-manager/
19//! [examples]: https://github.com/jamesmth/llvm-plugin-rs/tree/master/examples
20//! [official LLVM documentation]: https://llvm.org/docs/NewPassManager.html
21//!
22//! # Getting started
23//!
24//! An LLVM plugin is merely a dylib that is given a [PassBuilder] by the LLVM tool
25//! (e.g. [opt], [lld]) loading it. A [PassBuilder] allows registering callbacks on
26//! specific actions being performed by the LLVM tool.
27//!
28//! For instance, the `--passes` parameter of [opt] allows specifying a custom pass pipeline
29//! to be run on a given IR module. A plugin could therefore register a callback for
30//! parsing an element of the given pipeline (e.g. a pass name), in order to insert a custom
31//! pass to run by [opt].
32//!
33//! The following code illustrates the idea:
34//!
35//! ```no_run
36//! # use llvm_plugin::inkwell::module::Module;
37//! # use llvm_plugin::{
38//! # LlvmModulePass, ModuleAnalysisManager, PassBuilder, PipelineParsing, PreservedAnalyses,
39//! # };
40//! // A name and version is required.
41//! #[llvm_plugin::plugin(name = "plugin_name", version = "0.1")]
42//! fn plugin_registrar(builder: &mut PassBuilder) {
43//! // Add a callback to parse a name from the textual representation of
44//! // the pipeline to be run.
45//! builder.add_module_pipeline_parsing_callback(|name, manager| {
46//! if name == "custom-pass" {
47//! // the input pipeline contains the name "custom-pass",
48//! // so we add our custom pass to the pass manager
49//! manager.add_pass(CustomPass);
50//!
51//! // we notify the caller that we were able to parse
52//! // the given name
53//! PipelineParsing::Parsed
54//! } else {
55//! // in any other cases, we notify the caller that our
56//! // callback wasn't able to parse the given name
57//! PipelineParsing::NotParsed
58//! }
59//! });
60//! }
61//!
62//! struct CustomPass;
63//! impl LlvmModulePass for CustomPass {
64//! fn run_pass(
65//! &self,
66//! module: &mut Module,
67//! manager: &ModuleAnalysisManager
68//! ) -> PreservedAnalyses {
69//! // transform the IR
70//! # PreservedAnalyses::All
71//! }
72//! }
73//! ```
74//!
75//! Now, executing this command would run our custom pass on the input module:
76//!
77//! ```bash
78//! opt --load-pass-plugin=libplugin.so --passes=custom-pass module.bc -disable-output
79//! ```
80//!
81//! However, executing this command would not (`custom-pass2` cannot be parsed by our plugin):
82//!
83//! ```bash
84//! opt --load-pass-plugin=libplugin.so --passes=custom-pass2 module.bc -disable-output
85//! ```
86//!
87//! More callbacks are available, read the [PassBuilder] documentation for more details.
88//!
89//! # A note on Windows
90//!
91//! On this platform, LLVM plugins need the LLVM symbols directly from the executable loading
92//! them (most of the time `opt.exe` or `lld.exe`). Therefore, you need to specify the
93//! additional feature `win-link-opt` or `win-link-lld` while building a plugin. The former
94//! will link the plugin to `opt.lib`, the latter being for `lld.lib`.
95//!
96//! [opt]: https://www.llvm.org/docs/CommandGuide/opt.html
97//! [lld]: https://lld.llvm.org/
98
99#![deny(missing_docs)]
100#![cfg_attr(docsrs, feature(doc_auto_cfg))]
101
102mod analysis;
103pub use analysis::*;
104
105#[doc(hidden)]
106pub mod ffi;
107#[doc(hidden)]
108pub use ffi::*;
109
110pub use inkwell;
111use inkwell::module::Module;
112use inkwell::values::FunctionValue;
113
114mod pass_manager;
115pub use pass_manager::*;
116
117mod pass_builder;
118pub use pass_builder::*;
119
120/// Enum specifying whether analyses on an IR unit are not preserved due
121/// to the modification of such unit by a transformation pass.
122#[repr(C)]
123#[derive(Clone, Copy)]
124pub enum PreservedAnalyses {
125 /// This variant hints the pass manager that all the analyses are
126 /// preserved, so there is no need to re-execute analysis passes.
127 ///
128 /// Use this variant when a transformation pass doesn't modify some
129 /// IR unit.
130 All,
131
132 /// This variant hints the pass manager that all the analyses should
133 /// be re-executed.
134 ///
135 /// Use this variant when a transformation pass modifies some IR unit.
136 None,
137}
138
139/// Trait to use for implementing a transformation pass on an LLVM module.
140///
141/// A transformation pass is allowed to mutate the LLVM IR.
142pub trait LlvmModulePass {
143 /// Entrypoint for the pass.
144 ///
145 /// The given analysis manager allows the pass to query the pass
146 /// manager for the result of specific analysis passes.
147 ///
148 /// If this function makes modifications on the given module IR, it
149 /// should return `PreservedAnalyses::None` to indicate to the
150 /// pass manager that all analyses are now invalidated.
151 fn run_pass(
152 &self,
153 module: &mut Module<'_>,
154 manager: &ModuleAnalysisManager,
155 ) -> PreservedAnalyses;
156}
157
158/// Trait to use for implementing a transformation pass on an LLVM function.
159///
160/// A transformation pass is allowed to mutate the LLVM IR.
161pub trait LlvmFunctionPass {
162 /// Entrypoint for the pass.
163 ///
164 /// The given analysis manager allows the pass to query the pass
165 /// manager for the result of specific analysis passes.
166 ///
167 /// If this function makes modifications on the given function IR, it
168 /// should return `PreservedAnalyses::None` to indicate to the
169 /// pass manager that all analyses are now invalidated.
170 fn run_pass(
171 &self,
172 function: &mut FunctionValue<'_>,
173 manager: &FunctionAnalysisManager,
174 ) -> PreservedAnalyses;
175}
176
177/// Trait to use for implementing an analysis pass on an LLVM module.
178///
179/// An analysis pass is not allowed to mutate the LLVM IR.
180pub trait LlvmModuleAnalysis {
181 /// Result of the successful execution of this pass by the pass manager.
182 ///
183 /// This data can be queried by passes through a [ModuleAnalysisManager].
184 type Result;
185
186 /// Entrypoint for the pass.
187 ///
188 /// The given analysis manager allows the pass to query the pass
189 /// manager for the result of specific analysis passes.
190 ///
191 /// The returned result will be moved into a [Box](`std::boxed::Box`)
192 /// before being given to the pass manager. This one will then add it to
193 /// its internal cache, to avoid unnecessary calls to this entrypoint.
194 fn run_analysis(&self, module: &Module<'_>, manager: &ModuleAnalysisManager) -> Self::Result;
195
196 /// Identifier for the analysis type.
197 ///
198 /// This ID must be unique for each registered analysis type.
199 ///
200 /// # Warning
201 ///
202 /// The LLVM toolchain (e.g. [opt], [lld]) often registers builtin analysis
203 /// types during execution of passes. These builtin analyses always use
204 /// the address of global static variables as IDs, to prevent collisions.
205 ///
206 /// To make sure your custom analysis types don't collide with the builtin
207 /// ones used by the LLVM tool that loads your plugin, you should use static
208 /// variables' addresses as well.
209 ///
210 /// # Example
211 ///
212 /// ```
213 /// # use llvm_plugin::inkwell::module::Module;
214 /// # use llvm_plugin::{AnalysisKey, LlvmModuleAnalysis, ModuleAnalysisManager};
215 /// # struct Analysis;
216 /// # impl LlvmModuleAnalysis for Analysis {
217 /// # type Result = ();
218 /// # fn run_analysis(
219 /// # &self,
220 /// # _module: &Module,
221 /// # _manager: &ModuleAnalysisManager,
222 /// # ) -> Self::Result {}
223 /// #
224 /// fn id() -> AnalysisKey {
225 /// static ID: u8 = 0;
226 /// &ID
227 /// }
228 /// # }
229 /// ```
230 ///
231 /// [opt]: https://www.llvm.org/docs/CommandGuide/opt.html
232 /// [lld]: https://lld.llvm.org/
233 fn id() -> AnalysisKey;
234}
235
236/// Trait to use for implementing an analysis pass on an LLVM function.
237///
238/// An analysis pass is not allowed to mutate the LLVM IR.
239pub trait LlvmFunctionAnalysis {
240 /// Result of the successful execution of this pass by the pass manager.
241 ///
242 /// This data can be queried by passes through a [FunctionAnalysisManager].
243 type Result;
244
245 /// Entrypoint for the pass.
246 ///
247 /// The given analysis manager allows the pass to query the pass
248 /// manager for the result of specific analysis passes.
249 ///
250 /// The returned result will be moved into a [Box](`std::boxed::Box`)
251 /// before being given to the pass manager. This one will then add it to
252 /// its internal cache, to avoid unnecessary calls to this entrypoint.
253 fn run_analysis(
254 &self,
255 module: &FunctionValue<'_>,
256 manager: &FunctionAnalysisManager,
257 ) -> Self::Result;
258
259 /// Identifier for the analysis type.
260 ///
261 /// This ID must be unique for each registered analysis type.
262 ///
263 /// # Warning
264 ///
265 /// The LLVM toolchain (e.g. [opt], [lld]) often registers builtin analysis
266 /// types during execution of passes. These builtin analyses always use
267 /// the address of global static variables as IDs, to prevent collisions.
268 ///
269 /// To make sure your custom analysis types don't collide with the builtin
270 /// ones used by the LLVM tool that loads your plugin, you should use static
271 /// variables' addresses as well.
272 ///
273 /// # Example
274 ///
275 /// ```
276 /// # use llvm_plugin::inkwell::values::FunctionValue;
277 /// # use llvm_plugin::{AnalysisKey, LlvmFunctionAnalysis, FunctionAnalysisManager};
278 /// # struct Analysis;
279 /// # impl LlvmFunctionAnalysis for Analysis {
280 /// # type Result = ();
281 /// # fn run_analysis(
282 /// # &self,
283 /// # _function: &FunctionValue,
284 /// # _manager: &FunctionAnalysisManager,
285 /// # ) -> Self::Result {}
286 /// #
287 /// fn id() -> AnalysisKey {
288 /// static ID: u8 = 0;
289 /// &ID
290 /// }
291 /// # }
292 /// ```
293 ///
294 /// [opt]: https://www.llvm.org/docs/CommandGuide/opt.html
295 /// [lld]: https://lld.llvm.org/
296 fn id() -> AnalysisKey;
297}
298
299#[doc(hidden)]
300#[repr(C)]
301pub struct PassPluginLibraryInfo {
302 pub api_version: u32,
303 pub plugin_name: *const u8,
304 pub plugin_version: *const u8,
305 pub plugin_registrar: unsafe extern "C" fn(*mut std::ffi::c_void),
306}
307
308#[cfg(feature = "macros")]
309pub use llvm_plugin_macros::*;
310
311// See https://github.com/jamesmth/llvm-plugin-rs/issues/3
312#[cfg(all(target_os = "windows", feature = "llvm10-0"))]
313compile_error!("LLVM 10 not supported on Windows");
314
315#[cfg(all(
316 target_os = "windows",
317 any(
318 all(feature = "win-link-opt", feature = "win-link-lld"),
319 all(not(feature = "win-link-opt"), not(feature = "win-link-lld"))
320 )
321))]
322compile_error!(
323 "Either `win-link-opt` feature or `win-link-lld` feature
324 is needed on Windows (not both)."
325);
326
327// Taken from llvm-sys source code.
328//
329// Since we use `llvm-no-linking`, `llvm-sys` won't trigger that error
330// for us, so we need to take care of it ourselves.
331#[cfg(all(not(doc), LLVM_NOT_FOUND))]
332compile_error!(concat!(
333 "No suitable version of LLVM was found system-wide or pointed
334 to by LLVM_SYS_",
335 env!("LLVM_VERSION_MAJOR"),
336 "_PREFIX.
337
338 Refer to the llvm-sys documentation for more information.
339
340 llvm-sys: https://crates.io/crates/llvm-sys"
341));