Skip to content

Commit bc72f9c

Browse files
committed
Evaluate constants lazily in GVN.
1 parent 1180278 commit bc72f9c

File tree

1 file changed

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

1 file changed

+41
-38
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ struct VnState<'body, 'a, 'tcx> {
351351
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
352352
values: ValueSet<'a, 'tcx>,
353353
/// Values evaluated as constants if possible.
354-
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
354+
evaluated: IndexVec<VnIndex, Option<Option<&'a OpTy<'tcx>>>>,
355355
/// Cache the deref values.
356356
derefs: Vec<VnIndex>,
357357
ssa: &'body SsaLocals,
@@ -403,8 +403,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
403403
let (index, new) = self.values.insert(ty, value);
404404
if new {
405405
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
406-
let evaluated = self.eval_to_const(index);
407-
let _index = self.evaluated.push(evaluated);
406+
let _index = self.evaluated.push(None);
408407
debug_assert_eq!(index, _index);
409408
let _index = self.rev_locals.push(SmallVec::new());
410409
debug_assert_eq!(index, _index);
@@ -417,7 +416,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
417416
#[instrument(level = "trace", skip(self), ret)]
418417
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
419418
let index = self.values.insert_unique(ty, Value::Opaque);
420-
let _index = self.evaluated.push(None);
419+
let _index = self.evaluated.push(Some(None));
421420
debug_assert_eq!(index, _index);
422421
let _index = self.rev_locals.push(SmallVec::new());
423422
debug_assert_eq!(index, _index);
@@ -436,8 +435,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
436435
};
437436
let index =
438437
self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance });
439-
let evaluated = self.eval_to_const(index);
440-
let _index = self.evaluated.push(evaluated);
438+
let _index = self.evaluated.push(None);
441439
debug_assert_eq!(index, _index);
442440
let _index = self.rev_locals.push(SmallVec::new());
443441
debug_assert_eq!(index, _index);
@@ -460,8 +458,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
460458
(index, true)
461459
};
462460
if new {
463-
let evaluated = self.eval_to_const(index);
464-
let _index = self.evaluated.push(evaluated);
461+
let _index = self.evaluated.push(None);
465462
debug_assert_eq!(index, _index);
466463
let _index = self.rev_locals.push(SmallVec::new());
467464
debug_assert_eq!(index, _index);
@@ -518,7 +515,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
518515
}
519516

520517
#[instrument(level = "trace", skip(self), ret)]
521-
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
518+
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
522519
use Value::*;
523520
let ty = self.ty(value);
524521
// Avoid computing layouts inside a coroutine, as that can cause cycles.
@@ -538,10 +535,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
538535
self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
539536
}
540537
Aggregate(variant, ref fields) => {
541-
let fields = fields
542-
.iter()
543-
.map(|&f| self.evaluated[f].as_ref())
544-
.collect::<Option<Vec<_>>>()?;
538+
let fields =
539+
fields.iter().map(|&f| self.eval_to_const(f)).collect::<Option<Vec<_>>>()?;
545540
let variant = if ty.ty.is_enum() { Some(variant) } else { None };
546541
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
547542
{
@@ -570,8 +565,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
570565
}
571566
}
572567
RawPtr { pointer, metadata } => {
573-
let pointer = self.evaluated[pointer].as_ref()?;
574-
let metadata = self.evaluated[metadata].as_ref()?;
568+
let pointer = self.eval_to_const(pointer)?;
569+
let metadata = self.eval_to_const(metadata)?;
575570

576571
// Pointers don't have fields, so don't `project_field` them.
577572
let data = self.ecx.read_pointer(pointer).discard_err()?;
@@ -585,7 +580,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
585580
}
586581

587582
Projection(base, elem) => {
588-
let base = self.evaluated[base].as_ref()?;
583+
let base = self.eval_to_const(base)?;
589584
// `Index` by constants should have been replaced by `ConstantIndex` by
590585
// `simplify_place_projection`.
591586
let elem = elem.try_map(|_| None, |()| ty.ty)?;
@@ -596,7 +591,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
596591
return None;
597592
}
598593
let local = self.locals[place.local]?;
599-
let pointer = self.evaluated[local].as_ref()?;
594+
let pointer = self.eval_to_const(local)?;
600595
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
601596
for elem in place.projection.iter().skip(1) {
602597
// `Index` by constants should have been replaced by `ConstantIndex` by
@@ -609,7 +604,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
609604
}
610605

