Skip to content

Commit aca1bc5

Browse files
committed
Add documentation for custom mir
1 parent e598394 commit aca1bc5

File tree

1 file changed

+224
-36
lines changed
  • library/core/src/intrinsics

1 file changed

+224
-36
lines changed

library/core/src/intrinsics/mir.rs

+224-36
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121
//! #[custom_mir(dialect = "built")]
2222
//! pub fn simple(x: i32) -> i32 {
2323
//! mir!(
24-
//! let temp1: i32;
25-
//! let temp2: _;
24+
//! let temp2: i32;
2625
//!
2726
//! {
28-
//! temp1 = x;
27+
//! let temp1 = x;
2928
//! Goto(exit)
3029
//! }
3130
//!
@@ -38,22 +37,166 @@
3837
//! }
3938
//! ```
4039
//!
41-
//! Hopefully most of this is fairly self-explanatory. Expanding on some notable details:
40+
//! Hopefully the syntax is fairly self-explanatory to anyone familiar with MIR. The `custom_mir`
41+
//! attribute tells the compiler to treat the function as being custom MIR. This attribute only
42+
//! works on functions - there is no way to insert custom MIR into the middle of another function.
43+
//! The `dialect` and `phase` parameters indicate which version of MIR you are inserting here. This
44+
//! will normally be the phase that corresponds to the thing you are trying to test. The phase can
45+
//! be omitted for dialects that have just one.
4246
//!
43-
//! - The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
44-
//! attribute only works on functions - there is no way to insert custom MIR into the middle of
45-
//! another function.
46-
//! - The `dialect` and `phase` parameters indicate which version of MIR you are inserting here.
47-
//! This will normally be the phase that corresponds to the thing you are trying to test. The
48-
//! phase can be omitted for dialects that have just one.
49-
//! - You should define your function signature like you normally would. Externally, this function
50-
//! can be called like any other function.
51-
//! - Type inference works - you don't have to spell out the type of all of your locals.
47+
//! The input to the [`mir!`] macro is:
5248
//!
53-
//! For now, all statements and terminators are parsed from nested invocations of the special
54-
//! functions provided in this module. We additionally want to (but do not yet) support more
55-
//! "normal" Rust syntax in places where it makes sense. Also, most kinds of instructions are not
56-
//! supported yet.
49+
//! - A possibly empty list of local declarations. Locals can also be declared inline on
50+
//! assignments via `let`. Type inference generally works. Shadowing does not.
51+
//! - A list of basic blocks. The first of these is the start block and is where execution begins.
52+
//! All blocks other than the start block need to be given a name, so that they can be referred
53+
//! to later.
54+
//! - Each block is a list of semicolon terminated statements, followed by a terminator. The
55+
//! syntax for the various statements and terminators is designed to be as similar as possible
56+
//! to the syntax for analogous concepts in native Rust. See below for a list.
57+
//!
58+
//! # Examples
59+
//!
60+
//! ```rust
61+
//! #![feature(core_intrinsics, custom_mir)]
62+
//!
63+
//! extern crate core;
64+
//! use core::intrinsics::mir::*;
65+
//!
66+
//! #[custom_mir(dialect = "built")]
67+
//! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
68+
//! mir!(
69+
//! {
70+
//! match c {
71+
//! true => t,
72+
//! _ => f,
73+
//! }
74+
//! }
75+
//!
76+
//! t = {
77+
//! let temp = a;
78+
//! Goto(load_and_exit)
79+
//! }
80+
//!
81+
//! f = {
82+
//! temp = b;
83+
//! Goto(load_and_exit)
84+
//! }
85+
//!
86+
//! load_and_exit = {
87+
//! RET = *temp;
88+
//! Return()
89+
//! }
90+
//! )
91+
//! }
92+
//!
93+
//! #[custom_mir(dialect = "built")]
94+
//! fn unwrap_unchecked<T>(opt: Option<T>) -> T {
95+
//! mir!({
96+
//! RET = Move(Field(Variant(opt, 1), 0));
97+
//! Return()
98+
//! })
99+
//! }
100+
//! ```
101+
//!
102+
//! We can also set off compilation failures that happen in sufficiently late stages of the
103+
//! compiler:
104+
//!
105+
//! ```rust,compile_fail
106+
//! #![feature(core_intrinsics, custom_mir)]
107+
//!
108+
//! extern crate core;
109+
//! use core::intrinsics::mir::*;
110+
//!
111+
//! #[custom_mir(dialect = "built")]
112+
//! fn borrow_error(should_init: bool) -> i32 {
113+
//! mir!(
114+
//! let temp: i32;
115+
//!
116+
//! {
117+
//! match should_init {
118+
//! true => init,
119+
//! _ => use_temp,
120+
//! }
121+
//! }
122+
//!
123+
//! init = {
124+
//! temp = 0;
125+
//! Goto(use_temp)
126+
//! }
127+
//!
128+
//! use_temp = {
129+
//! RET = temp;
130+
//! Return()
131+
//! }
132+
//! )
133+
//! }
134+
//! ```
135+
//!
136+
//! ```text
137+
//! error[E0381]: used binding is possibly-uninitialized
138+
//! --> test.rs:24:13
139+
//! |
140+
//! 8 | / mir!(
141+
//! 9 | | let temp: i32;
142+
//! 10 | |
143+
//! 11 | | {
144+
//! ... |
145+
//! 19 | | temp = 0;
146+
//! | | -------- binding initialized here in some conditions
147+
//! ... |
148+
//! 24 | | RET = temp;
149+
//! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized
150+
//! 25 | | Return()
151+
//! 26 | | }
152+
//! 27 | | )
153+
//! | |_____- binding declared here but left uninitialized
154+
//!
155+
//! error: aborting due to previous error
156+
//!
157+
//! For more information about this error, try `rustc --explain E0381`.
158+
//! ```
159+
//!
160+
//! # Syntax
161+
//!
162+
//! The lists below are an exahustive description of how various MIR constructs can be created.
163+
//! Anything missing from the list should be assumed to not be supported, PRs welcome.
164+
//!
165+
//! #### Locals
166+
//!
167+
//! - The `_0` return local can always be accessed via `RET`.
168+
//! - Arguments can be accessed via their regular name.
169+
//! - All other locals need to be declared with `let` somewhere and then can be accessed by name.
170+
//!
171+
//! #### Places
172+
//! - Locals implicit convert to places.
173+
//! - Field accesses, derefs, and indexing work normally.
174+
//! - Fields in variants can be accessed via the [`Variant`] and [`Field`] methods, see their
175+
//! documentation for details.
176+
//!
177+
//! #### Operands
178+
//! - Places implicitly convert to `Copy` operands.
179+
//! - `Move` operands can be created via [`Move`].
180+
//! - Const blocks, literals, named constants, and const params all just work.
181+
//! - [`Static`] and [`StaticMut`] can be used to create `&T` and `*mut T`s to statics. These are
182+
//! constants in MIR and the only way to access statics.
183+
//!
184+
//! #### Statements
185+
//! - Assign statements work via normal Rust assignment.
186+
//! - [`Retag`] statements have an associated function.
187+
//!
188+
//! #### Rvalues
189+
//!
190+
//! - Operands implicitly convert to `Use` rvalues.
191+
//! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
192+
//! - [`Discriminant`] has an associated function.
193+
//!
194+
//! #### Terminators
195+
//!
196+
//! - [`Goto`] and [`Return`] have associated functions.
197+
//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
198+
//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
199+
//! otherwise branch.
57200
//!
58201
59202
#![unstable(
@@ -69,9 +212,10 @@
69212
pub struct BasicBlock;
70213

