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));