Skip to content

Add debuginfo C support for a SetType, Subrangetype, dynamic array type and replace arrays #135607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 71 additions & 1 deletion llvm/include/llvm-c/DebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,6 @@ LLVMMetadataRef LLVMDIBuilderCreateUnionType(
LLVMMetadataRef *Elements, unsigned NumElements, unsigned RunTimeLang,
const char *UniqueId, size_t UniqueIdLen);


/**
* Create debugging information entry for an array.
* \param Builder The DIBuilder.
Expand All @@ -702,6 +701,77 @@ LLVMDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size,
LLVMMetadataRef *Subscripts,
unsigned NumSubscripts);

/**
* Create debugging information entry for a set.
* \param Builder The DIBuilder.
* \param Scope The scope in which the set is defined.
* \param Name A name that uniquely identifies this set.
* \param NameLen The length of the C string passed to \c Name.
* \param File File where the set is located.
* \param Line Line number of the declaration.
* \param SizeInBits Set size.
* \param AlignInBits Set alignment.
* \param BaseTy The base type of the set.
*/
LLVMMetadataRef LLVMDIBuilderCreateSetType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef BaseTy);

/**
* Create a descriptor for a subrange with dynamic bounds.
* \param Builder The DIBuilder.
* \param Scope The scope in which the subrange is defined.
* \param Name A name that uniquely identifies this subrange.
* \param NameLen The length of the C string passed to \c Name.
* \param LineNo Line number.
* \param File File where the subrange is located.
* \param SizeInBits Member size.
* \param AlignInBits Member alignment.
* \param Flags Flags.
* \param BaseTy The base type of the subrange. eg integer or enumeration
* \param LowerBound Lower bound of the subrange.
* \param UpperBound Upper bound of the subrange.
* \param Stride Stride of the subrange.
* \param Bias Bias of the subrange.
Comment on lines +732 to +736
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the assertion failure, I think more info about the expected type is needed here, and you should mention which parameters are ok to be null. Same for the other functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you address this comment?

*/
LLVMMetadataRef LLVMDIBuilderCreateSubrangeType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t SizeInBits,
uint32_t AlignInBits, LLVMDIFlags Flags, LLVMMetadataRef BaseTy,
LLVMMetadataRef LowerBound, LLVMMetadataRef UpperBound,
LLVMMetadataRef Stride, LLVMMetadataRef Bias);

/**
* Create debugging information entry for a dynamic array.
* \param Builder The DIBuilder.
* \param Size Array size.
* \param AlignInBits Alignment.
* \param Ty Element type.
* \param Subscripts Subscripts.
* \param NumSubscripts Number of subscripts.
* \param DataLocation DataLocation.
* \param Associated Associated.
* \param Allocated Allocated.
* \param Rank Rank.
* \param BitStride BitStride.
*/
LLVMMetadataRef LLVMDIBuilderCreateDynamicArrayType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t Size,
uint32_t AlignInBits, LLVMMetadataRef Ty, LLVMMetadataRef *Subscripts,
unsigned NumSubscripts, LLVMMetadataRef DataLocation,
LLVMMetadataRef Associated, LLVMMetadataRef Allocated, LLVMMetadataRef Rank,
LLVMMetadataRef BitStride);

/**
* Replace arrays.
*
* @see DIBuilder::replaceArrays()
*/
void LLVMReplaceArrays(LLVMDIBuilderRef Builder, LLVMMetadataRef *T,
LLVMMetadataRef *Elements, unsigned NumElements);

