Skip to content

Commit 409f4d2

Browse files
committed
Support common enum operations in custom mir
1 parent c1b27ee commit 409f4d2

File tree

9 files changed

+279
-3
lines changed

9 files changed

+279
-3
lines changed

compiler/rustc_mir_build/src/build/custom/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_index::vec::IndexVec;
2525
use rustc_middle::{
2626
mir::*,
2727
thir::*,
28-
ty::{Ty, TyCtxt},
28+
ty::{ParamEnv, Ty, TyCtxt},
2929
};
3030
use rustc_span::Span;
3131

@@ -78,6 +78,7 @@ pub(super) fn build_custom_mir<'tcx>(
7878

7979
let mut pctxt = ParseCtxt {
8080
tcx,
81+
param_env: tcx.param_env(did),
8182
thir,
8283
source_scope: OUTERMOST_SOURCE_SCOPE,
8384
body: &mut body,
@@ -132,6 +133,7 @@ fn parse_attribute(attr: &Attribute) -> MirPhase {
132133

133134
struct ParseCtxt<'tcx, 'body> {
134135
tcx: TyCtxt<'tcx>,
136+
param_env: ParamEnv<'tcx>,
135137
thir: &'body Thir<'tcx>,
136138
source_scope: SourceScope,
137139

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

+69-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use rustc_middle::mir::interpret::{ConstValue, Scalar};
22
use rustc_middle::{mir::*, thir::*, ty};
3+
use rustc_span::Span;
4+
use rustc_target::abi::VariantIdx;
5+
6+
use crate::build::custom::ParseError;
7+
use crate::build::expr::as_constant::as_constant_inner;
38

49
use super::{parse_by_kind, PResult, ParseCtxt};
510

@@ -12,6 +17,14 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
1217
@call("mir_retag_raw", args) => {
1318
Ok(StatementKind::Retag(RetagKind::Raw, Box::new(self.parse_place(args[0])?)))
1419
},
20+
@call("mir_set_discriminant", args) => {
21+
let place = self.parse_place(args[0])?;
22+
let var = self.parse_integer_literal(args[1])? as u32;
23+
Ok(StatementKind::SetDiscriminant {
24+
place: Box::new(place),
25+
variant_index: VariantIdx::from_u32(var),
26+
})
27+
},
1528
ExprKind::Assign { lhs, rhs } => {
1629
let lhs = self.parse_place(*lhs)?;
1730
let rhs = self.parse_rvalue(*rhs)?;
@@ -21,18 +34,60 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
2134
}
2235

2336
pub fn parse_terminator(&self, expr_id: ExprId) -> PResult<TerminatorKind<'tcx>> {
24-
parse_by_kind!(self, expr_id, _, "terminator",
37+
parse_by_kind!(self, expr_id, expr, "terminator",
2538
@call("mir_return", _args) => {
2639
Ok(TerminatorKind::Return)
2740
},
2841
@call("mir_goto", args) => {
2942
Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
3043
},
44+
ExprKind::Match { scrutinee, arms } => {
45+
let discr = self.parse_operand(*scrutinee)?;
46+
self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t })
47+
},
3148
)
3249
}
3350

51+
fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult<SwitchTargets> {
52+
let Some((otherwise, rest)) = arms.split_last() else {
53+
return Err(ParseError {
54+
span,
55+
item_description: format!("no arms"),
56+
expected: "at least one arm".to_string(),
57+
})
58+
};
59+
60+
let otherwise = &self.thir[*otherwise];
61+
let PatKind::Wild = otherwise.pattern.kind else {
62+
return Err(ParseError {
63+
span: otherwise.span,
64+
item_description: format!("{:?}", otherwise.pattern.kind),
65+
expected: "wildcard pattern".to_string(),
66+
})
67+
};
68+
let otherwise = self.parse_block(otherwise.body)?;
69+
70+
let mut values = Vec::new();
71+
let mut targets = Vec::new();
72+
for arm in rest {
73+
let arm = &self.thir[*arm];
74+
let PatKind::Constant { value } = arm.pattern.kind else {
75+
return Err(ParseError {
76+
span: arm.pattern.span,
77+
item_description: format!("{:?}", arm.pattern.kind),
78+
expected: "constant pattern".to_string(),
79+
})
80+
};
81+
values.push(value.eval_bits(self.tcx, self.param_env, arm.pattern.ty));
82+
targets.push(self.parse_block(arm.body)?);
83+
}
84+
85+
Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
86+
}
87+
3488
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
3589
parse_by_kind!(self, expr_id, _, "rvalue",
90+
@call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
3691
ExprKind::Borrow { borrow_kind, arg } => Ok(
3792
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
3893
),
@@ -55,7 +110,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
55110
| ExprKind::ConstParam { .. }
56111
| ExprKind::ConstBlock { .. } => {
57112
Ok(Operand::Constant(Box::new(
58-
crate::build::expr::as_constant::as_constant_inner(expr, |_| None, self.tcx)
113+
as_constant_inner(expr, |_| None, self.tcx)
59114
)))
60115
},
61116
_ => self.parse_place(expr_id).map(Operand::Copy),
@@ -102,4 +157,16 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
102157
},
103158
)
104159
}
160+
161+
fn parse_integer_literal(&self, expr_id: ExprId) -> PResult<u128> {
162+
parse_by_kind!(self, expr_id, expr, "constant",
163+
ExprKind::Literal { .. }
164+
| ExprKind::NamedConst { .. }
165+
| ExprKind::NonHirLiteral { .. }
166+
| ExprKind::ConstBlock { .. } => Ok({
167+
let value = as_constant_inner(expr, |_| None, self.tcx);
168+
value.literal.eval_bits(self.tcx, self.param_env, value.ty())
169+
}),
170+
)
171+
}
105172
}

