Skip to content

Commit 5734996

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

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
@@ -86,18 +86,21 @@
8686
8787
use std::borrow::Cow;
8888
use std::hash::{Hash, Hasher};
89+
use std::ops::AddAssign;
8990

9091
use either::Either;
9192
use hashbrown::hash_table::{Entry, HashTable};
9293
use itertools::Itertools as _;
93-
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
94+
use rustc_abi::{
95+
self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx, WrappingRange,
96+
};
9497
use rustc_arena::DroplessArena;
9598
use rustc_const_eval::const_eval::DummyMachine;
9699
use rustc_const_eval::interpret::{
97100
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
98101
intern_const_alloc_for_constprop,
99102
};
100-
use rustc_data_structures::fx::FxHasher;
103+
use rustc_data_structures::fx::{FxHashMap, FxHasher};
101104
use rustc_data_structures::graph::dominators::Dominators;
102105
use rustc_hir::def::DefKind;
103106
use rustc_index::bit_set::DenseBitSet;
@@ -130,10 +133,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
130133
// Clone dominators because we need them while mutating the body.
131134
let dominators = body.basic_blocks.dominators().clone();
132135
let maybe_loop_headers = loops::maybe_loop_headers(body);
136+
let predecessors = body.basic_blocks.predecessors();
137+
let mut unique_predecessors = DenseBitSet::new_empty(body.basic_blocks.len());
138+
for (bb, _) in body.basic_blocks.iter_enumerated() {
139+
if predecessors[bb].len() == 1 {
140+
unique_predecessors.insert(bb);
141+
}
142+
}
133143

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

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

385407
impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
@@ -391,6 +413,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
391413
dominators: Dominators<BasicBlock>,
392414
local_decls: &'body LocalDecls<'tcx>,
393415
arena: &'a DroplessArena,
416+
unique_predecessors: DenseBitSet<BasicBlock>,
394417
) -> Self {
395418
// Compute a rough estimate of the number of values in the body from the number of
396419
// statements. This is meant to reduce the number of allocations, but it's all right if
@@ -413,6 +436,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
413436
dominators,
414437
reused_locals: DenseBitSet::new_empty(local_decls.len()),
415438
arena,
439+
ranges: IndexVec::with_capacity(num_values),
440+
unique_predecessors,
416441
}
417442
}
418443

@@ -429,6 +454,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
429454
debug_assert_eq!(index, _index);
430455
let _index = self.rev_locals.push(SmallVec::new());
431456
debug_assert_eq!(index, _index);
457+
let _index = self.ranges.push(Vec::new());
458+
debug_assert_eq!(index, _index);
432459
}
433460
index
434461
}
@@ -442,6 +469,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
442469
debug_assert_eq!(index, _index);
443470
let _index = self.rev_locals.push(SmallVec::new());
444471
debug_assert_eq!(index, _index);
472+
let _index = self.ranges.push(Vec::new());
473+
debug_assert_eq!(index, _index);
445474
index
446475
}
447476

@@ -480,6 +509,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
480509
debug_assert_eq!(index, _index);
481510
let _index = self.rev_locals.push(SmallVec::new());
482511
debug_assert_eq!(index, _index);
512+
let _index = self.ranges.push(Vec::new());
513+
debug_assert_eq!(index, _index);
483514

484515
Some(index)
485516
}
@@ -504,6 +535,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
504535
debug_assert_eq!(index, _index);
505536
let _index = self.rev_locals.push(SmallVec::new());
506537
debug_assert_eq!(index, _index);
538+
let _index = self.ranges.push(Vec::new());
539+
debug_assert_eq!(index, _index);
507540
}
508541
index
509542
}
@@ -526,6 +559,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526559
self.rev_locals[value].push(local);
527560
}
528561

562+
/// Create a new known range at the location.
563+
fn insert_range(&mut self, value: VnIndex, location: Location, range: WrappingRange) {
564+
self.ranges[value].push((location, range));
565+
}
566+
567+
/// Get the known range at the location.
568+
fn get_range(&self, value: VnIndex, location: Location) -> Option<WrappingRange> {
569+
// FIXME: This should use the intersection of all valid ranges.
570+
let (_, range) = self.ranges[value]
571+
.iter()
572+
.find(|(range_loc, _)| range_loc.dominates(location, &self.dominators))?;
573+
Some(*range)
574+
}
575+
529576
fn insert_bool(&mut self, flag: bool) -> VnIndex {
530577
// Booleans are deterministic.
531578
let value = Const::from_bool(self.tcx, flag);
@@ -1010,7 +1057,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10101057
Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
10111058
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
10121059
let value = self.simplify_place_value(place, location)?;
1013-
if let Some(const_) = self.try_as_constant(value) {
1060+
if let Some(const_) = self.try_as_constant(value, location) {
10141061
*operand = Operand::Constant(Box::new(const_));
10151062
}
10161063
Some(value)
@@ -1780,7 +1827,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17801827
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17811828
/// returns that result as an [`Operand`].
17821829
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1783-
if let Some(const_) = self.try_as_constant(index) {
1830+
if let Some(const_) = self.try_as_constant(index, location) {
17841831
Some(Operand::Constant(Box::new(const_)))
17851832
} else if let Some(place) = self.try_as_place(index, location, false) {
17861833
self.reused_locals.insert(place.local);
@@ -1791,7 +1838,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17911838
}
17921839

17931840
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1794-
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
1841+
fn try_as_constant(
1842+
&mut self,
1843+
index: VnIndex,
1844+
location: Location,
1845+
) -> Option<ConstOperand<'tcx>> {
17951846
// This was already constant in MIR, do not change it. If the constant is not
17961847
// deterministic, adding an additional mention of it in MIR will not give the same value as
17971848
// the former mention.
@@ -1800,21 +1851,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18001851
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
18011852
}
18021853

1803-
let op = self.eval_to_const(index)?;
1804-
if op.layout.is_unsized() {
1805-
// Do not attempt to propagate unsized locals.
1806-
return None;
1807-
}
1854+
if let Some(op) = self.eval_to_const(index) {
1855+
if op.layout.is_unsized() {
1856+
// Do not attempt to propagate unsized locals.
1857+
return None;
1858+
}
1859+
1860+
let value = op_to_prop_const(&mut self.ecx, op)?;
18081861

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

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

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

18201884
/// Construct a place which holds the same value as `index` and for which all locals strictly
@@ -1891,7 +1955,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18911955

18921956
let value = self.simplify_rvalue(lhs, rvalue, location);
18931957
if let Some(value) = value {
1894-
if let Some(const_) = self.try_as_constant(value) {
1958+
if let Some(const_) = self.try_as_constant(value, location) {
18951959
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
18961960
} else if let Some(place) = self.try_as_place(value, location, false)
18971961
&& *rvalue != Rvalue::Use(Operand::Move(place))
@@ -1920,14 +1984,68 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19201984
}
19211985

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