compiler_interrupts/lib.rs
1//! [][crates.io]
2//! [][docs.rs]
3//! [][license]
4//!
5//! `compiler-interrupts` provides Rust API for the Compiler Interrupts library.
6//! Check out the Compiler Interrupts [main repository][compiler-interrupts] for more info.
7//!
8//! ## Requirements
9//!
10//! * [Rust 1.45.0][rust] or later is required.
11//! Due to the usage of [`#[thread_local]`][thread_local] unstable feature,
12//! this package currently requires nightly Rust.
13//!
14//! ## Getting started
15//!
16//! Add this to your `Cargo.toml`.
17//!
18//! ``` toml
19//! [dependencies]
20//! compiler-interrupts = "1.0"
21//! ```
22//!
23//! Register the Compiler Interrupts handler in your program.
24//!
25//! ``` rust
26//! #![feature(thread_local)]
27//!
28//! #[thread_local]
29//! #[allow(non_upper_case_globals)]
30//! static mut prev_ic: i64 = 0;
31//!
32//! fn interrupt_handler(ic: i64) {
33//! let interval;
34//! unsafe {
35//! // save the last interval
36//! interval = ic - prev_ic;
37//!
38//! // update the instruction count
39//! prev_ic = ic;
40//! }
41//! if interval < 0 {
42//! panic!("IR interval was negative")
43//! }
44//! println!(
45//! "CI @ {}: last interval = {} IR",
46//! std::thread::current().name().expect("invalid thread name"),
47//! interval
48//! );
49//! }
50//!
51//! fn main() {
52//! // register the CI handler for 1000 IR and cycles interval
53//! unsafe {
54//! compiler_interrupts::register(1000, 1000, interrupt_handler);
55//! }
56//!
57//! // do something compute-intensive
58//! for _ in 0..100 {}
59//!
60//! println!("i can add an unsafe block, right?")
61//! }
62//! ```
63//!
64//! If you have [`cargo-compiler-interrupts`][cargo-compiler-interrupts] installed,
65//! you can now run `cargo build-ci` to start the compilation and integration.
66//! Check out the **[documentation][docs.rs]** for more info about the API.
67//!
68//! ## Contribution
69//!
70//! All issue reports, feature requests, pull requests and GitHub stars are welcomed
71//! and much appreciated.
72//!
73//! ## Author
74//!
75//! Quan Tran ([@quanshousio][quanshousio])
76//!
77//! ## Acknowledgements
78//!
79//! * My advisor [Jakob Eriksson][jakob] for the enormous support for this project.
80//! * [Nilanjana Basu][nilanjana] for implementing the Compiler Interrupts.
81//!
82//! ## License
83//!
84//! `compiler-interrupts` is available under the MIT license.
85//! See the [LICENSE][license] file for more info.
86//!
87//! [crates.io]: https://crates.io/crates/compiler-interrupts
88//! [docs.rs]: https://docs.rs/compiler-interrupts
89//! [license]: https://github.com/bitslab/compiler-interrupts-rs/blob/main/LICENSE
90//! [compiler-interrupts]: https://github.com/bitslab/CompilerInterrupts
91//! [rust]: https://www.rust-lang.org/tools/install
92//! [thread_local]: https://github.com/rust-lang/rust/issues/29594
93//! [cargo-compiler-interrupts]: https://github.com/bitslab/cargo-compiler-interrupts
94//! [quanshousio]: https://quanshousio.com
95//! [jakob]: https://www.linkedin.com/in/erikssonjakob
96//! [nilanjana]: https://www.linkedin.com/in/nilanjana-basu-99027959
97
98#![feature(thread_local)]
99
100/// Default large interval
101const LARGE_INTERVAL: i64 = 100000;
102
103/// Default small interval
104const SMALL_INTERVAL: i64 = 10000;
105
106/// Interrupt function for the framework.
107#[no_mangle]
108#[thread_local]
109static mut intvActionHook: fn(i64) = dummy;
110
111/// Store the interrupt handler from [`register`].
112#[allow(non_upper_case_globals)]
113#[thread_local]
114static mut int_handler: fn(i64) = dummy;
115
116/// Store the enable hook from [`register_enable_hook`].
117#[allow(non_upper_case_globals)]
118#[thread_local]
119static mut enableHook: Option<fn()> = None;
120
121/// Store the disable hook from [`register_disable_hook`].
122#[allow(non_upper_case_globals)]
123#[thread_local]
124static mut disableHook: Option<fn()> = None;
125
126/// IR interrupt interval for the framework.
127#[no_mangle]
128#[thread_local]
129static mut ci_ir_interval: i64 = LARGE_INTERVAL;
130
131/// IR interrupt reset interval when target target cycles is not exceeded
132/// for the framework.
133#[no_mangle]
134#[thread_local]
135static mut ci_reset_ir_interval: i64 = LARGE_INTERVAL / 2;
136
137/// Cycles interrupt interval for the framework.
138#[no_mangle]
139#[thread_local]
140static mut ci_cycles_interval: i64 = SMALL_INTERVAL;
141
142/// Cycles interrupt threshold to fire the interrupt or reset the IR counter
143/// for the framework.
144#[no_mangle]
145#[thread_local]
146static mut ci_cycles_threshold: i64 = (0.9 * LARGE_INTERVAL as f64) as i64;
147
148/// Thread-local local counter for the framework.
149#[no_mangle]
150#[thread_local]
151static mut LocalLC: i32 = 0;
152
153/// Thread-local disable counter for the framework.
154#[no_mangle]
155#[thread_local]
156static mut lc_disabled_count: i32 = 0;
157
158/// Thread-local next interval for the framework.
159#[no_mangle]
160#[thread_local]
161static mut NextInterval: i32 = 0;
162
163/// A dummy function.
164fn dummy(_: i64) {}
165
166/// Assigns the interrupt function to itself and calls the handler from [`register`].
167fn interrupt_handler(ic: i64) {
168 unsafe {
169 intvActionHook = dummy;
170 int_handler(ic);
171 intvActionHook = interrupt_handler
172 }
173}
174
175/// Registers a handler for Compiler Interrupts.
176///
177/// This function takes a IR interval, cycles interval, and
178/// function pointer to the Compiler Interrupts handler.
179/// The handler receives an approximation of the number of IR instructions
180/// since the last interrupt as the argument.
181///
182/// # Note
183///
184/// This function is thread-specific, which means it only registers
185/// on the thread they called on.
186///
187/// This function should not be called multiple times.
188/// Consecutive calls will override the previous intervals and handler.
189///
190/// # Safety
191///
192/// This function mutates a thread-local static variable which uses for the interrupt handler.
193/// Thread unsafety will not be introduced. However, calling the handler outside Rust would
194/// probably violate Rust's safe memory model; hence the function is considered unsafe.
195///
196/// # Examples
197///
198/// ```
199/// fn interrupt_handler(ic: i64) {
200/// println!("Compiler interrupt called with instruction count: {}", ic);
201/// }
202///
203/// unsafe {
204/// compiler_interrupts::register(10000, 10000, interrupt_handler);
205/// }
206/// ```
207pub unsafe fn register(ir_interval: i64, cycles_interval: i64, handler: fn(i64)) {
208 LocalLC += ci_ir_interval as i32;
209 ci_ir_interval = ir_interval;
210 ci_reset_ir_interval = ir_interval / 2;
211 ci_cycles_interval = cycles_interval;
212 ci_cycles_threshold = (0.9 * cycles_interval as f64) as i64;
213 int_handler = handler;
214 intvActionHook = interrupt_handler;
215}
216
217/// De-registers the handler for Compiler Interrupts.
218///
219/// This function removes the given interrupts handler from [`register`].
220///
221/// # Note
222///
223/// This function is thread-specific, which means it only de-registers
224/// on the thread they called on.
225///
226/// This function should not be called multiple times.
227/// Consecutive calls will do nothing as the handler has already been de-registered.
228///
229/// # Safety
230///
231/// This function mutates a thread-local static variable which uses for the interrupt handler.
232/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
233pub unsafe fn deregister() {
234 ci_ir_interval = LARGE_INTERVAL;
235 ci_reset_ir_interval = LARGE_INTERVAL / 2;
236 ci_cycles_interval = LARGE_INTERVAL;
237 ci_cycles_threshold = (0.9 * LARGE_INTERVAL as f64) as i64;
238 int_handler = dummy;
239 intvActionHook = dummy;
240}
241
242/// Enables Compiler Interrupts.
243///
244/// This function enables Compiler Interrupts if it is currently disabled.
245/// Compiler Interrupts are enabled by default.
246///
247/// # Note
248///
249/// This function is thread-specific, which means it only enables
250/// on the thread they called on.
251///
252/// This function can be called multiple times.
253/// Number of [`enable`] calls must be the same as the number of previous [`disable`] calls
254/// to re-enable the interrupts.
255///
256/// # Safety
257///
258/// This function mutates a thread-local static variable which uses for the counter.
259/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
260///
261/// # Examples
262///
263/// ```
264/// unsafe {
265/// // disables the interrupts
266/// compiler_interrupts::disable();
267/// }
268///
269/// for _ in 0..42 {
270/// println!("interrupts have been disabled");
271/// }
272///
273/// unsafe {
274/// // re-enables the interrupts
275/// compiler_interrupts::enable();
276/// }
277/// ```
278pub unsafe fn enable() {
279 if lc_disabled_count > 0 {
280 lc_disabled_count -= 1;
281 }
282 if let Some(hook) = enableHook {
283 hook();
284 }
285 if lc_disabled_count == 0 {
286 intvActionHook = interrupt_handler;
287 }
288}
289
290/// Disables Compiler Interrupts.
291///
292/// This function disables Compiler Interrupts if it is currently enabled.
293///
294/// # Note
295///
296/// This function is thread-specific, which means it only disables
297/// on the thread they called on.
298///
299/// This function can be called multiple times.
300/// Consecutive calls will do nothing as the interrupts are already disabled.
301///
302/// # Safety
303///
304/// This function mutates a thread-local static variable which uses for the counter.
305/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
306///
307/// # Examples
308///
309/// ```
310/// unsafe {
311/// // disables the interrupts
312/// compiler_interrupts::disable();
313/// }
314///
315/// for _ in 0..42 {
316/// println!("interrupts have been disabled");
317/// }
318///
319/// unsafe {
320/// // re-enables the interrupts
321/// compiler_interrupts::enable();
322/// }
323/// ```
324pub unsafe fn disable() {
325 intvActionHook = dummy;
326 lc_disabled_count += 1;
327 if let Some(hook) = disableHook {
328 hook();
329 }
330}
331
332/// Registers a hook when enabling Compiler Interrupts.
333///
334/// This function takes a function pointer to be called after enabling Compiler Interrupts.
335/// Compiler Interrupts can be enabled by calling [`enable`].
336///
337/// # Note
338///
339/// This function is thread-specific, which means it only registers
340/// on the thread they called on.
341///
342/// This function should not be called multiple times.
343/// Consecutive calls will override the previous hook.
344///
345/// # Safety
346///
347/// This function mutates a thread-local static variable which uses for the hook.
348/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
349pub unsafe fn register_enable_hook(hook: fn()) {
350 enableHook = Some(hook)
351}
352
353/// De-registers the hook when enabling Compiler Interrupts.
354///
355/// This function removes the given hook from [`register_enable_hook`].
356///
357/// # Note
358///
359/// This function is thread-specific, which means it only de-registers
360/// on the thread they called on.
361///
362/// This function should not be called multiple times.
363/// Consecutive calls will do nothing as the hook has already been removed.
364///
365/// # Safety
366///
367/// This function mutates a thread-local static variable which uses for the hook.
368/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
369pub unsafe fn deregister_enable_hook() {
370 enableHook = None
371}
372
373/// Registers a hook when disabling Compiler Interrupts.
374///
375/// This function takes a function pointer to be called before disabling Compiler Interrupts.
376/// Compiler Interrupts can be temporarily disabled by calling [`disable`].
377///
378/// # Note
379///
380/// This function is thread-specific, which means it only registers
381/// on the thread they called on.
382///
383/// This function should not be called multiple times.
384/// Consecutive calls will override the previous hook.
385///
386/// # Safety
387///
388/// This function mutates a thread-local static variable which uses for the hook.
389/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
390pub unsafe fn register_disable_hook(hook: fn()) {
391 disableHook = Some(hook)
392}
393
394/// De-registers the hook when disabling Compiler Interrupts.
395///
396/// This function removes the given hook from [`register_disable_hook`].
397///
398/// # Note
399///
400/// This function is thread-specific, which means it only de-registers
401/// on the thread they called on.
402///
403/// This function should not be called multiple times.
404/// Consecutive calls will do nothing as the hook has already been removed.
405///
406/// # Safety
407///
408/// This function mutates a thread-local static variable which uses for the hook.
409/// Thread unsafety will not be introduced. Rust considers mutating static variable unsafe.
410pub unsafe fn deregister_disable_hook() {
411 disableHook = None
412}
413
414/// Enables the probe instrumentation.
415///
416/// # Note
417///
418/// This function is thread-specific, which means it only enables
419/// on the thread they called on.
420///
421/// # Safety
422///
423/// This function is called outside the normal Rust program.
424#[no_mangle]
425pub unsafe fn instr_enable() {}
426
427/// Disables the probe instrumentation.
428///
429/// # Note
430///
431/// This function is thread-specific, which means it only disables
432/// on the thread they called on.
433///
434/// # Safety
435///
436/// This function is called outside the normal Rust program.
437#[no_mangle]
438pub unsafe fn instr_disable() {}