library/core/src/intrinsics/mir.rs

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ define!("mir_retag_raw", fn RetagRaw<T>(place: T));
8282
define!("mir_move", fn Move<T>(place: T) -> T);
8383
define!("mir_static", fn Static<T>(s: T) -> &'static T);
8484
define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
85+
define!(
86+
"mir_discriminant",
87+
/// Gets the discriminant of a place.
88+
fn Discriminant<T>(place: T) -> <T as ::core::marker::DiscriminantKind>::Discriminant
89+
);
90+
define!("mir_set_discriminant", fn SetDiscriminant<T>(place: T, index: u32));
8591

8692
/// Convenience macro for generating custom MIR.
8793
///
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#![feature(custom_mir, core_intrinsics)]
2+
3+
extern crate core;
4+
use core::intrinsics::mir::*;
5+
6+
// EMIT_MIR enums.switch_bool.built.after.mir
7+
#[custom_mir(dialect = "built")]
8+
pub fn switch_bool(b: bool) -> u32 {
9+
mir!(
10+
{
11+
match b {
12+
true => t,
13+
false => f,
14+
_ => f,
15+
}
16+
}
17+
18+
t = {
19+
RET = 5;
20+
Return()
21+
}
22+
23+
f = {
24+
RET = 10;
25+
Return()
26+
}
27+
)
28+
}
29+
30+
// EMIT_MIR enums.switch_option.built.after.mir
31+
#[custom_mir(dialect = "built")]
32+
pub fn switch_option(option: Option<()>) -> bool {
33+
mir!(
34+
{
35+
let discr = Discriminant(option);
36+
match discr {
37+
0 => n,
38+
1 => s,
39+
_ => s,
40+
}
41+
}
42+
43+
n = {
44+
RET = false;
45+
Return()
46+
}
47+
48+
s = {
49+
RET = true;
50+
Return()
51+
}
52+
)
53+
}
54+
55+
#[repr(u8)]
56+
enum Bool {
57+
False = 0,
58+
True = 1,
59+
}
60+
61+
// EMIT_MIR enums.switch_option_repr.built.after.mir
62+
#[custom_mir(dialect = "built")]
63+
fn switch_option_repr(option: Bool) -> bool {
64+
mir!(
65+
{
66+
let discr = Discriminant(option);
67+
match discr {
68+
0 => f,
69+
_ => t,
70+
}
71+
}
72+
73+
t = {
74+
RET = true;
75+
Return()
76+
}
77+
78+
f = {
79+
RET = false;
80+
Return()
81+
}
82+
)
83+
}
84+
85+
// EMIT_MIR enums.set_discr.built.after.mir
86+
#[custom_mir(dialect = "runtime", phase = "initial")]
87+
fn set_discr(option: &mut Option<()>) {
88+
mir!({
89+
SetDiscriminant(*option, 0);
90+
Return()
91+
})
92+
}
93+
94+
// EMIT_MIR enums.set_discr_repr.built.after.mir
95+
#[custom_mir(dialect = "runtime", phase = "initial")]
96+
fn set_discr_repr(b: &mut Bool) {
97+
mir!({
98+
SetDiscriminant(*b, 0);
99+
Return()
100+
})
101+
}
102+
103+
fn main() {
104+
assert_eq!(switch_bool(true), 5);
105+
assert_eq!(switch_bool(false), 10);
106+
107+
assert_eq!(switch_option(Some(())), true);
108+
assert_eq!(switch_option(None), false);
109+
110+
assert_eq!(switch_option_repr(Bool::True), true);
111+
assert_eq!(switch_option_repr(Bool::False), false);
112+
113+
let mut opt = Some(());
114+
set_discr(&mut opt);
115+
assert_eq!(opt, None);
116+
117+
let mut b = Bool::True;
118+
set_discr_repr(&mut b);
119+
assert!(matches!(b, Bool::False));
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// MIR for `set_discr` after built
2+
3+
fn set_discr(_1: &mut Option<()>) -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/enums.rs:+0:39: +0:39
5+
6+
bb0: {
7+
discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+2:9: +2:36
8+
return; // scope 0 at $DIR/enums.rs:+3:9: +3:17
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// MIR for `set_discr_repr` after built
2+
3+
fn set_discr_repr(_1: &mut Bool) -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/enums.rs:+0:33: +0:33
5+
6+
bb0: {
7+
discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+2:9: +2:31
8+
return; // scope 0 at $DIR/enums.rs:+3:9: +3:17
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// MIR for `switch_bool` after built
2+
3+
fn switch_bool(_1: bool) -> u32 {
4+
let mut _0: u32; // return place in scope 0 at $DIR/enums.rs:+0:32: +0:35
5+
6+
bb0: {
7+
switchInt(_1) -> [1: bb1, 0: bb2, otherwise: bb2]; // scope 0 at $DIR/enums.rs:+3:13: +7:14
8+
}
9+
10+
bb1: {
11+
_0 = const 5_u32; // scope 0 at $DIR/enums.rs:+11:13: +11:20
12+
return; // scope 0 at $DIR/enums.rs:+12:13: +12:21
13+
}
14+
15+
bb2: {
16+
_0 = const 10_u32; // scope 0 at $DIR/enums.rs:+16:13: +16:21
17+
return; // scope 0 at $DIR/enums.rs:+17:13: +17:21
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// MIR for `switch_option` after built
2+
3+
fn switch_option(_1: Option<()>) -> bool {
4+
let mut _0: bool; // return place in scope 0 at $DIR/enums.rs:+0:45: +0:49
5+
let mut _2: isize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
6+
7+
bb0: {
8+
_2 = discriminant(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
9+
switchInt(_2) -> [0: bb1, 1: bb2, otherwise: bb2]; // scope 0 at $DIR/enums.rs:+4:13: +8:14
10+
}
11+
12+
bb1: {
13+
_0 = const false; // scope 0 at $DIR/enums.rs:+12:13: +12:24
14+
return; // scope 0 at $DIR/enums.rs:+13:13: +13:21
15+
}
16+
17+
bb2: {
18+
_0 = const true; // scope 0 at $DIR/enums.rs:+17:13: +17:23
19+
return; // scope 0 at $DIR/enums.rs:+18:13: +18:21
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// MIR for `switch_option_repr` after built
2+
3+
fn switch_option_repr(_1: Bool) -> bool {
4+
let mut _0: bool; // return place in scope 0 at $DIR/enums.rs:+0:40: +0:44
5+
let mut _2: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
6+
7+
bb0: {
8+
_2 = discriminant(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
9+
switchInt(_2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/enums.rs:+4:13: +7:14
10+
}
11+
12+
bb1: {
13+
_0 = const true; // scope 0 at $DIR/enums.rs:+11:13: +11:23
14+
return; // scope 0 at $DIR/enums.rs:+12:13: +12:21
15+
}
16+
17+
bb2: {
18+
_0 = const false; // scope 0 at $DIR/enums.rs:+16:13: +16:24
19+
return; // scope 0 at $DIR/enums.rs:+17:13: +17:21
20+
}
21+
}

0 commit comments

Comments
 (0)