[llvm] [SPIRV] Emit NonSemantic DebugValue (PR #224158)

Diego Novillo via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 07:00:00 PDT 2026


================
@@ -259,6 +260,100 @@ unsigned SPIRVNonSemanticDebugHandler::toNSDISrcLang(unsigned DwarfSrcLang) {
   }
 }
 
+// Whether \p BT is a SPIR-V OpTypeBool.
+static bool isBooleanType(const DIBasicType *BT) {
+  return BT->getEncoding() == dwarf::DW_ATE_boolean;
+}
+
+// \p Value truncated to the width of \p BT, or 0 or 1 for a boolean.
+static uint64_t constantBits(uint64_t Value, const DIBasicType *BT) {
+  // A boolean is one of two values and a DIBasicType may give it no size, so
+  // masking to its width would turn every true into a false.
+  if (isBooleanType(BT))
+    return Value != 0;
+  unsigned Width = BT->getSizeInBits();
+  return Width >= 64 ? Value : Value & ((uint64_t(1) << Width) - 1);
+}
+
+// \p Value truncated to the width of \p BT. APInt::getZExtValue() asserts
+// above 64 bits, so the truncation comes first.
+static uint64_t constantBits(const APInt &Value, const DIBasicType *BT) {
+  unsigned Width = isBooleanType(BT) ? 1 : BT->getSizeInBits();
+  return constantBits(Value.getActiveBits() > 64
+                          ? Value.trunc(std::min(Width, 64u)).getZExtValue()
+                          : Value.getZExtValue(),
+                      BT);
+}
+
+// \p CI as a 64-bit value, truncated to the width of \p BT. A one-bit constant
+// is zero extended, widths 2 through 64 are sign extended, and anything wider
+// is truncated.
+static uint64_t constantBits(const ConstantInt *CI, const DIBasicType *BT) {
+  const APInt &Value = CI->getValue();
+  if (Value.getBitWidth() > 64)
+    return constantBits(Value, BT);
+  if (Value.getBitWidth() == 1)
+    return constantBits(Value.getZExtValue(), BT);
+  return constantBits(static_cast<uint64_t>(Value.getSExtValue()), BT);
+}
+
+// The DIBasicType that \p Ty resolves to through any typedefs, or null.
+//
+// This accepts: a boolean of any size, and a 16, 32 or 64-bit integer or float.
+// Anything else gives null, including a type reached through a cv-qualifier.
+static const DIBasicType *stripToScalarType(const DIType *Ty) {
+  // Only a typedef is looked through. A cv-qualified type is not emitted as a
+  // DebugType, so emitDebugLocalVariable() cannot name it and the variable
+  // gets no DebugLocalVariable.
+  while (const auto *DT = dyn_cast_or_null<DIDerivedType>(Ty)) {
+    if (DT->getTag() != dwarf::DW_TAG_typedef)
+      return nullptr;
+    Ty = DT->getBaseType();
+  }
+
+  const auto *BT = dyn_cast_or_null<DIBasicType>(Ty);
+  if (!BT)
+    return nullptr;
+
+  // OpTypeBool carries no width, so a boolean of any size is accepted.
+  if (isBooleanType(BT))
+    return BT;
+
+  unsigned Size = BT->getSizeInBits();
+  switch (BT->getEncoding()) {
+  case dwarf::DW_ATE_signed:
+  case dwarf::DW_ATE_unsigned:
+  case dwarf::DW_ATE_signed_char:
+  case dwarf::DW_ATE_unsigned_char:
+  case dwarf::DW_ATE_float:
+    return Size == 16 || Size == 32 || Size == 64 ? BT : nullptr;
----------------
dnovillo wrote:

Because those are the widths I can name without changing what the module requires. 32 needs no capability, 16 and 64 are reused only when the module already declares the type, and `OpTypeBool` carries no width so a boolean of any size is fine.

You did find a problem with the logic, though: 8 is gated exactly like 16, and the backend already emits `OpTypeInt 8 0` whenever an `i8` appears. We were dropping `char` assignments in that case. Added, with a test.

https://github.com/llvm/llvm-project/pull/224158


More information about the llvm-commits mailing list