Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ec1d458

Browse files
committed
debuginfo: split method declaration and definition
When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition. Now the subprogram definition gets added at the CU level with a specification link back to the abstract declaration. (cherry picked from commit 10b69dd)
1 parent 2013813 commit ec1d458

File tree

5 files changed

+110
-34
lines changed

5 files changed

+110
-34
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
321321
let tcx = self.tcx;
322322

323323
let def_id = instance.def_id();
324-
let containing_scope = get_containing_scope(self, instance);
324+
let (containing_scope, is_method) = get_containing_scope(self, instance);
325325
let span = tcx.def_span(def_id);
326326
let loc = self.lookup_debug_loc(span.lo());
327327
let file_metadata = file_metadata(self, &loc.file);
@@ -377,8 +377,29 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
377377
}
378378
}
379379

380-
unsafe {
381-
return llvm::LLVMRustDIBuilderCreateFunction(
380+
// When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because
381+
// LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
382+
// When we use this `decl` below, the subprogram definition gets created at the CU level
383+
// with a DW_AT_specification pointing back to the type's declaration.
384+
let decl = is_method.then(|| unsafe {
385+
llvm::LLVMRustDIBuilderCreateMethod(
386+
DIB(self),
387+
containing_scope,
388+
name.as_ptr().cast(),
389+
name.len(),
390+
linkage_name.as_ptr().cast(),
391+
linkage_name.len(),
392+
file_metadata,
393+
loc.line,
394+
function_type_metadata,
395+
flags,
396+
spflags & !DISPFlags::SPFlagDefinition,
397+
template_parameters,
398+
)
399+
});
400+
401+
return unsafe {
402+
llvm::LLVMRustDIBuilderCreateFunction(
382403
DIB(self),
383404
containing_scope,
384405
name.as_ptr().cast(),
@@ -393,9 +414,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
393414
spflags,
394415
maybe_definition_llfn,
395416
template_parameters,
396-
None,
397-
);
398-
}
417+
decl,
418+
)
419+
};
399420

400421
fn get_function_signature<'ll, 'tcx>(
401422
cx: &CodegenCx<'ll, 'tcx>,
@@ -494,14 +515,16 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
494515
names
495516
}
496517

518+
/// Returns a scope, plus `true` if that's a type scope for "class" methods,
519+
/// otherwise `false` for plain namespace scopes.
497520
fn get_containing_scope<'ll, 'tcx>(
498521
cx: &CodegenCx<'ll, 'tcx>,
499522
instance: Instance<'tcx>,
500-
) -> &'ll DIScope {
523+
) -> (&'ll DIScope, bool) {
501524
// First, let's see if this is a method within an inherent impl. Because
502525
// if yes, we want to make the result subroutine DIE a child of the
503526
// subroutine's self-type.
504-
let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
527+
if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
505528
// If the method does *not* belong to a trait, proceed
506529
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
507530
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
@@ -512,39 +535,34 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
512535

513536
// Only "class" methods are generally understood by LLVM,
514537
// so avoid methods on other types (e.g., `<*mut T>::null`).
515-
match impl_self_ty.kind() {
516-
ty::Adt(def, ..) if !def.is_box() => {
517-
// Again, only create type information if full debuginfo is enabled
518-
if cx.sess().opts.debuginfo == DebugInfo::Full
519-
&& !impl_self_ty.needs_subst()
520-
{
521-
Some(type_di_node(cx, impl_self_ty))
522-
} else {
523-
Some(namespace::item_namespace(cx, def.did()))
524-
}
538+
if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
539+
// Again, only create type information if full debuginfo is enabled
540+
if cx.sess().opts.debuginfo == DebugInfo::Full
541+
&& !impl_self_ty.needs_subst()
542+
{
543+
return (type_di_node(cx, impl_self_ty), true);
544+
} else {
545+
return (namespace::item_namespace(cx, def.did()), false);
525546
}
526-
_ => None,
527547
}
528548
} else {
529549
// For trait method impls we still use the "parallel namespace"
530550
// strategy
531-
None
532551
}
533-
});
552+
}
534553

535-
self_type.unwrap_or_else(|| {
536-
namespace::item_namespace(
537-
cx,
538-
DefId {
539-
krate: instance.def_id().krate,
540-
index: cx
541-
.tcx
542-
.def_key(instance.def_id())
543-
.parent
544-
.expect("get_containing_scope: missing parent?"),
545-
},
546-
)
547-
})
554+
let scope = namespace::item_namespace(
555+
cx,
556+
DefId {
557+
krate: instance.def_id().krate,
558+
index: cx
559+
.tcx
560+
.def_key(instance.def_id())
561+
.parent
562+
.expect("get_containing_scope: missing parent?"),
563+
},
564+
);
565+
(scope, false)
548566
}
549567
}
550568

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,21 @@ extern "C" {
19861986
Decl: Option<&'a DIDescriptor>,
19871987
) -> &'a DISubprogram;
19881988

1989+
pub fn LLVMRustDIBuilderCreateMethod<'a>(
1990+
Builder: &DIBuilder<'a>,
1991+
Scope: &'a DIDescriptor,
1992+
Name: *const c_char,
1993+
NameLen: size_t,
1994+
LinkageName: *const c_char,
1995+
LinkageNameLen: size_t,
1996+
File: &'a DIFile,
1997+
LineNo: c_uint,
1998+
Ty: &'a DIType,
1999+
Flags: DIFlags,
2000+
SPFlags: DISPFlags,
2001+
TParam: &'a DIArray,
2002+
) -> &'a DISubprogram;
2003+
19892004
pub fn LLVMRustDIBuilderCreateBasicType<'a>(
19902005
Builder: &DIBuilder<'a>,
19912006
Name: *const c_char,

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
831831
return wrap(Sub);
832832
}
833833

834+
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
835+
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
836+
const char *Name, size_t NameLen,
837+
const char *LinkageName, size_t LinkageNameLen,
838+
LLVMMetadataRef File, unsigned LineNo,
839+
LLVMMetadataRef Ty, LLVMRustDIFlags Flags,
840+
LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
841+
DITemplateParameterArray TParams =
842+
DITemplateParameterArray(unwrap<MDTuple>(TParam));
843+
DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
844+
DINode::DIFlags llvmFlags = fromRust(Flags);
845+
DISubprogram *Sub = Builder->createMethod(
846+
unwrapDI<DIScope>(Scope),
847+
StringRef(Name, NameLen),
848+
StringRef(LinkageName, LinkageNameLen),
849+
unwrapDI<DIFile>(File), LineNo,
850+
unwrapDI<DISubroutineType>(Ty),
851+
0, 0, nullptr, // VTable params aren't used
852+
llvmFlags, llvmSPFlags, TParams);
853+
return wrap(Sub);
854+
}
855+
834856
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
835857
LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
836858
uint64_t SizeInBits, unsigned Encoding) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# ignore-cross-compile
2+
include ../tools.mk
3+
4+
# With the upgrade to LLVM 16, this was getting:
5+
#
6+
# error: Cannot represent a difference across sections
7+
#
8+
# The error stemmed from DI function definitions under type scopes, fixed by
9+
# only declaring in type scope and defining the subprogram elsewhere.
10+
11+
all:
12+
$(RUSTC) lib.rs --test -C lto=fat -C debuginfo=2 -C incremental=$(TMPDIR)/inc-fat
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extern crate alloc;
2+
3+
#[cfg(test)]
4+
mod tests {
5+
#[test]
6+
fn something_alloc() {
7+
assert_eq!(Vec::<u32>::new(), Vec::<u32>::new());
8+
}
9+
}

0 commit comments

Comments
 (0)