Skip to content

Commit 3837576

Browse files
committed
GVN: Track known value ranges through assert and switchInt.
1 parent e951f47 commit 3837576

18 files changed

+486
-67
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 145 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,16 @@ use std::hash::{Hash, Hasher};
9090
use either::Either;
9191
use hashbrown::hash_table::{Entry, HashTable};
9292
use itertools::Itertools as _;
93-
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
93+
use rustc_abi::{
94+
self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx, WrappingRange,
95+
};
9496
use rustc_arena::DroplessArena;
9597
use rustc_const_eval::const_eval::DummyMachine;
9698
use rustc_const_eval::interpret::{
9799
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
98100
intern_const_alloc_for_constprop,
99101
};
100-
use rustc_data_structures::fx::FxHasher;
102+
use rustc_data_structures::fx::{FxHashMap, FxHasher};
101103
use rustc_data_structures::graph::dominators::Dominators;
102104
use rustc_hir::def::DefKind;
103105
use rustc_index::bit_set::DenseBitSet;
@@ -130,10 +132,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
130132
// Clone dominators because we need them while mutating the body.
131133
let dominators = body.basic_blocks.dominators().clone();
132134
let maybe_loop_headers = loops::maybe_loop_headers(body);
135+
let predecessors = body.basic_blocks.predecessors();
136+
let mut unique_predecessors = DenseBitSet::new_empty(body.basic_blocks.len());
137+
for (bb, _) in body.basic_blocks.iter_enumerated() {
138+
if predecessors[bb].len() == 1 {
139+
unique_predecessors.insert(bb);
140+
}
141+
}
133142

134143
let arena = DroplessArena::default();
135-
let mut state =
136-
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
144+
let mut state = VnState::new(
145+
tcx,
146+
body,
147+
typing_env,
148+
&ssa,
149+
dominators,
150+
&body.local_decls,
151+
&arena,
152+
unique_predecessors,
153+
);
137154

138155
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
139156
let opaque = state.new_opaque(body.local_decls[local].ty);
@@ -380,6 +397,10 @@ struct VnState<'body, 'a, 'tcx> {
380397
dominators: Dominators<BasicBlock>,
381398
reused_locals: DenseBitSet<Local>,
382399
arena: &'a DroplessArena,
400+
/// Known ranges at each locations.
401+
ranges: IndexVec<VnIndex, Vec<(Location, WrappingRange)>>,
402+
/// Determines if the basic block has a single unique predecessor.
403+
unique_predecessors: DenseBitSet<BasicBlock>,
383404
}
384405

385406
impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
@@ -391,6 +412,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
391412
dominators: Dominators<BasicBlock>,
392413
local_decls: &'body LocalDecls<'tcx>,
393414
arena: &'a DroplessArena,
415+
unique_predecessors: DenseBitSet<BasicBlock>,
394416
) -> Self {
395417
// Compute a rough estimate of the number of values in the body from the number of
396418
// statements. This is meant to reduce the number of allocations, but it's all right if
@@ -413,6 +435,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
413435
dominators,
414436
reused_locals: DenseBitSet::new_empty(local_decls.len()),
415437
arena,
438+
ranges: IndexVec::with_capacity(num_values),
439+
unique_predecessors,
416440
}
417441
}
418442

@@ -430,6 +454,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
430454
debug_assert_eq!(index, _index);
431455
let _index = self.rev_locals.push(SmallVec::new());
432456
debug_assert_eq!(index, _index);
457+
let _index = self.ranges.push(Vec::new());
458+
debug_assert_eq!(index, _index);
433459
index
434460
}
435461

@@ -442,6 +468,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
442468
debug_assert_eq!(index, _index);
443469
let _index = self.rev_locals.push(SmallVec::new());
444470
debug_assert_eq!(index, _index);
471+
let _index = self.ranges.push(Vec::new());
472+
debug_assert_eq!(index, _index);
445473
}
446474
index
447475
}
@@ -523,6 +551,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
523551
self.rev_locals[value].push(local);
524552
}
525553

