Skip to content

Commit eaf2f26

Browse files
authored
Rollup merge of #105814 - JakobDegen:custom-mir-terms, r=oli-obk
Support call and drop terminators in custom mir The only caveat with this change is that cleanup blocks are not supported. I would like to add them, but it's not quite clear to me what the best way to do that is, so I'll have to think about it some more. r? ``@oli-obk``
2 parents a8ad7f6 + 3d849ae commit eaf2f26

8 files changed

+266
-2
lines changed

compiler/rustc_mir_build/src/build/custom/parse/instruction.rs

+49
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
4242
@call("mir_goto", args) => {
4343
Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
4444
},
45+
@call("mir_unreachable", _args) => {
46+
Ok(TerminatorKind::Unreachable)
47+
},
48+
@call("mir_drop", args) => {
49+
Ok(TerminatorKind::Drop {
50+
place: self.parse_place(args[0])?,
51+
target: self.parse_block(args[1])?,
52+
unwind: None,
53+
})
54+
},
55+
@call("mir_drop_and_replace", args) => {
56+
Ok(TerminatorKind::DropAndReplace {
57+
place: self.parse_place(args[0])?,
58+
value: self.parse_operand(args[1])?,
59+
target: self.parse_block(args[2])?,
60+
unwind: None,
61+
})
62+
},
63+
@call("mir_call", args) => {
64+
let destination = self.parse_place(args[0])?;
65+
let target = self.parse_block(args[1])?;
66+
self.parse_call(args[2], destination, target)
67+
},
4568
ExprKind::Match { scrutinee, arms } => {
4669
let discr = self.parse_operand(*scrutinee)?;
4770
self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t })
@@ -86,6 +109,32 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
86109
Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
87110
}
88111

112+
fn parse_call(
113+
&self,
114+
expr_id: ExprId,
115+
destination: Place<'tcx>,
116+
target: BasicBlock,
117+
) -> PResult<TerminatorKind<'tcx>> {
118+
parse_by_kind!(self, expr_id, _, "function call",
119+
ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => {
120+
let fun = self.parse_operand(*fun)?;
121+
let args = args
122+
.iter()
123+
.map(|arg| self.parse_operand(*arg))
124+
.collect::<PResult<Vec<_>>>()?;
125+
Ok(TerminatorKind::Call {
126+
func: fun,
127+
args,
128+
destination,
129+
target: Some(target),
130+
cleanup: None,
131+
from_hir_call: *from_hir_call,
132+
fn_span: *fn_span,
133+
})
134+
},
135+
)
136+
}
137+
89138
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
90139
parse_by_kind!(self, expr_id, _, "rvalue",
91140
@call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),

library/core/src/intrinsics/mir.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
//! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
4545
//! "runtime", phase = "optimized")] if you don't.
4646
//!
47-
//! [dialect docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
47+
//! [dialect docs]:
48+
//! https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
4849
//!
4950
//! The input to the [`mir!`] macro is:
5051
//!
@@ -99,6 +100,30 @@
99100
//! Return()
100101
//! })
101102
//! }
103+
//!
104+
//! #[custom_mir(dialect = "runtime", phase = "optimized")]
105+
//! fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
106+
//! mir!(
107+
//! let unused;
108+
//! let popped;
109+
//!
110+
//! {
111+
//! Call(unused, pop, Vec::push(v, value))
112+
//! }
113+
//!
114+
//! pop = {
115+
//! Call(popped, drop, Vec::pop(v))
116+
//! }
117+
//!
118+
//! drop = {
119+
//! Drop(popped, ret)
120+
//! }
121+
//!
122+
//! ret = {
123+
//! Return()
124+
//! }
125+
//! )
126+
//! }
102127
//! ```
103128
//!
104129
//! We can also set off compilation failures that happen in sufficiently late stages of the
@@ -195,10 +220,16 @@
195220
//!
196221
//! #### Terminators
197222
//!
198-
//! - [`Goto`] and [`Return`] have associated functions.
223+
//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
224+
//! are no resume and abort terminators, and terminators that might unwind do not have any way to
225+
//! indicate the unwind block.
226+
//!
227+
//! - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] have associated functions.
199228
//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
200229
//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
201230
//! otherwise branch.
231+
//! - [`Call`] has an associated function as well. The third argument of this function is a normal
232+
//! function call expresion, for example `my_other_function(a, 5)`.
202233
//!
203234
204235
#![unstable(
@@ -223,6 +254,10 @@ macro_rules! define {
223254

224255
define!("mir_return", fn Return() -> BasicBlock);
225256
define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
257+
define!("mir_unreachable", fn Unreachable() -> BasicBlock);
258+
define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
259+
define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
260+
define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
226261
define!("mir_retag", fn Retag<T>(place: T));
227262
define!("mir_retag_raw", fn RetagRaw<T>(place: T));
228263
define!("mir_move", fn Move<T>(place: T) -> T);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// MIR for `assert_nonzero` after built
2+
3+
fn assert_nonzero(_1: i32) -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:27
5+
6+
bb0: {
7+
switchInt(_1) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/terminators.rs:+3:13: +6:14
8+
}
9+
10+
bb1: {
11+
unreachable; // scope 0 at $DIR/terminators.rs:+10:13: +10:26
12+
}
13+
14+
bb2: {
15+
return; // scope 0 at $DIR/terminators.rs:+14:13: +14:21
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// MIR for `direct_call` after built
2+
3+
fn direct_call(_1: i32) -> i32 {
4+
let mut _0: i32; // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:30
5+
6+
bb0: {
7+
_0 = ident::<i32>(_1) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:42
8+
// mir::Constant
9+
// + span: $DIR/terminators.rs:15:33: 15:38
10+
// + literal: Const { ty: fn(i32) -> i32 {ident::<i32>}, val: Value(<ZST>) }
11+
}
12+
13+
bb1: {
14+
return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// MIR for `drop_first` after built
2+
3+
fn drop_first(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:59: +0:59
5+
6+
bb0: {
7+
replace(_1 <- move _2) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:49
8+
}
9+
10+
bb1: {
11+
return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// MIR for `drop_second` after built
2+
3+
fn drop_second(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:60: +0:60
5+
6+
bb0: {
7+
drop(_2) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:30
8+
}
9+
10+
bb1: {
11+
return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// MIR for `indirect_call` after built
2+
3+
fn indirect_call(_1: i32, _2: fn(i32) -> i32) -> i32 {
4+
let mut _0: i32; // return place in scope 0 at $DIR/terminators.rs:+0:48: +0:51
5+
6+
bb0: {
7+
_0 = _2(_1) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:38
8+
}
9+
10+
bb1: {
11+
return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#![feature(custom_mir, core_intrinsics)]
2+
3+
extern crate core;
4+
use core::intrinsics::mir::*;
5+
6+
fn ident<T>(t: T) -> T {
7+
t
8+
}
9+
10+
// EMIT_MIR terminators.direct_call.built.after.mir
11+
#[custom_mir(dialect = "built")]
12+
fn direct_call(x: i32) -> i32 {
13+
mir!(
14+
{
15+
Call(RET, retblock, ident(x))
16+
}
17+
18+
retblock = {
19+
Return()
20+
}
21+
)
22+
}
23+
24+
// EMIT_MIR terminators.indirect_call.built.after.mir
25+
#[custom_mir(dialect = "built")]
26+
fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 {
27+
mir!(
28+
{
29+
Call(RET, retblock, f(x))
30+
}
31+
32+
retblock = {
33+
Return()
34+
}
35+
)
36+
}
37+
38+
struct WriteOnDrop<'a>(&'a mut i32, i32);
39+
40+
impl<'a> Drop for WriteOnDrop<'a> {
41+
fn drop(&mut self) {
42+
*self.0 = self.1;
43+
}
44+
}
45+
46+
// EMIT_MIR terminators.drop_first.built.after.mir
47+
#[custom_mir(dialect = "built")]
48+
fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
49+
mir!(
50+
{
51+
DropAndReplace(a, Move(b), retblock)
52+
}
53+
54+
retblock = {
55+
Return()
56+
}
57+
)
58+
}
59+
60+
// EMIT_MIR terminators.drop_second.built.after.mir
61+
#[custom_mir(dialect = "built")]
62+
fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
63+
mir!(
64+
{
65+
Drop(b, retblock)
66+
}
67+
68+
retblock = {
69+
Return()
70+
}
71+
)
72+
}
73+
74+
// EMIT_MIR terminators.assert_nonzero.built.after.mir
75+
#[custom_mir(dialect = "built")]
76+
fn assert_nonzero(a: i32) {
77+
mir!(
78+
{
79+
match a {
80+
0 => unreachable,
81+
_ => retblock
82+
}
83+
}
84+
85+
unreachable = {
86+
Unreachable()
87+
}
88+
89+
retblock = {
90+
Return()
91+
}
92+
)
93+
}
94+
95+
fn main() {
96+
assert_eq!(direct_call(5), 5);
97+
assert_eq!(indirect_call(5, ident), 5);
98+
99+
let mut a = 0;
100+
let mut b = 0;
101+
drop_first(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
102+
assert_eq!((a, b), (1, 0));
103+
104+
let mut a = 0;
105+
let mut b = 0;
106+
drop_second(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
107+
assert_eq!((a, b), (0, 1));
108+
}

0 commit comments

Comments
 (0)