Skip to content

Commit e6f70a5

Browse files
committed
add a Vtable kind of symbolic allocations
1 parent 967a9c9 commit e6f70a5

File tree

11 files changed

+106
-15
lines changed

11 files changed

+106
-15
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,13 @@ pub(crate) fn codegen_const_value<'tcx>(
195195
}
196196
Scalar::Ptr(ptr, _size) => {
197197
let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
198-
let alloc_kind = fx.tcx.get_global_alloc(alloc_id);
199-
let base_addr = match alloc_kind {
200-
Some(GlobalAlloc::Memory(alloc)) => {
198+
// For vtables, get the underlying data allocation.
199+
let alloc_id = match fx.tcx.global_alloc(alloc_id) {
200+
GlobalAlloc::Vtable(ty, trait_ref) => fx.tcx.vtable_allocation((ty, trait_ref)),
201+
_ => alloc_id,
202+
};
203+
let base_addr = match fx.tcx.global_alloc(alloc_id) {
204+
GlobalAlloc::Memory(alloc) => {
201205
let data_id = data_id_for_alloc_id(
202206
&mut fx.constants_cx,
203207
fx.module,
@@ -211,13 +215,14 @@ pub(crate) fn codegen_const_value<'tcx>(
211215
}
212216
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
213217
}
214-
Some(GlobalAlloc::Function(instance)) => {
218+
GlobalAlloc::Function(instance) => {
215219
let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
216220
let local_func_id =
217221
fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
218222
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
219223
}
220-
Some(GlobalAlloc::Static(def_id)) => {
224+
GlobalAlloc::Vtable(..) => bug!("vtables are already handled"),
225+
GlobalAlloc::Static(def_id) => {
221226
assert!(fx.tcx.is_static(def_id));
222227
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
223228
let local_data_id =
@@ -227,7 +232,6 @@ pub(crate) fn codegen_const_value<'tcx>(
227232
}
228233
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
229234
}
230-
None => bug!("missing allocation {:?}", alloc_id),
231235
};
232236
let val = if offset.bytes() != 0 {
233237
fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap())
@@ -360,7 +364,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
360364
//println!("alloc_id {}", alloc_id);
361365
let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
362366
GlobalAlloc::Memory(alloc) => alloc,
363-
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
367+
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::Vtable(..) => {
368+
unreachable!()
369+
}
364370
};
365371
let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
366372
module
@@ -424,7 +430,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
424430
read_target_uint(endianness, bytes).unwrap()
425431
};
426432

427-
let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap();
433+
let reloc_target_alloc = tcx.global_alloc(alloc_id);
428434
let data_id = match reloc_target_alloc {
429435
GlobalAlloc::Function(instance) => {
430436
assert_eq!(addend, 0);
@@ -436,6 +442,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
436442
GlobalAlloc::Memory(target_alloc) => {
437443
data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
438444
}
445+
GlobalAlloc::Vtable(ty, trait_ref) => {
446+
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
447+
data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
448+
}
439449
GlobalAlloc::Static(def_id) => {
440450
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
441451
{

compiler/rustc_codegen_gcc/src/common.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
183183
}
184184
Scalar::Ptr(ptr, _size) => {
185185
let (alloc_id, offset) = ptr.into_parts();
186+
// For vtables, get the underlying data allocation.
187+
let alloc_id = match self.tcx.global_alloc(alloc_id) {
188+
GlobalAlloc::Vtable(ty, trait_ref) => {
189+
self.tcx.vtable_allocation((ty, trait_ref))
190+
}
191+
_ => alloc_id,
192+
};
186193
let base_addr =
187194
match self.tcx.global_alloc(alloc_id) {
188195
GlobalAlloc::Memory(alloc) => {
@@ -201,6 +208,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
201208
GlobalAlloc::Function(fn_instance) => {
202209
self.get_fn_addr(fn_instance)
203210
},
211+
GlobalAlloc::Vtable(..) => panic!("vtables are already handled"),
204212
GlobalAlloc::Static(def_id) => {
205213
assert!(self.tcx.is_static(def_id));
206214
self.get_static(def_id).get_address(None)

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
240240
}
241241
Scalar::Ptr(ptr, _size) => {
242242
let (alloc_id, offset) = ptr.into_parts();
243+
// For vtables, get the underlying data allocation.
244+
let alloc_id = match self.tcx.global_alloc(alloc_id) {
245+
GlobalAlloc::Vtable(ty, trait_ref) => {
246+
self.tcx.vtable_allocation((ty, trait_ref))
247+
}
248+
_ => alloc_id,
249+
};
243250
let (base_addr, base_addr_space) = match self.tcx.global_alloc(alloc_id) {
244251
GlobalAlloc::Memory(alloc) => {
245252
let init = const_alloc_to_llvm(self, alloc);
@@ -257,6 +264,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
257264
self.get_fn_addr(fn_instance.polymorphize(self.tcx)),
258265
self.data_layout().instruction_address_space,
259266
),
267+
GlobalAlloc::Vtable(..) => bug!("vtables are already handled"),
260268
GlobalAlloc::Static(def_id) => {
261269
assert!(self.tcx.is_static(def_id));
262270
assert!(!self.tcx.is_thread_local_static(def_id));

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
101101

102102
let address_space = match cx.tcx.global_alloc(alloc_id) {
103103
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
104-
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA,
104+
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::Vtable(..) => {
105+
AddressSpace::DATA
106+
}
105107
};
106108

107109
llvals.push(cx.scalar_to_backend(

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub enum AllocKind {
6262
LiveData,
6363
/// A function allocation (that fn ptrs point to).
6464
Function,
65+
/// A (symbolic) vtable allocation.
66+
Vtable,
6567
/// A dead allocation.
6668
Dead,
6769
}
@@ -291,6 +293,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
291293
Some(GlobalAlloc::Function(..)) => {
292294
err_ub_format!("deallocating {alloc_id:?}, which is a function")
293295
}
296+
Some(GlobalAlloc::Vtable(..)) => {
297+
err_ub_format!("deallocating {alloc_id:?}, which is a vtable")
298+
}
294299
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
295300
err_ub_format!("deallocating {alloc_id:?}, which is static memory")
296301
}
@@ -475,6 +480,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
475480
(mem, None)
476481
}
477482
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
483+
Some(GlobalAlloc::Vtable(..)) => throw_ub!(DerefVtablePointer(id)),
478484
None => throw_ub!(PointerUseAfterFree(id)),
479485
Some(GlobalAlloc::Static(def_id)) => {
480486
assert!(self.tcx.is_static(def_id));
@@ -674,6 +680,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
674680
(alloc.size(), alloc.align, AllocKind::LiveData)
675681
}
676682
Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
683+
Some(GlobalAlloc::Vtable(..)) => {
684+
// No data to be accessed here.
685+
return (Size::ZERO, Align::ONE, AllocKind::Vtable);
686+
}
677687
// The rest must be dead.
678688
None => {
679689
// Deallocated pointers are allowed, we should be able to find
@@ -836,7 +846,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
836846
)?;
837847
}
838848
Some(GlobalAlloc::Function(func)) => {
839-
write!(fmt, " (fn: {})", func)?;
849+
write!(fmt, " (fn: {func})")?;
850+
}
851+
Some(GlobalAlloc::Vtable(ty, Some(trait_ref))) => {
852+
write!(fmt, " (vtable: impl {trait_ref} for {ty})")?;
853+
}
854+
Some(GlobalAlloc::Vtable(ty, None)) => {
855+
write!(fmt, " (vtable: impl ? for {ty})")?;
840856
}
841857
Some(GlobalAlloc::Static(did)) => {
842858
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
271271
WriteToReadOnly(AllocId),
272272
// Trying to access the data behind a function pointer.
273273
DerefFunctionPointer(AllocId),
274+
// Trying to access the data behind a vtable pointer.
275+
DerefVtablePointer(AllocId),
274276
/// The value validity check found a problem.
275277
/// Should only be thrown by `validity.rs` and always point out which part of the value
276278
/// is the problem.
@@ -359,6 +361,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
359361
),
360362
WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
361363
DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
364+
DerefVtablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
362365
ValidationFailure { path: None, msg } => {
363366
write!(f, "constructing invalid value: {msg}")
364367
}

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

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ impl fmt::Debug for AllocId {
196196
enum AllocDiscriminant {
197197
Alloc,
198198
Fn,
199+
Vtable,
199200
Static,
200201
}
201202

@@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
215216
AllocDiscriminant::Fn.encode(encoder);
216217
fn_instance.encode(encoder);
217218
}
219+
GlobalAlloc::Vtable(ty, poly_trait_ref) => {
220+
trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
221+
AllocDiscriminant::Vtable.encode(encoder);
222+
ty.encode(encoder);
223+
poly_trait_ref.encode(encoder);
224+
}
218225
GlobalAlloc::Static(did) => {
219226
assert!(!tcx.is_thread_local_static(did));
220227
// References to statics doesn't need to know about their allocations,
@@ -305,7 +312,7 @@ impl<'s> AllocDecodingSession<'s> {
305312
State::InProgress(TinyList::new_single(self.session_id), alloc_id);
306313
Some(alloc_id)
307314
}
308-
AllocDiscriminant::Fn | AllocDiscriminant::Static => {
315+
AllocDiscriminant::Fn | AllocDiscriminant::Static | AllocDiscriminant::Vtable => {
309316
// Fns and statics cannot be cyclic, and their `AllocId`
310317
// is determined later by interning.
311318
*entry =
@@ -355,6 +362,15 @@ impl<'s> AllocDecodingSession<'s> {
355362
let alloc_id = decoder.interner().create_fn_alloc(instance);
356363
alloc_id
357364
}
365+
AllocDiscriminant::Vtable => {
366+
assert!(alloc_id.is_none());
367+
trace!("creating static alloc ID");
368+
let ty = <Ty<'_> as Decodable<D>>::decode(decoder);
369+
let poly_trait_ref = <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
370+
trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
371+
let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
372+
alloc_id
373+
}
358374
AllocDiscriminant::Static => {
359375
assert!(alloc_id.is_none());
360376
trace!("creating extern static alloc ID");
@@ -380,6 +396,8 @@ impl<'s> AllocDecodingSession<'s> {
380396
pub enum GlobalAlloc<'tcx> {
381397
/// The alloc ID is used as a function pointer.
382398
Function(Instance<'tcx>),
399+
/// This alloc ID points to a symbolic (not-reified) vtable.
400+
Vtable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
383401
/// The alloc ID points to a "lazy" static variable that did not get computed (yet).
384402
/// This is also used to break the cycle in recursive statics.
385403
Static(DefId),
@@ -407,6 +425,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
407425
_ => bug!("expected function, got {:?}", self),
408426
}
409427
}
428+
429+
/// Panics if the `GlobalAlloc` is not `GlobalAlloc::Vtable`
430+
#[track_caller]
431+
#[inline]
432+
pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
433+
match *self {
434+
GlobalAlloc::Vtable(ty, poly_trait_ref) => (ty, poly_trait_ref),
435+
_ => bug!("expected vtable, got {:?}", self),
436+
}
437+
}
410438
}
411439

412440
pub(crate) struct AllocMap<'tcx> {
@@ -454,12 +482,12 @@ impl<'tcx> TyCtxt<'tcx> {
454482
}
455483

456484
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
457-
/// Should only be used for function pointers and statics, we don't want
458-
/// to dedup IDs for "real" memory!
485+
/// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
486+
/// don't want to dedup IDs for "real" memory!
459487
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
460488
let mut alloc_map = self.alloc_map.lock();
461489
match alloc {
462-
GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {}
490+
GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::Vtable(..) => {}
463491
GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"),
464492
}
465493
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) {
@@ -504,6 +532,11 @@ impl<'tcx> TyCtxt<'tcx> {
504532
}
505533
}
506534

535+
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
536+
pub fn create_vtable_alloc(self, ty: Ty<'tcx>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>) -> AllocId {
537+
self.reserve_and_set_dedup(GlobalAlloc::Vtable(ty, poly_trait_ref))
538+
}
539+
507540
/// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
508541
/// `Allocation` with a different `AllocId`.
509542
/// Statics with identical content will still point to the same `Allocation`, i.e.,

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,8 @@ pub fn write_allocations<'tcx>(
725725
// gracefully handle it and allow buggy rustc to be debugged via allocation printing.
726726
None => write!(w, " (deallocated)")?,
727727
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
728+
Some(GlobalAlloc::Vtable(ty, Some(trait_ref))) => write!(w, " (vtable: impl {trait_ref} for {ty})")?,
729+
Some(GlobalAlloc::Vtable(ty, None)) => write!(w, " (vtable: impl ? for {ty})")?,
728730
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
729731
match tcx.eval_static_initializer(did) {
730732
Ok(alloc) => {

compiler/rustc_middle/src/ty/codec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,4 +523,5 @@ impl_binder_encode_decode! {
523523
ty::ExistentialPredicate<'tcx>,
524524
ty::TraitRef<'tcx>,
525525
Vec<ty::GeneratorInteriorTypeCause<'tcx>>,
526+
ty::ExistentialTraitRef<'tcx>,
526527
}

compiler/rustc_middle/src/ty/print/pretty.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>:
12821282
p!("<too short allocation>")
12831283
}
12841284
}
1285-
// FIXME: for statics and functions, we could in principle print more detail.
1285+
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
12861286
Some(GlobalAlloc::Static(def_id)) => {
12871287
p!(write("<static({:?})>", def_id))
12881288
}
12891289
Some(GlobalAlloc::Function(_)) => p!("<function>"),
1290+
Some(GlobalAlloc::Vtable(..)) => p!("<vtable>"),
12901291
None => p!("<dangling pointer>"),
12911292
}
12921293
return Ok(self);

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,13 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte
14271427
output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP));
14281428
}
14291429
}
1430+
GlobalAlloc::Vtable(ty, trait_ref) => {
1431+
// FIXME(RJ) no ideas if this is correct. There is this nice
1432+
// `create_mono_items_for_vtable_methods` method but I wouldn't know how to call it from
1433+
// here. So instead we just generate the actual vtable and recurse.
1434+
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
1435+
collect_miri(tcx, alloc_id, output)
1436+
}
14301437
}
14311438
}
14321439

0 commit comments

Comments
 (0)