Skip to content

Commit de38c09

Browse files
committed
Evaluate constants lazily in GVN.
1 parent 3d8c1c1 commit de38c09

File tree

1 file changed

+44
-38
lines changed
  • compiler/rustc_mir_transform/src

1 file changed

+44
-38
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,10 @@ struct VnState<'body, 'a, 'tcx> {
362362
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
363363
values: ValueSet<'a, 'tcx>,
364364
/// Values evaluated as constants if possible.
365-
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
365+
/// - `None` are values not computed yet;
366+
/// - `Some(None)` are values for which computation has failed;
367+
/// - `Some(Some(op))` are successful computations.
368+
evaluated: IndexVec<VnIndex, Option<Option<&'a OpTy<'tcx>>>>,
366369
/// Cache the deref values.
367370
derefs: Vec<VnIndex>,
368371
ssa: &'body SsaLocals,
@@ -414,8 +417,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
414417
let (index, new) = self.values.insert(ty, value);
415418
if new {
416419
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
417-
let evaluated = self.eval_to_const(index);
418-
let _index = self.evaluated.push(evaluated);
420+
let _index = self.evaluated.push(None);
419421
debug_assert_eq!(index, _index);
420422
let _index = self.rev_locals.push(SmallVec::new());
421423
debug_assert_eq!(index, _index);
@@ -428,7 +430,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
428430
#[instrument(level = "trace", skip(self), ret)]
429431
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
430432
let index = self.values.insert_unique(ty, Value::Opaque);
431-
let _index = self.evaluated.push(None);
433+
let _index = self.evaluated.push(Some(None));
432434
debug_assert_eq!(index, _index);
433435
let _index = self.rev_locals.push(SmallVec::new());
434436
debug_assert_eq!(index, _index);
@@ -466,8 +468,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
466468
kind,
467469
provenance,
468470
});
469-
let evaluated = self.eval_to_const(index);
470-
let _index = self.evaluated.push(evaluated);
471+
let _index = self.evaluated.push(None);
471472
debug_assert_eq!(index, _index);
472473
let _index = self.rev_locals.push(SmallVec::new());
473474
debug_assert_eq!(index, _index);
@@ -491,8 +492,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
491492
(index, true)
492493
};
493494
if new {
494-
let evaluated = self.eval_to_const(index);
495-
let _index = self.evaluated.push(evaluated);
495+
let _index = self.evaluated.push(None);
496496
debug_assert_eq!(index, _index);
497497
let _index = self.rev_locals.push(SmallVec::new());
498498
debug_assert_eq!(index, _index);
@@ -549,7 +549,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
549549
}
550550

551551
#[instrument(level = "trace", skip(self), ret)]
552-
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
552+
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
553553
use Value::*;
554554
let ty = self.ty(value);
555555
// Avoid computing layouts inside a coroutine, as that can cause cycles.
@@ -569,10 +569,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
569569
self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
570570
}
571571
Aggregate(variant, ref fields) => {
572-
let fields = fields
573-
.iter()
574-
.map(|&f| self.evaluated[f].as_ref())
575-
.collect::<Option<Vec<_>>>()?;
572+
let fields =
573+
fields.iter().map(|&f| self.eval_to_const(f)).collect::<Option<Vec<_>>>()?;
576574
let variant = if ty.ty.is_enum() { Some(variant) } else { None };
577575
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
578576
{
@@ -601,8 +599,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
601599
}
602600
}
603601
RawPtr { pointer, metadata } => {
604-
let pointer = self.evaluated[pointer].as_ref()?;
605-
let metadata = self.evaluated[metadata].as_ref()?;
602+
let pointer = self.eval_to_const(pointer)?;
603+
let metadata = self.eval_to_const(metadata)?;
606604

607605
// Pointers don't have fields, so don't `project_field` them.
608606
let data = self.ecx.read_pointer(pointer).discard_err()?;
@@ -616,7 +614,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
616614
}
617615

