Skip to content

Commit

Permalink
Auto merge of #116012 - cjgillot:gvn-const, r=<try>
Browse files Browse the repository at this point in the history
Implement constant propagation on top of MIR SSA analysis

This implements the idea I proposed in #110719 (comment)

Based on #109597

The value numbering "GVN" pass formulates each rvalue that appears in MIR with an abstract form (the `Value` enum), and assigns an integer `VnIndex` to each. This abstract form can be used to deduplicate values, reusing an earlier local that holds the same value instead of recomputing. This part is proposed in #109597.

From this abstract representation, we can perform more involved simplifications, for example in #111344.

With the abstract representation `Value`, we can also attempt to evaluate each to a constant using the interpreter. This builds a `VnIndex -> OpTy` map. From this map, we can opportunistically replace an operand or a rvalue with a constant if their value has an associated `OpTy`.

The most relevant commit is [Evaluated computed values to constants.](2767c49)"

r? `@oli-obk`
  • Loading branch information
bors committed Sep 23, 2023
2 parents 0237aa3 + 81d5ac4 commit 7b24347
Show file tree
Hide file tree
Showing 252 changed files with 9,265 additions and 2,284 deletions.
23 changes: 11 additions & 12 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
assert_eq!(alloc_align, layout.align.abi);

let read_scalar = |start, size, s: abi::Scalar, ty| {
let val = alloc
.0
.read_scalar(
bx,
alloc_range(start, size),
/*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
)
.unwrap();
bx.scalar_to_backend(val, s, ty)
match alloc.0.read_scalar(
bx,
alloc_range(start, size),
/*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
) {
Ok(val) => bx.scalar_to_backend(val, s, ty),
Err(_) => bx.const_poison(ty),
}
};

// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
Expand All @@ -156,18 +155,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => {
let size = s.size(bx);
assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
let val = read_scalar(Size::ZERO, size, s, bx.type_ptr());
let val = read_scalar(offset, size, s, bx.backend_type(layout));
OperandRef { val: OperandValue::Immediate(val), layout }
}
Abi::ScalarPair(
a @ abi::Scalar::Initialized { .. },
b @ abi::Scalar::Initialized { .. },
) => {
let (a_size, b_size) = (a.size(bx), b.size(bx));
let b_offset = a_size.align_to(b.align(bx).abi);
let b_offset = (offset + a_size).align_to(b.align(bx).abi);
assert!(b_offset.bytes() > 0);
let a_val = read_scalar(
Size::ZERO,
offset,
a_size,
a,
bx.scalar_pair_element_backend_type(layout, 0, true),
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_const_eval/src/interpret/discriminant.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).

use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
use rustc_middle::{mir, ty};
use rustc_middle::mir;
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
use rustc_middle::ty::{self, Ty};
use rustc_target::abi::{self, TagEncoding};
use rustc_target::abi::{VariantIdx, Variants};

Expand Down Expand Up @@ -244,11 +245,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

pub fn discriminant_for_variant(
&self,
layout: TyAndLayout<'tcx>,
ty: Ty<'tcx>,
variant: VariantIdx,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?;
let discr_value = match layout.ty.discriminant_for_variant(*self.tcx, variant) {
let discr_layout = self.layout_of(ty.discriminant_ty(*self.tcx))?;
let discr_value = match ty.discriminant_for_variant(*self.tcx, variant) {
Some(discr) => {
// This type actually has discriminants.
assert_eq!(discr.ty, discr_layout.ty);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
sym::discriminant_value => {
let place = self.deref_pointer(&args[0])?;
let variant = self.read_discriminant(&place)?;
let discr = self.discriminant_for_variant(place.layout, variant)?;
let discr = self.discriminant_for_variant(place.layout.ty, variant)?;
self.write_immediate(*discr, dest)?;
}
sym::exact_div => {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
ImmTy { imm: val.into(), layout }
}

#[inline]
pub fn from_scalar_pair(a: Scalar<Prov>, b: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
debug_assert!(
matches!(layout.abi, Abi::ScalarPair(..)),
"`ImmTy::from_scalar_pair` on non-scalar-pair layout"
);
let imm = Immediate::ScalarPair(a, b);
ImmTy { imm, layout }
}

#[inline(always)]
pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
debug_assert!(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Discriminant(place) => {
let op = self.eval_place_to_op(place, None)?;
let variant = self.read_discriminant(&op)?;
let discr = self.discriminant_for_variant(op.layout, variant)?;
let discr = self.discriminant_for_variant(op.layout.ty, variant)?;
self.write_immediate(*discr, &dest)?;
}
}
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/mir/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,18 @@ impl<'tcx> ConstValue<'tcx> {
let end = end.try_into().unwrap();
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
}

pub fn has_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
let (alloc, start, end) = match *self {
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
ConstValue::Slice { data, meta } => (data, Size::ZERO, Size::from_bytes(meta)),
ConstValue::Indirect { alloc_id, offset } => {
(tcx.global_alloc(alloc_id).unwrap_memory(), offset, offset + size)
}
};
!alloc.inner().provenance().range_empty(super::AllocRange::from(start..end), &tcx)
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1333,7 +1333,7 @@ pub enum AggregateKind<'tcx> {
Generator(DefId, GenericArgsRef<'tcx>, hir::Movability),
}

#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp<'tcx> {
/// Returns the size of a value of that type
SizeOf,
Expand Down
Loading

0 comments on commit 7b24347

Please sign in to comment.