Skip to content

Commit

Permalink
Ensure nested allocations in statics do not get deduplicated
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Feb 26, 2024
1 parent 737208d commit 0aa7752
Show file tree
Hide file tree
Showing 17 changed files with 182 additions and 40 deletions.
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_gcc/src/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_hir::def::DefKind;

use crate::attributes;
use crate::base;
Expand All @@ -17,7 +18,10 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn predefine_static(&self, def_id: DefId, _linkage: Linkage, visibility: Visibility, symbol_name: &str) {
let attrs = self.tcx.codegen_fn_attrs(def_id);
let instance = Instance::mono(self.tcx, def_id);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
// the llvm type from the actual evaluated initializer.
let ty = if nested { self.tcx.types.unit} else { instance.ty(self.tcx, ty::ParamEnv::reveal_all()) };
let gcc_type = self.layout_of(ty).gcc_type(self);

let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
Expand Down
18 changes: 15 additions & 3 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use rustc_codegen_ssa::traits::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
Expand Down Expand Up @@ -229,9 +230,17 @@ impl<'ll> CodegenCx<'ll, '_> {
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
let instance = Instance::mono(self.tcx, def_id);
trace!(?instance);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
trace!(?ty);
let llty = self.layout_of(ty).llvm_type(self);

let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
// the llvm type from the actual evaluated initializer.
let llty = if nested {
self.type_i8()
} else {
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
trace!(?ty);
self.layout_of(ty).llvm_type(self)
};
self.get_static_inner(def_id, llty)
}

Expand Down Expand Up @@ -346,6 +355,9 @@ impl<'ll> CodegenCx<'ll, '_> {

fn codegen_static_item(&self, def_id: DefId) {
unsafe {
assert!(
llvm::LLVMGetInitializer(self.statics.borrow().get(&def_id).unwrap()).is_none()
);
let attrs = self.tcx.codegen_fn_attrs(def_id);

let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_codegen_llvm/src/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use crate::errors::SymbolAlreadyDefined;
use crate::llvm;
use crate::type_of::LayoutLlvmExt;
use rustc_codegen_ssa::traits::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::bug;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
Expand All @@ -21,7 +23,14 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
symbol_name: &str,
) {
let instance = Instance::mono(self.tcx, def_id);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
// the llvm type from the actual evaluated initializer.
let ty = if nested {
self.tcx.types.unit
} else {
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
};
let llty = self.layout_of(ty).llvm_type(self);

let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
};

let ret = if let InternKind::Static(_) = intern_kind {
create_static_alloc(ecx, cid.instance.def_id(), layout)?
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
} else {
ecx.allocate(layout, MemoryKind::Stack)?
};
Expand Down Expand Up @@ -381,10 +381,10 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
Ok(mplace) => {
// Since evaluation had no errors, validate the resulting constant.

// Temporarily allow access to the static_root_alloc_id for the purpose of validation.
let static_root_alloc_id = ecx.machine.static_root_alloc_id.take();
// Temporarily allow access to the static_root_ids for the purpose of validation.
let static_root_ids = ecx.machine.static_root_ids.take();
let validation = const_validate_mplace(&ecx, &mplace, cid);
ecx.machine.static_root_alloc_id = static_root_alloc_id;
ecx.machine.static_root_ids = static_root_ids;

let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();

Expand Down
19 changes: 16 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::fx::IndexEntry;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::LangItem;
use rustc_middle::mir;
use rustc_middle::mir::AssertMessage;
Expand Down Expand Up @@ -60,7 +61,19 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
pub(super) check_alignment: CheckAlignment,

/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization.
pub(crate) static_root_alloc_id: Option<AllocId>,
pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
}

pub trait HasStaticRootDefId {
/// Returns the `DefId` of the static item that is currently being evaluated.
/// Used for interning to be able to handle nested allocations.
fn static_def_id(&self) -> Option<LocalDefId>;
}

impl HasStaticRootDefId for CompileTimeInterpreter<'_, '_> {
fn static_def_id(&self) -> Option<LocalDefId> {
Some(self.static_root_ids?.1)
}
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -94,7 +107,7 @@ impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
stack: Vec::new(),
can_access_mut_global,
check_alignment,
static_root_alloc_id: None,
static_root_ids: None,
}
}
}
Expand Down Expand Up @@ -751,7 +764,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
ecx: &InterpCx<'mir, 'tcx, Self>,
alloc_id: AllocId,
) -> InterpResult<'tcx> {
if Some(alloc_id) == ecx.machine.static_root_alloc_id {
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
Err(ConstEvalErrKind::RecursiveStatic.into())
} else {
Ok(())
Expand Down
34 changes: 31 additions & 3 deletions compiler/rustc_const_eval/src/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
//! but that would require relying on type information, and given how many ways Rust has to lie
//! about type information, we want to avoid doing that.

use hir::def::DefKind;
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult};
use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_span::def_id::LocalDefId;
use rustc_span::sym;

use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
use crate::const_eval;
Expand All @@ -33,7 +37,7 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
FrameExtra = (),
AllocExtra = (),
MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
>;
> + const_eval::HasStaticRootDefId;

/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
///
Expand Down Expand Up @@ -67,10 +71,34 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
}
// link the alloc id to the actual allocation
let alloc = ecx.tcx.mk_const_alloc(alloc);
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
if let Some(static_id) = ecx.machine.static_def_id() {
intern_as_new_static(ecx.tcx, static_id, alloc_id, alloc);
} else {
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
}
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
}