/**
* Create debugging information entry for a vector type.
* \param Builder The DIBuilder.
Expand Down
47 changes: 47 additions & 0 deletions llvm/lib/IR/DebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,53 @@ return wrap(unwrap(Builder)->createEnumerationType(
LineNumber, SizeInBits, AlignInBits, Elts, unwrapDI<DIType>(ClassTy)));
}

LLVMMetadataRef LLVMDIBuilderCreateSetType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef BaseTy) {
return wrap(unwrap(Builder)->createSetType(
unwrapDI<DIScope>(Scope), {Name, NameLen}, unwrapDI<DIFile>(File),
LineNumber, SizeInBits, AlignInBits, unwrapDI<DIType>(BaseTy)));
}

LLVMMetadataRef LLVMDIBuilderCreateSubrangeType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t SizeInBits,
uint32_t AlignInBits, LLVMDIFlags Flags, LLVMMetadataRef BaseTy,
LLVMMetadataRef LowerBound, LLVMMetadataRef UpperBound,
LLVMMetadataRef Stride, LLVMMetadataRef Bias) {
return wrap(unwrap(Builder)->createSubrangeType(
{Name, NameLen}, unwrapDI<DIFile>(File), LineNo, unwrapDI<DIScope>(Scope),
SizeInBits, AlignInBits, map_from_llvmDIFlags(Flags),
unwrapDI<DIType>(BaseTy), unwrap(LowerBound), unwrap(UpperBound),
unwrap(Stride), unwrap(Bias)));
}

LLVMMetadataRef LLVMDIBuilderCreateDynamicArrayType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t Size,
uint32_t AlignInBits, LLVMMetadataRef Ty, LLVMMetadataRef *Subscripts,
unsigned NumSubscripts, LLVMMetadataRef DataLocation,
LLVMMetadataRef Associated, LLVMMetadataRef Allocated, LLVMMetadataRef Rank,
LLVMMetadataRef BitStride) {
auto Subs =
unwrap(Builder)->getOrCreateArray({unwrap(Subscripts), NumSubscripts});
return wrap(unwrap(Builder)->createArrayType(
unwrapDI<DIScope>(Scope), {Name, NameLen}, unwrapDI<DIFile>(File), LineNo,
Size, AlignInBits, unwrapDI<DIType>(Ty), Subs,
unwrapDI<DIExpression>(DataLocation), unwrapDI<DIExpression>(Associated),
unwrapDI<DIExpression>(Allocated), unwrapDI<DIExpression>(Rank),
unwrap(BitStride)));
}
Comment on lines +1346 to +1361
Copy link
Contributor

@OCHyams OCHyams Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is unwrapDI<DIExpression> is the right move for these parameters? createArrayType takes PointerUnion<DIExpression *, DIVariable *>, as it looks like it expects either
an expression or variable, but this implementation means only DIExpressions are accepted by the C interface.

Something like the code-suggestion below might be good to cover all inputs. Please can you update the function comment to reflect the accepted types, and update the test to check variables, expressions and null.

Suggested change
LLVMMetadataRef LLVMDIBuilderCreateDynamicArrayType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t Size,
uint32_t AlignInBits, LLVMMetadataRef Ty, LLVMMetadataRef *Subscripts,
unsigned NumSubscripts, LLVMMetadataRef DataLocation,
LLVMMetadataRef Associated, LLVMMetadataRef Allocated, LLVMMetadataRef Rank,
LLVMMetadataRef BitStride) {
auto Subs =
unwrap(Builder)->getOrCreateArray({unwrap(Subscripts), NumSubscripts});
return wrap(unwrap(Builder)->createArrayType(
unwrapDI<DIScope>(Scope), {Name, NameLen}, unwrapDI<DIFile>(File), LineNo,
Size, AlignInBits, unwrapDI<DIType>(Ty), Subs,
unwrapDI<DIExpression>(DataLocation), unwrapDI<DIExpression>(Associated),
unwrapDI<DIExpression>(Allocated), unwrapDI<DIExpression>(Rank),
unwrap(BitStride)));
}
/// MD may be nullptr, a DIExpression or DIVariable.
PointerUnion<DIExpression *, DIVariable *> unwrapExprVar(LLVMMetadataRef MD) {
if (!MD)
return nullptr;
MDNode *MDN = unwrapDI<MDNode>(MD);
if (auto *E = dyn_cast<DIExpression>(MDN))
return E;
assert(isa<DIVariable>(MDN) && "Expected DIExpression or DIVariable");
return cast<DIVariable>(MDN);
}
LLVMMetadataRef LLVMDIBuilderCreateDynamicArrayType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, unsigned LineNo, LLVMMetadataRef File, uint64_t Size,
uint32_t AlignInBits, LLVMMetadataRef Ty, LLVMMetadataRef *Subscripts,
unsigned NumSubscripts, LLVMMetadataRef DataLocation,
LLVMMetadataRef Associated, LLVMMetadataRef Allocated, LLVMMetadataRef Rank,
LLVMMetadataRef BitStride) {
auto Subs =
unwrap(Builder)->getOrCreateArray({unwrap(Subscripts), NumSubscripts});
return wrap(unwrap(Builder)->createArrayType(
unwrapDI<DIScope>(Scope), {Name, NameLen}, unwrapDI<DIFile>(File), LineNo,
Size, AlignInBits, unwrapDI<DIType>(Ty), Subs,
unwrapExprVar(DataLocation), unwrapExprVar(Associated),
unwrapExprVar(Allocated), unwrapExprVar(Rank), unwrap(BitStride)));
}


void LLVMReplaceArrays(LLVMDIBuilderRef Builder, LLVMMetadataRef *T,
LLVMMetadataRef *Elements, unsigned NumElements) {
auto CT = unwrap<DICompositeType>(*T);
auto Elts =
unwrap(Builder)->getOrCreateArray({unwrap(Elements), NumElements});
unwrap(Builder)->replaceArrays(CT, Elts);
}

LLVMMetadataRef LLVMDIBuilderCreateUnionType(
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
Expand Down
70 changes: 42 additions & 28 deletions llvm/test/Bindings/llvm-c/debug_info_new_format.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,36 @@

; CHECK: ; ModuleID = 'debuginfo.c'
; CHECK-NEXT: source_filename = "debuginfo.c"

; CHECK: define i64 @foo(i64 %0, i64 %1, <10 x i64> %2) !dbg !36 {
; CHECK: define i64 @foo(i64 %0, i64 %1, <10 x i64> %2) !dbg !46 {
; CHECK-NEXT: entry:
; CHECK-NEXT: #dbg_declare(i64 0, !43, !DIExpression(), !50)
; CHECK-NEXT: #dbg_declare(i64 0, !44, !DIExpression(), !50)
; CHECK-NEXT: #dbg_declare(i64 0, !45, !DIExpression(), !50)
; CHECK-NEXT: #dbg_label(!51, !50)
; CHECK-NEXT: #dbg_declare(i64 0, !51, !DIExpression(), !58)
; CHECK-NEXT: #dbg_declare(i64 0, !52, !DIExpression(), !58)
; CHECK-NEXT: #dbg_declare(i64 0, !53, !DIExpression(), !58)
; CHECK-NEXT: #dbg_label(!59, !58)
; CHECK-NEXT: br label %vars
; CHECK-NEXT: #dbg_label(!52, !50)
; CHECK-NEXT: #dbg_label(!60, !58)
; CHECK-NEXT: br label %vars
; CHECK: vars:

; CHECK: vars: ; preds = %entry, %entry
; CHECK-NEXT: %p1 = phi i64 [ 0, %entry ]
; CHECK-NEXT: %p2 = phi i64 [ 0, %entry ]
; CHECK-NEXT: #dbg_value(i64 0, !46, !DIExpression(DW_OP_constu, 0, DW_OP_stack_value), !53)
; CHECK-NEXT: #dbg_value(i64 1, !48, !DIExpression(DW_OP_constu, 1, DW_OP_stack_value), !53)
; CHECK-NEXT: #dbg_value(i64 0, !54, !DIExpression(DW_OP_constu, 0, DW_OP_stack_value), !61)
; CHECK-NEXT: #dbg_value(i64 1, !56, !DIExpression(DW_OP_constu, 1, DW_OP_stack_value), !61)
; CHECK-NEXT: %a = add i64 %p1, %p2
; CHECK-NEXT: ret i64 0
; CHECK-NEXT: }

; CHECK: !llvm.dbg.cu = !{!0}
; CHECK-NEXT: !FooType = !{!33}
; CHECK-NEXT: !EnumTest = !{!3}
; CHECK-NEXT: !LargeEnumTest = !{!11}

; CHECK-NEXT: !SubrangeType = !{!36}
; CHECK-NEXT: !SetType1 = !{!37}
; CHECK-NEXT: !SetType2 = !{!38}
; CHECK-NEXT: !DynType = !{!39}
; CHECK-NEXT: !ClassType = !{!42}

; CHECK: !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "llvm-c-test", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !16, imports: !24, macros: !28, splitDebugInlining: false, sysroot: "/")
; CHECK-NEXT: !1 = !DIFile(filename: "debuginfo.c", directory: ".")
; CHECK-NEXT: !2 = !{!3, !11}
Expand Down Expand Up @@ -63,21 +69,29 @@
; CHECK-NEXT: !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 192, dwarfAddressSpace: 0)
; CHECK-NEXT: !34 = !DICompositeType(tag: DW_TAG_structure_type, name: "MyStruct", scope: !4, file: !1, size: 192, elements: !35, runtimeLang: DW_LANG_C89, identifier: "MyStruct")
; CHECK-NEXT: !35 = !{!6, !6, !6}
; CHECK-NEXT: !36 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 42, type: !37, scopeLine: 42, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !42)
; CHECK-NEXT: !37 = !DISubroutineType(types: !38)
; CHECK-NEXT: !38 = !{!6, !6, !39}
; CHECK-NEXT: !39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 640, flags: DIFlagVector, elements: !40)
; CHECK-NEXT: !36 = !DISubrangeType(name: "foo", scope: !1, file: !1, line: 42, size: 64, baseType: !6, lowerBound: i64 0, upperBound: i64 1, stride: i64 8, bias: i64 4)
; CHECK-NEXT: !37 = !DIDerivedType(tag: DW_TAG_set_type, name: "enumset", scope: !1, file: !1, line: 42, baseType: !3, size: 64)
; CHECK-NEXT: !38 = !DIDerivedType(tag: DW_TAG_set_type, name: "subrangeset", scope: !1, file: !1, line: 42, baseType: !36, size: 64)
; CHECK-NEXT: !39 = !DICompositeType(tag: DW_TAG_array_type, name: "foo", scope: !1, file: !1, line: 42, baseType: !6, size: 640, elements: !40, dataLocation: !DIExpression())
; CHECK-NEXT: !40 = !{!41}
; CHECK-NEXT: !41 = !DISubrange(count: 10, lowerBound: 0)
; CHECK-NEXT: !42 = !{!43, !44, !45, !46, !48, !49}
; CHECK-NEXT: !43 = !DILocalVariable(name: "a", arg: 1, scope: !36, file: !1, line: 42, type: !6)
; CHECK-NEXT: !44 = !DILocalVariable(name: "b", arg: 2, scope: !36, file: !1, line: 42, type: !6)
; CHECK-NEXT: !45 = !DILocalVariable(name: "c", arg: 3, scope: !36, file: !1, line: 42, type: !39)
; CHECK-NEXT: !46 = !DILocalVariable(name: "d", scope: !47, file: !1, line: 43, type: !6)
; CHECK-NEXT: !47 = distinct !DILexicalBlock(scope: !36, file: !1, line: 42)
; CHECK-NEXT: !48 = !DILocalVariable(name: "e", scope: !47, file: !1, line: 44, type: !6)
; CHECK-NEXT: !49 = !DILabel(scope: !36, name: "label3", file: !1, line: 42)
; CHECK-NEXT: !50 = !DILocation(line: 42, scope: !36)
; CHECK-NEXT: !51 = !DILabel(scope: !36, name: "label1", file: !1, line: 42)
; CHECK-NEXT: !52 = !DILabel(scope: !36, name: "label2", file: !1, line: 42)
; CHECK-NEXT: !53 = !DILocation(line: 43, scope: !36)
; CHECK-NEXT: !42 = !DICompositeType(tag: DW_TAG_class_type, name: "Class", scope: !4, file: !1, size: 192, flags: DIFlagFwdDecl, elements: !43, identifier: "FooClass")
; CHECK-NEXT: !43 = !{!44}
; CHECK-NEXT: !44 = !{!6, !6, !45}
; CHECK-NEXT: !45 = !DIBasicType(name: "Int32", size: 32)
; CHECK-NEXT: !46 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 42, type: !47, scopeLine: 42, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !50)
; CHECK-NEXT: !47 = !DISubroutineType(types: !48)
; CHECK-NEXT: !48 = !{!6, !6, !49}
; CHECK-NEXT: !49 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 640, flags: DIFlagVector, elements: !40)
; CHECK-NEXT: !50 = !{!51, !52, !53, !54, !56, !57}
; CHECK-NEXT: !51 = !DILocalVariable(name: "a", arg: 1, scope: !46, file: !1, line: 42, type: !6)
; CHECK-NEXT: !52 = !DILocalVariable(name: "b", arg: 2, scope: !46, file: !1, line: 42, type: !6)
; CHECK-NEXT: !53 = !DILocalVariable(name: "c", arg: 3, scope: !46, file: !1, line: 42, type: !49)
; CHECK-NEXT: !54 = !DILocalVariable(name: "d", scope: !55, file: !1, line: 43, type: !6)
; CHECK-NEXT: !55 = distinct !DILexicalBlock(scope: !46, file: !1, line: 42)
; CHECK-NEXT: !56 = !DILocalVariable(name: "e", scope: !55, file: !1, line: 44, type: !6)
; CHECK-NEXT: !57 = !DILabel(scope: !46, name: "label3", file: !1, line: 42)
; CHECK-NEXT: !58 = !DILocation(line: 42, scope: !46)
; CHECK-NEXT: !59 = !DILabel(scope: !46, name: "label1", file: !1, line: 42)
; CHECK-NEXT: !60 = !DILabel(scope: !46, name: "label2", file: !1, line: 42)
; CHECK-NEXT: !61 = !DILocation(line: 43, scope: !46)
46 changes: 46 additions & 0 deletions llvm/tools/llvm-c-test/debuginfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,52 @@ int llvm_test_dibuilder(void) {
M, "LargeEnumTest",
LLVMMetadataAsValue(LLVMGetModuleContext(M), LargeEnumTest));

LLVMValueRef FooVal3 = LLVMConstInt(LLVMInt64Type(), 8, false);
LLVMValueRef FooVal4 = LLVMConstInt(LLVMInt64Type(), 4, false);
LLVMMetadataRef lo = LLVMValueAsMetadata(FooVal1);
LLVMMetadataRef hi = LLVMValueAsMetadata(FooVal2);
LLVMMetadataRef strd = LLVMValueAsMetadata(FooVal3);
LLVMMetadataRef bias = LLVMValueAsMetadata(FooVal4);
LLVMMetadataRef SubrangeMetadataTy = LLVMDIBuilderCreateSubrangeType(
DIB, File, "foo", 3, 42, File, 64, 0, 0, Int64Ty, lo, hi, strd, bias);
LLVMAddNamedMetadataOperand(
M, "SubrangeType",
LLVMMetadataAsValue(LLVMGetModuleContext(M), SubrangeMetadataTy));

LLVMMetadataRef SetMetadataTy1 = LLVMDIBuilderCreateSetType(
DIB, File, "enumset", 7, File, 42, 64, 0, EnumTest);
LLVMMetadataRef SetMetadataTy2 = LLVMDIBuilderCreateSetType(
DIB, File, "subrangeset", 11, File, 42, 64, 0, SubrangeMetadataTy);
LLVMAddNamedMetadataOperand(
M, "SetType1",
LLVMMetadataAsValue(LLVMGetModuleContext(M), SetMetadataTy1));
LLVMAddNamedMetadataOperand(
M, "SetType2",
LLVMMetadataAsValue(LLVMGetModuleContext(M), SetMetadataTy2));

LLVMMetadataRef DynSubscripts[] = {
LLVMDIBuilderGetOrCreateSubrange(DIB, 0, 10),
};
LLVMMetadataRef L = LLVMDIBuilderCreateExpression(DIB, NULL, 0);
LLVMMetadataRef DynamicArrayMetadataTy = LLVMDIBuilderCreateDynamicArrayType(
DIB, File, "foo", 3, 42, File, 64 * 10, 0, Int64Ty, DynSubscripts, 1, L,
NULL, NULL, NULL, NULL);
LLVMAddNamedMetadataOperand(
M, "DynType",
LLVMMetadataAsValue(LLVMGetModuleContext(M), DynamicArrayMetadataTy));

LLVMMetadataRef StructPTy = LLVMDIBuilderCreateForwardDecl(
DIB, 2 /*DW_TAG_class_type*/, "Class1", 5, NameSpace, File, 0, 0, 192, 0,
"FooClass", 8);

LLVMMetadataRef Int32Ty =
LLVMDIBuilderCreateBasicType(DIB, "Int32", 5, 32, 0, LLVMDIFlagZero);
LLVMMetadataRef StructElts[] = {Int64Ty, Int64Ty, Int32Ty};
LLVMMetadataRef ClassArr = LLVMDIBuilderGetOrCreateArray(DIB, StructElts, 3);
LLVMReplaceArrays(DIB, &StructPTy, &ClassArr, 1);
LLVMAddNamedMetadataOperand(
M, "ClassType", LLVMMetadataAsValue(LLVMGetModuleContext(M), StructPTy));

// Using the new debug format, debug records get attached to instructions.
// Insert a `br` and `ret` now to absorb the debug records which are
// currently "trailing", meaning that they're associated with a block
Expand Down