Skip to content

Commit 3b85d2c

Browse files
committed
Auto merge of #121644 - oli-obk:unique_static_innards2, r=RalfJung,nnethercote
Ensure nested allocations in statics neither get deduplicated nor duplicated This PR generates new `DefId`s for nested allocations in static items and feeds all the right queries to make the compiler believe these are regular `static` items. I chose this design, because all other designs are fragile and make the compiler horribly complex for such a niche use case. At present this wrecks incremental compilation performance *in case nested allocations exist* (because any query creating a `DefId` will be recomputed and never loaded from the cache). This will be resolved later in #115613 . All other statics are unaffected by this change and will not have performance regressions (heh, famous last words) This PR contains various smaller refactorings that can be pulled out into separate PRs. It is best reviewed commit-by-commit. The last commit is where the actual magic happens. r? `@RalfJung` on the const interner and engine changes fixes #79738
2 parents 5b7343b + e277373 commit 3b85d2c

File tree

66 files changed

+461
-170
lines changed

Some content is hidden

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

66 files changed

+461
-170
lines changed

compiler/rustc_ast_lowering/src/asm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
196196
.get_partial_res(sym.id)
197197
.and_then(|res| res.full_res())
198198
.and_then(|res| match res {
199-
Res::Def(DefKind::Static(_), def_id) => Some(def_id),
199+
Res::Def(DefKind::Static { .. }, def_id) => Some(def_id),
200200
_ => None,
201201
});
202202

compiler/rustc_codegen_gcc/src/consts.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
6363
global_value
6464
}
6565

66-
fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
66+
fn codegen_static(&self, def_id: DefId) {
6767
let attrs = self.tcx.codegen_fn_attrs(def_id);
6868

6969
let value = match codegen_static_initializer(&self, def_id) {
@@ -92,7 +92,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
9292

9393
// As an optimization, all shared statics which do not have interior
9494
// mutability are placed into read-only memory.
95-
if !is_mutable && self.type_is_freeze(ty) {
95+
if !self.tcx.static_mutability(def_id).unwrap().is_mut() && self.type_is_freeze(ty) {
9696
#[cfg(feature = "master")]
9797
global.global_set_readonly();
9898
}
@@ -349,7 +349,7 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(
349349
cx.const_struct(&llvals, true)
350350
}
351351

352-
pub fn codegen_static_initializer<'gcc, 'tcx>(
352+
fn codegen_static_initializer<'gcc, 'tcx>(
353353
cx: &CodegenCx<'gcc, 'tcx>,
354354
def_id: DefId,
355355
) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> {

compiler/rustc_codegen_gcc/src/mono_item.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#[cfg(feature = "master")]
22
use gccjit::{FnAttribute, VarAttribute};
33
use rustc_codegen_ssa::traits::PreDefineMethods;
4+
use rustc_hir::def::DefKind;
45
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6+
use rustc_middle::bug;
57
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
68
use rustc_middle::mir::mono::{Linkage, Visibility};
79
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
@@ -23,7 +25,14 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
2325
) {
2426
let attrs = self.tcx.codegen_fn_attrs(def_id);
2527
let instance = Instance::mono(self.tcx, def_id);
26-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
28+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
29+
// Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out
30+
// the gcc type from the actual evaluated initializer.
31+
let ty = if nested {
32+
self.tcx.types.unit
33+
} else {
34+
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
35+
};
2736
let gcc_type = self.layout_of(ty).gcc_type(self);
2837

2938
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);

compiler/rustc_codegen_llvm/src/consts.rs

+67-43
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::type_::Type;
99
use crate::type_of::LayoutLlvmExt;
1010
use crate::value::Value;
1111
use rustc_codegen_ssa::traits::*;
12+
use rustc_hir::def::DefKind;
1213
use rustc_hir::def_id::DefId;
1314
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
1415
use rustc_middle::mir::interpret::{
@@ -17,7 +18,7 @@ use rustc_middle::mir::interpret::{
1718
};
1819
use rustc_middle::mir::mono::MonoItem;
1920
use rustc_middle::ty::layout::LayoutOf;
20-
use rustc_middle::ty::{self, Instance, Ty};
21+
use rustc_middle::ty::{self, Instance};
2122
use rustc_middle::{bug, span_bug};
2223
use rustc_session::config::Lto;
2324
use rustc_target::abi::{
@@ -114,7 +115,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
114115
cx.const_struct(&llvals, true)
115116
}
116117

117-
pub fn codegen_static_initializer<'ll, 'tcx>(
118+
fn codegen_static_initializer<'ll, 'tcx>(
118119
cx: &CodegenCx<'ll, 'tcx>,
119120
def_id: DefId,
120121
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
@@ -147,11 +148,10 @@ fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align:
147148
fn check_and_apply_linkage<'ll, 'tcx>(
148149
cx: &CodegenCx<'ll, 'tcx>,
149150
attrs: &CodegenFnAttrs,
150-
ty: Ty<'tcx>,
151+
llty: &'ll Type,
151152
sym: &str,
152153
def_id: DefId,
153154
) -> &'ll Value {
154-
let llty = cx.layout_of(ty).llvm_type(cx);
155155
if let Some(linkage) = attrs.import_linkage {
156156
debug!("get_static: sym={} linkage={:?}", sym, linkage);
157157

@@ -226,9 +226,28 @@ impl<'ll> CodegenCx<'ll, '_> {
226226
}
227227
}
228228

229+
#[instrument(level = "debug", skip(self))]
229230
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
230231
let instance = Instance::mono(self.tcx, def_id);
231-
if let Some(&g) = self.instances.borrow().get(&instance) {
232+
trace!(?instance);
233+
234+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
235+
// Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out
236+
// the llvm type from the actual evaluated initializer.
237+
let llty = if nested {
238+
self.type_i8()
239+
} else {
240+
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
241+
trace!(?ty);
242+
self.layout_of(ty).llvm_type(self)
243+
};
244+
self.get_static_inner(def_id, llty)
245+
}
246+
247+
#[instrument(level = "debug", skip(self, llty))]
248+
pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value {
249+
if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) {
250+
trace!("used cached value");
232251
return g;
233252
}
234253

@@ -240,14 +259,12 @@ impl<'ll> CodegenCx<'ll, '_> {
240259
statics defined in the same CGU, but did not for `{def_id:?}`"
241260
);
242261

243-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
244-
let sym = self.tcx.symbol_name(instance).name;
262+
let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name;
245263
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
246264

247-
debug!("get_static: sym={} instance={:?} fn_attrs={:?}", sym, instance, fn_attrs);
265+
debug!(?sym, ?fn_attrs);
248266

249267
let g = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) {
250-
let llty = self.layout_of(ty).llvm_type(self);
251268
if let Some(g) = self.get_declared_value(sym) {
252269
if self.val_ty(g) != self.type_ptr() {
253270
span_bug!(self.tcx.def_span(def_id), "Conflicting types for static");
@@ -264,7 +281,7 @@ impl<'ll> CodegenCx<'ll, '_> {
264281

265282
g
266283
} else {
267-
check_and_apply_linkage(self, fn_attrs, ty, sym, def_id)
284+
check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
268285
};
269286

270287
// Thread-local statics in some other crate need to *always* be linked
@@ -332,34 +349,18 @@ impl<'ll> CodegenCx<'ll, '_> {
332349
}
333350
}
334351

335-
self.instances.borrow_mut().insert(instance, g);
352+
self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g);
336353
g
337354
}
338-
}
339-
340-
impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
341-
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
342-
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
343-
unsafe {
344-
// Upgrade the alignment in cases where the same constant is used with different
345-
// alignment requirements
346-
let llalign = align.bytes() as u32;
347-
if llalign > llvm::LLVMGetAlignment(gv) {
348-
llvm::LLVMSetAlignment(gv, llalign);
349-
}
350-
}
351-
return gv;
352-
}
353-
let gv = self.static_addr_of_mut(cv, align, kind);
354-
unsafe {
355-
llvm::LLVMSetGlobalConstant(gv, True);
356-
}
357-
self.const_globals.borrow_mut().insert(cv, gv);
358-
gv
359-
}
360355

361-
fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
356+
fn codegen_static_item(&self, def_id: DefId) {
362357
unsafe {
358+
assert!(
359+
llvm::LLVMGetInitializer(
360+
self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap()
361+
)
362+
.is_none()
363+
);
363364
let attrs = self.tcx.codegen_fn_attrs(def_id);
364365

365366
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
@@ -368,13 +369,11 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
368369
};
369370
let alloc = alloc.inner();
370371

371-
let g = self.get_static(def_id);
372-
373372
let val_llty = self.val_ty(v);
374373

375-
let instance = Instance::mono(self.tcx, def_id);
376-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
377-
let llty = self.layout_of(ty).llvm_type(self);
374+
let g = self.get_static_inner(def_id, val_llty);
375+
let llty = self.val_ty(g);
376+
378377
let g = if val_llty == llty {
379378
g
380379
} else {
@@ -409,16 +408,15 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
409408
self.statics_to_rauw.borrow_mut().push((g, new_g));
410409
new_g
411410
};
412-
set_global_alignment(self, g, self.align_of(ty));
411+
set_global_alignment(self, g, alloc.align);
413412
llvm::LLVMSetInitializer(g, v);
414413

415414
if self.should_assume_dso_local(g, true) {
416415
llvm::LLVMRustSetDSOLocal(g, true);
417416
}
418417

419-
// As an optimization, all shared statics which do not have interior
420-
// mutability are placed into read-only memory.
421-
if !is_mutable && self.type_is_freeze(ty) {
418+
// Forward the allocation's mutability (picked by the const interner) to LLVM.
419+
if alloc.mutability.is_not() {
422420
llvm::LLVMSetGlobalConstant(g, llvm::True);
423421
}
424422

@@ -541,6 +539,32 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
541539
}
542540
}
543541
}
542+
}
543+
544+
impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
545+
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
546+
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
547+
unsafe {
548+
// Upgrade the alignment in cases where the same constant is used with different
549+
// alignment requirements
550+
let llalign = align.bytes() as u32;
551+
if llalign > llvm::LLVMGetAlignment(gv) {
552+
llvm::LLVMSetAlignment(gv, llalign);
553+
}
554+
}
555+
return gv;
556+
}
557+
let gv = self.static_addr_of_mut(cv, align, kind);
558+
unsafe {
559+
llvm::LLVMSetGlobalConstant(gv, True);
560+
}
561+
self.const_globals.borrow_mut().insert(cv, gv);
562+
gv
563+
}
564+
565+
fn codegen_static(&self, def_id: DefId) {
566+
self.codegen_static_item(def_id)
567+
}
544568