618616
Projection(base, elem) => {
619-
let base = self.evaluated[base].as_ref()?;
617+
let base = self.eval_to_const(base)?;
620618
// `Index` by constants should have been replaced by `ConstantIndex` by
621619
// `simplify_place_projection`.
622620
let elem = elem.try_map(|_| None, |()| ty.ty)?;
@@ -625,7 +623,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
625623
Address { base, projection, .. } => {
626624
debug_assert!(!projection.contains(&ProjectionElem::Deref));
627625
let pointer = match base {
628-
AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?,
626+
AddressBase::Deref(pointer) => self.eval_to_const(pointer)?,
629627
// We have no stack to point to.
630628
AddressBase::Local(_) => return None,
631629
};
@@ -641,7 +639,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
641639
}
642640

643641
Discriminant(base) => {
644-
let base = self.evaluated[base].as_ref()?;
642+
let base = self.eval_to_const(base)?;
645643
let variant = self.ecx.read_discriminant(base).discard_err()?;
646644
let discr_value =
647645
self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
@@ -658,7 +656,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
658656
NullOp::SizeOf => arg_layout.size.bytes(),
659657
NullOp::AlignOf => arg_layout.align.bytes(),
660658
NullOp::OffsetOf(fields) => self
661-
.ecx
662659
.tcx
663660
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
664661
.bytes(),
@@ -668,34 +665,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
668665
ImmTy::from_uint(val, ty).into()
669666
}
670667
UnaryOp(un_op, operand) => {
671-
let operand = self.evaluated[operand].as_ref()?;
668+
let operand = self.eval_to_const(operand)?;
672669
let operand = self.ecx.read_immediate(operand).discard_err()?;
673670
let val = self.ecx.unary_op(un_op, &operand).discard_err()?;
674671
val.into()
675672
}
676673
BinaryOp(bin_op, lhs, rhs) => {
677-
let lhs = self.evaluated[lhs].as_ref()?;
674+
let lhs = self.eval_to_const(lhs)?;
675+
let rhs = self.eval_to_const(rhs)?;
678676
let lhs = self.ecx.read_immediate(lhs).discard_err()?;
679-
let rhs = self.evaluated[rhs].as_ref()?;
680677
let rhs = self.ecx.read_immediate(rhs).discard_err()?;
681678
let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
682679
val.into()
683680
}
684681
Cast { kind, value } => match kind {
685682
CastKind::IntToInt | CastKind::IntToFloat => {
686-
let value = self.evaluated[value].as_ref()?;
683+
let value = self.eval_to_const(value)?;
687684
let value = self.ecx.read_immediate(value).discard_err()?;
688685
let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?;
689686
res.into()
690687
}
691688
CastKind::FloatToFloat | CastKind::FloatToInt => {
692-
let value = self.evaluated[value].as_ref()?;
689+
let value = self.eval_to_const(value)?;
693690
let value = self.ecx.read_immediate(value).discard_err()?;
694691
let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
695692
res.into()
696693
}
697694
CastKind::Transmute | CastKind::Subtype => {
698-
let value = self.evaluated[value].as_ref()?;
695+
let value = self.eval_to_const(value)?;
699696
// `offset` for immediates generally only supports projections that match the
700697
// type of the immediate. However, as a HACK, we exploit that it can also do
701698
// limited transmutes: it only works between types with the same layout, and
@@ -707,12 +704,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
707704
&& !matches!(s1.primitive(), Primitive::Pointer(..))
708705
}
709706
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
710-
a1.size(&self.ecx) == a2.size(&self.ecx) &&
711-
b1.size(&self.ecx) == b2.size(&self.ecx) &&
712-
// The alignment of the second component determines its offset, so that also needs to match.
713-
b1.align(&self.ecx) == b2.align(&self.ecx) &&
714-
// None of the inputs may be a pointer.
715-
!matches!(a1.primitive(), Primitive::Pointer(..))
707+
a1.size(&self.ecx) == a2.size(&self.ecx)
708+
&& b1.size(&self.ecx) == b2.size(&self.ecx)
709+
// The alignment of the second component determines its offset, so that also needs to match.
710+
&& b1.align(&self.ecx) == b2.align(&self.ecx)
711+
// None of the inputs may be a pointer.
712+
&& !matches!(a1.primitive(), Primitive::Pointer(..))
716713
&& !matches!(b1.primitive(), Primitive::Pointer(..))
717714
}
718715
_ => false,
@@ -724,7 +721,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
724721
value.offset(Size::ZERO, ty, &self.ecx).discard_err()?
725722
}
726723
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
727-
let src = self.evaluated[value].as_ref()?;
724+
let src = self.eval_to_const(value)?;
728725
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
729726
self.ecx.unsize_into(src, ty, &dest).discard_err()?;
730727
self.ecx
@@ -733,13 +730,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
733730
dest.into()
734731
}
735732
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
736-
let src = self.evaluated[value].as_ref()?;
733+
let src = self.eval_to_const(value)?;
737734
let src = self.ecx.read_immediate(src).discard_err()?;
738735
let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?;
739736
ret.into()
740737
}
741738
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
742-
let src = self.evaluated[value].as_ref()?;
739+
let src = self.eval_to_const(value)?;
743740
let src = self.ecx.read_immediate(src).discard_err()?;
744741
ImmTy::from_immediate(*src, ty).into()
745742
}
@@ -749,6 +746,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
749746
Some(op)
750747
}
751748

749+
fn eval_to_const(&mut self, index: VnIndex) -> Option<&'a OpTy<'tcx>> {
750+
if let Some(op) = self.evaluated[index] {
751+
return op;
752+
}
753+
let op = self.eval_to_const_inner(index);
754+
self.evaluated[index] = Some(self.arena.alloc(op).as_ref());
755+
self.evaluated[index].unwrap()
756+
}
757+
752758
/// Represent the *value* we obtain by dereferencing an `Address` value.
753759
#[instrument(level = "trace", skip(self), ret)]
754760
fn dereference_address(
@@ -884,7 +890,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
884890
if let ProjectionElem::Index(idx_local) = elem
885891
&& let Some(idx) = self.locals[idx_local]
886892
{
887-
if let Some(offset) = self.evaluated[idx].as_ref()
893+
if let Some(offset) = self.eval_to_const(idx)
888894
&& let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
889895
&& let Some(min_length) = offset.checked_add(1)
890896
{
@@ -1369,8 +1375,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
13691375

13701376
let layout = self.ecx.layout_of(lhs_ty).ok()?;
13711377

1372-
let as_bits = |value: VnIndex| {
1373-
let constant = self.evaluated[value].as_ref()?;
1378+
let mut as_bits = |value: VnIndex| {
1379+
let constant = self.eval_to_const(value)?;
13741380
if layout.backend_repr.is_scalar() {
13751381
let scalar = self.ecx.read_scalar(constant).discard_err()?;
13761382
scalar.to_bits(constant.layout.size).discard_err()
@@ -1770,7 +1776,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17701776
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
17711777
}
17721778

1773-
let op = self.evaluated[index].as_ref()?;
1779+
let op = self.eval_to_const(index)?;
17741780
if op.layout.is_unsized() {
17751781
// Do not attempt to propagate unsized locals.
17761782
return None;

0 commit comments

Comments
 (0)