[llvm] ef24b4b - [Coroutines] Fix debug info scoping for nested structs in coroutine frames (#147622)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 10 10:13:30 PDT 2025
Author: Grigory Pastukhov
Date: 2025-07-10T10:13:26-07:00
New Revision: ef24b4b3261fa8f391710cfd73691e97233faaa2
URL: https://github.com/llvm/llvm-project/commit/ef24b4b3261fa8f391710cfd73691e97233faaa2
DIFF: https://github.com/llvm/llvm-project/commit/ef24b4b3261fa8f391710cfd73691e97233faaa2.diff
LOG: [Coroutines] Fix debug info scoping for nested structs in coroutine frames (#147622)
When generating debug info for coroutine frames, nested struct types
were incorrectly inheriting the top-level function scope instead of
having their parent struct as scope. This caused assertion failures in
DebugInfoMetadata.h during member list replacement for complex nested
struct hierarchies.
Fix by passing the parent DIStruct as scope when recursively calling
solveDIType for nested struct fields, ensuring proper debug info scoping
hierarchy.
Add regression test that validates proper nested struct scoping
hierarchy and
prevents future regressions.
Added:
llvm/test/Transforms/Coroutines/coro-split-dbg-nested-struct.ll
Modified:
llvm/lib/Transforms/Coroutines/CoroFrame.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 7224a56cd7b8a..fe30c6dc6abe4 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -639,10 +639,10 @@ static DIType *solveDIType(DIBuilder &Builder, Type *Ty,
SmallVector<Metadata *, 16> Elements;
for (unsigned I = 0; I < StructTy->getNumElements(); I++) {
DIType *DITy = solveDIType(Builder, StructTy->getElementType(I), Layout,
- Scope, LineNum, DITypeCache);
+ DIStruct, LineNum, DITypeCache);
assert(DITy);
Elements.push_back(Builder.createMemberType(
- Scope, DITy->getName(), Scope->getFile(), LineNum,
+ DIStruct, DITy->getName(), DIStruct->getFile(), LineNum,
DITy->getSizeInBits(), DITy->getAlignInBits(),
Layout.getStructLayout(StructTy)->getElementOffsetInBits(I),
llvm::DINode::FlagArtificial, DITy));
diff --git a/llvm/test/Transforms/Coroutines/coro-split-dbg-nested-struct.ll b/llvm/test/Transforms/Coroutines/coro-split-dbg-nested-struct.ll
new file mode 100644
index 0000000000000..12dfa16991326
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-dbg-nested-struct.ll
@@ -0,0 +1,61 @@
+; RUN: opt < %s -passes='cgscc(coro-split)' -S | FileCheck %s
+
+; Test that nested structs in coroutine frames have correct debug info scoping.
+
+; Minimal nested struct types that used to trigger a scoping issue:
+; we used to set the wrong `scope` for the `DIDerivedType` member entries of the `DICompositeType`
+; as well as the `scope` for `DICompositeType` for the inner struct itself.
+%"struct.Inner" = type { i32, ptr }
+%"struct.Outer" = type { %"struct.Inner", i64 }
+%"class.Promise" = type { %"struct.Outer" }
+
+define void @test_coro_function() presplitcoroutine !dbg !10 {
+entry:
+ %__promise = alloca %"class.Promise", align 8
+ %0 = call token @llvm.coro.id(i32 0, ptr %__promise, ptr null, ptr null)
+ %1 = call ptr @llvm.coro.begin(token %0, ptr null)
+ %2 = call token @llvm.coro.save(ptr null)
+ ret void
+}
+
+; CHECK: define void @test_coro_function()
+
+; Check that frame debug info is generated
+; CHECK: ![[FRAME_TYPE:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{{.*}}.coro_frame_ty"
+
+; Key validation: Check that nested structs have the correct scope hierarchy
+; 1. Promise should be scoped to the frame
+; CHECK: ![[PROMISE:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "class_Promise", scope: ![[FRAME_TYPE]]
+
+; 2. Members of Promise should be scoped to Promise (check this before Outer since it comes first in output)
+; CHECK: !DIDerivedType(tag: DW_TAG_member, name: "struct_Outer", scope: ![[PROMISE]]
+
+; 3. Outer should be scoped to Promise (not the frame!)
+; CHECK: ![[OUTER:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "struct_Outer", scope: ![[PROMISE]]
+
+; 4. First Outer member should be scoped to Outer
+; CHECK: !DIDerivedType(tag: DW_TAG_member, name: "struct_Inner", scope: ![[OUTER]]
+
+; 5. Inner should be scoped to Outer (proper nesting)
+; CHECK: ![[INNER:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "struct_Inner", scope: ![[OUTER]]
+
+; 6. Members of Inner should be scoped to Inner
+; CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__int_32", scope: ![[INNER]]
+; CHECK: !DIDerivedType(tag: DW_TAG_member, name: "PointerType", scope: ![[INNER]]
+
+; 7. Second Outer member comes after Inner (due to output order)
+; CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__int_64", scope: ![[OUTER]]
+
+declare token @llvm.coro.id(i32, ptr readnone, ptr readonly, ptr)
+declare ptr @llvm.coro.begin(token, ptr writeonly)
+declare token @llvm.coro.save(ptr)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cpp", directory: ".")
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = distinct !DISubprogram(name: "test_coro_function", scope: !1, file: !1, line: 1, type: !11, spFlags: DISPFlagDefinition, unit: !0)
+!11 = !DISubroutineType(types: !12)
+!12 = !{null}
More information about the llvm-commits
mailing list