Skip to content

Commit a255de8

Browse files
committed
Reinstate ConstValueKind::Slice.
1 parent a8c8dea commit a255de8

File tree

36 files changed

+173
-257
lines changed

36 files changed

+173
-257
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

+11
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,17 @@ pub(crate) fn codegen_const_value<'tcx>(
209209
layout,
210210
),
211211
ConstValueKind::ScalarPair(..) => todo!(),
212+
ConstValueKind::Slice { data, start, end } => {
213+
let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
214+
let ptr = pointer_for_allocation(fx, alloc_id)
215+
.offset_i64(fx, i64::try_from(start).unwrap())
216+
.get_addr(fx);
217+
let len = fx
218+
.bcx
219+
.ins()
220+
.iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap());
221+
CValue::by_val_pair(ptr, len, layout)
222+
}
212223
}
213224
}
214225

compiler/rustc_codegen_ssa/src/mir/operand.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::traits::*;
77
use crate::MemFlags;
88

99
use rustc_middle::mir;
10-
use rustc_middle::mir::interpret::{alloc_range, ConstValue, ConstValueKind};
10+
use rustc_middle::mir::interpret::{alloc_range, ConstValue, ConstValueKind, Pointer, Scalar};
1111
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
1212
use rustc_middle::ty::Ty;
1313
use rustc_target::abi::{self, Abi, Align, Size};
@@ -102,7 +102,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
102102
ConstValueKind::ZeroSized => return OperandRef::zero_sized(layout),
103103
ConstValueKind::ScalarPair(a, b) => {
104104
let Abi::ScalarPair(a_scalar, b_scalar) = layout.abi else {
105-
bug!("from_const: invalid ByVal layout: {:#?}", layout);
105+
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
106106
};
107107
let a_llval = bx.scalar_to_backend(
108108
a,
@@ -116,6 +116,25 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
116116
);
117117
OperandValue::Pair(a_llval, b_llval)
118118
}
119+
ConstValueKind::Slice { data, start, end } => {
120+
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
121+
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
122+
};
123+
let a = Scalar::from_pointer(
124+
Pointer::new(
125+
bx.tcx().reserve_and_set_memory_alloc(data),
126+
Size::from_bytes(start),
127+
),
128+
&bx.tcx(),
129+
);
130+
let a_llval = bx.scalar_to_backend(
131+
a,
132+
a_scalar,
133+
bx.scalar_pair_element_backend_type(layout, 0, true),
134+
);
135+
let b_llval = bx.const_usize((end - start) as u64);
136+
OperandValue::Pair(a_llval, b_llval)
137+
}
119138
ConstValueKind::Indirect { alloc_id, offset } => {
120139
let alloc = bx.tcx().global_alloc(alloc_id).unwrap_memory();
121140
return Self::from_const_alloc(bx, layout, alloc, offset);

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use rustc_hir::def_id::DefId;
66
use rustc_middle::mir::{
77
self,
8-
interpret::{AllocId, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar},
8+
interpret::{
9+
Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
10+
},
911
BinOp, NonDivergingIntrinsic,
1012
};
1113
use rustc_middle::ty;
@@ -42,11 +44,10 @@ fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<
4244
}
4345

4446
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
45-
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, usize) {
47+
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
4648
let path = crate::util::type_name(tcx, ty);
47-
let bytes = path.into_bytes();
48-
let len = bytes.len();
49-
(tcx.allocate_bytes(bytes), len)
49+
let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes());
50+
tcx.mk_const_alloc(alloc)
5051
}
5152