/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
/// appear as if it were a user-written `static` (though it has no HIR).
fn intern_as_new_static<'tcx>(
tcx: TyCtxtAt<'tcx>,
static_id: LocalDefId,
alloc_id: AllocId,
alloc: ConstAllocation<'tcx>,
) {
let feed = tcx.create_def(
static_id,
sym::nested,
DefKind::Static { mt: alloc.0.mutability, nested: true },
);
tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone());
feed.eval_static_initializer(Ok(alloc));
feed.generics_of(tcx.generics_of(static_id).clone());
feed.def_ident_span(tcx.def_ident_span(static_id));
feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
}
/// How a constant value should be interned.
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
pub enum InternKind {
Expand Down
35 changes: 25 additions & 10 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::ptr;

use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir::def::DefKind;
use rustc_middle::mir::display_allocation;
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Align, HasDataLayout, Size};
Expand Down Expand Up @@ -744,19 +745,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// be held throughout the match.
match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Static(def_id)) => {
assert!(self.tcx.is_static(def_id));
// Thread-local statics do not have a constant address. They *must* be accessed via
// `ThreadLocalRef`; we can never have a pointer to them as a regular constant value.
assert!(!self.tcx.is_thread_local_static(def_id));
// Use size and align of the type.
let ty = self
.tcx
.type_of(def_id)
.no_bound_vars()
.expect("statics should not have generic parameters");
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
assert!(layout.is_sized());
(layout.size, layout.align.abi, AllocKind::LiveData)

let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else {
bug!("GlobalAlloc::Static is not a static")
};

let (size, align) = if nested {
// Nested anonymous statics are untyped, so let's get their
// size and alignment from the allocaiton itself. This always
// succeeds, as the query is fed at DefId creation time, so no
// evaluation actually occurs.
let alloc = self.tcx.eval_static_initializer(def_id).unwrap();
(alloc.0.size(), alloc.0.align)
} else {
// Use size and align of the type.
let ty = self
.tcx
.type_of(def_id)
.no_bound_vars()
.expect("statics should not have generic parameters");
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
assert!(layout.is_sized());
(layout.size, layout.align.abi)
};
(size, align, AllocKind::LiveData)
}
Some(GlobalAlloc::Memory(alloc)) => {
// Need to duplicate the logic here, because the global allocations have
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_const_eval/src/interpret/util.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::const_eval::CompileTimeEvalContext;
use crate::interpret::{MemPlaceMeta, MemoryKind};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::def_id::DefId;
use std::ops::ControlFlow;

use super::MPlaceTy;
Expand Down Expand Up @@ -89,13 +89,13 @@ pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>(

pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
static_def_id: DefId,
static_def_id: LocalDefId,
layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?;
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id);
assert_eq!(ecx.machine.static_root_alloc_id, None);
ecx.machine.static_root_alloc_id = Some(alloc_id);
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
assert_eq!(ecx.machine.static_root_ids, None);
ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
Ok(ecx.ptr_with_meta_to_mplace(Pointer::from(alloc_id).into(), MemPlaceMeta::None, layout))
}
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ impl DefKind {
| DefKind::AssocTy
| DefKind::TyParam
| DefKind::ExternCrate => DefPathData::TypeNs(name),
// It's not exactly an anon const, but wrt DefPathData, there
// is not difference.
DefKind::Static { nested: true, .. } => DefPathData::AnonConst,
DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
| DefKind::AssocTy
| DefKind::Fn
| DefKind::Const
| DefKind::Static { .. }
| DefKind::Static { nested: false, .. }
| DefKind::AssocFn
| DefKind::AssocConst
| DefKind::Macro(_)
Expand All @@ -903,6 +903,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::LifetimeParam
| DefKind::Static { nested: true, .. }
| DefKind::GlobalAsm => false,
}
}
Expand Down Expand Up @@ -956,7 +957,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool {
| DefKind::AssocTy
| DefKind::Fn
| DefKind::Const
| DefKind::Static { .. }
| DefKind::Static { nested: false, .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::AssocConst
Expand All @@ -969,6 +970,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool {
| DefKind::LifetimeParam
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Static { nested: true, .. }
| DefKind::OpaqueTy
| DefKind::GlobalAsm
| DefKind::Impl { .. }
Expand Down Expand Up @@ -1148,7 +1150,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
| DefKind::Field
| DefKind::Fn
| DefKind::Const
| DefKind::Static { .. }
| DefKind::Static { nested: false, .. }
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::Impl { .. }
Expand Down Expand Up @@ -1190,6 +1192,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
| DefKind::Mod
| DefKind::ForeignMod
| DefKind::Macro(..)
| DefKind::Static { nested: true, .. }
| DefKind::Use
| DefKind::LifetimeParam
| DefKind::GlobalAsm
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{HashMapExt, Lock};
use rustc_data_structures::tiny_list::TinyList;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::HashStable;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_serialize::{Decodable, Encodable};
Expand Down Expand Up @@ -627,6 +627,16 @@ impl<'tcx> TyCtxt<'tcx> {
}
}

/// Freezes an `AllocId` created with `reserve` by pointing it at a static item. Trying to
/// call this function twice, even with the same `DefId` will ICE the compiler.
pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) {
if let Some(old) =
self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Static(def_id.to_def_id()))
{
bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
}
}

/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
/// twice for the same `(AllocId, Allocation)` pair.
fn set_alloc_id_same_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
Expand Down
Loading

0 comments on commit 0aa7752

Please sign in to comment.