Skip to content

Commit 5611395

Browse files
committed
interpret: factor out common code for place mutation
1 parent 7e244f8 commit 5611395

File tree

3 files changed

+89
-81
lines changed

3 files changed

+89
-81
lines changed

compiler/rustc_const_eval/src/interpret/operand.rs

+26
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,32 @@ impl<Prov: Provenance> Immediate<Prov> {
111111
Immediate::Uninit => bug!("Got uninit where a scalar or scalar pair was expected"),
112112
}
113113
}
114+
115+
/// Assert that this immediate is a valid value for the given ABI.
116+
pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) {
117+
match (self, abi) {
118+
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
119+
assert_eq!(scalar.size(), s.size(cx));
120+
if !matches!(s.primitive(), abi::Pointer(..)) {
121+
assert!(matches!(scalar, Scalar::Int(..)));
122+
}
123+
}
124+
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
125+
assert_eq!(a_val.size(), a.size(cx));
126+
if !matches!(a.primitive(), abi::Pointer(..)) {
127+
assert!(matches!(a_val, Scalar::Int(..)));
128+
}
129+
assert_eq!(b_val.size(), b.size(cx));
130+
if !matches!(b.primitive(), abi::Pointer(..)) {
131+
assert!(matches!(b_val, Scalar::Int(..)));
132+
}
133+
}
134+
(Immediate::Uninit, _) => {}
135+
_ => {
136+
bug!("value {self:?} does not match ABI {abi:?})",)
137+
}
138+
}
139+
}
114140
}
115141

116142
// ScalarPair needs a type to interpret, so we often have an immediate and a type together

compiler/rustc_const_eval/src/interpret/place.rs

+62-81
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
180180
Ptr(MemPlace<Prov>),
181181

182182
/// To support alloc-free locals, we are able to write directly to a local. The offset indicates
183-
/// where in the local this place is located; if it is `None`, no projection has been applied.
183+
/// where in the local this place is located; if it is `None`, no projection has been applied
184+
/// and the type of the place is exactly the type of the local.
184185
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
185186
/// (Without that optimization, we'd just always be a `MemPlace`.)
186187
/// `Local` places always refer to the current stack frame, so they are unstable under
@@ -555,6 +556,40 @@ where
555556
Ok(place)
556557
}
557558

559+
/// Given a place, returns either the underlying mplace or a reference to where the value of
560+
/// this place is stored.
561+
fn as_mplace_or_mutable_local(
562+
&mut self,
563+
place: &PlaceTy<'tcx, M::Provenance>,
564+
) -> InterpResult<
565+
'tcx,
566+
Either<MPlaceTy<'tcx, M::Provenance>, (&mut Immediate<M::Provenance>, TyAndLayout<'tcx>)>,
567+
> {
568+
Ok(match place.to_place().as_mplace_or_local() {
569+
Left(mplace) => Left(mplace),
570+
Right((local, offset, locals_addr, layout)) => {
571+
if offset.is_some() {
572+
// This has been projected to a part of this local, or had the type changed.
573+
// FIMXE: there are cases where we could still avoid allocating an mplace.
574+
Left(place.force_mplace(self)?)
575+
} else {
576+
debug_assert_eq!(locals_addr, self.frame().locals_addr());
577+
debug_assert_eq!(self.layout_of_local(self.frame(), local, None)?, layout);
578+
match self.frame_mut().locals[local].access_mut()? {
579+
Operand::Indirect(mplace) => {
580+
// The local is in memory.
581+
Left(MPlaceTy { mplace: *mplace, layout })
582+
}
583+
Operand::Immediate(local_val) => {
584+
// The local still has the optimized representation.
585+
Right((local_val, layout))
586+
}
587+
}
588+
}
589+
}
590+
})
591+
}
592+
558593
/// Write an immediate to a place
559594
#[inline(always)]
560595
#[instrument(skip(self), level = "trace")]
@@ -606,60 +641,20 @@ where
606641
) -> InterpResult<'tcx> {
607642
assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
608643