5253
/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
@@ -62,8 +63,8 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
6263
Ok(match name {
6364
sym::type_name => {
6465
ensure_monomorphic_enough(tcx, tp_ty)?;
65-
let (alloc, len) = alloc_type_name(tcx, tp_ty);
66-
ConstValue::from_slice(tcx, Pointer::from(alloc), len)
66+
let alloc = alloc_type_name(tcx, tp_ty);
67+
ConstValue::from_raw_slice(tcx, alloc, 0, alloc.inner().len())
6768
}
6869
sym::needs_drop => {
6970
ensure_monomorphic_enough(tcx, tp_ty)?;

compiler/rustc_const_eval/src/interpret/operand.rs

+13
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
775775
Operand::Immediate(Immediate::ScalarPair(adjust_scalar(a)?, adjust_scalar(b)?))
776776
}
777777
ConstValueKind::ZeroSized => Operand::Immediate(Immediate::Uninit),
778+
ConstValueKind::Slice { data, start, end } => {
779+
// We rely on mutability being set correctly in `data` to prevent writes
780+
// where none should happen.
781+
let ptr = Pointer::new(
782+
self.tcx.reserve_and_set_memory_alloc(data),
783+
Size::from_bytes(start), // offset: `start`
784+
);
785+
Operand::Immediate(Immediate::new_slice(
786+
Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx),
787+
u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start`
788+
self,
789+
))
790+
}
778791
};
779792
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
780793
}

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

+27-42
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use rustc_target::abi::{HasDataLayout, Size};
1313
use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
1414

1515
use super::{
16-
alloc_range, AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, ScalarSizeMismatch,
16+
AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
17+
ScalarSizeMismatch,
1718
};
1819

1920
/// Represents the result of const evaluation via the `eval_to_allocation` query.
@@ -27,13 +28,13 @@ pub struct ConstAlloc<'tcx> {
2728

2829
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
2930
#[derive(HashStable)]
30-
pub struct ConstValue<'tcx>(pub(crate) Interned<'tcx, ConstValueKind>);
31+
pub struct ConstValue<'tcx>(pub(crate) Interned<'tcx, ConstValueKind<'tcx>>);
3132

3233
/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
3334
/// array length computations, enum discriminants and the pattern matching logic.
3435
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
35-
#[derive(HashStable)]
36-
pub enum ConstValueKind {
36+
#[derive(HashStable, Lift)]
37+
pub enum ConstValueKind<'tcx> {
3738
/// Used for types with `layout::abi::Scalar` ABI.
3839
///
3940
/// Not using the enum `Value` to encode that this must not be `Uninit`.
@@ -47,9 +48,16 @@ pub enum ConstValueKind {
4748
/// Only for ZSTs.
4849
ZeroSized,
4950

51+
/// Used for `&[u8]` and `&str`.
52+
///
53+
/// This is worth an optimized representation since Rust has literals of these types.
54+
/// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
55+
/// measurable performance improvements on stress tests.
56+
Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
57+
5058
/// A value not representable by the other variants; needs to be stored in-memory.
5159
///
52-
/// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
60+
/// Must *not* be used for scalars, scalar pairs or ZST.
5361
Indirect {
5462
/// The backing memory of the value. May contain more memory than needed for just the value
5563
/// if this points into some other larger ConstValue.
@@ -68,12 +76,12 @@ static_assert_size!(ConstValue<'_>, 8);
6876

6977
impl<'tcx> ConstValue<'tcx> {
7078
#[inline]
71-
pub fn new(tcx: TyCtxt<'tcx>, kind: ConstValueKind) -> ConstValue<'tcx> {
79+
pub fn new(tcx: TyCtxt<'tcx>, kind: ConstValueKind<'tcx>) -> ConstValue<'tcx> {
7280
tcx.intern_const_value(kind)
7381
}
7482

7583
#[inline]
76-
pub fn kind(self) -> &'tcx ConstValueKind {
84+
pub fn kind(self) -> &'tcx ConstValueKind<'tcx> {
7785
self.0.0
7886
}
7987

@@ -82,6 +90,7 @@ impl<'tcx> ConstValue<'tcx> {
8290
match self.kind() {
8391
ConstValueKind::Indirect { .. }
8492
| ConstValueKind::ScalarPair(..)
93+
| ConstValueKind::Slice { .. }
8594
| ConstValueKind::ZeroSized => None,
8695
ConstValueKind::Scalar(val) => Some(*val),
8796
}
@@ -162,6 +171,16 @@ impl<'tcx> ConstValue<'tcx> {
162171
)
163172
}
164173

174+
#[inline]
175+
pub fn from_raw_slice(
176+
tcx: TyCtxt<'tcx>,
177+
data: ConstAllocation<'tcx>,
178+
start: usize,
179+
end: usize,
180+
) -> Self {
181+
Self::new(tcx, ConstValueKind::Slice { data, start, end })
182+
}
183+
165184
#[inline]
166185
pub fn from_memory(tcx: TyCtxt<'tcx>, alloc_id: AllocId, offset: Size) -> Self {
167186
debug_assert!(matches!(tcx.global_alloc(alloc_id), super::GlobalAlloc::Memory(_)));
@@ -178,41 +197,7 @@ impl<'tcx> ConstValue<'tcx> {
178197
let length = length.try_to_target_usize(tcx).unwrap() as usize;
179198
(alloc, start, start + length)
180199
}
181-
&ConstValueKind::Indirect { alloc_id, offset } => {
182-
// The reference itself is stored behind an indirection.
183-
// Load the reference, and then load the actual slice contents.
184-
let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
185-
let ptr_size = tcx.data_layout.pointer_size;
186-
if a.size() < offset + 2 * ptr_size {
187-
// (partially) dangling reference
188-
return None;
189-
}
190-
// Read the wide pointer components.
191-
let ptr = a
192-
.read_scalar(
193-
&tcx,
194-
alloc_range(offset, ptr_size),
195-
/* read_provenance */ true,
196-
)
197-
.ok()?;
198-
let ptr = ptr.to_pointer(&tcx).ok()?;
199-
let len = a
200-
.read_scalar(
201-
&tcx,
202-
alloc_range(offset + ptr_size, ptr_size),
203-
/* read_provenance */ false,
204-
)
205-
.ok()?;
206-
let len = len.to_target_usize(&tcx).ok()?;
207-
let len: usize = len.try_into().ok()?;
208-
if len == 0 {
209-
return Some(&[]);
210-
}
211-
// Non-empty slice, must have memory. We know this is a relative pointer.
212-
let (inner_alloc_id, offset) = ptr.into_parts();
213-
let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
214-
(data, offset.bytes_usize(), offset.bytes_usize() + len)
215-
}
200+
&ConstValueKind::Slice { data, start, end } => (data, start, end),
216201
_ => {
217202
bug!("`try_get_slice_bytes` on non-slice constant")
218203
}

compiler/rustc_middle/src/mir/pretty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
459459
ConstValueKind::ZeroSized => "<ZST>".to_string(),
460460
ConstValueKind::Scalar(s) => format!("Scalar({s:?})"),
461461
ConstValueKind::ScalarPair(a, b) => format!("ScalarPair({a:?}, {b:?})"),
462+
ConstValueKind::Slice { .. } => "Slice(..)".to_string(),
462463
ConstValueKind::Indirect { .. } => "ByRef(..)".to_string(),
463464
};
464465

@@ -713,6 +714,10 @@ pub fn write_allocations<'tcx>(
713714
ConstValueKind::ScalarPair(a, b) => {
714715
alloc_ids_from_scalar(a).into_iter().chain(alloc_ids_from_scalar(b).into_iter())
715716
}
717+
ConstValueKind::Slice { .. } => {
718+
// `u8`/`str` slices, shouldn't contain pointers that we want to print.
719+
None.into_iter().chain(None.into_iter())
720+
}
716721
ConstValueKind::Indirect { alloc_id, .. } => {
717722
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
718723
// Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.

compiler/rustc_middle/src/ty/context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pub struct CtxtInterners<'tcx> {
156156
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
157157
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
158158
fields: InternedSet<'tcx, List<FieldIdx>>,
159-
const_value: InternedSet<'tcx, ConstValueKind>,
159+
const_value: InternedSet<'tcx, ConstValueKind<'tcx>>,
160160
}
161161

162162
impl<'tcx> CtxtInterners<'tcx> {
@@ -1499,7 +1499,7 @@ direct_interners! {
14991499
region: pub(crate) intern_region(RegionKind<'tcx>): Region -> Region<'tcx>,
15001500
const_: intern_const(ConstData<'tcx>): Const -> Const<'tcx>,
15011501
const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
1502-
const_value: pub(crate) intern_const_value(ConstValueKind): ConstValue -> ConstValue<'tcx>,
1502+
const_value: pub(crate) intern_const_value(ConstValueKind<'tcx>): ConstValue -> ConstValue<'tcx>,
15031503
layout: pub mk_layout(LayoutS): Layout -> Layout<'tcx>,
15041504
adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>,
15051505
external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>):

compiler/rustc_middle/src/ty/structural_impls.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
627627
impl<'a, 'tcx> Lift<'tcx> for interpret::ConstValue<'a> {
628628
type Lifted = interpret::ConstValue<'tcx>;
629629
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
630-
Some(interpret::ConstValue::new(tcx, *self.kind()))
630+
tcx.lift(*self.kind()).map(|kind| interpret::ConstValue::new(tcx, kind))
631631
}
632632
}
633633

compiler/rustc_mir_build/src/build/expr/as_constant.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
use crate::build::{parse_float_into_constval, Builder};
44
use rustc_ast as ast;
55
use rustc_middle::mir;
6-
use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar};
6+
use rustc_middle::mir::interpret::{
7+
Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
8+
};
79
use rustc_middle::mir::*;
810
use rustc_middle::thir::*;
911
use rustc_middle::ty::{
@@ -128,24 +130,27 @@ fn lit_to_mir_constant<'tcx>(
128130

129131
let value = match (lit, &ty.kind()) {
130132
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
131-
let s = s.as_str().as_bytes();
132-
let allocation = tcx.allocate_bytes(s);
133-
ConstValue::from_slice(tcx, allocation.into(), s.len())
133+
let s = s.as_str();
134+
let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
135+
let allocation = tcx.mk_const_alloc(allocation);
136+
ConstValue::from_raw_slice(tcx, allocation, 0, s.len())
134137
}
135138
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
136139
if matches!(inner_ty.kind(), ty::Slice(_)) =>
137140
{
138-
let allocation = tcx.allocate_bytes(data as &[u8]);
139-
ConstValue::from_slice(tcx, allocation.into(), data.len())
141+
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
142+
let allocation = tcx.mk_const_alloc(allocation);
143+
ConstValue::from_raw_slice(tcx, allocation, 0, data.len())
140144
}
141145
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
142146
let id = tcx.allocate_bytes(&**data);
143147
ConstValue::from_pointer(tcx, id.into())
144148
}
145149
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
146150
{
147-
let allocation = tcx.allocate_bytes(data as &[u8]);
148-
ConstValue::from_slice(tcx, allocation.into(), data.len())
151+
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
152+
let allocation = tcx.mk_const_alloc(allocation);
153+
ConstValue::from_raw_slice(tcx, allocation, 0, data.len())
149154
}
150155
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
151156
ConstValue::from_scalar(tcx, Scalar::from_uint(*n, Size::from_bytes(1)))

compiler/rustc_monomorphize/src/collector.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1461,5 +1461,10 @@ fn collect_const_value<'tcx>(
14611461
}
14621462
ConstValueKind::ZeroSized => {}
14631463
ConstValueKind::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
1464+
ConstValueKind::Slice { data, start: _, end: _ } => {
1465+
for &id in data.inner().provenance().ptrs().values() {
1466+
collect_alloc(tcx, id, output);
1467+
}
1468+
}
14641469
}
14651470
}

compiler/rustc_smir/src/rustc_smir/alloc.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_middle::mir::interpret::{alloc_range, AllocRange, ConstValue, ConstValueKind};
1+
use rustc_middle::mir::interpret::{alloc_range, AllocRange, ConstValue, ConstValueKind, Pointer};
22

33
use crate::{
44
rustc_smir::{Stable, Tables},
@@ -61,6 +61,34 @@ pub fn new_allocation<'tcx>(
6161
.unwrap();
6262
allocation.stable(tables)
6363
}
64+
ConstValueKind::Slice { data, start, end } => {
65+
let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
66+
let ptr = Pointer::new(alloc_id, rustc_target::abi::Size::from_bytes(start));
67+
let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
68+
let scalar_len = rustc_middle::mir::interpret::Scalar::from_target_usize(
69+
(end - start) as u64,
70+
&tables.tcx,
71+
);
72+
let layout =
73+
tables.tcx.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty)).unwrap();
74+
let mut allocation =
75+
rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi);
76+
allocation
77+
.write_scalar(
78+
&tables.tcx,
79+
alloc_range(rustc_target::abi::Size::ZERO, tables.tcx.data_layout.pointer_size),
80+
scalar_ptr,
81+
)
82+
.unwrap();
83+
allocation
84+
.write_scalar(
85+
&tables.tcx,
86+
alloc_range(tables.tcx.data_layout.pointer_size, scalar_len.size()),
87+
scalar_len,
88+
)
89+
.unwrap();
90+
allocation.stable(tables)
91+
}
6492
ConstValueKind::Indirect { alloc_id, offset } => {
6593
let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
6694
let ty_size = tables

tests/mir-opt/building/issue_101867.main.built.after.mir

-4
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,3 @@ fn main() -> () {
6767
resume;
6868
}
6969
}
70-
71-
alloc1 (size: 14, align: 1) {
72-
65 78 70 6c 69 63 69 74 20 70 61 6e 69 63 │ explicit panic
73-
}

0 commit comments

Comments
 (0)