summaryrefslogtreecommitdiffstats
path: root/library/core/src/intrinsics/mir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/core/src/intrinsics/mir.rs')
-rw-r--r--library/core/src/intrinsics/mir.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
new file mode 100644
index 000000000..8ba1c1228
--- /dev/null
+++ b/library/core/src/intrinsics/mir.rs
@@ -0,0 +1,289 @@
+//! Rustc internal tooling for hand-writing MIR.
+//!
+//! If for some reasons you are not writing rustc tests and have found yourself considering using
+//! this feature, turn back. This is *exceptionally* unstable. There is no attempt at all to make
+//! anything work besides those things which the rustc test suite happened to need. If you make a
+//! typo you'll probably ICE. Really, this is not the solution to your problems. Consider instead
+//! supporting the [stable MIR project group](https://github.com/rust-lang/project-stable-mir).
+//!
+//! The documentation for this module describes how to use this feature. If you are interested in
+//! hacking on the implementation, most of that documentation lives at
+//! `rustc_mir_building/src/build/custom/mod.rs`.
+//!
+//! Typical usage will look like this:
+//!
+//! ```rust
+//! #![feature(core_intrinsics, custom_mir)]
+//!
+//! extern crate core;
+//! use core::intrinsics::mir::*;
+//!
+//! #[custom_mir(dialect = "built")]
+//! pub fn simple(x: i32) -> i32 {
+//! mir!(
+//! let temp1: i32;
+//! let temp2: _;
+//!
+//! {
+//! temp1 = x;
+//! Goto(exit)
+//! }
+//!
+//! exit = {
+//! temp2 = Move(temp1);
+//! RET = temp2;
+//! Return()
+//! }
+//! )
+//! }
+//! ```
+//!
+//! Hopefully most of this is fairly self-explanatory. Expanding on some notable details:
+//!
+//! - The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
+//! attribute only works on functions - there is no way to insert custom MIR into the middle of
+//! another function.
+//! - The `dialect` and `phase` parameters indicate which version of MIR you are inserting here.
+//! This will normally be the phase that corresponds to the thing you are trying to test. The
+//! phase can be omitted for dialects that have just one.
+//! - You should define your function signature like you normally would. Externally, this function
+//! can be called like any other function.
+//! - Type inference works - you don't have to spell out the type of all of your locals.
+//!
+//! For now, all statements and terminators are parsed from nested invocations of the special
+//! functions provided in this module. We additionally want to (but do not yet) support more
+//! "normal" Rust syntax in places where it makes sense. Also, most kinds of instructions are not
+//! supported yet.
+//!
+
+#![unstable(
+ feature = "custom_mir",
+ reason = "MIR is an implementation detail and extremely unstable",
+ issue = "none"
+)]
+#![allow(unused_variables, non_snake_case, missing_debug_implementations)]
+
+/// Type representing basic blocks.
+///
+/// All terminators will have this type as a return type. It helps achieve some type safety.
+pub struct BasicBlock;
+
+macro_rules! define {
+ ($name:literal, $($sig:tt)*) => {
+ #[rustc_diagnostic_item = $name]
+ pub $($sig)* { panic!() }
+ }
+}
+
+define!("mir_return", fn Return() -> BasicBlock);
+define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
+define!("mir_retag", fn Retag<T>(place: T));
+define!("mir_retag_raw", fn RetagRaw<T>(place: T));
+define!("mir_move", fn Move<T>(place: T) -> T);
+define!("mir_static", fn Static<T>(s: T) -> &'static T);
+define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
+
+/// Convenience macro for generating custom MIR.
+///
+/// See the module documentation for syntax details. This macro is not magic - it only transforms
+/// your MIR into something that is easier to parse in the compiler.
+#[rustc_macro_transparency = "transparent"]
+pub macro mir {
+ (
+ $(let $local_decl:ident $(: $local_decl_ty:ty)? ;)*
+
+ {
+ $($entry:tt)*
+ }
+
+ $(
+ $block_name:ident = {
+ $($block:tt)*
+ }
+ )*
+ ) => {{
+ // First, we declare all basic blocks.
+ $(
+ let $block_name: ::core::intrinsics::mir::BasicBlock;
+ )*
+
+ {
+ // Now all locals
+ #[allow(non_snake_case)]
+ let RET;
+ $(
+ let $local_decl $(: $local_decl_ty)? ;
+ )*
+
+ ::core::intrinsics::mir::__internal_extract_let!($($entry)*);
+ $(
+ ::core::intrinsics::mir::__internal_extract_let!($($block)*);
+ )*
+
+ {
+ // Finally, the contents of the basic blocks
+ ::core::intrinsics::mir::__internal_remove_let!({
+ {}
+ { $($entry)* }
+ });
+ $(
+ ::core::intrinsics::mir::__internal_remove_let!({
+ {}
+ { $($block)* }
+ });
+ )*
+
+ RET
+ }
+ }
+ }}
+}
+
+/// Helper macro that extracts the `let` declarations out of a bunch of statements.
+///
+/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
+/// statement out of the input, does the appropriate thing with it, and then recursively calls the
+/// same macro on the remainder of the input.
+#[doc(hidden)]
+pub macro __internal_extract_let {
+ // If it's a `let` like statement, keep the `let`
+ (
+ let $var:ident $(: $ty:ty)? = $expr:expr; $($rest:tt)*
+ ) => {
+ let $var $(: $ty)?;
+ ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+ },
+ // Due to #86730, we have to handle const blocks separately
+ (
+ let $var:ident $(: $ty:ty)? = const $block:block; $($rest:tt)*
+ ) => {
+ let $var $(: $ty)?;
+ ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+ },
+ // Otherwise, output nothing
+ (
+ $stmt:stmt; $($rest:tt)*
+ ) => {
+ ::core::intrinsics::mir::__internal_extract_let!($($rest)*);
+ },
+ (
+ $expr:expr
+ ) => {}
+}
+
+/// Helper macro that removes the `let` declarations from a bunch of statements.
+///
+/// Because expression position macros cannot expand to statements + expressions, we need to be
+/// slightly creative here. The general strategy is also statement munching as above, but the output
+/// of the macro is "stored" in the subsequent macro invocation. Easiest understood via example:
+/// ```text
+/// invoke!(
+/// {
+/// {
+/// x = 5;
+/// }
+/// {
+/// let d = e;
+/// Call()
+/// }
+/// }
+/// )
+/// ```
+/// becomes
+/// ```text
+/// invoke!(
+/// {
+/// {
+/// x = 5;
+/// d = e;
+/// }
+/// {
+/// Call()
+/// }
+/// }
+/// )
+/// ```
+#[doc(hidden)]
+pub macro __internal_remove_let {
+ // If it's a `let` like statement, remove the `let`
+ (
+ {
+ {
+ $($already_parsed:tt)*
+ }
+ {
+ let $var:ident $(: $ty:ty)? = $expr:expr;
+ $($rest:tt)*
+ }
+ }
+ ) => { ::core::intrinsics::mir::__internal_remove_let!(
+ {
+ {
+ $($already_parsed)*
+ $var = $expr;
+ }
+ {
+ $($rest)*
+ }
+ }
+ )},
+ // Due to #86730 , we have to handle const blocks separately
+ (
+ {
+ {
+ $($already_parsed:tt)*
+ }
+ {
+ let $var:ident $(: $ty:ty)? = const $block:block;
+ $($rest:tt)*
+ }
+ }
+ ) => { ::core::intrinsics::mir::__internal_remove_let!(
+ {
+ {
+ $($already_parsed)*
+ $var = const $block;
+ }
+ {
+ $($rest)*
+ }
+ }
+ )},
+ // Otherwise, keep going
+ (
+ {
+ {
+ $($already_parsed:tt)*
+ }
+ {
+ $stmt:stmt;
+ $($rest:tt)*
+ }
+ }
+ ) => { ::core::intrinsics::mir::__internal_remove_let!(
+ {
+ {
+ $($already_parsed)*
+ $stmt;
+ }
+ {
+ $($rest)*
+ }
+ }
+ )},
+ (
+ {
+ {
+ $($already_parsed:tt)*
+ }
+ {
+ $expr:expr
+ }
+ }
+ ) => {
+ {
+ $($already_parsed)*
+ $expr
+ }
+ },
+}