609-
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
610-
// but not factored as a separate function.
611-
let mplace = match dest.to_place().as_mplace_or_local() {
612-
Right((local, offset, locals_addr, layout)) => {
613-
if offset.is_some() {
614-
// This has been projected to a part of this local. We could have complicated
615-
// logic to still keep this local as an `Operand`... but it's much easier to
616-
// just fall back to the indirect path.
617-
dest.force_mplace(self)?
618-
} else {
619-
debug_assert_eq!(locals_addr, self.frame().locals_addr());
620-
match self.frame_mut().locals[local].access_mut()? {
621-
Operand::Immediate(local_val) => {
622-
// Local can be updated in-place.
623-
*local_val = src;
624-
// Double-check that the value we are storing and the local fit to each other.
625-
// (*After* doing the update for borrow checker reasons.)
626-
if cfg!(debug_assertions) {
627-
let local_layout =
628-
self.layout_of_local(&self.frame(), local, None)?;
629-
match (src, local_layout.abi) {
630-
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
631-
assert_eq!(scalar.size(), s.size(self))
632-
}
633-
(
634-
Immediate::ScalarPair(a_val, b_val),
635-
Abi::ScalarPair(a, b),
636-
) => {
637-
assert_eq!(a_val.size(), a.size(self));
638-
assert_eq!(b_val.size(), b.size(self));
639-
}
640-
(Immediate::Uninit, _) => {}
641-
(src, abi) => {
642-
bug!(
643-
"value {src:?} cannot be written into local with type {} (ABI {abi:?})",
644-
local_layout.ty
645-
)
646-
}
647-
};
648-
}
649-
return Ok(());
650-
}
651-
Operand::Indirect(mplace) => {
652-
// The local is in memory, go on below.
653-
MPlaceTy { mplace: *mplace, layout }
654-
}
655-
}
644+
match self.as_mplace_or_mutable_local(&dest.to_place())? {
645+
Right((local_val, local_layout)) => {
646+
// Local can be updated in-place.
647+
*local_val = src;
648+
// Double-check that the value we are storing and the local fit to each other.
649+
if cfg!(debug_assertions) {
650+
src.assert_matches_abi(local_layout.abi, self);
656651
}
657652
}
658-
Left(mplace) => mplace, // already referring to memory
659-
};
660-
661-
// This is already in memory, write there.
662-
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)
653+
Left(mplace) => {
654+
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?;
655+
}
656+
}
657+
Ok(())
663658
}
664659

665660
/// Write an immediate to memory.
@@ -671,6 +666,9 @@ where
671666
layout: TyAndLayout<'tcx>,
672667
dest: MemPlace<M::Provenance>,
673668
) -> InterpResult<'tcx> {
669+
if cfg!(debug_assertions) {
670+
value.assert_matches_abi(layout.abi, self);
671+
}
674672
// Note that it is really important that the type here is the right one, and matches the
675673
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
676674
// to handle padding properly, which is only correct if we never look at this data with the
@@ -721,35 +719,18 @@ where
721719
&mut self,
722720
dest: &impl Writeable<'tcx, M::Provenance>,
723721
) -> InterpResult<'tcx> {
724-
let mplace = match dest.to_place().as_mplace_or_local() {
725-
Left(mplace) => mplace,
726-
Right((local, offset, locals_addr, layout)) => {
727-
if offset.is_some() {
728-
// This has been projected to a part of this local. We could have complicated
729-
// logic to still keep this local as an `Operand`... but it's much easier to
730-
// just fall back to the indirect path.
731-
// FIXME: share the logic with `write_immediate_no_validate`.
732-
dest.force_mplace(self)?
733-
} else {
734-
debug_assert_eq!(locals_addr, self.frame().locals_addr());
735-
match self.frame_mut().locals[local].access_mut()? {
736-
Operand::Immediate(local) => {
737-
*local = Immediate::Uninit;
738-
return Ok(());
739-
}
740-
Operand::Indirect(mplace) => {
741-
// The local is in memory, go on below.
742-
MPlaceTy { mplace: *mplace, layout }
743-
}
744-
}
745-
}
722+
match self.as_mplace_or_mutable_local(&dest.to_place())? {
723+
Right((local_val, _local_layout)) => {
724+
*local_val = Immediate::Uninit;
746725
}
747-
};
748-
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
749-
// Zero-sized access
750-
return Ok(());
751-
};
752-
alloc.write_uninit()?;
726+
Left(mplace) => {
727+
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
728+
// Zero-sized access
729+
return Ok(());
730+
};
731+
alloc.write_uninit()?;
732+
}
733+
}
753734
Ok(())
754735
}
755736

compiler/rustc_const_eval/src/interpret/visitor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
8181
self.visit_value(new_val)
8282
}
8383

84+
/// Traversal logic; should not be overloaded.
8485
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
8586
let ty = v.layout().ty;
8687
trace!("walk_value: type: {ty}");

0 commit comments

Comments
 (0)