545569
/// Add a global value to a list to be stored in the `llvm.used` variable, an array of ptr.
546570
fn add_used_global(&self, global: &'ll Value) {

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
2626
use rustc_codegen_ssa::traits::*;
2727
use rustc_fs_util::path_to_c_string;
2828
use rustc_hir::def::CtorKind;
29+
use rustc_hir::def::DefKind;
2930
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
3031
use rustc_middle::bug;
3132
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@@ -1309,6 +1310,11 @@ pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, glo
13091310
};
13101311

13111312
let is_local_to_unit = is_node_local_to_unit(cx, def_id);
1313+
1314+
let DefKind::Static { nested, .. } = cx.tcx.def_kind(def_id) else { bug!() };
1315+
if nested {
1316+
return;
1317+
}
13121318
let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
13131319
let type_di_node = type_di_node(cx, variable_type);
13141320
let var_name = tcx.item_name(def_id);

compiler/rustc_codegen_llvm/src/mono_item.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::errors::SymbolAlreadyDefined;
55
use crate::llvm;
66
use crate::type_of::LayoutLlvmExt;
77
use rustc_codegen_ssa::traits::*;
8+
use rustc_hir::def::DefKind;
89
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
10+
use rustc_middle::bug;
911
use rustc_middle::mir::mono::{Linkage, Visibility};
1012
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
1113
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
@@ -21,7 +23,14 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
2123
symbol_name: &str,
2224
) {
2325
let instance = Instance::mono(self.tcx, def_id);
24-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
26+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
27+
// Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out
28+
// the llvm type from the actual evaluated initializer.
29+
let ty = if nested {
30+
self.tcx.types.unit
31+
} else {
32+
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
33+
};
2534
let llty = self.layout_of(ty).llvm_type(self);
2635

2736
let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
8787

8888
// Only consider nodes that actually have exported symbols.
8989
match tcx.def_kind(def_id) {
90-
DefKind::Fn | DefKind::Static(_) => {}
90+
DefKind::Fn | DefKind::Static { .. } => {}
9191
DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {}
9292
_ => return None,
9393
};
@@ -483,7 +483,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
483483
let target = &tcx.sess.target.llvm_target;
484484
// WebAssembly cannot export data symbols, so reduce their export level
485485
if target.contains("emscripten") {
486-
if let DefKind::Static(_) = tcx.def_kind(sym_def_id) {
486+
if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
487487
return SymbolExportLevel::Rust;
488488
}
489489
}

compiler/rustc_codegen_ssa/src/mono_item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
3030

3131
match *self {
3232
MonoItem::Static(def_id) => {
33-
cx.codegen_static(def_id, cx.tcx().is_mutable_static(def_id));
33+
cx.codegen_static(def_id);
3434
}
3535
MonoItem::GlobalAsm(item_id) => {
3636
let item = cx.tcx().hir().item(item_id);

compiler/rustc_codegen_ssa/src/traits/statics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_target::abi::Align;
44

55
pub trait StaticMethods: BackendTypes {
66
fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value;
7-
fn codegen_static(&self, def_id: DefId, is_mutable: bool);
7+
fn codegen_static(&self, def_id: DefId);
88

99
/// Mark the given global value as "used", to prevent the compiler and linker from potentially
1010
/// removing a static variable that may otherwise appear unused.

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
3737
|| matches!(
3838
ecx.tcx.def_kind(cid.instance.def_id()),
3939
DefKind::Const
40-
| DefKind::Static(_)
40+
| DefKind::Static { .. }
4141
| DefKind::ConstParam
4242
| DefKind::AnonConst
4343
| DefKind::InlineConst
@@ -59,7 +59,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
5959
};
6060

6161
let ret = if let InternKind::Static(_) = intern_kind {
62-
create_static_alloc(ecx, cid.instance.def_id(), layout)?
62+
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
6363
} else {
6464
ecx.allocate(layout, MemoryKind::Stack)?
6565
};
@@ -380,7 +380,11 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
380380
}
381381
Ok(mplace) => {
382382
// Since evaluation had no errors, validate the resulting constant.
383+
384+
// Temporarily allow access to the static_root_ids for the purpose of validation.
385+
let static_root_ids = ecx.machine.static_root_ids.take();
383386
let res = const_validate_mplace(&ecx, &mplace, cid);
387+
ecx.machine.static_root_ids = static_root_ids;
384388

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

0 commit comments

Comments
 (0)