21
21
//! #[custom_mir(dialect = "built")]
22
22
//! pub fn simple(x: i32) -> i32 {
23
23
//! mir!(
24
- //! let temp1: i32;
25
- //! let temp2: _;
24
+ //! let temp2: i32;
26
25
//!
27
26
//! {
28
- //! temp1 = x;
29
- //! Goto(exit )
27
+ //! let temp1 = x;
28
+ //! Goto(my_second_block )
30
29
//! }
31
30
//!
32
- //! exit = {
31
+ //! my_second_block = {
33
32
//! temp2 = Move(temp1);
34
33
//! RET = temp2;
35
34
//! Return()
38
37
//! }
39
38
//! ```
40
39
//!
41
- //! Hopefully most of this is fairly self-explanatory. Expanding on some notable details:
40
+ //! The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
41
+ //! attribute only works on functions - there is no way to insert custom MIR into the middle of
42
+ //! another function. The `dialect` and `phase` parameters indicate which [version of MIR][dialect
43
+ //! docs] you are inserting here. Generally you'll want to use `#![custom_mir(dialect = "built")]`
44
+ //! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
45
+ //! "runtime", phase = "optimized")] if you don't.
42
46
//!
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
+ //! [dialect docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
52
48
//!
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
+ //! The input to the [`mir!`] macro is:
50
+ //!
51
+ //! - A possibly empty list of local declarations. Locals can also be declared inline on
52
+ //! assignments via `let`. Type inference generally works. Shadowing does not.
53
+ //! - A list of basic blocks. The first of these is the start block and is where execution begins.
54
+ //! All blocks other than the start block need to be given a name, so that they can be referred
55
+ //! to later.
56
+ //! - Each block is a list of semicolon terminated statements, followed by a terminator. The
57
+ //! syntax for the various statements and terminators is designed to be as similar as possible
58
+ //! to the syntax for analogous concepts in native Rust. See below for a list.
59
+ //!
60
+ //! # Examples
61
+ //!
62
+ //! ```rust
63
+ //! #![feature(core_intrinsics, custom_mir)]
64
+ //!
65
+ //! extern crate core;
66
+ //! use core::intrinsics::mir::*;
67
+ //!
68
+ //! #[custom_mir(dialect = "built")]
69
+ //! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
70
+ //! mir!(
71
+ //! {
72
+ //! match c {
73
+ //! true => t,
74
+ //! _ => f,
75
+ //! }
76
+ //! }
77
+ //!
78
+ //! t = {
79
+ //! let temp = a;
80
+ //! Goto(load_and_exit)
81
+ //! }
82
+ //!
83
+ //! f = {
84
+ //! temp = b;
85
+ //! Goto(load_and_exit)
86
+ //! }
87
+ //!
88
+ //! load_and_exit = {
89
+ //! RET = *temp;
90
+ //! Return()
91
+ //! }
92
+ //! )
93
+ //! }
94
+ //!
95
+ //! #[custom_mir(dialect = "built")]
96
+ //! fn unwrap_unchecked<T>(opt: Option<T>) -> T {
97
+ //! mir!({
98
+ //! RET = Move(Field(Variant(opt, 1), 0));
99
+ //! Return()
100
+ //! })
101
+ //! }
102
+ //! ```
103
+ //!
104
+ //! We can also set off compilation failures that happen in sufficiently late stages of the
105
+ //! compiler:
106
+ //!
107
+ //! ```rust,compile_fail
108
+ //! #![feature(core_intrinsics, custom_mir)]
109
+ //!
110
+ //! extern crate core;
111
+ //! use core::intrinsics::mir::*;
112
+ //!
113
+ //! #[custom_mir(dialect = "built")]
114
+ //! fn borrow_error(should_init: bool) -> i32 {
115
+ //! mir!(
116
+ //! let temp: i32;
117
+ //!
118
+ //! {
119
+ //! match should_init {
120
+ //! true => init,
121
+ //! _ => use_temp,
122
+ //! }
123
+ //! }
124
+ //!
125
+ //! init = {
126
+ //! temp = 0;
127
+ //! Goto(use_temp)
128
+ //! }
129
+ //!
130
+ //! use_temp = {
131
+ //! RET = temp;
132
+ //! Return()
133
+ //! }
134
+ //! )
135
+ //! }
136
+ //! ```
137
+ //!
138
+ //! ```text
139
+ //! error[E0381]: used binding is possibly-uninitialized
140
+ //! --> test.rs:24:13
141
+ //! |
142
+ //! 8 | / mir!(
143
+ //! 9 | | let temp: i32;
144
+ //! 10 | |
145
+ //! 11 | | {
146
+ //! ... |
147
+ //! 19 | | temp = 0;
148
+ //! | | -------- binding initialized here in some conditions
149
+ //! ... |
150
+ //! 24 | | RET = temp;
151
+ //! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized
152
+ //! 25 | | Return()
153
+ //! 26 | | }
154
+ //! 27 | | )
155
+ //! | |_____- binding declared here but left uninitialized
156
+ //!
157
+ //! error: aborting due to previous error
158
+ //!
159
+ //! For more information about this error, try `rustc --explain E0381`.
160
+ //! ```
161
+ //!
162
+ //! # Syntax
163
+ //!
164
+ //! The lists below are an exhaustive description of how various MIR constructs can be created.
165
+ //! Anything missing from the list should be assumed to not be supported, PRs welcome.
166
+ //!
167
+ //! #### Locals
168
+ //!
169
+ //! - The `_0` return local can always be accessed via `RET`.
170
+ //! - Arguments can be accessed via their regular name.
171
+ //! - All other locals need to be declared with `let` somewhere and then can be accessed by name.
172
+ //!
173
+ //! #### Places
174
+ //! - Locals implicit convert to places.
175
+ //! - Field accesses, derefs, and indexing work normally.
176
+ //! - Fields in variants can be accessed via the [`Variant`] and [`Field`] associated functions,
177
+ //! see their documentation for details.
178
+ //!
179
+ //! #### Operands
180
+ //! - Places implicitly convert to `Copy` operands.
181
+ //! - `Move` operands can be created via [`Move`].
182
+ //! - Const blocks, literals, named constants, and const params all just work.
183
+ //! - [`Static`] and [`StaticMut`] can be used to create `&T` and `*mut T`s to statics. These are
184
+ //! constants in MIR and the only way to access statics.
185
+ //!
186
+ //! #### Statements
187
+ //! - Assign statements work via normal Rust assignment.
188
+ //! - [`Retag`] statements have an associated function.
189
+ //!
190
+ //! #### Rvalues
191
+ //!
192
+ //! - Operands implicitly convert to `Use` rvalues.
193
+ //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
194
+ //! - [`Discriminant`] has an associated function.
195
+ //!
196
+ //! #### Terminators
197
+ //!
198
+ //! - [`Goto`] and [`Return`] have associated functions.
199
+ //! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
200
+ //! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
201
+ //! otherwise branch.
57
202
//!
58
203
59
204
#![ unstable(
69
214
pub struct BasicBlock ;
70
215
71
216
macro_rules! define {
72
- ( $name: literal, $( $sig: tt) * ) => {
217
+ ( $name: literal, $( # [ $meta : meta ] ) * fn $ ( $sig: tt) * ) => {
73
218
#[ rustc_diagnostic_item = $name]
74
- pub $( $sig) * { panic!( ) }
219
+ $( #[ $meta ] ) *
220
+ pub fn $( $sig) * { panic!( ) }
75
221
}
76
222
}
77
223
@@ -82,8 +228,73 @@ define!("mir_retag_raw", fn RetagRaw<T>(place: T));
82
228
define ! ( "mir_move" , fn Move <T >( place: T ) -> T ) ;
83
229
define ! ( "mir_static" , fn Static <T >( s: T ) -> & ' static T ) ;
84
230
define ! ( "mir_static_mut" , fn StaticMut <T >( s: T ) -> * mut T ) ;
231
+ define ! (
232
+ "mir_discriminant" ,
233
+ /// Gets the discriminant of a place.
234
+ fn Discriminant <T >( place: T ) -> <T as :: core:: marker:: DiscriminantKind >:: Discriminant
235
+ ) ;
236
+ define ! ( "mir_set_discriminant" , fn SetDiscriminant <T >( place: T , index: u32 ) ) ;
237
+ define ! (
238
+ "mir_field" ,
239
+ /// Access the field with the given index of some place.
240
+ ///
241
+ /// This only makes sense to use in conjunction with [`Variant`]. If the type you are looking to
242
+ /// access the field of does not have variants, you can use normal field projection syntax.
243
+ ///
244
+ /// There is no proper way to do a place projection to a variant in Rust, and so these two
245
+ /// functions are a workaround. You can access a field of a variant via `Field(Variant(place,
246
+ /// var_idx), field_idx)`, where `var_idx` and `field_idx` are appropriate literals. Some
247
+ /// caveats:
248
+ ///
249
+ /// - The return type of `Variant` is always `()`. Don't worry about that, the correct MIR will
250
+ /// still be generated.
251
+ /// - In some situations, the return type of `Field` cannot be inferred. You may need to
252
+ /// annotate it on the function in these cases.
253
+ /// - Since `Field` is a function call which is not a place expression, using this on the left
254
+ /// hand side of an expression is rejected by the compiler. [`place!`] is a macro provided to
255
+ /// work around that issue. Wrap the left hand side of an assignment in the macro to convince
256
+ /// the compiler that it's ok.
257
+ ///
258
+ /// # Examples
259
+ ///
260
+ /// ```rust
261
+ /// #![feature(custom_mir, core_intrinsics)]
262
+ ///
263
+ /// extern crate core;
264
+ /// use core::intrinsics::mir::*;
265
+ ///
266
+ /// #[custom_mir(dialect = "built")]
267
+ /// fn unwrap_deref(opt: Option<&i32>) -> i32 {
268
+ /// mir!({
269
+ /// RET = *Field::<&i32>(Variant(opt, 1), 0);
270
+ /// Return()
271
+ /// })
272
+ /// }
273
+ ///
274
+ /// #[custom_mir(dialect = "built")]
275
+ /// fn set(opt: &mut Option<i32>) {
276
+ /// mir!({
277
+ /// place!(Field(Variant(*opt, 1), 0)) = 5;
278
+ /// Return()
279
+ /// })
280
+ /// }
281
+ /// ```
282
+ fn Field <F >( place: ( ) , field: u32 ) -> F
283
+ ) ;
284
+ define ! (
285
+ "mir_variant" ,
286
+ /// Adds a variant projection with the given index to the place.
287
+ ///
288
+ /// See [`Field`] for documentation.
289
+ fn Variant <T >( place: T , index: u32 ) -> ( )
290
+ ) ;
291
+ define ! (
292
+ "mir_make_place" ,
293
+ #[ doc( hidden) ]
294
+ fn __internal_make_place<T >( place: T ) -> * mut T
295
+ ) ;
85
296
86
- /// Convenience macro for generating custom MIR.
297
+ /// Macro for generating custom MIR.
87
298
///
88
299
/// See the module documentation for syntax details. This macro is not magic - it only transforms
89
300
/// your MIR into something that is easier to parse in the compiler.
@@ -139,6 +350,13 @@ pub macro mir {
139
350
} }
140
351
}
141
352
353
+ /// Helper macro that allows you to treat a value expression like a place expression.
354
+ ///
355
+ /// See the documentation on [`Variant`] for why this is necessary and how to use it.
356
+ pub macro place( $e: expr) {
357
+ ( * :: core:: intrinsics:: mir:: __internal_make_place ( $e) )
358
+ }
359
+
142
360
/// Helper macro that extracts the `let` declarations out of a bunch of statements.
143
361
///
144
362
/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
0 commit comments