Skip to content

Commit fad3972

Browse files
committed
GVN: Track known value ranges through assert and switchInt.
1 parent 838a912 commit fad3972

13 files changed

+149
-65
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ 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::{
@@ -380,6 +382,8 @@ struct VnState<'body, 'a, 'tcx> {
380382
dominators: Dominators<BasicBlock>,
381383
reused_locals: DenseBitSet<Local>,
382384
arena: &'a DroplessArena,
385+
/// Known ranges at each locations.
386+
ranges: IndexVec<VnIndex, Vec<(Location, WrappingRange)>>,
383387
}
384388

385389
impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
@@ -413,6 +417,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
413417
dominators,
414418
reused_locals: DenseBitSet::new_empty(local_decls.len()),
415419
arena,
420+
ranges: IndexVec::with_capacity(num_values),
416421
}
417422
}
418423

@@ -429,6 +434,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
429434
debug_assert_eq!(index, _index);
430435
let _index = self.rev_locals.push(SmallVec::new());
431436
debug_assert_eq!(index, _index);
437+
let _index = self.ranges.push(Vec::new());
438+
debug_assert_eq!(index, _index);
432439
}
433440
index
434441
}
@@ -442,6 +449,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
442449
debug_assert_eq!(index, _index);
443450
let _index = self.rev_locals.push(SmallVec::new());
444451
debug_assert_eq!(index, _index);
452+
let _index = self.ranges.push(Vec::new());
453+
debug_assert_eq!(index, _index);
445454
index
446455
}
447456

@@ -480,6 +489,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
480489
debug_assert_eq!(index, _index);
481490
let _index = self.rev_locals.push(SmallVec::new());
482491
debug_assert_eq!(index, _index);
492+
let _index = self.ranges.push(Vec::new());
493+
debug_assert_eq!(index, _index);
483494

484495
Some(index)
485496
}
@@ -504,6 +515,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
504515
debug_assert_eq!(index, _index);
505516
let _index = self.rev_locals.push(SmallVec::new());
506517
debug_assert_eq!(index, _index);
518+
let _index = self.ranges.push(Vec::new());
519+
debug_assert_eq!(index, _index);
507520
}
508521
index
509522
}
@@ -526,6 +539,18 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526539
self.rev_locals[value].push(local);
527540
}
528541

542+
fn insert_range(&mut self, value: VnIndex, location: Location, range: WrappingRange) {
543+
self.ranges[value].push((location, range));
544+
}
545+
546+
fn get_range(&self, value: VnIndex, location: Location) -> Option<WrappingRange> {
547+
// FIXME: This should use the intersection of all valid ranges.
548+
let (_, range) = self.ranges[value]
549+
.iter()
550+
.find(|(range_loc, _)| range_loc.dominates(location, &self.dominators))?;
551+
Some(*range)
552+
}
553+
529554
fn insert_bool(&mut self, flag: bool) -> VnIndex {
530555
// Booleans are deterministic.
531556
let value = Const::from_bool(self.tcx, flag);
@@ -1010,7 +1035,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10101035
Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
10111036
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
10121037
let value = self.simplify_place_value(place, location)?;
1013-
if let Some(const_) = self.try_as_constant(value) {
1038+
if let Some(const_) = self.try_as_constant(value, location) {
10141039
*operand = Operand::Constant(Box::new(const_));
10151040
}
10161041
Some(value)
@@ -1780,7 +1805,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17801805
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17811806
/// returns that result as an [`Operand`].
17821807
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1783-
if let Some(const_) = self.try_as_constant(index) {
1808+
if let Some(const_) = self.try_as_constant(index, location) {
17841809
Some(Operand::Constant(Box::new(const_)))
17851810
} else if let Some(place) = self.try_as_place(index, location, false) {
17861811
self.reused_locals.insert(place.local);
@@ -1791,7 +1816,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17911816
}
17921817

17931818
/// 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>> {
1819+
fn try_as_constant(
1820+
&mut self,
1821+
index: VnIndex,
1822+
location: Location,
1823+
) -> Option<ConstOperand<'tcx>> {
17951824
// This was already constant in MIR, do not change it. If the constant is not
17961825
// deterministic, adding an additional mention of it in MIR will not give the same value as
17971826
// the former mention.
@@ -1800,21 +1829,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18001829
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
18011830
}
18021831

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-
}
1832+
if let Some(op) = self.eval_to_const(index) {
1833+
if op.layout.is_unsized() {
1834+
// Do not attempt to propagate unsized locals.
1835+
return None;
1836+
}
18081837

1809-
let value = op_to_prop_const(&mut self.ecx, op)?;
1838+
let value = op_to_prop_const(&mut self.ecx, op)?;
18101839

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));
1840+
// Check that we do not leak a pointer.
1841+
// Those pointers may lose part of their identity in codegen.
1842+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1843+
assert!(!value.may_have_provenance(self.tcx, op.layout.size));
1844+
1845+
let const_ = Const::Val(value, op.layout.ty);
1846+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ });
1847+
}
18151848