554+
/// Create a new known range at the location.
555+
fn insert_range(&mut self, value: VnIndex, location: Location, range: WrappingRange) {
556+
self.ranges[value].push((location, range));
557+
}
558+
559+
/// Get the known range at the location.
560+
fn get_range(&self, value: VnIndex, location: Location) -> Option<WrappingRange> {
561+
// FIXME: This should use the intersection of all valid ranges.
562+
let (_, range) = self.ranges[value]
563+
.iter()
564+
.find(|(range_loc, _)| range_loc.dominates(location, &self.dominators))?;
565+
Some(*range)
566+
}
567+
526568
fn insert_bool(&mut self, flag: bool) -> VnIndex {
527569
// Booleans are deterministic.
528570
let value = Const::from_bool(self.tcx, flag);
@@ -1011,7 +1053,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10111053
Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
10121054
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
10131055
let value = self.simplify_place_value(place, location)?;
1014-
if let Some(const_) = self.try_as_constant(value) {
1056+
if let Some(const_) = self.try_as_constant(value, location) {
10151057
*operand = Operand::Constant(Box::new(const_));
10161058
} else if let Value::RuntimeChecks(c) = self.get(value) {
10171059
*operand = Operand::RuntimeChecks(c);
@@ -1782,7 +1824,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17821824
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17831825
/// returns that result as an [`Operand`].
17841826
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1785-
if let Some(const_) = self.try_as_constant(index) {
1827+
if let Some(const_) = self.try_as_constant(index, location) {
17861828
Some(Operand::Constant(Box::new(const_)))
17871829
} else if let Value::RuntimeChecks(c) = self.get(index) {
17881830
Some(Operand::RuntimeChecks(c))
@@ -1795,7 +1837,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17951837
}
17961838

17971839
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1798-
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
1840+
fn try_as_constant(
1841+
&mut self,
1842+
index: VnIndex,
1843+
location: Location,
1844+
) -> Option<ConstOperand<'tcx>> {
17991845
// This was already constant in MIR, do not change it. If the constant is not
18001846
// deterministic, adding an additional mention of it in MIR will not give the same value as
18011847
// the former mention.
@@ -1804,21 +1850,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18041850
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
18051851
}
18061852

1807-
let op = self.eval_to_const(index)?;
1808-
if op.layout.is_unsized() {
1809-
// Do not attempt to propagate unsized locals.
1810-
return None;
1811-
}
1853+
if let Some(op) = self.eval_to_const(index) {
1854+
if op.layout.is_unsized() {
1855+
// Do not attempt to propagate unsized locals.
1856+
return None;
1857+
}
1858+
1859+
let value = op_to_prop_const(&mut self.ecx, op)?;
18121860

1813-
let value = op_to_prop_const(&mut self.ecx, op)?;
1861+
// Check that we do not leak a pointer.
1862+
// Those pointers may lose part of their identity in codegen.
1863+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1864+
assert!(!value.may_have_provenance(self.tcx, op.layout.size));
18141865

1815-
// Check that we do not leak a pointer.
1816-
// Those pointers may lose part of their identity in codegen.
1817-
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1818-
assert!(!value.may_have_provenance(self.tcx, op.layout.size));
1866+
let const_ = Const::Val(value, op.layout.ty);
1867+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ });
1868+
}
18191869

1820-
let const_ = Const::Val(value, op.layout.ty);
1821-
Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ })
1870+
if let Some(range) = self.get_range(index, location)
1871+
&& range.start == range.end
1872+
{
1873+
let ty = self.ty(index);
1874+
let layout = self.ecx.layout_of(ty).ok()?;
1875+
let value = ConstValue::Scalar(Scalar::from_uint(range.start, layout.size));
1876+
let const_ = Const::Val(value, self.ty(index));
1877+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ });
1878+
}
1879+
1880+
None
18221881
}
18231882

18241883
/// Construct a place which holds the same value as `index` and for which all locals strictly
@@ -1895,7 +1954,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18951954