611606
Discriminant(base) => {
612-
let base = self.evaluated[base].as_ref()?;
607+
let base = self.eval_to_const(base)?;
613608
let variant = self.ecx.read_discriminant(base).discard_err()?;
614609
let discr_value =
615610
self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
@@ -626,7 +621,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
626621
NullOp::SizeOf => arg_layout.size.bytes(),
627622
NullOp::AlignOf => arg_layout.align.bytes(),
628623
NullOp::OffsetOf(fields) => self
629-
.ecx
630624
.tcx
631625
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
632626
.bytes(),
@@ -636,34 +630,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
636630
ImmTy::from_uint(val, ty).into()
637631
}
638632
UnaryOp(un_op, operand) => {
639-
let operand = self.evaluated[operand].as_ref()?;
633+
let operand = self.eval_to_const(operand)?;
640634
let operand = self.ecx.read_immediate(operand).discard_err()?;
641635
let val = self.ecx.unary_op(un_op, &operand).discard_err()?;
642636
val.into()
643637
}
644638
BinaryOp(bin_op, lhs, rhs) => {
645-
let lhs = self.evaluated[lhs].as_ref()?;
639+
let lhs = self.eval_to_const(lhs)?;
640+
let rhs = self.eval_to_const(rhs)?;
646641
let lhs = self.ecx.read_immediate(lhs).discard_err()?;
647-
let rhs = self.evaluated[rhs].as_ref()?;
648642
let rhs = self.ecx.read_immediate(rhs).discard_err()?;
649643
let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
650644
val.into()
651645
}
652646
Cast { kind, value } => match kind {
653647
CastKind::IntToInt | CastKind::IntToFloat => {
654-
let value = self.evaluated[value].as_ref()?;
648+
let value = self.eval_to_const(value)?;
655649
let value = self.ecx.read_immediate(value).discard_err()?;
656650
let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?;
657651
res.into()
658652
}
659653
CastKind::FloatToFloat | CastKind::FloatToInt => {
660-
let value = self.evaluated[value].as_ref()?;
654+
let value = self.eval_to_const(value)?;
661655
let value = self.ecx.read_immediate(value).discard_err()?;
662656
let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
663657
res.into()
664658
}
665659
CastKind::Transmute | CastKind::Subtype => {
666-
let value = self.evaluated[value].as_ref()?;
660+
let value = self.eval_to_const(value)?;
667661
// `offset` for immediates generally only supports projections that match the
668662
// type of the immediate. However, as a HACK, we exploit that it can also do
669663
// limited transmutes: it only works between types with the same layout, and
@@ -675,12 +669,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
675669
&& !matches!(s1.primitive(), Primitive::Pointer(..))
676670
}
677671
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
678-
a1.size(&self.ecx) == a2.size(&self.ecx) &&
679-
b1.size(&self.ecx) == b2.size(&self.ecx) &&
680-
// The alignment of the second component determines its offset, so that also needs to match.
681-
b1.align(&self.ecx) == b2.align(&self.ecx) &&
682-
// None of the inputs may be a pointer.
683-
!matches!(a1.primitive(), Primitive::Pointer(..))
672+
a1.size(&self.ecx) == a2.size(&self.ecx)
673+
&& b1.size(&self.ecx) == b2.size(&self.ecx)
674+
// The alignment of the second component determines its offset, so that also needs to match.
675+
&& b1.align(&self.ecx) == b2.align(&self.ecx)
676+
// None of the inputs may be a pointer.
677+
&& !matches!(a1.primitive(), Primitive::Pointer(..))
684678
&& !matches!(b1.primitive(), Primitive::Pointer(..))
685679
}
686680
_ => false,
@@ -692,7 +686,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
692686
value.offset(Size::ZERO, ty, &self.ecx).discard_err()?
693687
}
694688
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
695-
let src = self.evaluated[value].as_ref()?;
689+
let src = self.eval_to_const(value)?;
696690
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
697691
self.ecx.unsize_into(src, ty, &dest).discard_err()?;
698692
self.ecx
@@ -701,13 +695,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
701695
dest.into()
702696
}
703697
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
704-
let src = self.evaluated[value].as_ref()?;
698+
let src = self.eval_to_const(value)?;
705699
let src = self.ecx.read_immediate(src).discard_err()?;
706700
let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?;
707701
ret.into()
708702
}
709703
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
710-
let src = self.evaluated[value].as_ref()?;
704+
let src = self.eval_to_const(value)?;
711705
let src = self.ecx.read_immediate(src).discard_err()?;
712706
ImmTy::from_immediate(*src, ty).into()
713707
}
@@ -717,6 +711,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
717711
Some(op)
718712
}
719713

714+
fn eval_to_const(&mut self, index: VnIndex) -> Option<&'a OpTy<'tcx>> {
715+
if let Some(op) = self.evaluated[index] {
716+
return op;
717+
}
718+
let op = self.eval_to_const_inner(index);
719+
self.evaluated[index] = Some(self.arena.alloc(op).as_ref());
720+
self.evaluated[index].unwrap()
721+
}
722+
720723
fn project(
721724
&mut self,
722725
place_ty: PlaceTy<'tcx>,
@@ -822,7 +825,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
822825
if let ProjectionElem::Index(idx_local) = elem
823826
&& let Some(idx) = self.locals[idx_local]
824827
{
825-
if let Some(offset) = self.evaluated[idx].as_ref()
828+
if let Some(offset) = self.eval_to_const(idx)
826829
&& let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
827830
&& let Some(min_length) = offset.checked_add(1)
828831
{
@@ -1312,8 +1315,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
13121315

13131316
let layout = self.ecx.layout_of(lhs_ty).ok()?;
13141317

1315-
let as_bits = |value: VnIndex| {
1316-
let constant = self.evaluated[value].as_ref()?;
1318+
let mut as_bits = |value: VnIndex| {
1319+
let constant = self.eval_to_const(value)?;
13171320
if layout.backend_repr.is_scalar() {
13181321
let scalar = self.ecx.read_scalar(constant).discard_err()?;
13191322
scalar.to_bits(constant.layout.size).discard_err()
@@ -1713,7 +1716,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17131716
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
17141717
}
17151718

1716-
let op = self.evaluated[index].as_ref()?;
1719+
let op = self.eval_to_const(index)?;
17171720
if op.layout.is_unsized() {
17181721
// Do not attempt to propagate unsized locals.
17191722
return None;

0 commit comments

Comments
 (0)