[llvm] [DWARF] Generalize DWARFTypePrinter to a template class (PR #109459)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 20 12:05:37 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-debuginfo

Author: Zequan Wu (ZequanWu)

<details>
<summary>Changes</summary>

This generalizes DWARFTypePrinter class to a template class so that it can be reused for lldb's DWARFDIE type. 

This is a split of https://github.com/llvm/llvm-project/pull/90008. The difference is that this doesn't have `Visitor` template parameter which can be added later if necessary.

---

Patch is 49.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/109459.diff


4 Files Affected:

- (modified) llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h (+767-19) 
- (modified) llvm/lib/DebugInfo/DWARF/CMakeLists.txt (-1) 
- (modified) llvm/lib/DebugInfo/DWARF/DWARFDie.cpp (+2-2) 
- (removed) llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp (-675) 


``````````diff
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
index e05271740e6157..5315ac194f8ede 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
@@ -9,9 +9,10 @@
 #ifndef LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H
 #define LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H
 
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/BinaryFormat/Dwarf.h"
-#include "llvm/DebugInfo/DWARF/DWARFDie.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
 
 #include <string>
 
@@ -19,9 +20,55 @@ namespace llvm {
 
 class raw_ostream;
 
+inline std::optional<uint64_t> getLanguage(DWARFDie D) {
+  if (std::optional<DWARFFormValue> LV =
+          D.getDwarfUnit()->getUnitDIE().find(dwarf::DW_AT_language))
+    return LV->getAsUnsignedConstant();
+  return std::nullopt;
+}
+
+namespace detail {
+inline llvm::SmallString<128> toString(const llvm::format_object_base &Fmt) {
+  size_t NextBufferSize = 127;
+  llvm::SmallString<128> V;
+
+  while (true) {
+    V.resize_for_overwrite(NextBufferSize);
+
+    // Try formatting into the SmallVector.
+    size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
+
+    // If BytesUsed fit into the vector, we win.
+    if (BytesUsed <= NextBufferSize) {
+      V.resize(BytesUsed);
+      return V;
+    }
+
+    // Otherwise, try again with a new size.
+    assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?");
+    NextBufferSize = BytesUsed;
+  }
+}
+
+/// Returns True if the DIE TAG is one of the ones that is scopped.
+inline bool scopedTAGs(dwarf::Tag Tag) {
+  switch (Tag) {
+  case dwarf::DW_TAG_structure_type:
+  case dwarf::DW_TAG_class_type:
+  case dwarf::DW_TAG_union_type:
+  case dwarf::DW_TAG_namespace:
+  case dwarf::DW_TAG_enumeration_type:
+    return true;
+  default:
+    break;
+  }
+  return false;
+}
+} // namespace detail
+
 // FIXME: We should have pretty printers per language. Currently we print
 // everything as if it was C++ and fall back to the TAG type name.
-struct DWARFTypePrinter {
+template <typename DieType> struct DWARFTypePrinter {
   raw_ostream &OS;
   bool Word = true;
   bool EndedWithTemplate = false;
@@ -31,37 +78,738 @@ struct DWARFTypePrinter {
   /// Dump the name encoded in the type tag.
   void appendTypeTagName(dwarf::Tag T);
 
-  void appendArrayType(const DWARFDie &D);
+  void appendArrayType(const DieType &D);
 
-  DWARFDie skipQualifiers(DWARFDie D);
+  DieType skipQualifiers(DieType D);
 
-  bool needsParens(DWARFDie D);
+  bool needsParens(DieType D);
 
-  void appendPointerLikeTypeBefore(DWARFDie D, DWARFDie Inner, StringRef Ptr);
+  void appendPointerLikeTypeBefore(DieType D, DieType Inner, StringRef Ptr);
 
-  DWARFDie appendUnqualifiedNameBefore(DWARFDie D,
-                                       std::string *OriginalFullName = nullptr);
+  DieType appendUnqualifiedNameBefore(DieType D,
+                                      std::string *OriginalFullName = nullptr);
 
-  void appendUnqualifiedNameAfter(DWARFDie D, DWARFDie Inner,
+  void appendUnqualifiedNameAfter(DieType D, DieType Inner,
                                   bool SkipFirstParamIfArtificial = false);
-  void appendQualifiedName(DWARFDie D);
-  DWARFDie appendQualifiedNameBefore(DWARFDie D);
-  bool appendTemplateParameters(DWARFDie D, bool *FirstParameter = nullptr);
-  void decomposeConstVolatile(DWARFDie &N, DWARFDie &T, DWARFDie &C,
-                              DWARFDie &V);
-  void appendConstVolatileQualifierAfter(DWARFDie N);
-  void appendConstVolatileQualifierBefore(DWARFDie N);
+  void appendQualifiedName(DieType D);
+  DieType appendQualifiedNameBefore(DieType D);
+  bool appendTemplateParameters(DieType D, bool *FirstParameter = nullptr);
+  void appendAndTerminateTemplateParameters(DieType D);
+  void decomposeConstVolatile(DieType &N, DieType &T, DieType &C, DieType &V);
+  void appendConstVolatileQualifierAfter(DieType N);
+  void appendConstVolatileQualifierBefore(DieType N);
 
   /// Recursively append the DIE type name when applicable.
-  void appendUnqualifiedName(DWARFDie D,
+  void appendUnqualifiedName(DieType D,
                              std::string *OriginalFullName = nullptr);
 
-  void appendSubroutineNameAfter(DWARFDie D, DWARFDie Inner,
+  void appendSubroutineNameAfter(DieType D, DieType Inner,
                                  bool SkipFirstParamIfArtificial, bool Const,
                                  bool Volatile);
-  void appendScopes(DWARFDie D);
+  void appendScopes(DieType D);
 };
 
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendTypeTagName(dwarf::Tag T) {
+  StringRef TagStr = TagString(T);
+  static constexpr StringRef Prefix = "DW_TAG_";
+  static constexpr StringRef Suffix = "_type";
+  if (!TagStr.starts_with(Prefix) || !TagStr.ends_with(Suffix))
+    return;
+  OS << TagStr.substr(Prefix.size(),
+                      TagStr.size() - (Prefix.size() + Suffix.size()))
+     << " ";
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendArrayType(const DieType &D) {
+  for (const DieType &C : D.children()) {
+    if (C.getTag() != dwarf::DW_TAG_subrange_type)
+      continue;
+    std::optional<uint64_t> LB;
+    std::optional<uint64_t> Count;
+    std::optional<uint64_t> UB;
+    std::optional<unsigned> DefaultLB;
+    if (std::optional<DWARFFormValue> L = C.find(dwarf::DW_AT_lower_bound))
+      LB = L->getAsUnsignedConstant();
+    if (std::optional<DWARFFormValue> CountV = C.find(dwarf::DW_AT_count))
+      Count = CountV->getAsUnsignedConstant();
+    if (std::optional<DWARFFormValue> UpperV = C.find(dwarf::DW_AT_upper_bound))
+      UB = UpperV->getAsUnsignedConstant();
+    if (std::optional<DWARFFormValue> LV =
+            D.getDwarfUnit()->getUnitDIE().find(dwarf::DW_AT_language))
+      if (std::optional<uint64_t> LC = LV->getAsUnsignedConstant())
+        if ((DefaultLB =
+                 LanguageLowerBound(static_cast<dwarf::SourceLanguage>(*LC))))
+          if (LB && *LB == *DefaultLB)
+            LB = std::nullopt;
+    if (!LB && !Count && !UB)
+      OS << "[]";
+    else if (!LB && (Count || UB) && DefaultLB)
+      OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']';
+    else {
+      OS << "[[";
+      if (LB)
+        OS << *LB;
+      else
+        OS << '?';
+      OS << ", ";
+      if (Count)
+        if (LB)
+          OS << *LB + *Count;
+        else
+          OS << "? + " << *Count;
+      else if (UB)
+        OS << *UB + 1;
+      else
+        OS << '?';
+      OS << ")]";
+    }
+  }
+  EndedWithTemplate = false;
+}
+
+namespace detail {
+template <typename DieType>
+DieType resolveReferencedType(DieType D,
+                              dwarf::Attribute Attr = dwarf::DW_AT_type) {
+  return D.getAttributeValueAsReferencedDie(Attr).resolveTypeUnitReference();
+}
+template <typename DieType>
+DieType resolveReferencedType(DieType D, DWARFFormValue F) {
+  return D.getAttributeValueAsReferencedDie(F).resolveTypeUnitReference();
+}
+} // namespace detail
+
+template <typename DieType>
+DieType DWARFTypePrinter<DieType>::skipQualifiers(DieType D) {
+  while (D && (D.getTag() == dwarf::DW_TAG_const_type ||
+               D.getTag() == dwarf::DW_TAG_volatile_type))
+    D = detail::resolveReferencedType(D);
+  return D;
+}
+
+template <typename DieType>
+bool DWARFTypePrinter<DieType>::needsParens(DieType D) {
+  D = skipQualifiers(D);
+  return D && (D.getTag() == dwarf::DW_TAG_subroutine_type ||
+               D.getTag() == dwarf::DW_TAG_array_type);
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendPointerLikeTypeBefore(DieType D,
+                                                            DieType Inner,
+                                                            StringRef Ptr) {
+  appendQualifiedNameBefore(Inner);
+  if (Word)
+    OS << ' ';
+  if (needsParens(Inner))
+    OS << '(';
+  OS << Ptr;
+  Word = false;
+  EndedWithTemplate = false;
+}
+
+template <typename DieType>
+DieType DWARFTypePrinter<DieType>::appendUnqualifiedNameBefore(
+    DieType D, std::string *OriginalFullName) {
+  Word = true;
+  if (!D) {
+    OS << "void";
+    return DieType();
+  }
+  DieType InnerDIE;
+  auto Inner = [&] { return InnerDIE = detail::resolveReferencedType(D); };
+  const dwarf::Tag T = D.getTag();
+  switch (T) {
+  case dwarf::DW_TAG_pointer_type: {
+    appendPointerLikeTypeBefore(D, Inner(), "*");
+    break;
+  }
+  case dwarf::DW_TAG_subroutine_type: {
+    appendQualifiedNameBefore(Inner());
+    if (Word) {
+      OS << ' ';
+    }
+    Word = false;
+    break;
+  }
+  case dwarf::DW_TAG_array_type: {
+    appendQualifiedNameBefore(Inner());
+    break;
+  }
+  case dwarf::DW_TAG_reference_type:
+    appendPointerLikeTypeBefore(D, Inner(), "&");
+    break;
+  case dwarf::DW_TAG_rvalue_reference_type:
+    appendPointerLikeTypeBefore(D, Inner(), "&&");
+    break;
+  case dwarf::DW_TAG_ptr_to_member_type: {
+    appendQualifiedNameBefore(Inner());
+    if (needsParens(InnerDIE))
+      OS << '(';
+    else if (Word)
+      OS << ' ';
+    if (DieType Cont =
+            detail::resolveReferencedType(D, dwarf::DW_AT_containing_type)) {
+      appendQualifiedName(Cont);
+      EndedWithTemplate = false;
+      OS << "::";
+    }
+    OS << "*";
+    Word = false;
+    break;
+  }
+  case dwarf::DW_TAG_LLVM_ptrauth_type:
+    appendQualifiedNameBefore(Inner());
+    break;
+  case dwarf::DW_TAG_const_type:
+  case dwarf::DW_TAG_volatile_type:
+    appendConstVolatileQualifierBefore(D);
+    break;
+  case dwarf::DW_TAG_namespace: {
+    if (const char *Name = dwarf::toString(D.find(dwarf::DW_AT_name), nullptr))
+      OS << Name;
+    else
+      OS << "(anonymous namespace)";
+    break;
+  }
+  case dwarf::DW_TAG_unspecified_type: {
+    StringRef TypeName = D.getShortName();
+    if (TypeName == "decltype(nullptr)")
+      TypeName = "std::nullptr_t";
+    Word = true;
+    OS << TypeName;
+    EndedWithTemplate = false;
+    break;
+  }
+    /*
+  case DW_TAG_structure_type:
+  case DW_TAG_class_type:
+  case DW_TAG_enumeration_type:
+  case DW_TAG_base_type:
+  */
+  default: {
+    const char *NamePtr = dwarf::toString(D.find(dwarf::DW_AT_name), nullptr);
+    if (!NamePtr) {
+      appendTypeTagName(D.getTag());
+      return DieType();
+    }
+    Word = true;
+    StringRef Name = NamePtr;
+    static constexpr StringRef MangledPrefix = "_STN|";
+    if (Name.consume_front(MangledPrefix)) {
+      auto Separator = Name.find('|');
+      assert(Separator != StringRef::npos);
+      StringRef BaseName = Name.substr(0, Separator);
+      StringRef TemplateArgs = Name.substr(Separator + 1);
+      if (OriginalFullName)
+        *OriginalFullName = (BaseName + TemplateArgs).str();
+      Name = BaseName;
+    } else
+      EndedWithTemplate = Name.ends_with(">");
+    OS << Name;
+    // This check would be insufficient for operator overloads like
+    // "operator>>" - but for now Clang doesn't try to simplify them, so this
+    // is OK. Add more nuanced operator overload handling here if/when needed.
+    if (Name.ends_with(">"))
+      break;
+    if (!appendTemplateParameters(D))
+      break;
+
+    if (EndedWithTemplate)
+      OS << ' ';
+    OS << '>';
+    EndedWithTemplate = true;
+    Word = true;
+    break;
+  }
+  }
+  return InnerDIE;
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendUnqualifiedNameAfter(
+    DieType D, DieType Inner, bool SkipFirstParamIfArtificial) {
+  if (!D)
+    return;
+  switch (D.getTag()) {
+  case dwarf::DW_TAG_subroutine_type: {
+    appendSubroutineNameAfter(D, Inner, SkipFirstParamIfArtificial, false,
+                              false);
+    break;
+  }
+  case dwarf::DW_TAG_array_type: {
+    appendArrayType(D);
+    break;
+  }
+  case dwarf::DW_TAG_const_type:
+  case dwarf::DW_TAG_volatile_type:
+    appendConstVolatileQualifierAfter(D);
+    break;
+  case dwarf::DW_TAG_ptr_to_member_type:
+  case dwarf::DW_TAG_reference_type:
+  case dwarf::DW_TAG_rvalue_reference_type:
+  case dwarf::DW_TAG_pointer_type: {
+    if (needsParens(Inner))
+      OS << ')';
+    appendUnqualifiedNameAfter(Inner, detail::resolveReferencedType(Inner),
+                               /*SkipFirstParamIfArtificial=*/D.getTag() ==
+                                   dwarf::DW_TAG_ptr_to_member_type);
+    break;
+  }
+  case dwarf::DW_TAG_LLVM_ptrauth_type: {
+    auto getValOrNull = [&](dwarf::Attribute Attr) -> uint64_t {
+      if (auto Form = D.find(Attr))
+        return *Form->getAsUnsignedConstant();
+      return 0;
+    };
+    SmallVector<const char *, 2> optionsVec;
+    if (getValOrNull(dwarf::DW_AT_LLVM_ptrauth_isa_pointer))
+      optionsVec.push_back("isa-pointer");
+    if (getValOrNull(dwarf::DW_AT_LLVM_ptrauth_authenticates_null_values))
+      optionsVec.push_back("authenticates-null-values");
+    if (auto AuthenticationMode =
+            D.find(dwarf::DW_AT_LLVM_ptrauth_authentication_mode)) {
+      switch (*AuthenticationMode->getAsUnsignedConstant()) {
+      case 0:
+      case 1:
+        optionsVec.push_back("strip");
+        break;
+      case 2:
+        optionsVec.push_back("sign-and-strip");
+        break;
+      default:
+        // Default authentication policy
+        break;
+      }
+    }
+    std::string options;
+    for (const auto *option : optionsVec) {
+      if (options.size())
+        options += ",";
+      options += option;
+    }
+    if (options.size())
+      options = ", \"" + options + "\"";
+    std::string PtrauthString;
+    llvm::raw_string_ostream PtrauthStream(PtrauthString);
+    PtrauthStream
+        << "__ptrauth(" << getValOrNull(dwarf::DW_AT_LLVM_ptrauth_key) << ", "
+        << getValOrNull(dwarf::DW_AT_LLVM_ptrauth_address_discriminated)
+        << ", 0x0"
+        << utohexstr(
+               getValOrNull(dwarf::DW_AT_LLVM_ptrauth_extra_discriminator),
+               true)
+        << options << ")";
+    OS << PtrauthStream.str();
+    break;
+  }
+    /*
+  case DW_TAG_structure_type:
+  case DW_TAG_class_type:
+  case DW_TAG_enumeration_type:
+  case DW_TAG_base_type:
+  case DW_TAG_namespace:
+  */
+  default:
+    break;
+  }
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendQualifiedName(DieType D) {
+  if (D && detail::scopedTAGs(D.getTag()))
+    appendScopes(D.getParent());
+  appendUnqualifiedName(D);
+}
+
+template <typename DieType>
+DieType DWARFTypePrinter<DieType>::appendQualifiedNameBefore(DieType D) {
+  if (D && detail::scopedTAGs(D.getTag()))
+    appendScopes(D.getParent());
+  return appendUnqualifiedNameBefore(D);
+}
+
+template <typename DieType>
+bool DWARFTypePrinter<DieType>::appendTemplateParameters(DieType D,
+                                                         bool *FirstParameter) {
+  bool FirstParameterValue = true;
+  bool IsTemplate = false;
+  if (!FirstParameter)
+    FirstParameter = &FirstParameterValue;
+  for (const DieType &C : D) {
+    auto Sep = [&] {
+      if (*FirstParameter)
+        OS << '<';
+      else
+        OS << ", ";
+      IsTemplate = true;
+      EndedWithTemplate = false;
+      *FirstParameter = false;
+    };
+    if (C.getTag() == dwarf::DW_TAG_GNU_template_parameter_pack) {
+      IsTemplate = true;
+      appendTemplateParameters(C, FirstParameter);
+    }
+    if (C.getTag() == dwarf::DW_TAG_template_value_parameter) {
+      DieType T = detail::resolveReferencedType(C);
+      Sep();
+      if (T.getTag() == dwarf::DW_TAG_enumeration_type) {
+        OS << '(';
+        appendQualifiedName(T);
+        OS << ')';
+        auto V = C.find(dwarf::DW_AT_const_value);
+        OS << std::to_string(*V->getAsSignedConstant());
+        continue;
+      }
+      // /Maybe/ we could do pointer/reference type parameters, looking for the
+      // symbol in the ELF symbol table to get back to the variable...
+      // but probably not worth it.
+      if (T.getTag() == dwarf::DW_TAG_pointer_type ||
+          T.getTag() == dwarf::DW_TAG_reference_type)
+        continue;
+      const char *RawName = dwarf::toString(T.find(dwarf::DW_AT_name), nullptr);
+      assert(RawName);
+      StringRef Name = RawName;
+      auto V = C.find(dwarf::DW_AT_const_value);
+      bool IsQualifiedChar = false;
+      if (Name == "bool") {
+        OS << (*V->getAsUnsignedConstant() ? "true" : "false");
+      } else if (Name == "short") {
+        OS << "(short)";
+        OS << std::to_string(*V->getAsSignedConstant());
+      } else if (Name == "unsigned short") {
+        OS << "(unsigned short)";
+        OS << std::to_string(*V->getAsSignedConstant());
+      } else if (Name == "int")
+        OS << std::to_string(*V->getAsSignedConstant());
+      else if (Name == "long") {
+        OS << std::to_string(*V->getAsSignedConstant());
+        OS << "L";
+      } else if (Name == "long long") {
+        OS << std::to_string(*V->getAsSignedConstant());
+        OS << "LL";
+      } else if (Name == "unsigned int") {
+        OS << std::to_string(*V->getAsUnsignedConstant());
+        OS << "U";
+      } else if (Name == "unsigned long") {
+        OS << std::to_string(*V->getAsUnsignedConstant());
+        OS << "UL";
+      } else if (Name == "unsigned long long") {
+        OS << std::to_string(*V->getAsUnsignedConstant());
+        OS << "ULL";
+      } else if (Name == "char" ||
+                 (IsQualifiedChar =
+                      (Name == "unsigned char" || Name == "signed char"))) {
+        // FIXME: check T's DW_AT_type to see if it's signed or not (since
+        // char signedness is implementation defined).
+        auto Val = *V->getAsSignedConstant();
+        // Copied/hacked up from Clang's CharacterLiteral::print - incomplete
+        // (doesn't actually support different character types/widths, sign
+        // handling's not done, and doesn't correctly test if a character is
+        // printable or needs to use a numeric escape sequence instead)
+        if (IsQualifiedChar) {
+          OS << '(';
+          OS << Name;
+          OS << ')';
+        }
+        switch (Val) {
+        case '\\':
+          OS << "'\\\\'";
+          break;
+        case '\'':
+          OS << "'\\''";
+          break;
+        case '\a':
+          // TODO: K&R: the meaning of '\\a' is different in traditional C
+          OS << "'\\a'";
+          break;
+        case '\b':
+          OS << "'\\b'";
+          break;
+        case '\f':
+          OS << "'\\f'";
+          break;
+        case '\n':
+          OS << "'\\n'";
+          break;
+        case '\r':
+          OS << "'\\r'";
+          break;
+        case '\t':
+          OS << "'\\t'";
+          break;
+        case '\v':
+          OS << "'\\v'";
+          break;
+        default:
+          if ((Val & ~0xFFu) == ~0xFFu)
+            Val &= 0xFFu;
+          if (Val < 127 && Val >= 32) {
+            OS << "'";
+            OS << (char)Val;
+            OS << "'";
+          } else if (Val < 256)
+            OS << llvm::format("'\\x%02" PRIx64 "'", Val);
+          else if (Val <= 0xFFFF)
+            OS << llvm::format("'\\u%04" PRIx64 "'", Val);
+          else
+            OS << llvm::format("'\\U%08" PRIx64 "'", Val);
+        }
+      }
+      continue;
+    }
+    if (C.getTag() == dwarf::DW_TAG_GNU_template_template_param) {
+      const char *RawName =
+          dwarf::toString(C.find(dwarf::DW_AT_GNU_template_name), nullptr);
+      assert(RawName);
+      StringRef Name = RawName;
+      Sep();
+      OS << Name;
+      continue;
+    }
+    if (C.getTag() != dwarf::DW_TAG_template_type_parameter)
+      continue;
+    auto TypeAttr = C.find(dwarf::DW_AT_type);
+    Sep();
+    appendQualifiedName(TypeAttr ? detail::resolveReferencedType(C, *TypeAttr)
+                                 : DieType());
+  }
+  if (IsTemplate && *FirstParameter && FirstParameter == &FirstParameterValue) {
+    OS << '<';
+    EndedWithTemplate = false;
+  }
+  return IsTemplate;
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::appendAndTerminateTemplateParameters(DieType D) {
+  bool R = appendTemplateParameters(D);
+  if (!R)
+    return;
+
+  if (EndedWithTemplate)
+    OS << " ";
+  OS << ">";
+  EndedWithTemplate = true;
+  Word = true;
+}
+
+template <typename DieType>
+void DWARFTypePrinter<DieType>::decomposeConstVolatile(DieType &N, DieT...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list