Skip to content

Commit 85dc22f

Browse files
committed
interpret: factor out common code for place mutation
1 parent 8ad808d commit 85dc22f

3 files changed

Lines changed: 89 additions & 81 deletions

File tree

‎compiler/rustc_const_eval/src/interpret/operand.rs‎

Lines changed: 26 additions & 0 deletions
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+
pubfnassert_matches_abi(self,abi:Abi,cx:&implHasDataLayout){
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‎

Lines changed: 62 additions & 81 deletions
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
@@ -557,6 +558,40 @@ where
557558
Ok(place)
558559
}
559560

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

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

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

‎compiler/rustc_const_eval/src/interpret/visitor.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
8282
self.visit_value(new_val)
8383
}
8484

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

0 commit comments

Comments
 (0)