Skip to content

Commit d97e04f

Browse files
committed
Auto merge of rust-lang#115764 - RalfJung:const-by-ref-alloc-id, r=oli-obk
some ConstValue refactoring In particular, use AllocId instead of Allocation in ConstValue::ByRef. This helps avoid redundant AllocIds when a `ByRef` constant gets put back into the interpreter. r? `@oli-obk` Fixes rust-lang#105536
2 parents fea0460 + 04a4df5 commit d97e04f

File tree

57 files changed

+422
-220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+422
-220
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
55
use rustc_middle::mir::interpret::{
6-
read_target_uint, AllocId, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
6+
read_target_uint, AllocId, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
77
};
88

99
use cranelift_module::*;
@@ -116,7 +116,7 @@ pub(crate) fn codegen_const_value<'tcx>(
116116
}
117117

118118
match const_val {
119-
ConstValue::ZeroSized => unreachable!(), // we already handles ZST above
119+
ConstValue::ZeroSized => unreachable!(), // we already handled ZST above
120120
ConstValue::Scalar(x) => match x {
121121
Scalar::Int(int) => {
122122
if fx.clif_type(layout.ty).is_some() {
@@ -200,13 +200,14 @@ pub(crate) fn codegen_const_value<'tcx>(
200200
CValue::by_val(val, layout)
201201
}
202202
},
203-
ConstValue::ByRef { alloc, offset } => CValue::by_ref(
204-
pointer_for_allocation(fx, alloc)
203+
ConstValue::Indirect { alloc_id, offset } => CValue::by_ref(
204+
pointer_for_allocation(fx, alloc_id)
205205
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
206206
layout,
207207
),
208208
ConstValue::Slice { data, start, end } => {
209-
let ptr = pointer_for_allocation(fx, data)
209+
let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
210+
let ptr = pointer_for_allocation(fx, alloc_id)
210211
.offset_i64(fx, i64::try_from(start).unwrap())
211212
.get_addr(fx);
212213
let len = fx
@@ -220,9 +221,9 @@ pub(crate) fn codegen_const_value<'tcx>(
220221

221222
fn pointer_for_allocation<'tcx>(
222223
fx: &mut FunctionCx<'_, '_, 'tcx>,
223-
alloc: ConstAllocation<'tcx>,
224+
alloc_id: AllocId,
224225
) -> crate::pointer::Pointer {
225-
let alloc_id = fx.tcx.create_memory_alloc(alloc);
226+
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
226227
let data_id = data_id_for_alloc_id(
227228
&mut fx.constants_cx,
228229
&mut *fx.module,
@@ -353,6 +354,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
353354
unreachable!()
354355
}
355356
};
357+
// FIXME: should we have a cache so we don't do this multiple times for the same `ConstAllocation`?
356358
let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
357359
module.declare_anonymous_data(alloc.inner().mutability.is_mut(), false).unwrap()
358360
});

compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
172172
.expect("simd_shuffle idx not const");
173173

174174
let idx_bytes = match idx_const {
175-
ConstValue::ByRef { alloc, offset } => {
175+
ConstValue::Indirect { alloc_id, offset } => {
176+
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
176177
let size = Size::from_bytes(
177178
4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */
178179
);

compiler/rustc_codegen_ssa/src/mir/operand.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
105105
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
106106
};
107107
let a = Scalar::from_pointer(
108-
Pointer::new(bx.tcx().create_memory_alloc(data), Size::from_bytes(start)),
108+
Pointer::new(
109+
bx.tcx().reserve_and_set_memory_alloc(data),
110+
Size::from_bytes(start),
111+
),
109112
&bx.tcx(),
110113
);
111114
let a_llval = bx.scalar_to_backend(
@@ -116,7 +119,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
116119
let b_llval = bx.const_usize((end - start) as u64);
117120
OperandValue::Pair(a_llval, b_llval)
118121
}
119-
ConstValue::ByRef { alloc, offset } => {
122+
ConstValue::Indirect { alloc_id, offset } => {
123+
let alloc = bx.tcx().global_alloc(alloc_id).unwrap_memory();
120124
return Self::from_const_alloc(bx, layout, alloc, offset);
121125
}
122126
};
@@ -182,6 +186,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
182186
_ if layout.is_zst() => OperandRef::zero_sized(layout),
183187
_ => {
184188
// Neither a scalar nor scalar pair. Load from a place
189+
// FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
190+
// same `ConstAllocation`?
185191
let init = bx.const_data_from_alloc(alloc);
186192
let base_addr = bx.static_addr_of(init, alloc_align, None);
187193

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+41-64
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
1818
use crate::errors;
1919
use crate::interpret::eval_nullary_intrinsic;
2020
use crate::interpret::{
21-
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
22-
Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
23-
RefTracking, StackPopCleanup,
21+
intern_const_alloc_recursive, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, Immediate,
22+
InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
23+
StackPopCleanup,
2424
};
2525

2626
// Returns a pointer to where the result lives
@@ -105,91 +105,68 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
105105
)
106106
}
107107

108-
/// This function converts an interpreter value into a constant that is meant for use in the
109-
/// type system.
108+
/// This function converts an interpreter value into a MIR constant.
110109
#[instrument(skip(ecx), level = "debug")]
111110
pub(super) fn op_to_const<'tcx>(
112111
ecx: &CompileTimeEvalContext<'_, 'tcx>,
113112
op: &OpTy<'tcx>,
114113
) -> ConstValue<'tcx> {
115-
// We do not have value optimizations for everything.
116-
// Only scalars and slices, since they are very common.
117-
// Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result
118-
// from scalar unions that are initialized with one of their zero sized variants. We could
119-
// instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
120-
// the usual cases of extracting e.g. a `usize`, without there being a real use case for the
121-
// `Undef` situation.
122-
let try_as_immediate = match op.layout.abi {
114+
// Handle ZST consistently and early.
115+
if op.layout.is_zst() {
116+
return ConstValue::ZeroSized;
117+
}
118+
119+
// All scalar types should be stored as `ConstValue::Scalar`. This is needed to make
120+
// `ConstValue::try_to_scalar` efficient; we want that to work for *all* constants of scalar
121+
// type (it's used throughout the compiler and having it work just on literals is not enough)
122+
// and we want it to be fast (i.e., don't go to an `Allocation` and reconstruct the `Scalar`
123+
// from its byte-serialized form).
124+
let force_as_immediate = match op.layout.abi {
123125
Abi::Scalar(abi::Scalar::Initialized { .. }) => true,
124-
Abi::ScalarPair(..) => match op.layout.ty.kind() {
125-
ty::Ref(_, inner, _) => match *inner.kind() {
126-
ty::Slice(elem) => elem == ecx.tcx.types.u8,
127-
ty::Str => true,
128-
_ => false,
129-
},
130-
_ => false,
131-
},
126+
// We don't *force* `ConstValue::Slice` for `ScalarPair`. This has the advantage that if the
127+
// input `op` is a place, then turning it into a `ConstValue` and back into a `OpTy` will
128+
// not have to generate any duplicate allocations (we preserve the original `AllocId` in
129+
// `ConstValue::Indirect`). It means accessing the contents of a slice can be slow (since
130+
// they can be stored as `ConstValue::Indirect`), but that's not relevant since we barely
131+
// ever have to do this. (`try_get_slice_bytes_for_diagnostics` exists to provide this
132+
// functionality.)
132133
_ => false,
133134
};
134-
let immediate = if try_as_immediate {
135+
let immediate = if force_as_immediate {
135136
Right(ecx.read_immediate(op).expect("normalization works on validated constants"))
136137
} else {
137-
// It is guaranteed that any non-slice scalar pair is actually ByRef here.
138-
// When we come back from raw const eval, we are always by-ref. The only way our op here is
139-
// by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
140-
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
141-
// structs containing such.
142138
op.as_mplace_or_imm()
143139
};
144140

145141
debug!(?immediate);
146142

147-
// We know `offset` is relative to the allocation, so we can use `into_parts`.
148-
let to_const_value = |mplace: &MPlaceTy<'_>| {
149-
debug!("to_const_value(mplace: {:?})", mplace);
150-
match mplace.ptr().into_parts() {
151-
(Some(alloc_id), offset) => {
152-
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
153-
ConstValue::ByRef { alloc, offset }
154-
}
155-
(None, offset) => {
156-
assert!(mplace.layout.is_zst());
157-
assert_eq!(
158-
offset.bytes() % mplace.layout.align.abi.bytes(),
159-
0,
160-
"this MPlaceTy must come from a validated constant, thus we can assume the \
161-
alignment is correct",
162-
);
163-
ConstValue::ZeroSized
164-
}
165-
}
166-
};
167143
match immediate {
168-
Left(ref mplace) => to_const_value(mplace),
169-
// see comment on `let try_as_immediate` above
144+
Left(ref mplace) => {
145+
// We know `offset` is relative to the allocation, so we can use `into_parts`.
146+
let (alloc_id, offset) = mplace.ptr().into_parts();
147+
let alloc_id = alloc_id.expect("cannot have `fake` place fot non-ZST type");
148+
ConstValue::Indirect { alloc_id, offset }
149+
}
150+
// see comment on `let force_as_immediate` above
170151
Right(imm) => match *imm {
171-
_ if imm.layout.is_zst() => ConstValue::ZeroSized,
172152
Immediate::Scalar(x) => ConstValue::Scalar(x),
173153
Immediate::ScalarPair(a, b) => {
174154
debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
155+
// FIXME: assert that this has an appropriate type.
156+
// Currently we actually get here for non-[u8] slices during valtree construction!
157+
let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to actually allocated memory";
175158
// We know `offset` is relative to the allocation, so we can use `into_parts`.
176-
let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() {
177-
(Some(alloc_id), offset) => {
178-
(ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
179-
}
180-
(None, _offset) => (
181-
ecx.tcx.mk_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
182-
b"" as &[u8],
183-
)),
184-
0,
185-
),
186-
};
187-
let len = b.to_target_usize(ecx).unwrap();
188-
let start = start.try_into().unwrap();
159+
// We use `ConstValue::Slice` so that we don't have to generate an allocation for
160+
// `ConstValue::Indirect` here.
161+
let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
162+
let alloc_id = alloc_id.expect(msg);
163+
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
164+
let start = offset.bytes_usize();
165+
let len = b.to_target_usize(ecx).expect(msg);
189166
let len: usize = len.try_into().unwrap();
190167
ConstValue::Slice { data, start, end: start + len }
191168
}
192-
Immediate::Uninit => to_const_value(&op.assert_mem_place()),
169+
Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
193170
},
194171
}
195172
}

compiler/rustc_const_eval/src/interpret/cast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
8484
)
8585
.ok_or_else(|| err_inval!(TooGeneric))?;
8686

87-
let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
87+
let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
8888
self.write_pointer(fn_ptr, dest)?;
8989
}
9090
_ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty),
@@ -116,7 +116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
116116
ty::ClosureKind::FnOnce,
117117
)
118118
.ok_or_else(|| err_inval!(TooGeneric))?;
119-
let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
119+
let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
120120
self.write_pointer(fn_ptr, dest)?;
121121
}
122122
_ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty),

compiler/rustc_const_eval/src/interpret/intern.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use rustc_middle::ty::{self, layout::TyAndLayout, Ty};
2424
use rustc_ast::Mutability;
2525

2626
use super::{
27-
AllocId, Allocation, ConstAllocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy,
28-
Projectable, ValueVisitor,
27+
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, Projectable,
28+
ValueVisitor,
2929
};
3030
use crate::const_eval;
3131
use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
@@ -455,19 +455,23 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
455455
{
456456
/// A helper function that allocates memory for the layout given and gives you access to mutate
457457
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
458-
/// current `Memory` and returned.
458+
/// current `Memory` and interned as read-only into the global memory.
459459
pub fn intern_with_temp_alloc(
460460
&mut self,
461461
layout: TyAndLayout<'tcx>,
462462
f: impl FnOnce(
463463
&mut InterpCx<'mir, 'tcx, M>,
464464
&PlaceTy<'tcx, M::Provenance>,
465465
) -> InterpResult<'tcx, ()>,
466-
) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
466+
) -> InterpResult<'tcx, AllocId> {
467+
// `allocate` picks a fresh AllocId that we will associate with its data below.
467468
let dest = self.allocate(layout, MemoryKind::Stack)?;
468469
f(self, &dest.clone().into())?;
469470
let mut alloc = self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap()).unwrap().1;
470471
alloc.mutability = Mutability::Not;
471-
Ok(self.tcx.mk_const_alloc(alloc))
472+
let alloc = self.tcx.mk_const_alloc(alloc);
473+
let alloc_id = dest.ptr().provenance.unwrap(); // this was just allocated, it must have provenance
474+
self.tcx.set_alloc_id_memory(alloc_id, alloc);
475+
Ok(alloc_id)
472476
}
473477
}

compiler/rustc_const_eval/src/interpret/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
555555
def_id: DefId,
556556
) -> InterpResult<$tcx, Pointer> {
557557
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
558-
Ok(Pointer::new(ecx.tcx.create_static_alloc(def_id), Size::ZERO))
558+
Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id), Size::ZERO))
559559
}
560560

561561
#[inline(always)]

compiler/rustc_const_eval/src/interpret/memory.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
176176
M::adjust_alloc_base_pointer(self, ptr)
177177
}
178178

179-
pub fn create_fn_alloc_ptr(
180-
&mut self,
181-
fn_val: FnVal<'tcx, M::ExtraFnVal>,
182-
) -> Pointer<M::Provenance> {
179+
pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer<M::Provenance> {
183180
let id = match fn_val {
184-
FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance),
181+
FnVal::Instance(instance) => self.tcx.reserve_and_set_fn_alloc(instance),
185182
FnVal::Other(extra) => {
186183
// FIXME(RalfJung): Should we have a cache here?
187184
let id = self.tcx.reserve_alloc_id();

compiler/rustc_const_eval/src/interpret/operand.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -756,11 +756,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
756756
};
757757
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
758758
let op = match val_val {
759-
ConstValue::ByRef { alloc, offset } => {
760-
let id = self.tcx.create_memory_alloc(alloc);
759+
ConstValue::Indirect { alloc_id, offset } => {
761760
// We rely on mutability being set correctly in that allocation to prevent writes
762761
// where none should happen.
763-
let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
762+
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
764763
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
765764
}
766765
ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
@@ -769,7 +768,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
769768
// We rely on mutability being set correctly in `data` to prevent writes
770769
// where none should happen.
771770
let ptr = Pointer::new(
772-
self.tcx.create_memory_alloc(data),
771+
self.tcx.reserve_and_set_memory_alloc(data),
773772
Size::from_bytes(start), // offset: `start`
774773
);
775774
Operand::Immediate(Immediate::new_slice(

compiler/rustc_const_eval/src/interpret/traits.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
2727
ensure_monomorphic_enough(*self.tcx, ty)?;
2828
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
2929

30-
let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref);
30+
let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref);
3131
let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?;
3232
Ok(vtable_ptr.into())
3333
}

0 commit comments

Comments
 (0)