71214
macro_rules! define {
72-
($name:literal, $($sig:tt)*) => {
215+
($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => {
73216
#[rustc_diagnostic_item = $name]
74-
pub $($sig)* { panic!() }
217+
$( #[ $meta ] )*
218+
pub fn $($sig)* { panic!() }
75219
}
76220
}
77221

@@ -88,11 +232,67 @@ define!(
88232
fn Discriminant<T>(place: T) -> <T as ::core::marker::DiscriminantKind>::Discriminant
89233
);
90234
define!("mir_set_discriminant", fn SetDiscriminant<T>(place: T, index: u32));
91-
define!("mir_field", fn Field<F>(place: (), field: u32) -> F);
92-
define!("mir_variant", fn Variant<T>(place: T, index: u32) -> ());
93-
define!("mir_make_place", fn __internal_make_place<T>(place: T) -> *mut T);
235+
define!(
236+
"mir_field",
237+
/// Access the field with the given index of some place.
238+
///
239+
/// This only makes sense to use in conjunction with [`Variant`]. If the type you are looking to
240+
/// access the field of does not have variants, you can use normal field projection syntax.
241+
///
242+
/// There is no proper way to do a place projection to a variant in Rust, and so these two
243+
/// functions are a workaround. You can access a field of a variant via `Field(Variant(place,
244+
/// var_idx), field_idx)`, where `var_idx` and `field_idx` are appropriate literals. Some
245+
/// caveats:
246+
///
247+
/// - The return type of `Variant` is always `()`. Don't worry about that, the correct MIR will
248+
/// still be generated.
249+
/// - In some situations, the return type of `Field` cannot be inferred. You may need to
250+
/// annotate it on the function in these cases.
251+
/// - Since `Field` is a function call which is not a place expression, using this on the left
252+
/// hand side of an expression is rejected by the compiler. [`place!`] is a macro provided to
253+
/// work around that issue. Wrap the left hand side of an assignment in the macro to convince
254+
/// the compiler that it's ok.
255+
///
256+
/// # Examples
257+
///
258+
/// ```rust
259+
/// #![feature(custom_mir, core_intrinsics)]
260+
///
261+
/// extern crate core;
262+
/// use core::intrinsics::mir::*;
263+
///
264+
/// #[custom_mir(dialect = "built")]
265+
/// fn unwrap_deref(opt: Option<&i32>) -> i32 {
266+
/// mir!({
267+
/// RET = *Field::<&i32>(Variant(opt, 1), 0);
268+
/// Return()
269+
/// })
270+
/// }
271+
///
272+
/// #[custom_mir(dialect = "built")]
273+
/// fn set(opt: &mut Option<i32>) {
274+
/// mir!({
275+
/// place!(Field(Variant(*opt, 1), 0)) = 5;
276+
/// Return()
277+
/// })
278+
/// }
279+
/// ```
280+
fn Field<F>(place: (), field: u32) -> F
281+
);
282+
define!(
283+
"mir_variant",
284+
/// Adds a variant projection with the given index to the place.
285+
///
286+
/// See [`Field`] for documentation.
287+
fn Variant<T>(place: T, index: u32) -> ()
288+
);
289+
define!(
290+
"mir_make_place",
291+
#[doc(hidden)]
292+
fn __internal_make_place<T>(place: T) -> *mut T
293+
);
94294

95-
/// Convenience macro for generating custom MIR.
295+
/// Macro for generating custom MIR.
96296
///
97297
/// See the module documentation for syntax details. This macro is not magic - it only transforms
98298
/// your MIR into something that is easier to parse in the compiler.
@@ -150,19 +350,7 @@ pub macro mir {
150350

151351
/// Helper macro that allows you to treat a value expression like a place expression.
152352
///
153-
/// This is necessary in combination with the [`Field`] and [`Variant`] methods. Specifically,
154-
/// something like this won't compile on its own, reporting an error about not being able to assign
155-
/// to such an expression:
156-
///
157-
/// ```rust,ignore(syntax-highlighting-only)
158-
/// Field(something, 0) = 5;
159-
/// ```
160-
///
161-
/// Instead, you'll need to write
162-
///
163-
/// ```rust,ignore(syntax-highlighting-only)
164-
/// place!(Field(something, 0)) = 5;
165-
/// ```
353+
/// See the documentation on [`Variant`] for why this is necessary and how to use it.
166354
pub macro place($e:expr) {
167355
(*::core::intrinsics::mir::__internal_make_place($e))
168356
}

0 commit comments

Comments
 (0)