Auto merge of #33583 - luqmana:tri-bool-mir, r=arielb1
MIR: Don't generate 3-armed boolean switch from match. Fixes #33540. Snippet from issue: ```Rust fn foo(x: bool, y: bool) -> u32 { match (x, y) { (false, _) => 0, (_, false) => 1, (true, true) => 2, } } ``` Generated MIR: ``` fn foo(arg0: bool, arg1: bool) -> u32 { let var0: bool; // "x" in scope 1 at 3bbm.rs:17:8: 17:9 let var1: bool; // "y" in scope 1 at 3bbm.rs:17:17: 17:18 let mut tmp0: (bool, bool); let mut tmp1: bool; let mut tmp2: bool; let mut tmp3: (&'static str, &'static str, u32); let mut tmp4: &'static (&'static str, &'static str, u32); bb0: { var0 = arg0; // scope 1 at 3bbm.rs:17:8: 17:9 var1 = arg1; // scope 1 at 3bbm.rs:17:17: 17:18 tmp1 = var0; // scope 5 at 3bbm.rs:18:12: 18:13 tmp2 = var1; // scope 6 at 3bbm.rs:18:15: 18:16 tmp0 = (tmp1, tmp2); // scope 4 at 3bbm.rs:18:11: 18:17 if((tmp0.0: bool)) -> [true: bb4, false: bb1]; // scope 3 at 3bbm.rs:19:10: 19:15 } bb1: { return = const 0u32; // scope 10 at 3bbm.rs:19:23: 19:24 goto -> bb7; // scope 3 at 3bbm.rs:18:5: 22:6 } bb2: { return = const 1u32; // scope 11 at 3bbm.rs:20:23: 20:24 goto -> bb7; // scope 3 at 3bbm.rs:18:5: 22:6 } bb3: { return = const 2u32; // scope 12 at 3bbm.rs:21:25: 21:26 goto -> bb7; // scope 3 at 3bbm.rs:18:5: 22:6 } bb4: { if((tmp0.1: bool)) -> [true: bb5, false: bb2]; // scope 3 at 3bbm.rs:20:13: 20:18 } bb5: { if((tmp0.0: bool)) -> [true: bb3, false: bb6]; // scope 3 at 3bbm.rs:21:10: 21:14 } bb6: { tmp4 = promoted0; // scope 3 at 3bbm.rs:18:5: 22:6 core::panicking::panic(tmp4); // scope 3 at 3bbm.rs:18:5: 22:6 } bb7: { return; // scope 0 at 3bbm.rs:17:1: 23:2 } } ``` Not sure about this approach. I was also thinking maybe just a standalone pass? cc @arielb1, @nagisa
This commit is contained in:
commit
12d165352c
@ -162,21 +162,53 @@ pub fn perform_test(&mut self,
|
||||
}
|
||||
|
||||
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
|
||||
let otherwise = self.cfg.start_new_block();
|
||||
let targets: Vec<_> =
|
||||
options.iter()
|
||||
.map(|_| self.cfg.start_new_block())
|
||||
.chain(Some(otherwise))
|
||||
.collect();
|
||||
let (targets, term) = match switch_ty.sty {
|
||||
// If we're matching on boolean we can
|
||||
// use the If TerminatorKind instead
|
||||
ty::TyBool => {
|
||||
assert!(options.len() > 0 && options.len() <= 2);
|
||||
|
||||
let (true_bb, else_bb) =
|
||||
(self.cfg.start_new_block(),
|
||||
self.cfg.start_new_block());
|
||||
|
||||
let targets = match &options[0] {
|
||||
&ConstVal::Bool(true) => vec![true_bb, else_bb],
|
||||
&ConstVal::Bool(false) => vec![else_bb, true_bb],
|
||||
v => span_bug!(test.span, "expected boolean value but got {:?}", v)
|
||||
};
|
||||
|
||||
(targets,
|
||||
TerminatorKind::If {
|
||||
cond: Operand::Consume(lvalue.clone()),
|
||||
targets: (true_bb, else_bb)
|
||||
})
|
||||
|
||||
}
|
||||
_ => {
|
||||
// The switch may be inexhaustive so we
|
||||
// add a catch all block
|
||||
let otherwise = self.cfg.start_new_block();
|
||||
let targets: Vec<_> =
|
||||
options.iter()
|
||||
.map(|_| self.cfg.start_new_block())
|
||||
.chain(Some(otherwise))
|
||||
.collect();
|
||||
|
||||
(targets.clone(),
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: lvalue.clone(),
|
||||
switch_ty: switch_ty,
|
||||
values: options.clone(),
|
||||
targets: targets
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
self.cfg.terminate(block,
|
||||
scope_id,
|
||||
test.span,
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: lvalue.clone(),
|
||||
switch_ty: switch_ty,
|
||||
values: options.clone(),
|
||||
targets: targets.clone(),
|
||||
});
|
||||
term);
|
||||
targets
|
||||
}
|
||||
|
||||
|
34
src/test/run-pass/exhaustive-bool-match-sanity.rs
Normal file
34
src/test/run-pass/exhaustive-bool-match-sanity.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Issue #33540
|
||||
// We previously used to generate a 3-armed boolean `SwitchInt` in the
|
||||
// MIR of the function `foo` below. #33583 changed rustc to
|
||||
// generate an `If` terminator instead. This test is to just ensure
|
||||
// sanity in that we generate an if-else chain giving the correct
|
||||
// results.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_mir]
|
||||
fn foo(x: bool, y: bool) -> u32 {
|
||||
match (x, y) {
|
||||
(false, _) => 0,
|
||||
(_, false) => 1,
|
||||
(true, true) => 2
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(foo(false, true), 0);
|
||||
assert_eq!(foo(false, false), 0);
|
||||
assert_eq!(foo(true, false), 1);
|
||||
assert_eq!(foo(true, true), 2);
|
||||
}
|
Loading…
Reference in New Issue
Block a user