Add an InstSimplify for repetitive array expressions
This commit is contained in:
parent
fb546ee09b
commit
a285d202cf
7 changed files with 176 additions and 0 deletions
|
@ -48,6 +48,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
|
|||
ctx.simplify_ref_deref(rvalue);
|
||||
ctx.simplify_ptr_aggregate(rvalue);
|
||||
ctx.simplify_cast(rvalue);
|
||||
ctx.simplify_repeated_aggregate(rvalue);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -68,6 +69,35 @@ struct InstSimplifyContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> InstSimplifyContext<'_, 'tcx> {
|
||||
/// Transform aggregates like [0, 0, 0, 0, 0] into [0; 5].
|
||||
/// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in
|
||||
/// InstSimplify helps unoptimized builds.
|
||||
fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
|
||||
let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else {
|
||||
return;
|
||||
};
|
||||
if fields.len() < 5 {
|
||||
return;
|
||||
}
|
||||
let first = &fields[rustc_abi::FieldIdx::ZERO];
|
||||
let Operand::Constant(first) = first else {
|
||||
return;
|
||||
};
|
||||
let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else {
|
||||
return;
|
||||
};
|
||||
if fields.iter().all(|field| {
|
||||
let Operand::Constant(field) = field else {
|
||||
return false;
|
||||
};
|
||||
let field = field.const_.eval(self.tcx, self.typing_env, field.span);
|
||||
field == Ok(first_val)
|
||||
}) {
|
||||
let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
|
||||
*rvalue = Rvalue::Repeat(Operand::Constant(first.clone()), len);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform boolean comparisons into logical operations.
|
||||
fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) {
|
||||
match rvalue {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
- // MIR for `const_items` before InstSimplify-after-simplifycfg
|
||||
+ // MIR for `const_items` after InstSimplify-after-simplifycfg
|
||||
|
||||
fn const_items() -> [u8; 5] {
|
||||
let mut _0: [u8; 5];
|
||||
|
||||
bb0: {
|
||||
- _0 = [const const_items::A, const const_items::B, const const_items::C, const const_items::D, const const_items::E];
|
||||
+ _0 = [const const_items::A; 5];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
- // MIR for `equal_referents` before InstSimplify-after-simplifycfg
|
||||
+ // MIR for `equal_referents` after InstSimplify-after-simplifycfg
|
||||
|
||||
fn equal_referents() -> [&u8; 5] {
|
||||
let mut _0: [&u8; 5];
|
||||
|
||||
bb0: {
|
||||
_0 = [const equal_referents::A, const equal_referents::B, const equal_referents::C, const equal_referents::D, const equal_referents::E];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
- // MIR for `literals` before InstSimplify-after-simplifycfg
|
||||
+ // MIR for `literals` after InstSimplify-after-simplifycfg
|
||||
|
||||
fn literals() -> [u8; 5] {
|
||||
let mut _0: [u8; 5];
|
||||
|
||||
bb0: {
|
||||
- _0 = [const 0_u8, const 0_u8, const 0_u8, const 0_u8, const 0_u8];
|
||||
+ _0 = [const 0_u8; 5];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
- // MIR for `local` before InstSimplify-after-simplifycfg
|
||||
+ // MIR for `local` after InstSimplify-after-simplifycfg
|
||||
|
||||
fn local() -> [u8; 5] {
|
||||
let mut _0: [u8; 5];
|
||||
let _1: u8;
|
||||
let mut _2: u8;
|
||||
let mut _3: u8;
|
||||
let mut _4: u8;
|
||||
let mut _5: u8;
|
||||
let mut _6: u8;
|
||||
scope 1 {
|
||||
debug val => _1;
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = const 0_u8;
|
||||
StorageLive(_2);
|
||||
_2 = copy _1;
|
||||
StorageLive(_3);
|
||||
_3 = copy _1;
|
||||
StorageLive(_4);
|
||||
_4 = copy _1;
|
||||
StorageLive(_5);
|
||||
_5 = copy _1;
|
||||
StorageLive(_6);
|
||||
_6 = copy _1;
|
||||
_0 = [move _2, move _3, move _4, move _5, move _6];
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
56
tests/mir-opt/instsimplify/aggregate_array.rs
Normal file
56
tests/mir-opt/instsimplify/aggregate_array.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
//@ test-mir-pass: InstSimplify-after-simplifycfg
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// This is the easy case, and the most plausible to run into in real code.
|
||||
// EMIT_MIR aggregate_array.literals.InstSimplify-after-simplifycfg.diff
|
||||
pub fn literals() -> [u8; 5] {
|
||||
// CHECK-LABEL: fn literals(
|
||||
// CHECK: _0 = [const 0_u8; 5];
|
||||
[0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
// Check that hiding the const value behind a const item doesn't prevent the optimization
|
||||
// EMIT_MIR aggregate_array.const_items.InstSimplify-after-simplifycfg.diff
|
||||
pub fn const_items() -> [u8; 5] {
|
||||
const A: u8 = 0;
|
||||
const B: u8 = 0;
|
||||
const C: u8 = 0;
|
||||
const D: u8 = 0;
|
||||
const E: u8 = 0;
|
||||
|
||||
// CHECK-LABEL: fn const_items(
|
||||
// CHECK: _0 = [const const_items::A; 5];
|
||||
[A, B, C, D, E]
|
||||
}
|
||||
|
||||
// EMIT_MIR aggregate_array.strs.InstSimplify-after-simplifycfg.diff
|
||||
pub fn strs() -> [&'static str; 5] {
|
||||
// CHECK-LABEL: fn strs(
|
||||
// CHECK: _0 = [const "a"; 5];
|
||||
["a", "a", "a", "a", "a"]
|
||||
}
|
||||
|
||||
// InstSimplify isn't able to see through the move operands, but GVN can.
|
||||
// EMIT_MIR aggregate_array.local.InstSimplify-after-simplifycfg.diff
|
||||
pub fn local() -> [u8; 5] {
|
||||
// CHECK-LABEL: fn local(
|
||||
// CHECK: _0 = [move _2, move _3, move _4, move _5, move _6];
|
||||
let val = 0;
|
||||
[val, val, val, val, val]
|
||||
}
|
||||
|
||||
// All of these consts refer to the same value, but the addresses are all different.
|
||||
// It would be wrong to apply the optimization here.
|
||||
// EMIT_MIR aggregate_array.equal_referents.InstSimplify-after-simplifycfg.diff
|
||||
pub fn equal_referents() -> [&'static u8; 5] {
|
||||
const DATA: &[u8] = &[0, 0, 0, 0, 0];
|
||||
const A: &u8 = &DATA[0];
|
||||
const B: &u8 = &DATA[1];
|
||||
const C: &u8 = &DATA[2];
|
||||
const D: &u8 = &DATA[3];
|
||||
const E: &u8 = &DATA[4];
|
||||
|
||||
// CHECK-LABEL: fn equal_referents(
|
||||
// CHECK: _0 = [const equal_referents::A, const equal_referents::B, const equal_referents::C, const equal_referents::D, const equal_referents::E];
|
||||
[A, B, C, D, E]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
- // MIR for `strs` before InstSimplify-after-simplifycfg
|
||||
+ // MIR for `strs` after InstSimplify-after-simplifycfg
|
||||
|
||||
fn strs() -> [&str; 5] {
|
||||
let mut _0: [&str; 5];
|
||||
|
||||
bb0: {
|
||||
- _0 = [const "a", const "a", const "a", const "a", const "a"];
|
||||
+ _0 = [const "a"; 5];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue