Skip to content

Commit a982541

Browse files
committed
Support arbitrary let statements in custom mir
1 parent bddad59 commit a982541

File tree

3 files changed

+189
-4
lines changed

3 files changed

+189
-4
lines changed

library/core/src/intrinsics/mir.rs

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,14 @@ pub macro mir {
9090
(
9191
$(let $local_decl:ident $(: $local_decl_ty:ty)? ;)*
9292

93-
$entry_block:block
93+
{
94+
$($entry:tt)*
95+
}
9496

9597
$(
96-
$block_name:ident = $block:block
98+
$block_name:ident = {
99+
$($block:tt)*
100+
}
97101
)*
98102
) => {{
99103
// First, we declare all basic blocks.
@@ -109,15 +113,146 @@ pub macro mir {
109113
let $local_decl $(: $local_decl_ty)? ;
110114
)*
111115

116+
::core::intrinsics::mir::__internal_extract_let!($($entry)*);
117+
$(
118+
::core::intrinsics::mir::__internal_extract_let!($($block)*);
119+
)*
120+
112121
{
113122
// Finally, the contents of the basic blocks
114-
$entry_block;
123+
::core::intrinsics::mir::__internal_remove_let!({
124+
{}
125+
{ $($entry)* }
126+
});
115127
$(
116-
$block;
128+
::core::intrinsics::mir::__internal_remove_let!({
129+
{}
130+
{ $($block)* }
131+
});
117132
)*
118133

119134
RET
120135
}
121136
}
122137
}}
123138
}
139+
140+
/// Helper macro that extracts the `let` declarations out of a bunch of statements.
141+
///
142+
/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
143+
/// statement out of the input, does the appropriate thing with it, and then recursively calls the
144+
/// same macro on the remainder of the input.
145+
#[doc(hidden)]
146+
pub macro __internal_extract_let {
147+
// If it's a `let` like statement, keep the `let`
148+
(
149+
let $var:ident $(: $ty:ty)? = $expr:expr; $($rest:tt)*
150+
) => {
151+
let $var $(: $ty)?;
152+
::core::intrinsics::mir::__internal_extract_let!($($rest)*);
153+
},
154+
// Otherwise, output nothing
155+
(
156+
$stmt:stmt; $($rest:tt)*
157+
) => {
158+
::core::intrinsics::mir::__internal_extract_let!($($rest)*);
159+
},
160+
(
161+
$expr:expr
162+
) => {}
163+
}
164+
165+
/// Helper macro that removes the `let` declarations from a bunch of statements.
166+
///
167+
/// Because expression position macros cannot expand to statements + expressions, we need to be
168+
/// slightly creative here. The general strategy is also statement munching as above, but the output
169+
/// of the macro is "stored" in the subsequent macro invocation. Easiest understood via example:
170+
/// ```text
171+
/// invoke!(
172+
/// {
173+
/// {
174+
/// x = 5;
175+
/// }
176+
/// {
177+
/// let d = e;
178+
/// Call()
179+
/// }
180+
/// }
181+
/// )
182+
/// ```
183+
/// becomes
184+
/// ```text
185+
/// invoke!(
186+
/// {
187+
/// {
188+
/// x = 5;
189+
/// d = e;
190+
/// }
191+
/// {
192+
/// Call()
193+
/// }
194+
/// }
195+
/// )
196+
/// ```
197+
#[doc(hidden)]
198+
pub macro __internal_remove_let {
199+
// If it's a `let` like statement, remove the `let`
200+
(
201+
{
202+
{
203+
$($already_parsed:tt)*
204+
}
205+
{
206+
let $var:ident $(: $ty:ty)? = $expr:expr;
207+
$($rest:tt)*
208+
}
209+
}
210+
) => { ::core::intrinsics::mir::__internal_remove_let!(
211+
{
212+
{
213+
$($already_parsed)*
214+
$var = $expr;
215+
}
216+
{
217+
$($rest)*
218+
}
219+
}
220+
)},
221+
// Otherwise, keep going
222+
(
223+
{
224+
{
225+
$($already_parsed:tt)*
226+
}
227+
{
228+
$stmt:stmt;
229+
$($rest:tt)*
230+
}
231+
}
232+
) => { ::core::intrinsics::mir::__internal_remove_let!(
233+
{
234+
{
235+
$($already_parsed)*
236+
$stmt;
237+
}
238+
{
239+
$($rest)*
240+
}
241+
}
242+
)},
243+
(
244+
{
245+
{
246+
$($already_parsed:tt)*
247+
}
248+
{
249+
$expr:expr
250+
}
251+
}
252+
) => {
253+
{
254+
$($already_parsed)*
255+
$expr
256+
}
257+
},
258+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// MIR for `arbitrary_let` after built
2+
3+
fn arbitrary_let(_1: i32) -> i32 {
4+
let mut _0: i32; // return place in scope 0 at $DIR/arbitrary_let.rs:+0:29: +0:32
5+
let mut _2: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
6+
let mut _3: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
7+
8+
bb0: {
9+
_2 = _1; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
10+
goto -> bb2; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
11+
}
12+
13+
bb1: {
14+
_0 = _3; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
15+
return; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
16+
}
17+
18+
bb2: {
19+
_3 = _2; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
20+
goto -> bb1; // scope 0 at $DIR/arbitrary_let.rs:+0:1: +0:32
21+
}
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![feature(custom_mir, core_intrinsics)]
2+
3+
extern crate core;
4+
use core::intrinsics::mir::*;
5+
use core::ptr::{addr_of, addr_of_mut};
6+
7+
// EMIT_MIR arbitrary_let.arbitrary_let.built.after.mir
8+
#[custom_mir(dialect = "built")]
9+
fn arbitrary_let(x: i32) -> i32 {
10+
mir!(
11+
{
12+
let y = x;
13+
Goto(second)
14+
}
15+
third = {
16+
RET = z;
17+
Return()
18+
}
19+
second = {
20+
let z = y;
21+
Goto(third)
22+
}
23+
)
24+
}
25+
26+
fn main() {
27+
assert_eq!(arbitrary_let(5), 5);
28+
}

0 commit comments

Comments
 (0)