Skip to content

Commit 40d5520

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

18 files changed

+485
-67
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 144 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

@@ -429,6 +453,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
429453
debug_assert_eq!(index, _index);
430454
let _index = self.rev_locals.push(SmallVec::new());
431455
debug_assert_eq!(index, _index);
456+
let _index = self.ranges.push(Vec::new());
457+
debug_assert_eq!(index, _index);
432458
}
433459
index
434460
}
@@ -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
index
446474
}
447475

@@ -480,6 +508,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
480508
debug_assert_eq!(index, _index);
481509
let _index = self.rev_locals.push(SmallVec::new());
482510
debug_assert_eq!(index, _index);
511+
let _index = self.ranges.push(Vec::new());
512+
debug_assert_eq!(index, _index);
483513

484514
Some(index)
485515
}
@@ -504,6 +534,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
504534
debug_assert_eq!(index, _index);
505535
let _index = self.rev_locals.push(SmallVec::new());
506536
debug_assert_eq!(index, _index);
537+
let _index = self.ranges.push(Vec::new());
538+
debug_assert_eq!(index, _index);
507539
}
508540
index
509541
}
@@ -526,6 +558,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526558
self.rev_locals[value].push(local);
527559
}
528560

561+
/// Create a new known range at the location.
562+
fn insert_range(&mut self, value: VnIndex, location: Location, range: WrappingRange) {
563+
self.ranges[value].push((location, range));
564+
}
565+
566+
/// Get the known range at the location.
567+
fn get_range(&self, value: VnIndex, location: Location) -> Option<WrappingRange> {
568+
// FIXME: This should use the intersection of all valid ranges.
569+
let (_, range) = self.ranges[value]
570+
.iter()
571+
.find(|(range_loc, _)| range_loc.dominates(location, &self.dominators))?;
572+
Some(*range)
573+
}
574+
529575
fn insert_bool(&mut self, flag: bool) -> VnIndex {
530576
// Booleans are deterministic.
531577
let value = Const::from_bool(self.tcx, flag);
@@ -1010,7 +1056,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10101056
Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
10111057
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
10121058
let value = self.simplify_place_value(place, location)?;
1013-
if let Some(const_) = self.try_as_constant(value) {
1059+
if let Some(const_) = self.try_as_constant(value, location) {
10141060
*operand = Operand::Constant(Box::new(const_));
10151061
}
10161062
Some(value)
@@ -1780,7 +1826,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17801826
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17811827
/// returns that result as an [`Operand`].
17821828
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1783-
if let Some(const_) = self.try_as_constant(index) {
1829+
if let Some(const_) = self.try_as_constant(index, location) {
17841830
Some(Operand::Constant(Box::new(const_)))
17851831
} else if let Some(place) = self.try_as_place(index, location, false) {
17861832
self.reused_locals.insert(place.local);
@@ -1791,7 +1837,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17911837
}
17921838

17931839
/// 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>> {
1840+
fn try_as_constant(
1841+
&mut self,
1842+
index: VnIndex,
1843+
location: Location,
1844+
) -> Option<ConstOperand<'tcx>> {
17951845
// This was already constant in MIR, do not change it. If the constant is not
17961846
// deterministic, adding an additional mention of it in MIR will not give the same value as
17971847
// the former mention.
@@ -1800,21 +1850,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18001850
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
18011851
}
18021852

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-
}
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)?;
18081860

1809-
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));
18101865

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));
1866+
let const_ = Const::Val(value, op.layout.ty);
1867+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ });
1868+
}
18151869

1816-
let const_ = Const::Val(value, op.layout.ty);
1817-
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
18181881
}
18191882

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

18921955
let value = self.simplify_rvalue(lhs, rvalue, location);
18931956
if let Some(value) = value {
1894-
if let Some(const_) = self.try_as_constant(value) {
1957+
if let Some(const_) = self.try_as_constant(value, location) {
18951958
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
18961959
} else if let Some(place) = self.try_as_place(value, location, false)
18971960
&& *rvalue != Rvalue::Use(Operand::Move(place))
@@ -1920,14 +1983,68 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19201983
}
19211984

19221985
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);
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+
let successor = Location { block: *target, statement_index: 0 };
1999+
if location.block != successor.block
2000+
&& self.unique_predecessors.contains(successor.block)
2001+
{
2002+
let val = *expected as u128;
2003+
let range = WrappingRange { start: val, end: val };
2004+
self.insert_range(value, successor, range);
2005+
}
2006+
}
2007+
}
2008+
TerminatorKind::SwitchInt { discr, targets } => {
2009+
if let Some(value) = self.simplify_operand(discr, location) {
2010+
let mut distinct_targets: FxHashMap<BasicBlock, u8> = FxHashMap::default();
2011+
for (_, target) in targets.iter() {
2012+
let targets = distinct_targets.entry(target).or_default();
2013+
if *targets == 0 {
2014+
*targets = 1;
2015+
} else {
2016+
*targets = 2;
2017+
}
2018+
}
2019+
for (val, target) in targets.iter() {
2020+
if distinct_targets[&target] != 1 {
2021+
continue;
2022+
}
2023+
let successor = Location { block: target, statement_index: 0 };
2024+
if location.block != successor.block
2025+
&& self.unique_predecessors.contains(successor.block)
2026+
{
2027+
let range = WrappingRange { start: val, end: val };
2028+
self.insert_range(value, successor, range);
2029+
}
2030+
}
2031+
2032+
let otherwise = Location { block: targets.otherwise(), statement_index: 0 };
2033+
if self.ty(value).is_bool()
2034+
&& let [val] = targets.all_values()
2035+
&& location.block != otherwise.block
2036+
&& self.unique_predecessors.contains(otherwise.block)
2037+
{
2038+
let range = if val.get() == 0 {
2039+
WrappingRange { start: 1, end: 1 }
2040+
} else {
2041+
WrappingRange { start: 0, end: 0 }
2042+
};
2043+
self.insert_range(value, otherwise, range);
2044+
}
2045+
}
19302046
}
2047+
_ => {}
19312048
}
19322049
// Terminators that can write to memory may invalidate (nested) derefs.
19332050
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)