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.
This commit is contained in:
parent
a368898de7
commit
10b69dde3f
5 changed files with 109 additions and 34 deletions
|
@ -322,7 +322,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
|
||||||
let def_id = instance.def_id();
|
let def_id = instance.def_id();
|
||||||
let containing_scope = get_containing_scope(self, instance);
|
let (containing_scope, is_method) = get_containing_scope(self, instance);
|
||||||
let span = tcx.def_span(def_id);
|
let span = tcx.def_span(def_id);
|
||||||
let loc = self.lookup_debug_loc(span.lo());
|
let loc = self.lookup_debug_loc(span.lo());
|
||||||
let file_metadata = file_metadata(self, &loc.file);
|
let file_metadata = file_metadata(self, &loc.file);
|
||||||
|
@ -378,8 +378,29 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
// When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because
|
||||||
return llvm::LLVMRustDIBuilderCreateFunction(
|
// LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
|
||||||
|
// When we use this `decl` below, the subprogram definition gets created at the CU level
|
||||||
|
// with a DW_AT_specification pointing back to the type's declaration.
|
||||||
|
let decl = is_method.then(|| unsafe {
|
||||||
|
llvm::LLVMRustDIBuilderCreateMethod(
|
||||||
|
DIB(self),
|
||||||
|
containing_scope,
|
||||||
|
name.as_ptr().cast(),
|
||||||
|
name.len(),
|
||||||
|
linkage_name.as_ptr().cast(),
|
||||||
|
linkage_name.len(),
|
||||||
|
file_metadata,
|
||||||
|
loc.line,
|
||||||
|
function_type_metadata,
|
||||||
|
flags,
|
||||||
|
spflags & !DISPFlags::SPFlagDefinition,
|
||||||
|
template_parameters,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return unsafe {
|
||||||
|
llvm::LLVMRustDIBuilderCreateFunction(
|
||||||
DIB(self),
|
DIB(self),
|
||||||
containing_scope,
|
containing_scope,
|
||||||
name.as_ptr().cast(),
|
name.as_ptr().cast(),
|
||||||
|
@ -394,9 +415,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
spflags,
|
spflags,
|
||||||
maybe_definition_llfn,
|
maybe_definition_llfn,
|
||||||
template_parameters,
|
template_parameters,
|
||||||
None,
|
decl,
|
||||||
);
|
)
|
||||||
}
|
};
|
||||||
|
|
||||||
fn get_function_signature<'ll, 'tcx>(
|
fn get_function_signature<'ll, 'tcx>(
|
||||||
cx: &CodegenCx<'ll, 'tcx>,
|
cx: &CodegenCx<'ll, 'tcx>,
|
||||||
|
@ -493,14 +514,16 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
names
|
names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a scope, plus `true` if that's a type scope for "class" methods,
|
||||||
|
/// otherwise `false` for plain namespace scopes.
|
||||||
fn get_containing_scope<'ll, 'tcx>(
|
fn get_containing_scope<'ll, 'tcx>(
|
||||||
cx: &CodegenCx<'ll, 'tcx>,
|
cx: &CodegenCx<'ll, 'tcx>,
|
||||||
instance: Instance<'tcx>,
|
instance: Instance<'tcx>,
|
||||||
) -> &'ll DIScope {
|
) -> (&'ll DIScope, bool) {
|
||||||
// First, let's see if this is a method within an inherent impl. Because
|
// First, let's see if this is a method within an inherent impl. Because
|
||||||
// if yes, we want to make the result subroutine DIE a child of the
|
// if yes, we want to make the result subroutine DIE a child of the
|
||||||
// subroutine's self-type.
|
// subroutine's self-type.
|
||||||
let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
|
if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
|
||||||
// If the method does *not* belong to a trait, proceed
|
// If the method does *not* belong to a trait, proceed
|
||||||
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
|
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
|
||||||
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
|
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
|
||||||
|
@ -511,39 +534,33 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
|
|
||||||
// Only "class" methods are generally understood by LLVM,
|
// Only "class" methods are generally understood by LLVM,
|
||||||
// so avoid methods on other types (e.g., `<*mut T>::null`).
|
// so avoid methods on other types (e.g., `<*mut T>::null`).
|
||||||
match impl_self_ty.kind() {
|
if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
|
||||||
ty::Adt(def, ..) if !def.is_box() => {
|
// Again, only create type information if full debuginfo is enabled
|
||||||
// Again, only create type information if full debuginfo is enabled
|
if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param()
|
||||||
if cx.sess().opts.debuginfo == DebugInfo::Full
|
{
|
||||||
&& !impl_self_ty.has_param()
|
return (type_di_node(cx, impl_self_ty), true);
|
||||||
{
|
} else {
|
||||||
Some(type_di_node(cx, impl_self_ty))
|
return (namespace::item_namespace(cx, def.did()), false);
|
||||||
} else {
|
|
||||||
Some(namespace::item_namespace(cx, def.did()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For trait method impls we still use the "parallel namespace"
|
// For trait method impls we still use the "parallel namespace"
|
||||||
// strategy
|
// strategy
|
||||||
None
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
self_type.unwrap_or_else(|| {
|
let scope = namespace::item_namespace(
|
||||||
namespace::item_namespace(
|
cx,
|
||||||
cx,
|
DefId {
|
||||||
DefId {
|
krate: instance.def_id().krate,
|
||||||
krate: instance.def_id().krate,
|
index: cx
|
||||||
index: cx
|
.tcx
|
||||||
.tcx
|
.def_key(instance.def_id())
|
||||||
.def_key(instance.def_id())
|
.parent
|
||||||
.parent
|
.expect("get_containing_scope: missing parent?"),
|
||||||
.expect("get_containing_scope: missing parent?"),
|
},
|
||||||
},
|
);
|
||||||
)
|
(scope, false)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1987,6 +1987,21 @@ extern "C" {
|
||||||
Decl: Option<&'a DIDescriptor>,
|
Decl: Option<&'a DIDescriptor>,
|
||||||
) -> &'a DISubprogram;
|
) -> &'a DISubprogram;
|
||||||
|
|
||||||
|
pub fn LLVMRustDIBuilderCreateMethod<'a>(
|
||||||
|
Builder: &DIBuilder<'a>,
|
||||||
|
Scope: &'a DIDescriptor,
|
||||||
|
Name: *const c_char,
|
||||||
|
NameLen: size_t,
|
||||||
|
LinkageName: *const c_char,
|
||||||
|
LinkageNameLen: size_t,
|
||||||
|
File: &'a DIFile,
|
||||||
|
LineNo: c_uint,
|
||||||
|
Ty: &'a DIType,
|
||||||
|
Flags: DIFlags,
|
||||||
|
SPFlags: DISPFlags,
|
||||||
|
TParam: &'a DIArray,
|
||||||
|
) -> &'a DISubprogram;
|
||||||
|
|
||||||
pub fn LLVMRustDIBuilderCreateBasicType<'a>(
|
pub fn LLVMRustDIBuilderCreateBasicType<'a>(
|
||||||
Builder: &DIBuilder<'a>,
|
Builder: &DIBuilder<'a>,
|
||||||
Name: *const c_char,
|
Name: *const c_char,
|
||||||
|
|
|
@ -831,6 +831,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
|
||||||
return wrap(Sub);
|
return wrap(Sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
|
||||||
|
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
|
||||||
|
const char *Name, size_t NameLen,
|
||||||
|
const char *LinkageName, size_t LinkageNameLen,
|
||||||
|
LLVMMetadataRef File, unsigned LineNo,
|
||||||
|
LLVMMetadataRef Ty, LLVMRustDIFlags Flags,
|
||||||
|
LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
|
||||||
|
DITemplateParameterArray TParams =
|
||||||
|
DITemplateParameterArray(unwrap<MDTuple>(TParam));
|
||||||
|
DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
|
||||||
|
DINode::DIFlags llvmFlags = fromRust(Flags);
|
||||||
|
DISubprogram *Sub = Builder->createMethod(
|
||||||
|
unwrapDI<DIScope>(Scope),
|
||||||
|
StringRef(Name, NameLen),
|
||||||
|
StringRef(LinkageName, LinkageNameLen),
|
||||||
|
unwrapDI<DIFile>(File), LineNo,
|
||||||
|
unwrapDI<DISubroutineType>(Ty),
|
||||||
|
0, 0, nullptr, // VTable params aren't used
|
||||||
|
llvmFlags, llvmSPFlags, TParams);
|
||||||
|
return wrap(Sub);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
|
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
|
||||||
LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
|
LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
|
||||||
uint64_t SizeInBits, unsigned Encoding) {
|
uint64_t SizeInBits, unsigned Encoding) {
|
||||||
|
|
12
tests/run-make/issue-109934-lto-debuginfo/Makefile
Normal file
12
tests/run-make/issue-109934-lto-debuginfo/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# ignore-cross-compile
|
||||||
|
include ../tools.mk
|
||||||
|
|
||||||
|
# With the upgrade to LLVM 16, this was getting:
|
||||||
|
#
|
||||||
|
# error: Cannot represent a difference across sections
|
||||||
|
#
|
||||||
|
# The error stemmed from DI function definitions under type scopes, fixed by
|
||||||
|
# only declaring in type scope and defining the subprogram elsewhere.
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(RUSTC) lib.rs --test -C lto=fat -C debuginfo=2 -C incremental=$(TMPDIR)/inc-fat
|
9
tests/run-make/issue-109934-lto-debuginfo/lib.rs
Normal file
9
tests/run-make/issue-109934-lto-debuginfo/lib.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn something_alloc() {
|
||||||
|
assert_eq!(Vec::<u32>::new(), Vec::<u32>::new());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue