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

Conversation

demoitem
Copy link

This change adds some support to the C DebugInfo capability to create set types,
subrange types, dynamic array types and the ability to replace arrays.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Apr 14, 2025

@llvm/pr-subscribers-debuginfo

Author: peter mckinna (demoitem)

Changes

This change adds some support to the C DebugInfo capability to create set types,
subrange types, dynamic array types and the ability to replace arrays.


Full diff: https://github.com/llvm/llvm-project/pull/135607.diff

2 Files Affected:

  • (modified) llvm/include/llvm-c/DebugInfo.h (+72-1)
  • (modified) llvm/lib/IR/DebugInfo.cpp (+51)
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index 11e0b9b4c81e8..0f267d9941b08 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -673,7 +673,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.
@@ -689,6 +688,78 @@ 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 this module is imported into.
+ * \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 this module is imported into.
+ * \param Name       A name that uniquely identifies this set.
+ * \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.
+ */
+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.
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index cefd6fe14a3ac..c2f12f0efa030 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -1309,6 +1309,57 @@ 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<DIExpression>(LowerBound), unwrap<DIExpression>(UpperBound),
+          unwrap<DIExpression>(Stride), unwrap<DIExpression>(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,
+      unwrap<DIExpression>(DataLocation), unwrap<DIExpression>(Associated),
+      unwrap<DIExpression>(Allocated), unwrap<DIExpression>(Rank),
+      unwrap(BitStride)));
+}
+
+void LLVMReplaceArrays(LLVMDIBuilderRef Builder, LLVMMetadataRef *T,
+                       LLVMMetadataRef *Elements, unsigned NumElements) {
+  auto Arr = unwrap<DICompositeType>(*T);
+  auto Elts = unwrap(Builder)->getOrCreateArray({unwrap(Elements),
+		                                 NumElements});
+  unwrap(Builder)->replaceArrays(Arr, Elts);
+}
+
 LLVMMetadataRef LLVMDIBuilderCreateUnionType(
   LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
   size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,

@llvmbot
Copy link
Member

llvmbot commented Apr 14, 2025

@llvm/pr-subscribers-llvm-ir

Author: peter mckinna (demoitem)

Changes

This change adds some support to the C DebugInfo capability to create set types,
subrange types, dynamic array types and the ability to replace arrays.


Full diff: https://github.com/llvm/llvm-project/pull/135607.diff

2 Files Affected:

  • (modified) llvm/include/llvm-c/DebugInfo.h (+72-1)
  • (modified) llvm/lib/IR/DebugInfo.cpp (+51)
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index 11e0b9b4c81e8..0f267d9941b08 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -673,7 +673,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.
@@ -689,6 +688,78 @@ 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 this module is imported into.
+ * \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 this module is imported into.
+ * \param Name       A name that uniquely identifies this set.
+ * \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.
+ */
+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.
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index cefd6fe14a3ac..c2f12f0efa030 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -1309,6 +1309,57 @@ 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<DIExpression>(LowerBound), unwrap<DIExpression>(UpperBound),
+          unwrap<DIExpression>(Stride), unwrap<DIExpression>(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,
+      unwrap<DIExpression>(DataLocation), unwrap<DIExpression>(Associated),
+      unwrap<DIExpression>(Allocated), unwrap<DIExpression>(Rank),
+      unwrap(BitStride)));
+}
+
+void LLVMReplaceArrays(LLVMDIBuilderRef Builder, LLVMMetadataRef *T,
+                       LLVMMetadataRef *Elements, unsigned NumElements) {
+  auto Arr = unwrap<DICompositeType>(*T);
+  auto Elts = unwrap(Builder)->getOrCreateArray({unwrap(Elements),
+		                                 NumElements});
+  unwrap(Builder)->replaceArrays(Arr, Elts);
+}
+
 LLVMMetadataRef LLVMDIBuilderCreateUnionType(
   LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
   size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,

Copy link
Contributor

@OCHyams OCHyams left a comment

Choose a reason for hiding this comment

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

Hi @demoitem, thanks for the PR. This seems reasonable to me, but should probably be tested. Please could you test by adding calls to llvm/tools/llvm-c-test/debuginfo.c and updating the relevant checks in llvm/test/Bindings/llvm-c/debug_info_new_format.ll?

/**
* Create debugging information entry for a set.
* @param Builder The DIBuilder.
* \param Scope The scope this module is imported into.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the Scope param description right - perhaps something like The scope in which the set is defined. makes more sense?. Same question for LLVMDIBuilderCreateSubrangeType.

Copy link
Author

Choose a reason for hiding this comment

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

Okay changed that.

* \param Line Line number of the declaration.
* \param SizeInBits Set size.
* \param AlignInBits Set alignment.
* @param BaseTy The base type of the set.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: While we're here, for consistency please can we use \param instead of @param for BaseTy and Builder too?

Copy link
Author

Choose a reason for hiding this comment

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

Yes fixed those as well.

@demoitem
Copy link
Author

Added that test case. Let me know if there is anything else.

@OCHyams OCHyams added the llvm:as-a-library LLVM-C and C++ API label Apr 22, 2025
@demoitem
Copy link
Author

I'm just checking the status of this request. I see that the build failed but am at a loss to
understand why. The error seems to lack any information as to what must be fixed.

@DavidSpickett
Copy link
Collaborator

Yes without any backtrace it's not great:

llvm-c-test: /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-bt7k7-1/llvm-project/github-pull-requests/llvm/include/llvm/Support/Casting.h:578: decltype(auto) llvm::cast(From *) [To = llvm::DIExpression, From = llvm::Metadata]: Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.

If you build locally you may be able to get the same error with a backtrace printed out as well. If it doesn't print out, attach a debugger to the test case and backtrace in there. You may need to add -DLLVM_ENABLE_ASSERTIONS=ON to your CMake config to get the assert to happen.

If you are using a debug build, then assertions are already enabled.

(https://www.llvm.org/docs/CMake.html#llvm-enable-assertions)

This assertion is to do with LLVM's own version of runtime type information, so something has tried to cast some object to type To when it is not that type. Of course it doesn't tell you what To is, but perhaps you can get that if you see how the program got to the assertion.

@demoitem
Copy link
Author

demoitem commented May 7, 2025

I wonder if someone could merge this change. I have built it against the latest main and it compiles fine.
ninja check produced no errors on my linux machine so I cannot explain the build problems here.

@DavidSpickett
Copy link
Collaborator

DavidSpickett commented May 7, 2025

I can reproduce the failure locally with cmake config:

cmake -G Ninja ../llvm/llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=True '-DLLVM_ENABLE_PROJECTS=llvm;clang-tools-extra;clang'
$ ./bin/llvm-lit ../llvm-project/llvm/test/Bindings/llvm-c/debug_info_new_format.ll -a
-- Testing: 1 tests, 1 workers --
FAIL: LLVM :: Bindings/llvm-c/debug_info_new_format.ll (1 of 1)
******************** TEST 'LLVM :: Bindings/llvm-c/debug_info_new_format.ll' FAILED ********************
Exit Code: 2

Command Output (stderr):
--
/home/david.spickett/build-llvm-aarch64/bin/llvm-c-test --test-dibuilder | /home/david.spickett/build-llvm-aarch64/bin/FileCheck /home/david.spickett/llvm-project/llvm/test/Bindings/llvm-c/debug_info_new_format.ll # RUN: at line 1
+ /home/david.spickett/build-llvm-aarch64/bin/llvm-c-test --test-dibuilder
+ /home/david.spickett/build-llvm-aarch64/bin/FileCheck /home/david.spickett/llvm-project/llvm/test/Bindings/llvm-c/debug_info_new_format.ll
llvm-c-test: /home/david.spickett/llvm-project/llvm/include/llvm/Support/Casting.h:578: decltype(auto) llvm::cast(From *) [To = llvm::DIExpression, From = llvm::Metadata]: Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
FileCheck error: '<stdin>' is empty.
FileCheck command line:  /home/david.spickett/build-llvm-aarch64/bin/FileCheck /home/david.spickett/llvm-project/llvm/test/Bindings/llvm-c/debug_info_new_format.ll

--

********************
********************
Failed Tests (1):
  LLVM :: Bindings/llvm-c/debug_info_new_format.ll

If you use a debug build (-DCMAKE_BUILD_TYPE=Debug) you can get more information about what it attempted to do:

$ gdb -args /home/david.spickett/build-llvm-aarch64/bin/llvm-c-test --test-dibuilder

This is the backtrace:

(gdb) bt
#0  __pthread_kill_implementation (threadid=281474842230816, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
#1  0x0000fffff7b5f244 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  0x0000fffff7b1a67c in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x0000fffff7b07130 in __GI_abort () at ./stdlib/abort.c:79
#4  0x0000fffff7b13fd4 in __assert_fail_base (fmt=0xfffff7c2c550 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=assertion@entry=0xaaaaab7292dd "isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"", 
    file=file@entry=0xaaaaab8b84b7 "/home/david.spickett/llvm-project/llvm/include/llvm/Support/Casting.h", line=line@entry=578, 
    function=function@entry=0xaaaaabc61c28 "decltype(auto) llvm::cast(From *) [To = llvm::DIExpression, From = llvm::Metadata]") at ./assert/assert.c:94
#5  0x0000fffff7b1404c in __GI___assert_fail (assertion=0xaaaaab7292dd "isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"", 
    file=0xaaaaab8b84b7 "/home/david.spickett/llvm-project/llvm/include/llvm/Support/Casting.h", line=578, 
    function=0xaaaaabc61c28 "decltype(auto) llvm::cast(From *) [To = llvm::DIExpression, From = llvm::Metadata]") at ./assert/assert.c:103
#6  0x0000aaaab08f9684 in llvm::cast<llvm::DIExpression, llvm::Metadata> (Val=0xaaaab6a154a0) at /home/david.spickett/llvm-project/llvm/include/llvm/Support/Casting.h:578
#7  0x0000aaaab2c4e9c4 in llvm::unwrap<llvm::DIExpression> (P=0xaaaab6a154a0) at /home/david.spickett/llvm-project/llvm/include/llvm/IR/Metadata.h:148
#8  0x0000aaaab2c442c0 in LLVMDIBuilderCreateSubrangeType (Builder=0xaaaab69d3860, Scope=0xaaaab6a10660, Name=0xaaaaab308484 "foo", NameLen=3, LineNo=42, File=0xaaaab6a10660, SizeInBits=64, AlignInBits=0, 
    Flags=LLVMDIFlagZero, BaseTy=0xaaaab6a12928, LowerBound=0xaaaab6a154a0, UpperBound=0xaaaab6a19510, Stride=0x0, Bias=0x0) at /home/david.spickett/llvm-project/llvm/lib/IR/DebugInfo.cpp:1342
#9  0x0000aaaab01c16b4 in llvm_test_dibuilder () at /home/david.spickett/llvm-project/llvm/tools/llvm-c-test/debuginfo.c:240
#10 0x0000aaaab01cd0e0 in main (argc=2, argv=0xfffffffff238) at /home/david.spickett/llvm-project/llvm/tools/llvm-c-test/main.c:113

The To and From types:

(gdb) p Val
$5 = (llvm::Metadata *) 0xaaaab6a154a0
(gdb) ptype From
type = class llvm::Metadata {
<...>
(gdb) ptype To
type = class llvm::DIExpression : public llvm::MDNode {
  private:
    std::vector<unsigned long> Elements;
<...>

The cast is actually requested by this wrapper macro:

│      147  // Create wrappers for C Binding types (see CBindingWrapping.h).                                                                                                                                      
│  >   148  DEFINE_ISA_CONVERSION_FUNCTIONS(Metadata, LLVMMetadataRef)

And I have no idea what that's doing or why, but there are some ways you can investigate at least.

Before merging you will least one approval before this can be merged. Perhaps @OCHyams has the time to review, but if not, someone from @llvm/pr-subscribers-debug-info will do so.

Copy link
Contributor

@OCHyams OCHyams left a comment

Choose a reason for hiding this comment

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

Sorry, this fell off my radar.

I can see that in DwarfUnit::constructSubrangeDIE DIExpression s are valid for lowerBound, upperBound, stride and bias... but I can't see any LLVM tests that exercise those. That's not something you need to address with this commit, but I thought it worth bringing up as I was going to ask "is this actually a supported configuration", and found that by digging about.

cc @tromey who looks to have added DISubrangeType initially in #126772 - would you be able to give this patch a quick review if you are able?

I think I found the cause of the assertion failure - please see my inline comments.

Comment on lines +732 to +736
* \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.
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 lo = LLVMValueAsMetadata(FooVal1);
LLVMMetadataRef hi = LLVMValueAsMetadata(FooVal2);
LLVMMetadataRef SubrangeMetadataTy = LLVMDIBuilderCreateSubrangeType(
DIB, File, "foo", 3, 42, File, 64, 0, 0, Int64Ty, lo, hi, NULL, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the assertion caused by passing NULL in here? The C API call calls unwrap which calls llvm::cast, which asserts if given non-null pointers.

As @DavidSpickett points out, the test might pass locally for you if you're not building with assertions.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the quick response. My reading of the backtrace suggests it is the lowerbound which is
asserting. There seems to be a problem wrapping a DIExpression. I may have been hasty in treating those
parameters as expressions as the DIBuilder function treats them as either a constant, a variable or and
expression. So how is one supposed to wrap that? The strange thing is that without asserts it works and
generates the correct debug tags. Or am I completely wrong in that assessment.

@tromey
Copy link
Contributor

tromey commented May 7, 2025

I can see that in DwarfUnit::constructSubrangeDIE DIExpression s are valid for lowerBound, upperBound, stride and bias... but I can't see any LLVM tests that exercise those. That's not something you need to address with this commit, but I thought it worth bringing up as I was going to ask "is this actually a supported configuration", and found that by digging about.

Sorry about not adding tests for that. Expressions in this context are needed by Ada but I haven't yet implemented that in gnat-llvm.

@tromey
Copy link
Contributor

tromey commented May 7, 2025

cc @tromey who looks to have added DISubrangeType initially in #126772 - would you be able to give this patch a quick review if you are able?

Other than the unwrap thing (which I think applies to multiple arguments here), it seems fine to me. Thanks.

@demoitem
Copy link
Author

I think it's ready now. I fixed those wrapper problems and the builds completed ok. Just need someone
with permission to do the merge. Thanks to all for the help. Takes me ages to build the system with my old laptop,
literally days. I think the docs should emphasize that you need to have asserts enabled to do the tests.

@demoitem
Copy link
Author

Ping

@OCHyams
Copy link
Contributor

OCHyams commented May 21, 2025

Just in case the comment gets lost (github has a habit of swallowing inline comments...) - there's an outstanding nit for the function comments (specifically for the argument descriptions):

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.

@demoitem
Copy link
Author

Right. Will fix those. Just waiting for the build to finish. Takes a while, several days in fact on this laptop, Tends to overheat.

@demoitem
Copy link
Author

I have modified the header to mention which parameters are not null and tested the rest which
can safely be null.

@tromey
Copy link
Contributor

tromey commented May 27, 2025

I have modified the header to mention which parameters are not null and tested the rest which can safely be null.

It seems a bit strange to require those in particular to be non-null, because they're really only useful for Fortran.

@demoitem
Copy link
Author

demoitem commented Jun 2, 2025

Wondering if this is ok to merge now.

@demoitem
Copy link
Author

demoitem commented Jun 3, 2025

I had another look after that query about Fortran and changed the unwrap to unwrapDI for those parameters
and now they support null values. I'm not an expert on unwrapping and am mostly confused about the whole
business. I just want C access to these functions. Anyway it seems to be working properly and the test show
that. If there are no other issues can someone merge this? Thanks.

@tromey
Copy link
Contributor

tromey commented Jun 4, 2025

I had another look after that query about Fortran and changed the unwrap to unwrapDI for those parameters and now they support null values. I'm not an expert on unwrapping and am mostly confused about the whole business.

I think the rule is that unwrap requires a non-NULL value and unwrapDI allows one. unwrap ultimately uses cast<>, which disallows NULL, while unwrapDI explicitly checks for an allows it. So then for each parameter one must consider whether NULL makes sense.

Like in the patch, the set type constructor uses unwrapDI<DIType>(BaseTy). But this means you could construct a set type without an underlying element type -- which probably is fine in the sense that it won't cause a crash or whatever, but at the same time is sort of useless.

On the other hand, the patch requires a non-NULL bias: unwrap(Bias). But this field is pretty specific to Ada, as in, I've never heard of another language that has this idea (a value can be stored with a bias so it will fit in fewer bits). So the patch as-is forces most callers to pass a wrapped 0 here.

Anyway, that's my understanding. Hopefully it's accurate, if not I'd appreciate a correction.

@OCHyams
Copy link
Contributor

OCHyams commented Jun 4, 2025

I was just having a look and there's a third option (first on this list) -

  • unwrap() can take null, because it reinterpret_casts
  • unwrap<T>() casts using cast, so can't take nulls
  • unwrapDI<T>() checks for null explicitly

It's not particularly clear at all, I was also a bit confused there trying to understand why unwrap(BitStride) was ok with null.

Takes me ages to build the system with my old laptop, literally days.

I've left a comment in line. I appreciate this has been a bit of a protracted review, sorry about that. If making those final changes is going to be difficult with your hardware I might be able to take it over, if that's helpful (can't promise when though).

Copy link
Contributor

@OCHyams OCHyams left a comment

Choose a reason for hiding this comment

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

(... actually post the inline comment...)

Comment on lines +1346 to +1361
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)));
}
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)));
}

@tromey
Copy link
Contributor

tromey commented Jun 4, 2025

I was just having a look and there's a third option (first on this list) -

Thanks for the comment, I was unaware of the unwrap/unwrap<T> distinction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants