Skip to content

Commit 2c5e0bb

Browse files
committed
don't force all slice-typed ConstValue to be ConstValue::Slice
1 parent 430c386 commit 2c5e0bb

File tree

3 files changed

+27
-50
lines changed

3 files changed

+27
-50
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 20 additions & 32 deletions
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
@@ -119,26 +119,17 @@ pub(super) fn op_to_const<'tcx>(
119119

120120
// We do not have value optimizations for everything.
121121
// Only scalars and slices, since they are very common.
122-
let try_as_immediate = match op.layout.abi {
122+
let force_as_immediate = match op.layout.abi {
123123
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-
},
124+
// We don't *force* `ConstValue::Slice` for `ScalarPair`. This has the advantage that if the
125+
// input `op` is a place, then turning it into a `ConstValue` and back into a `OpTy` will
126+
// not have to generate any duplicate allocations (we preserve the original `AllocId` in
127+
// `ConstValue::Indirect`).
132128
_ => false,
133129
};
134-
let immediate = if try_as_immediate {
130+
let immediate = if force_as_immediate {
135131
Right(ecx.read_immediate(op).expect("normalization works on validated constants"))
136132
} else {
137-
// It is guaranteed that any non-slice scalar pair is actually `Indirect` 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.
142133
op.as_mplace_or_imm()
143134
};
144135

@@ -151,25 +142,22 @@ pub(super) fn op_to_const<'tcx>(
151142
let alloc_id = alloc_id.expect("cannot have `fake` place fot non-ZST type");
152143
ConstValue::Indirect { alloc_id, offset }
153144
}
154-
// see comment on `let try_as_immediate` above
145+
// see comment on `let force_as_immediate` above
155146
Right(imm) => match *imm {
156147
Immediate::Scalar(x) => ConstValue::Scalar(x),
157148
Immediate::ScalarPair(a, b) => {
158149
debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
150+
// FIXME: assert that this has an appropriate type.
151+
// Currently we actually get here for non-[u8] slices during valtree construction!
152+
let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to actually allocated memory";
159153
// We know `offset` is relative to the allocation, so we can use `into_parts`.
160-
let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() {
161-
(Some(alloc_id), offset) => {
162-
(ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
163-
}
164-
(None, _offset) => (
165-
ecx.tcx.mk_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
166-
b"" as &[u8],
167-
)),
168-
0,
169-
),
170-
};
171-
let len = b.to_target_usize(ecx).unwrap();
172-
let start = start.try_into().unwrap();
154+
// We use `ConstValue::Slice` so that we don't have to generate an allocation for
155+
// `ConstValue::Indirect` here.
156+
let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
157+
let alloc_id = alloc_id.expect(msg);
158+
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
159+
let start = offset.bytes_usize();
160+
let len = b.to_target_usize(ecx).expect(msg);
173161
let len: usize = len.try_into().unwrap();
174162
ConstValue::Slice { data, start, end: start + len }
175163
}

compiler/rustc_middle/src/mir/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub use self::error::{
149149
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
150150
};
151151

152-
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
152+
pub use self::value::{ConstAlloc, ConstValue, Scalar};
153153

154154
pub use self::allocation::{
155155
alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,

compiler/rustc_middle/src/mir/interpret/value.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_target::abi::{HasDataLayout, Size};
1212
use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
1313

1414
use super::{
15-
AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
15+
AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
1616
ScalarSizeMismatch,
1717
};
1818

@@ -40,10 +40,14 @@ pub enum ConstValue<'tcx> {
4040

4141
/// Used for `&[u8]` and `&str`.
4242
///
43-
/// This is worth the optimization since Rust has literals of that type.
43+
/// This is worth an optimized representation since Rust has literals of these types.
44+
/// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
45+
/// measurable performance improvements on stress tests.
4446
Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
4547

4648
/// A value not representable by the other variants; needs to be stored in-memory.
49+
///
50+
/// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
4751
Indirect {
4852
/// The backing memory of the value. May contain more memory than needed for just the value
4953
/// if this points into some other larger ConstValue.
@@ -511,18 +515,3 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
511515
Ok(Double::from_bits(self.to_u64()?.into()))
512516
}
513517
}
514-
515-
/// Gets the bytes of a constant slice value.
516-
pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
517-
if let ConstValue::Slice { data, start, end } = val {
518-
let len = end - start;
519-
data.inner()
520-
.get_bytes_strip_provenance(
521-
cx,
522-
AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
523-
)
524-
.unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
525-
} else {
526-
bug!("expected const slice, but found another const value");
527-
}
528-
}

0 commit comments

Comments
 (0)