1 From 10b69dde3fd15334ea2382d2dc9e9a261de1afaf Mon Sep 17 00:00:00 2001
2 From: Josh Stone <jistone@redhat.com>
3 Date: Wed, 3 May 2023 15:52:31 -0700
4 Subject: [PATCH] debuginfo: split method declaration and definition
6 When we're adding a method to a type DIE, we only want a DW_AT_declaration
7 there, because LLVM LTO can't unify type definitions when a child DIE is a
8 full subprogram definition. Now the subprogram definition gets added at the
9 CU level with a specification link back to the abstract declaration.
11 .../rustc_codegen_llvm/src/debuginfo/mod.rs | 85 +++++++++++--------
12 compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 ++++
13 .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 22 +++++
14 .../issue-109934-lto-debuginfo/Makefile | 12 +++
15 .../issue-109934-lto-debuginfo/lib.rs | 9 ++
16 5 files changed, 109 insertions(+), 34 deletions(-)
17 create mode 100644 tests/run-make/issue-109934-lto-debuginfo/Makefile
18 create mode 100644 tests/run-make/issue-109934-lto-debuginfo/lib.rs
20 diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
21 index 2e9f89f419696..b138b0c0e70a1 100644
22 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
23 +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
24 @@ -322,7 +322,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
27 let def_id = instance.def_id();
28 - let containing_scope = get_containing_scope(self, instance);
29 + let (containing_scope, is_method) = get_containing_scope(self, instance);
30 let span = tcx.def_span(def_id);
31 let loc = self.lookup_debug_loc(span.lo());
32 let file_metadata = file_metadata(self, &loc.file);
33 @@ -378,8 +378,29 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
38 - return llvm::LLVMRustDIBuilderCreateFunction(
39 + // When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because
40 + // LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
41 + // When we use this `decl` below, the subprogram definition gets created at the CU level
42 + // with a DW_AT_specification pointing back to the type's declaration.
43 + let decl = is_method.then(|| unsafe {
44 + llvm::LLVMRustDIBuilderCreateMethod(
47 + name.as_ptr().cast(),
49 + linkage_name.as_ptr().cast(),
53 + function_type_metadata,
55 + spflags & !DISPFlags::SPFlagDefinition,
56 + template_parameters,
61 + llvm::LLVMRustDIBuilderCreateFunction(
65 @@ -394,9 +415,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
67 maybe_definition_llfn,
76 fn get_function_signature<'ll, 'tcx>(
77 cx: &CodegenCx<'ll, 'tcx>,
78 @@ -493,14 +514,16 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
82 + /// Returns a scope, plus `true` if that's a type scope for "class" methods,
83 + /// otherwise `false` for plain namespace scopes.
84 fn get_containing_scope<'ll, 'tcx>(
85 cx: &CodegenCx<'ll, 'tcx>,
86 instance: Instance<'tcx>,
88 + ) -> (&'ll DIScope, bool) {
89 // First, let's see if this is a method within an inherent impl. Because
90 // if yes, we want to make the result subroutine DIE a child of the
91 // subroutine's self-type.
92 - let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
93 + if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
94 // If the method does *not* belong to a trait, proceed
95 if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
96 let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
97 @@ -511,39 +534,33 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
99 // Only "class" methods are generally understood by LLVM,
100 // so avoid methods on other types (e.g., `<*mut T>::null`).
101 - match impl_self_ty.kind() {
102 - ty::Adt(def, ..) if !def.is_box() => {
103 - // Again, only create type information if full debuginfo is enabled
104 - if cx.sess().opts.debuginfo == DebugInfo::Full
105 - && !impl_self_ty.needs_subst()
107 - Some(type_di_node(cx, impl_self_ty))
109 - Some(namespace::item_namespace(cx, def.did()))
111 + if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
112 + // Again, only create type information if full debuginfo is enabled
113 + if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.needs_subst()
115 + return (type_di_node(cx, impl_self_ty), true);
117 + return (namespace::item_namespace(cx, def.did()), false);
122 // For trait method impls we still use the "parallel namespace"
129 - self_type.unwrap_or_else(|| {
130 - namespace::item_namespace(
133 - krate: instance.def_id().krate,
136 - .def_key(instance.def_id())
138 - .expect("get_containing_scope: missing parent?"),
142 + let scope = namespace::item_namespace(
145 + krate: instance.def_id().krate,
148 + .def_key(instance.def_id())
150 + .expect("get_containing_scope: missing parent?"),
157 diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
158 index c95148013eb74..1f98d91c32054 100644
159 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
160 +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
161 @@ -1987,6 +1987,21 @@ extern "C" {
162 Decl: Option<&'a DIDescriptor>,
163 ) -> &'a DISubprogram;
165 + pub fn LLVMRustDIBuilderCreateMethod<'a>(
166 + Builder: &DIBuilder<'a>,
167 + Scope: &'a DIDescriptor,
168 + Name: *const c_char,
170 + LinkageName: *const c_char,
171 + LinkageNameLen: size_t,
176 + SPFlags: DISPFlags,
177 + TParam: &'a DIArray,
178 + ) -> &'a DISubprogram;
180 pub fn LLVMRustDIBuilderCreateBasicType<'a>(
181 Builder: &DIBuilder<'a>,
183 diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
184 index cadb6b1e23fe9..49acd71b3e106 100644
185 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
186 +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
187 @@ -831,6 +831,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
191 +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
192 + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
193 + const char *Name, size_t NameLen,
194 + const char *LinkageName, size_t LinkageNameLen,
195 + LLVMMetadataRef File, unsigned LineNo,
196 + LLVMMetadataRef Ty, LLVMRustDIFlags Flags,
197 + LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
198 + DITemplateParameterArray TParams =
199 + DITemplateParameterArray(unwrap<MDTuple>(TParam));
200 + DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
201 + DINode::DIFlags llvmFlags = fromRust(Flags);
202 + DISubprogram *Sub = Builder->createMethod(
203 + unwrapDI<DIScope>(Scope),
204 + StringRef(Name, NameLen),
205 + StringRef(LinkageName, LinkageNameLen),
206 + unwrapDI<DIFile>(File), LineNo,
207 + unwrapDI<DISubroutineType>(Ty),
208 + 0, 0, nullptr, // VTable params aren't used
209 + llvmFlags, llvmSPFlags, TParams);
213 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
214 LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
215 uint64_t SizeInBits, unsigned Encoding) {
216 diff --git a/tests/run-make/issue-109934-lto-debuginfo/Makefile b/tests/run-make/issue-109934-lto-debuginfo/Makefile
218 index 0000000000000..3b7a99d3dbc62
220 +++ b/tests/run-make/issue-109934-lto-debuginfo/Makefile
222 +# ignore-cross-compile
225 +# With the upgrade to LLVM 16, this was getting:
227 +# error: Cannot represent a difference across sections
229 +# The error stemmed from DI function definitions under type scopes, fixed by
230 +# only declaring in type scope and defining the subprogram elsewhere.
233 + $(RUSTC) lib.rs --test -C lto=fat -C debuginfo=2 -C incremental=$(TMPDIR)/inc-fat
234 diff --git a/tests/run-make/issue-109934-lto-debuginfo/lib.rs b/tests/run-make/issue-109934-lto-debuginfo/lib.rs
236 index 0000000000000..c405928bd1824
238 +++ b/tests/run-make/issue-109934-lto-debuginfo/lib.rs
245 + fn something_alloc() {
246 + assert_eq!(Vec::<u32>::new(), Vec::<u32>::new());