18961955
let value = self.simplify_rvalue(lhs, rvalue, location);
18971956
if let Some(value) = value {
1898-
if let Some(const_) = self.try_as_constant(value) {
1957+
if let Some(const_) = self.try_as_constant(value, location) {
18991958
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
19001959
} else if let Some(place) = self.try_as_place(value, location, false)
19011960
&& *rvalue != Rvalue::Use(Operand::Move(place))
@@ -1924,14 +1983,73 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19241983
}
19251984

19261985
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1927-
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
1928-
if let Some(local) = destination.as_local()
1929-
&& self.ssa.is_ssa(local)
1930-
{
1931-
let ty = self.local_decls[local].ty;
1932-
let opaque = self.new_opaque(ty);
1933-
self.assign(local, opaque);
1986+
match &mut terminator.kind {
1987+
TerminatorKind::Call { destination, .. } => {
1988+
if let Some(local) = destination.as_local()
1989+
&& self.ssa.is_ssa(local)
1990+
{
1991+
let ty = self.local_decls[local].ty;
1992+
let opaque = self.new_opaque(ty);
1993+
self.assign(local, opaque);
1994+
}
1995+
}
1996+
TerminatorKind::Assert { cond, expected, target, .. } => {
1997+
if let Some(value) = self.simplify_operand(cond, location)
1998+
&& !matches!(self.get(value), Value::Constant { .. })
1999+
{
2000+
let successor = Location { block: *target, statement_index: 0 };
2001+
if location.block != successor.block
2002+
&& self.unique_predecessors.contains(successor.block)
2003+
{
2004+
let val = *expected as u128;
2005+
let range = WrappingRange { start: val, end: val };
2006+
self.insert_range(value, successor, range);
2007+
}
2008+
}
2009+
}
2010+
TerminatorKind::SwitchInt { discr, targets } => {
2011+
if let Some(value) = self.simplify_operand(discr, location)
2012+
&& targets.all_targets().len() < 16
2013+
&& !matches!(self.get(value), Value::Constant { .. })
2014+
{
2015+
let mut distinct_targets: FxHashMap<BasicBlock, u8> = FxHashMap::default();
2016+
for (_, target) in targets.iter() {
2017+
let targets = distinct_targets.entry(target).or_default();
2018+
if *targets == 0 {
2019+
*targets = 1;
2020+
} else {
2021+
*targets = 2;
2022+
}
2023+
}
2024+
for (val, target) in targets.iter() {
2025+
if distinct_targets[&target] != 1 {
2026+
continue;
2027+
}
2028+
let successor = Location { block: target, statement_index: 0 };
2029+
if location.block != successor.block
2030+
&& self.unique_predecessors.contains(successor.block)
2031+
{
2032+
let range = WrappingRange { start: val, end: val };
2033+
self.insert_range(value, successor, range);
2034+
}
2035+
}
2036+
2037+
let otherwise = Location { block: targets.otherwise(), statement_index: 0 };
2038+
if self.ty(value).is_bool()
2039+
&& let [val] = targets.all_values()
2040+
&& location.block != otherwise.block
2041+
&& self.unique_predecessors.contains(otherwise.block)
2042+
{
2043+
let range = if val.get() == 0 {
2044+
WrappingRange { start: 1, end: 1 }
2045+
} else {
2046+
WrappingRange { start: 0, end: 0 }
2047+
};
2048+
self.insert_range(value, otherwise, range);
2049+
}
2050+
}
19342051
}
2052+
_ => {}
19352053
}
19362054
// Terminators that can write to memory may invalidate (nested) derefs.
19372055
if terminator.kind.can_write_to_memory() {

tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
bb3: {
4141
_2 = discriminant(((((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>) as Ok).0: std::option::Option<std::vec::Vec<u8>>));
42-
switchInt(copy _2) -> [0: bb5, 1: bb7, otherwise: bb1];
42+
switchInt(move _2) -> [0: bb5, 1: bb7, otherwise: bb1];
4343
}
4444

4545
bb4: {
@@ -112,15 +112,15 @@
112112
}
113113

114114
bb18 (cleanup): {
115-
switchInt(copy _3) -> [0: bb19, otherwise: bb9];
115+
switchInt(const 0_isize) -> [0: bb19, otherwise: bb9];
116116
}
117117

118118
bb19 (cleanup): {
119119
goto -> bb9;
120120
}
121121

122122
bb20 (cleanup): {
123-
switchInt(copy _4) -> [0: bb18, otherwise: bb9];
123+
switchInt(const 0_isize) -> [0: bb18, otherwise: bb9];
124124
}
125125
}
126126

tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
bb3: {
3737
_2 = discriminant(((((_1 as Some).0: std::option::Option<std::option::Option<T>>) as Some).0: std::option::Option<T>));
38-
switchInt(copy _2) -> [0: bb6, 1: bb7, otherwise: bb1];
38+
switchInt(move _2) -> [0: bb6, 1: bb7, otherwise: bb1];
3939
}
4040

4141
bb4: {
@@ -105,15 +105,15 @@
105105
}
106106

107107
bb18 (cleanup): {
108-
switchInt(copy _3) -> [1: bb19, otherwise: bb9];
108+
switchInt(const 1_isize) -> [1: bb19, otherwise: bb9];
109109
}
110110

111111
bb19 (cleanup): {
112112
goto -> bb9;
113113
}
114114

115115
bb20 (cleanup): {
116-
switchInt(copy _4) -> [1: bb18, otherwise: bb9];
116+
switchInt(const 1_isize) -> [1: bb18, otherwise: bb9];
117117
}
118118
}
119119

tests/mir-opt/gvn.arithmetic.GVN.panic-abort.diff

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@
222222
_32 = copy _1;
223223
- _33 = Eq(copy _32, const 0_u64);
224224
- assert(!move _33, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable];
225-
+ _33 = copy _29;
226-
+ assert(!copy _29, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable];
225+
+ _33 = const false;
226+
+ assert(!const false, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind unreachable];
227227
}
228228

229229
bb12: {
@@ -283,8 +283,8 @@
283283
_44 = copy _1;
284284
- _45 = Eq(copy _44, const 0_u64);
285285
- assert(!move _45, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable];
286-
+ _45 = copy _29;
287-
+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable];
286+
+ _45 = const false;
287+
+ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 0_u64) -> [success: bb18, unwind unreachable];
288288
}
289289

290290
bb18: {
@@ -304,8 +304,8 @@
304304
_48 = copy _1;
305305
- _49 = Eq(copy _48, const 0_u64);
306306
- assert(!move _49, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable];
307-
+ _49 = copy _29;
308-
+ assert(!copy _29, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable];
307+
+ _49 = const false;
308+
+ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_u64) -> [success: bb20, unwind unreachable];
309309
}
310310

311311
bb20: {

0 commit comments

Comments
 (0)