1816-
let const_ = Const::Val(value, op.layout.ty);
1817-
Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ })
1849+
if let Some(range) = self.get_range(index, location)
1850+
&& range.start == range.end
1851+
{
1852+
let ty = self.ty(index);
1853+
let layout = self.ecx.layout_of(ty).ok()?;
1854+
let value = ConstValue::Scalar(Scalar::from_uint(range.start, layout.size));
1855+
let const_ = Const::Val(value, self.ty(index));
1856+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ });
1857+
}
1858+
1859+
None
18181860
}
18191861

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

18921934
let value = self.simplify_rvalue(lhs, rvalue, location);
18931935
if let Some(value) = value {
1894-
if let Some(const_) = self.try_as_constant(value) {
1936+
if let Some(const_) = self.try_as_constant(value, location) {
18951937
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
18961938
} else if let Some(place) = self.try_as_place(value, location, false)
18971939
&& *rvalue != Rvalue::Use(Operand::Move(place))
@@ -1920,14 +1962,56 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19201962
}
19211963

19221964
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);
1965+
match &mut terminator.kind {
1966+
TerminatorKind::Call { destination, .. } => {
1967+
if let Some(local) = destination.as_local()
1968+
&& self.ssa.is_ssa(local)
1969+
{
1970+
let ty = self.local_decls[local].ty;
1971+
let opaque = self.new_opaque(ty);
1972+
self.assign(local, opaque);
1973+
}
1974+
}
1975+
TerminatorKind::Assert { cond, expected, target, .. } => {
1976+
if let Some(value) = self.simplify_operand(cond, location) {
1977+
let successor = Location { block: *target, statement_index: 0 };
1978+
if location.block != successor.block
1979+
&& location.dominates(successor, &self.dominators)
1980+
{
1981+
let val = *expected as u128;
1982+
let range = WrappingRange { start: val, end: val };
1983+
self.insert_range(value, successor, range);
1984+
}
1985+
}
1986+
}
1987+
TerminatorKind::SwitchInt { discr, targets } => {
1988+
if let Some(value) = self.simplify_operand(discr, location) {
1989+
for (val, target) in targets.iter() {
1990+
let successor = Location { block: target, statement_index: 0 };
1991+
if location.block != successor.block
1992+
&& location.dominates(successor, &self.dominators)
1993+
{
1994+
let range = WrappingRange { start: val, end: val };
1995+
self.insert_range(value, successor, range);
1996+
}
1997+
}
1998+
1999+
let otherwise = Location { block: targets.otherwise(), statement_index: 0 };
2000+
if self.ty(value).is_bool()
2001+
&& let [val] = targets.all_values()
2002+
&& location.block != otherwise.block
2003+
&& location.dominates(otherwise, &self.dominators)
2004+
{
2005+
let range = if val.get() == 0 {
2006+
WrappingRange { start: 1, end: 1 }
2007+
} else {
2008+
WrappingRange { start: 0, end: 0 }
2009+
};
2010+
self.insert_range(value, otherwise, range);
2011+
}
2012+
}
19302013
}
2014+
_ => {}
19312015
}
19322016
// Terminators that can write to memory may invalidate (nested) derefs.
19332017
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: {

tests/mir-opt/gvn.arithmetic.GVN.panic-unwind.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 continue];
225-
+ _33 = copy _29;
226-
+ assert(!copy _29, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind continue];
225+
+ _33 = const false;
226+
+ assert(!const false, "attempt to divide `{}` by zero", const 1_u64) -> [success: bb12, unwind continue];
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 continue];
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 continue];
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 continue];
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 continue];
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 continue];
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 continue];
309309
}
310310

311311
bb20: {

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@
283283
_24 = copy _2;
284284
- _25 = Eq(copy _24, const 0_u64);
285285
- assert(!move _25, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _23) -> [success: bb6, unwind unreachable];
286-
+ _25 = copy _20;
287-
+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind unreachable];
286+
+ _25 = const false;
287+
+ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb6, unwind unreachable];
288288
}
289289

290290
bb6: {
@@ -520,8 +520,8 @@
520520
_74 = copy _2;
521521
- _75 = Eq(copy _74, const 0_u64);
522522
- assert(!move _75, "attempt to divide `{}` by zero", copy _73) -> [success: bb20, unwind unreachable];
523-
+ _75 = copy _20;
524-
+ assert(!copy _20, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind unreachable];
523+
+ _75 = const false;
524+
+ assert(!const false, "attempt to divide `{}` by zero", copy _1) -> [success: bb20, unwind unreachable];
525525
}
526526

527527
bb20: {
@@ -544,8 +544,8 @@
544544
_79 = copy _2;
545545
- _80 = Eq(copy _79, const 0_u64);
546546
- assert(!move _80, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _78) -> [success: bb22, unwind unreachable];
547-
+ _80 = copy _20;
548-
+ assert(!copy _20, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind unreachable];
547+
+ _80 = const false;
548+
+ assert(!const false, "attempt to calculate the remainder of `{}` with a divisor of zero", copy _1) -> [success: bb22, unwind unreachable];
549549
}
550550

551551
bb22: {

0 commit comments

Comments
 (0)