Skip to content

Patch series to reapply #118734 and substantially improve it #120534

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

Closed
wants to merge 6 commits into from

Conversation

chandlerc
Copy link
Member

@chandlerc chandlerc commented Dec 19, 2024

This PR is for a patch series of 6 patches to try and fully address the Clang builtin string tables. I'm posting it as a single PR to give context to the series and explain what the overarching goal is, but each of the 6 patches is designed to be reasonable to review on its own and land on its own. However, intermediate states may push MSVC's compile time and memory usage due to especially large string tables.

The overarching goal of this patch series is to re-land the port of Clang's builtin tables to use offsets into string tables rather than containing dynamically relocated pointers to individual strings. A naive port as in #118734 has some serious issues due to forming extraordinarily large string tables, and many of them. To address this, subsequent patches restructure the builtin tables to make them smaller through sharding, use of TableGen-ed string tables without duplicates, and extracting a common prefix that can be dynamically added. The first patch results in a string table over 500KiB, but at the end the largest is well below 200KiB.

Combined, this patch series reduces the readonly data used by Clang's builtin tables by roughly 50% in addition to removing the dynamic relocations needed. The net result is a 30% reduction in the binary's dynamic relocation metadata, and a 2% reduction in the total binary size.

    FILE SIZE        VM SIZE
 --------------  --------------
  +0.8%  +405Ki  +0.8%  +405Ki    .rodata
  +0.0% +6.61Ki  +0.0% +6.61Ki    .text
  +0.0%    +300  +0.0%    +300    .dynstr
  +0.0%    +252  +0.0%    +252    .eh_frame
  +0.0%    +168  +0.0%    +168    .dynsym
  +0.0%     +48  +0.0%     +48    .eh_frame_hdr
  +0.0%     +36  +0.0%     +36    .gnu.hash
  +2.3%     +18  +2.3%     +18    [LOAD #2 [R]]
  +0.0%     +14  +0.0%     +14    .gnu.version
 -33.3%     -12  [ = ]       0    [Unmapped]
  [ = ]       0 -38.5%    -320    .relro_padding
 -28.9% -1.36Mi -28.9% -1.36Mi    .data.rel.ro
 -29.6% -2.57Mi -29.6% -2.57Mi    .rela.dyn
  -2.2% -3.53Mi  -2.2% -3.53Mi    TOTAL

An overview of the various patches in the series:

  1. Reapplies Switch builtin strings to use string tables #118734 but using the new ADT for representing the string tables and offsets It also restructures the builtin tables to support sharding both the main and target builtins into multiple shards. This both allows each shard to be smaller, and also allows independent construction of each shard. And last but not least, it supports factoring a common prefix out of a shard.
  2. Leverages the new structure to switch RISCV (a target with larger tables) to directly generate some of its builtins data with TableGen rather than using X-macros. This lets TableGen generate the string table, eliminating redundant strings and reducing macro-based compile time memory overhead. It also factors out common prefixes.
  3. Repeats the restructuring of (2) for AArch64 and ARM, which includes the largest builtin table (SVE).
  4. Teach common builtin tablegen emission to generate string tables, updating all the targets that leverage this.
  5. Add prefix factoring to common builtin tablegen and use it in the x86 target.
  6. Use prefix factoring in the Hexagon builtins.

Each patch has a more detailed description in its commit message.

@llvmbot
Copy link
Member

llvmbot commented Dec 19, 2024

@llvm/pr-subscribers-backend-xtensa
@llvm/pr-subscribers-backend-x86
@llvm/pr-subscribers-backend-directx
@llvm/pr-subscribers-backend-amdgpu
@llvm/pr-subscribers-clang-static-analyzer-1
@llvm/pr-subscribers-backend-m68k
@llvm/pr-subscribers-backend-systemz
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-backend-arm

Author: Chandler Carruth (chandlerc)

Changes

This PR is for a patch series of 5 patches to try and fully address the Clang builtin string tables. I'm posting it as a single PR to give context to the series and explain what the overarching goal is, but each of the 5 patches is designed to be reasonable to review on its own and land on its own. The only issue I expect is that MSVC 16.9 on one build bot is likely to miscompile the intermediate steps, and MSVC will consume a large amount of memory during compilation until all the patches are in place.

That said, I'm happy to have each patch reviewed here, or I can post independent PRs for each patch, whichever would work best for reviewers.

The overarching goal of this patch series is to re-land the port of Clang's builtin tables to use offsets into string tables rather than containing dynamically relocated pointers to individual strings. A naive port as in #119638 has some serious issues due to forming extraordinarily large string tables, and many of them. To address this, subsequent patches restructure the builtin tables to make them smaller through sharding, use of TableGen-ed string tables without duplicates, and extracting a common prefix that can be dynamically added. The first patch results in a string table over 500KiB, but at the end the largest is well below 200KiB.

Combined, this patch series reduces the readonly data used by Clang's builtins by roughly 50% in addition to removing the dynamic relocations needed. The net result is a 30% reduction in the binary's dynamic relocation metadata, and a 2% reduction in the total binary size.

    FILE SIZE        VM SIZE
 --------------  --------------
  +1.6%  +763Ki  +1.6%  +763Ki    .rodata
  +0.0% +5.61Ki  +0.0% +5.61Ki    .text
  +0.0%    +344  +0.0%    +344    .dynstr
  +0.0%    +264  +0.0%    +264    .eh_frame
  +0.0%    +192  +0.0%    +192    .dynsym
  +0.0%     +56  +0.0%     +56    .eh_frame_hdr
  +0.0%     +40  +0.0%     +40    .gnu.hash
  +3.1%     +24  +3.1%     +24    [LOAD #<!-- -->2 [R]]
  +0.0%     +16  +0.0%     +16    .gnu.version
  +200%     +16  [ = ]       0    [Unmapped]
  [ = ]       0 -91.9% -1.78Ki    .relro_padding
 -29.4% -1.35Mi -29.4% -1.35Mi    .data.rel.ro
 -30.0% -2.53Mi -30.0% -2.53Mi    .rela.dyn
  -1.9% -3.13Mi  -1.9% -3.13Mi    TOTAL

An overview of the various patches in the series:

  1. Reapplies #119638 but using the new ADT for representing the string tables and offsets.
  2. Restructures builtin tables to support sharding both the main and target builtins into multiple shards. This both allows each shard to be smaller, and also allows independent construction of each shard.
  3. Leverages the sharded structure to switch RISCV (a target with larger tables) to directly generate some of its builtins data with TableGen rather than using X-macros. This lets TableGen generate the string table, eliminitaing redundant strings and reducing macro-based compile time memory overhead.
  4. Repeats the restructuring of (3) for AArch64 and ARM, which includes the largest builtin table (SVE).
  5. Switch Neon, SVE, SME, and RVV builtins to extract a prefix of their names from the strings in the tables, and add it back when needed dynamically.

Each patch has a somewhat more detailed description in its commit message.

The struture at the end opens up a lot of room to make more improvements, especially leveraging TableGen more heavily by having the ability to fall back to X-macro structures where necessary. I have a partial patch to move the main builtins to TableGen as well, but that will be more involved and unlikely to have as significant of impact on string literal size or binary size.


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

62 Files Affected:

  • (modified) clang/include/clang/Basic/Builtins.h (+187-35)
  • (removed) clang/include/clang/Basic/BuiltinsNEON.def (-22)
  • (modified) clang/include/clang/Basic/BuiltinsPPC.def (+1)
  • (removed) clang/include/clang/Basic/BuiltinsRISCVVector.def (-22)
  • (modified) clang/include/clang/Basic/TargetBuiltins.h (+24-10)
  • (modified) clang/include/clang/Basic/TargetInfo.h (+6-4)
  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+2-2)
  • (modified) clang/lib/AST/ExprConstant.cpp (+7-8)
  • (modified) clang/lib/Basic/Builtins.cpp (+131-49)
  • (modified) clang/lib/Basic/Targets/AArch64.cpp (+109-31)
  • (modified) clang/lib/Basic/Targets/AArch64.h (+1-1)
  • (modified) clang/lib/Basic/Targets/AMDGPU.cpp (+17-9)
  • (modified) clang/lib/Basic/Targets/AMDGPU.h (+1-1)
  • (modified) clang/lib/Basic/Targets/ARC.h (+3-1)
  • (modified) clang/lib/Basic/Targets/ARM.cpp (+58-23)
  • (modified) clang/lib/Basic/Targets/ARM.h (+1-1)
  • (modified) clang/lib/Basic/Targets/AVR.h (+3-1)
  • (modified) clang/lib/Basic/Targets/BPF.cpp (+15-7)
  • (modified) clang/lib/Basic/Targets/BPF.h (+1-1)
  • (modified) clang/lib/Basic/Targets/CSKY.cpp (-4)
  • (modified) clang/lib/Basic/Targets/CSKY.h (+3-1)
  • (modified) clang/lib/Basic/Targets/DirectX.h (+3-1)
  • (modified) clang/lib/Basic/Targets/Hexagon.cpp (+18-11)
  • (modified) clang/lib/Basic/Targets/Hexagon.h (+1-1)
  • (modified) clang/lib/Basic/Targets/Lanai.h (+3-1)
  • (modified) clang/lib/Basic/Targets/LoongArch.cpp (+17-9)
  • (modified) clang/lib/Basic/Targets/LoongArch.h (+1-1)
  • (modified) clang/lib/Basic/Targets/M68k.cpp (+2-1)
  • (modified) clang/lib/Basic/Targets/M68k.h (+1-1)
  • (modified) clang/lib/Basic/Targets/MSP430.h (+1-1)
  • (modified) clang/lib/Basic/Targets/Mips.cpp (+16-9)
  • (modified) clang/lib/Basic/Targets/Mips.h (+1-1)
  • (modified) clang/lib/Basic/Targets/NVPTX.cpp (+18-11)
  • (modified) clang/lib/Basic/Targets/NVPTX.h (+1-1)
  • (modified) clang/lib/Basic/Targets/PNaCl.h (+3-1)
  • (modified) clang/lib/Basic/Targets/PPC.cpp (+18-11)
  • (modified) clang/lib/Basic/Targets/PPC.h (+1-1)
  • (modified) clang/lib/Basic/Targets/RISCV.cpp (+54-14)
  • (modified) clang/lib/Basic/Targets/RISCV.h (+1-1)
  • (modified) clang/lib/Basic/Targets/SPIR.cpp (+2-1)
  • (modified) clang/lib/Basic/Targets/SPIR.h (+4-2)
  • (modified) clang/lib/Basic/Targets/Sparc.h (+1-1)
  • (modified) clang/lib/Basic/Targets/SystemZ.cpp (+17-9)
  • (modified) clang/lib/Basic/Targets/SystemZ.h (+1-1)
  • (modified) clang/lib/Basic/Targets/TCE.h (+3-1)
  • (modified) clang/lib/Basic/Targets/VE.cpp (+14-7)
  • (modified) clang/lib/Basic/Targets/VE.h (+1-1)
  • (modified) clang/lib/Basic/Targets/WebAssembly.cpp (+18-11)
  • (modified) clang/lib/Basic/Targets/WebAssembly.h (+1-1)
  • (modified) clang/lib/Basic/Targets/X86.cpp (+51-26)
  • (modified) clang/lib/Basic/Targets/X86.h (+2-2)
  • (modified) clang/lib/Basic/Targets/XCore.cpp (+16-9)
  • (modified) clang/lib/Basic/Targets/XCore.h (+1-1)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+6-4)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaARM.cpp (+6-10)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+9-7)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+1-1)
  • (modified) clang/lib/StaticAnalyzer/Core/CheckerContext.cpp (+1-1)
  • (modified) clang/utils/TableGen/NeonEmitter.cpp (+35-22)
  • (modified) clang/utils/TableGen/RISCVVEmitter.cpp (+60-20)
  • (modified) clang/utils/TableGen/SveEmitter.cpp (+95-23)
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e27d8ccce73664..28daa4e1aafe48 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -18,6 +18,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringTable.h"
 #include <cstring>
 
 // VC++ defines 'alloca' as an object-like macro, which interferes with our
@@ -55,6 +56,7 @@ struct HeaderDesc {
 #undef HEADER
   } ID;
 
+  constexpr HeaderDesc() : ID() {}
   constexpr HeaderDesc(HeaderID ID) : ID(ID) {}
 
   const char *getName() const;
@@ -68,14 +70,151 @@ enum ID {
   FirstTSBuiltin
 };
 
+struct InfosShard;
+
+/// The info used to represent each builtin.
 struct Info {
-  llvm::StringLiteral Name;
-  const char *Type, *Attributes;
-  const char *Features;
+  // Rather than store pointers to the string literals describing these four
+  // aspects of builtins, we store offsets into a common string table.
+  struct StrOffsets {
+    llvm::StringTable::Offset Name;
+    llvm::StringTable::Offset Type;
+    llvm::StringTable::Offset Attributes;
+    llvm::StringTable::Offset Features;
+  } Offsets;
+
   HeaderDesc Header;
   LanguageID Langs;
+
+  /// Get the name for the builtin represented by this `Info` object.
+  ///
+  /// Must be provided the `Shard` for this `Info` object.
+  std::string getName(const InfosShard &Shard) const;
 };
 
+/// A constexpr function to construct an infos array from X-macros.
+///
+/// The input array uses the same data structure, but the offsets are actually
+/// _lengths_ when input. This is all we can compute from the X-macro approach
+/// to builtins. This function will convert these lengths into actual offsets to
+/// a string table built up through sequentially appending strings with the
+/// given lengths.
+template <size_t N>
+static constexpr std::array<Info, N> MakeInfos(std::array<Info, N> Infos) {
+  // Translate lengths to offsets. We start past the initial empty string at
+  // offset zero.
+  unsigned Offset = 1;
+  for (auto &I : Infos) {
+    Info::StrOffsets NewOffsets = {};
+    NewOffsets.Name = Offset;
+    Offset += I.Offsets.Name.value();
+    NewOffsets.Type = Offset;
+    Offset += I.Offsets.Type.value();
+    NewOffsets.Attributes = Offset;
+    Offset += I.Offsets.Attributes.value();
+    NewOffsets.Features = Offset;
+    Offset += I.Offsets.Features.value();
+    I.Offsets = NewOffsets;
+  }
+  return Infos;
+}
+
+/// A shard of a target's builtins string table and info.
+///
+/// Target builtins are sharded across multiple tables due to different
+/// structures, origins, and also to improve the overall scaling by avoiding a
+/// single table across all builtins.
+struct InfosShard {
+  const llvm::StringTable *Strings;
+  llvm::ArrayRef<Info> Infos;
+
+  llvm::StringLiteral NamePrefix = "";
+};
+
+// A detail macro used below to emit a string literal that, after string literal
+// concatenation, ends up triggering the `-Woverlength-strings` warning. While
+// the warning is useful in general to catch accidentally excessive strings,
+// here we are creating them intentionally.
+//
+// This relies on a subtle aspect of `_Pragma`: that the *diagnostic* ones don't
+// turn into actual tokens that would disrupt string literal concatenation.
+#ifdef __clang__
+#define CLANG_BUILTIN_DETAIL_STR_TABLE(S)                                      \
+  _Pragma("clang diagnostic push")                                             \
+      _Pragma("clang diagnostic ignored \"-Woverlength-strings\"")             \
+          S _Pragma("clang diagnostic pop")
+#else
+#define CLANG_BUILTIN_DETAIL_STR_TABLE(S) S
+#endif
+
+// We require string tables to start with an empty string so that a `0` offset
+// can always be used to refer to an empty string. To satisfy that when building
+// string tables with X-macros, we use this start macro prior to expanding the
+// X-macros.
+#define CLANG_BUILTIN_STR_TABLE_START CLANG_BUILTIN_DETAIL_STR_TABLE("\0")
+
+// A macro that can be used with `Builtins.def` and similar files as an X-macro
+// to add the string arguments to a builtin string table. This is typically the
+// target for the `BUILTIN`, `LANGBUILTIN`, or `LIBBUILTIN` macros in those
+// files.
+#define CLANG_BUILTIN_STR_TABLE(ID, TYPE, ATTRS)                               \
+  CLANG_BUILTIN_DETAIL_STR_TABLE(#ID "\0" TYPE "\0" ATTRS "\0" /*FEATURE*/ "\0")
+
+// A macro that can be used with target builtin `.def` and `.inc` files as an
+// X-macro to add the string arguments to a builtin string table. this is
+// typically the target for the `TARGET_BUILTIN` macro.
+#define CLANG_TARGET_BUILTIN_STR_TABLE(ID, TYPE, ATTRS, FEATURE)               \
+  CLANG_BUILTIN_DETAIL_STR_TABLE(#ID "\0" TYPE "\0" ATTRS "\0" FEATURE "\0")
+
+// A macro that can be used with target builtin `.def` and `.inc` files as an
+// X-macro to add the string arguments to a builtin string table. this is
+// typically the target for the `TARGET_HEADER_BUILTIN` macro. We can't delegate
+// to `TARGET_BUILTIN` because the `FEATURE` string changes position.
+#define CLANG_TARGET_HEADER_BUILTIN_STR_TABLE(ID, TYPE, ATTRS, HEADER, LANGS,  \
+                                              FEATURE)                         \
+  CLANG_BUILTIN_DETAIL_STR_TABLE(#ID "\0" TYPE "\0" ATTRS "\0" FEATURE "\0")
+
+// A detail macro used internally to compute the desired string table
+// `StrOffsets` struct for arguments to `MakeInfos`.
+#define CLANG_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS)                      \
+  Builtin::Info::StrOffsets {                                                  \
+    sizeof(#ID), sizeof(TYPE), sizeof(ATTRS), sizeof("")                       \
+  }
+
+// A detail macro used internally to compute the desired string table
+// `StrOffsets` struct for arguments to `Storage::Make`.
+#define CLANG_TARGET_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS, FEATURE)      \
+  Builtin::Info::StrOffsets {                                                  \
+    sizeof(#ID), sizeof(TYPE), sizeof(ATTRS), sizeof(FEATURE)                  \
+  }
+
+// A set of macros that can be used with builtin `.def' files as an X-macro to
+// create an `Info` struct for a particular builtin. It both computes the
+// `StrOffsets` value for the string table (the lengths here, translated to
+// offsets by the `MakeInfos` function), and the other metadata for each
+// builtin.
+//
+// There is a corresponding macro for each of `BUILTIN`, `LANGBUILTIN`,
+// `LIBBUILTIN`, `TARGET_BUILTIN`, and `TARGET_HEADER_BUILTIN`.
+#define CLANG_BUILTIN_ENTRY(ID, TYPE, ATTRS)                                   \
+  Builtin::Info{CLANG_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS),             \
+                HeaderDesc::NO_HEADER, ALL_LANGUAGES},
+#define CLANG_LANGBUILTIN_ENTRY(ID, TYPE, ATTRS, LANG)                         \
+  Builtin::Info{CLANG_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS),             \
+                HeaderDesc::NO_HEADER, LANG},
+#define CLANG_LIBBUILTIN_ENTRY(ID, TYPE, ATTRS, HEADER, LANG)                  \
+  Builtin::Info{CLANG_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS),             \
+                HeaderDesc::HEADER, LANG},
+#define CLANG_TARGET_BUILTIN_ENTRY(ID, TYPE, ATTRS, FEATURE)                   \
+  Builtin::Info{                                                               \
+      CLANG_TARGET_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS, FEATURE),       \
+      HeaderDesc::NO_HEADER, ALL_LANGUAGES},
+#define CLANG_TARGET_HEADER_BUILTIN_ENTRY(ID, TYPE, ATTRS, HEADER, LANG,       \
+                                          FEATURE)                             \
+  Builtin::Info{                                                               \
+      CLANG_TARGET_BUILTIN_DETAIL_STR_OFFSETS(ID, TYPE, ATTRS, FEATURE),       \
+      HeaderDesc::HEADER, LANG},
+
 /// Holds information about both target-independent and
 /// target-specific builtins, allowing easy queries by clients.
 ///
@@ -83,11 +222,16 @@ struct Info {
 /// AuxTSRecords. Their IDs are shifted up by TSRecords.size() and need to
 /// be translated back with getAuxBuiltinID() before use.
 class Context {
-  llvm::ArrayRef<Info> TSRecords;
-  llvm::ArrayRef<Info> AuxTSRecords;
+  llvm::SmallVector<InfosShard> BuiltinShards;
+
+  llvm::SmallVector<InfosShard> TargetShards;
+  llvm::SmallVector<InfosShard> AuxTargetShards;
+
+  unsigned NumTargetBuiltins = 0;
+  unsigned NumAuxTargetBuiltins = 0;
 
 public:
-  Context() = default;
+  Context();
 
   /// Perform target-specific initialization
   /// \param AuxTarget Target info to incorporate builtins from. May be nullptr.
@@ -100,10 +244,17 @@ class Context {
 
   /// Return the identifier name for the specified builtin,
   /// e.g. "__builtin_abs".
-  llvm::StringRef getName(unsigned ID) const { return getRecord(ID).Name; }
+  std::string getName(unsigned ID) const;
+
+  /// Return the identifier name for the specified builtin inside single quotes
+  /// for a diagnostic, e.g. "'__builtin_abs'".
+  std::string getQuotedName(unsigned ID) const;
 
   /// Get the type descriptor string for the specified builtin.
-  const char *getTypeString(unsigned ID) const { return getRecord(ID).Type; }
+  const char *getTypeString(unsigned ID) const;
+
+  /// Get the attributes descriptor string for the specified builtin.
+  const char *getAttributesString(unsigned ID) const;
 
   /// Return true if this function is a target-specific builtin.
   bool isTSBuiltin(unsigned ID) const {
@@ -112,40 +263,40 @@ class Context {
 
   /// Return true if this function has no side effects.
   bool isPure(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'U') != nullptr;
+    return strchr(getAttributesString(ID), 'U') != nullptr;
   }
 
   /// Return true if this function has no side effects and doesn't
   /// read memory.
   bool isConst(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'c') != nullptr;
+    return strchr(getAttributesString(ID), 'c') != nullptr;
   }
 
   /// Return true if we know this builtin never throws an exception.
   bool isNoThrow(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'n') != nullptr;
+    return strchr(getAttributesString(ID), 'n') != nullptr;
   }
 
   /// Return true if we know this builtin never returns.
   bool isNoReturn(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'r') != nullptr;
+    return strchr(getAttributesString(ID), 'r') != nullptr;
   }
 
   /// Return true if we know this builtin can return twice.
   bool isReturnsTwice(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'j') != nullptr;
+    return strchr(getAttributesString(ID), 'j') != nullptr;
   }
 
   /// Returns true if this builtin does not perform the side-effects
   /// of its arguments.
   bool isUnevaluated(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'u') != nullptr;
+    return strchr(getAttributesString(ID), 'u') != nullptr;
   }
 
   /// Return true if this is a builtin for a libc/libm function,
   /// with a "__builtin_" prefix (e.g. __builtin_abs).
   bool isLibFunction(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'F') != nullptr;
+    return strchr(getAttributesString(ID), 'F') != nullptr;
   }
 
   /// Determines whether this builtin is a predefined libc/libm
@@ -156,21 +307,21 @@ class Context {
   /// they do not, but they are recognized as builtins once we see
   /// a declaration.
   bool isPredefinedLibFunction(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'f') != nullptr;
+    return strchr(getAttributesString(ID), 'f') != nullptr;
   }
 
   /// Returns true if this builtin requires appropriate header in other
   /// compilers. In Clang it will work even without including it, but we can emit
   /// a warning about missing header.
   bool isHeaderDependentFunction(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'h') != nullptr;
+    return strchr(getAttributesString(ID), 'h') != nullptr;
   }
 
   /// Determines whether this builtin is a predefined compiler-rt/libgcc
   /// function, such as "__clear_cache", where we know the signature a
   /// priori.
   bool isPredefinedRuntimeFunction(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'i') != nullptr;
+    return strchr(getAttributesString(ID), 'i') != nullptr;
   }
 
   /// Determines whether this builtin is a C++ standard library function
@@ -178,7 +329,7 @@ class Context {
   /// specialization, where the signature is determined by the standard library
   /// declaration.
   bool isInStdNamespace(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'z') != nullptr;
+    return strchr(getAttributesString(ID), 'z') != nullptr;
   }
 
   /// Determines whether this builtin can have its address taken with no
@@ -192,33 +343,33 @@ class Context {
 
   /// Determines whether this builtin has custom typechecking.
   bool hasCustomTypechecking(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 't') != nullptr;
+    return strchr(getAttributesString(ID), 't') != nullptr;
   }
 
   /// Determines whether a declaration of this builtin should be recognized
   /// even if the type doesn't match the specified signature.
   bool allowTypeMismatch(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'T') != nullptr ||
+    return strchr(getAttributesString(ID), 'T') != nullptr ||
            hasCustomTypechecking(ID);
   }
 
   /// Determines whether this builtin has a result or any arguments which
   /// are pointer types.
   bool hasPtrArgsOrResult(unsigned ID) const {
-    return strchr(getRecord(ID).Type, '*') != nullptr;
+    return strchr(getTypeString(ID), '*') != nullptr;
   }
 
   /// Return true if this builtin has a result or any arguments which are
   /// reference types.
   bool hasReferenceArgsOrResult(unsigned ID) const {
-    return strchr(getRecord(ID).Type, '&') != nullptr ||
-           strchr(getRecord(ID).Type, 'A') != nullptr;
+    return strchr(getTypeString(ID), '&') != nullptr ||
+           strchr(getTypeString(ID), 'A') != nullptr;
   }
 
   /// If this is a library function that comes from a specific
   /// header, retrieve that header name.
   const char *getHeaderName(unsigned ID) const {
-    return getRecord(ID).Header.getName();
+    return getInfo(ID).Header.getName();
   }
 
   /// Determine whether this builtin is like printf in its
@@ -243,27 +394,25 @@ class Context {
   /// Such functions can be const when the MathErrno lang option and FP
   /// exceptions are disabled.
   bool isConstWithoutErrnoAndExceptions(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'e') != nullptr;
+    return strchr(getAttributesString(ID), 'e') != nullptr;
   }
 
   bool isConstWithoutExceptions(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'g') != nullptr;
+    return strchr(getAttributesString(ID), 'g') != nullptr;
   }
 
-  const char *getRequiredFeatures(unsigned ID) const {
-    return getRecord(ID).Features;
-  }
+  const char *getRequiredFeatures(unsigned ID) const;
 
   unsigned getRequiredVectorWidth(unsigned ID) const;
 
   /// Return true if builtin ID belongs to AuxTarget.
   bool isAuxBuiltinID(unsigned ID) const {
-    return ID >= (Builtin::FirstTSBuiltin + TSRecords.size());
+    return ID >= (Builtin::FirstTSBuiltin + NumTargetBuiltins);
   }
 
   /// Return real builtin ID (i.e. ID it would have during compilation
   /// for AuxTarget).
-  unsigned getAuxBuiltinID(unsigned ID) const { return ID - TSRecords.size(); }
+  unsigned getAuxBuiltinID(unsigned ID) const { return ID - NumTargetBuiltins; }
 
   /// Returns true if this is a libc/libm function without the '__builtin_'
   /// prefix.
@@ -275,16 +424,19 @@ class Context {
 
   /// Return true if this function can be constant evaluated by Clang frontend.
   bool isConstantEvaluated(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'E') != nullptr;
+    return strchr(getAttributesString(ID), 'E') != nullptr;
   }
 
   /// Returns true if this is an immediate (consteval) function
   bool isImmediate(unsigned ID) const {
-    return strchr(getRecord(ID).Attributes, 'G') != nullptr;
+    return strchr(getAttributesString(ID), 'G') != nullptr;
   }
 
 private:
-  const Info &getRecord(unsigned ID) const;
+  std::pair<const InfosShard &, const Info &>
+  getShardAndInfo(unsigned ID) const;
+
+  const Info &getInfo(unsigned ID) const { return getShardAndInfo(ID).second; }
 
   /// Helper function for isPrintfLike and isScanfLike.
   bool isLike(unsigned ID, unsigned &FormatIdx, bool &HasVAListArg,
diff --git a/clang/include/clang/Basic/BuiltinsNEON.def b/clang/include/clang/Basic/BuiltinsNEON.def
deleted file mode 100644
index 9627005ba9824e..00000000000000
--- a/clang/include/clang/Basic/BuiltinsNEON.def
+++ /dev/null
@@ -1,22 +0,0 @@
-//===--- BuiltinsNEON.def - NEON Builtin function database ------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the NEON-specific builtin function database.  Users of
-// this file must define the BUILTIN macro to make use of this information.
-//
-//===----------------------------------------------------------------------===//
-
-// The format of this database matches clang/Basic/Builtins.def.
-
-#define GET_NEON_BUILTINS
-#include "clang/Basic/arm_neon.inc"
-#include "clang/Basic/arm_fp16.inc"
-#undef GET_NEON_BUILTINS
-
-#undef BUILTIN
-#undef TARGET_BUILTIN
diff --git a/clang/include/clang/Basic/BuiltinsPPC.def b/clang/include/clang/Basic/BuiltinsPPC.def
index 161df386f00f03..bb7d54bbb793eb 100644
--- a/clang/include/clang/Basic/BuiltinsPPC.def
+++ b/clang/include/clang/Basic/BuiltinsPPC.def
@@ -1138,5 +1138,6 @@ UNALIASED_CUSTOM_BUILTIN(mma_pmxvbf16ger2nn, "vW512*VVi15i15i3", true,
 // FIXME: Obviously incomplete.
 
 #undef BUILTIN
+#undef TARGET_BUILTIN
 #undef CUSTOM_BUILTIN
 #undef UNALIASED_CUSTOM_BUILTIN
diff --git a/clang/include/clang/Basic/BuiltinsRISCVVector.def b/clang/include/clang/Basic/BuiltinsRISCVVector.def
deleted file mode 100644
index 6dfa87a1a1d313..00000000000000
--- a/clang/include/clang/Basic/BuiltinsRISCVVector.def
+++ /dev/null
@@ -1,22 +0,0 @@
-//==- BuiltinsRISCVVector.def - RISC-V Vector Builtin Database ---*- C++ -*-==//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the RISC-V-specific builtin function database.  Users of
-// this file must define the BUILTIN macro to make use of this information.
-//
-//===----------------------------------------------------------------------===//
-
-#if defined(BUILTIN) && !defined(TARGET_BUILTIN)
-#   define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BUILTIN(ID, TYPE, ATTRS)
-#endif
-
-#include "clang/Basic/riscv_vector_builtins.inc"
-#include "clang/Basic/riscv_sifive_vector_builtins.inc"
-
-#undef BUILTIN
-#undef TARGET_BUILTIN
diff --git a/clang/include/clang/Basic/TargetBuiltins.h b/clang/include/clang/Basic/TargetBuiltins.h
index a14fd2c4b224d8..238f7510a8ed60 100644
--- a/clang/include/clang/Basic/TargetBuiltins.h
+++ b/clang/include/clang/Basic/TargetBuiltins.h
@@ -26,9 +26,12 @@ namespace clang {
   namespace NEON {
   enum {
     LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1,
-#define BUILTIN(ID, TYPE, ATTRS) BI##ID,
-#define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BI##ID,
-#include "clang/Basic/BuiltinsNEON.def"
+#define GET_NEON_BUILTIN_ENUMERATORS
+#include "clang/Basic/arm_neon.inc"
+    FirstFp16Builtin,
+    LastNeonBuiltin = FirstFp16Builtin - 1,
+#include "clang/Basic/arm_fp16.inc"
+#undef GET_NEON_BUILTIN_ENUMERATORS
     FirstTSBuiltin
   };
   }
@@ -47,9 +50,16 @@ namespace clang {
   namespace SVE {
   enum {
     LastNEONBuiltin = NEON::FirstTSBuiltin - 1,
-#define BUILTIN(ID, TYPE, ATTRS) BI##ID,
+#define GET_SVE_BUILTIN_ENUMERATORS
+#include "clang/Basic/arm_sve_builtins.inc"
+#undef GET_...
[truncated]

@chandlerc
Copy link
Member Author

@dyung - I believe this PR may be a credible path to address the issues hit with your MSVC builders, would appreciate any help testing it in advance if possible.

Copy link

github-actions bot commented Dec 19, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@chandlerc chandlerc changed the title Patch series to reapply #119638 and substantially improve it Patch series to reapply #118734 and substantially improve it Dec 19, 2024
@dyung
Copy link
Collaborator

dyung commented Dec 19, 2024

@dyung - I believe this PR may be a credible path to address the issues hit with your MSVC builders, would appreciate any help testing it in advance if possible.

Sure, I'll give it a try

@chandlerc chandlerc force-pushed the prefix-arm-rv branch 2 times, most recently from b71ac53 to 2bcc4e5 Compare December 19, 2024 08:35
@dyung
Copy link
Collaborator

dyung commented Dec 19, 2024

@dyung - I believe this PR may be a credible path to address the issues hit with your MSVC builders, would appreciate any help testing it in advance if possible.

Sure, I'll give it a try

You seem to still be working on it, can you tell me which commit I should try building/testing when you are done?

@chandlerc
Copy link
Member Author

@dyung - I believe this PR may be a credible path to address the issues hit with your MSVC builders, would appreciate any help testing it in advance if possible.

Sure, I'll give it a try

You seem to still be working on it, can you tell me which commit I should try building/testing when you are done?

Sorry, was just tidying up things that I missed until the PR was uploaded. Only rebasing on top-of-tree and a clang-format issue, nothing that changes functionality. And I think all done now so you should be good-to-go with the top of this PR (I think 2bcc4e5 (#120534) is the link to that commit)

@dyung
Copy link
Collaborator

dyung commented Dec 19, 2024

@dyung - I believe this PR may be a credible path to address the issues hit with your MSVC builders, would appreciate any help testing it in advance if possible.

Sure, I'll give it a try

You seem to still be working on it, can you tell me which commit I should try building/testing when you are done?

Sorry, was just tidying up things that I missed until the PR was uploaded. Only rebasing on top-of-tree and a clang-format issue, nothing that changes functionality. And I think all done now so you should be good-to-go with the top of this PR (I think 2bcc4e5 (#120534) is the link to that commit)

I'll try it and let you know. Give me about an hour or so.

github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 28, 2025
…d MSVC (#123548)

Historically, the main example of *very* large string tables used the
`EmitCharArray` to work around MSVC limitations with string literals,
but that was switched (without removing the API) in order to consolidate
on a nicer emission primitive.

While this large string table in `IntrinsicsImpl.inc` seems to compile
correctly on MSVC without the work around in `EmitCharArray` (and that
this PR adds back to the nicer emission path), other users have
repeatedly hit this MSVC limitation as you can see in the discussion on
PR llvm/llvm-project#120534. This PR teaches the
string offset table emission to look at
the size of the table and switch to the char array emission strategy
when the table becomes too large.

This work around does have the downside of making compile times worse
for large string tables, but that appears unavoidable until we can
identify known good MSVC versions and switch to requiring them for all
LLVM users. It also reduces searchability of the generated string table
-- I looked at emitting a comment with each string but it is tricky
because the escaping rules for an inline comment are different from
those of of a string literal, and there's no real way to turn the string
literal into a comment.

While improving the output in this way, also clean up the output to not
emit an extraneous empty string at the end of the string table, and
update the `StringTable` class to not look for that. It isn't actually
used by anything and is wasteful.

This PR also switches the `IntrinsicsImpl.inc` string tables over to the
new `StringTable` runtime abstraction. I didn't want to do this until
landing the MSVC workaround in case it caused even this example to start
hitting the MSVC bug, but I wanted to switch here so that I could
simplify the API for emitting the string table with the workaround
present. With the two different emission strategies, its important to
use a very exact syntax and that seems better encapsulated in the API.

Last but not least, the `SDNodeInfoEmitter` is updated, including its
tests to match the new output.

This PR should unblock landing
llvm/llvm-project#120534 and letting us switch
all of
Clang's builtins to use string tables. That PR has all the details
motivating the overall effort.

Follow-up patches will try to consolidate the remaining users onto the
single interface, but those at least were easy to separate into
follow-ups and keep this PR somewhat smaller.
@chandlerc
Copy link
Member Author

All of the dependent changes have landed, and I've re-structured this patch series to hopefully be easier to review. I still don't have a good way to land this incrementally, as the tools to work around MSVC limitations are only available when using TableGen-ed string tables, and those aren't available at first.

I could split out some of the later patches, but those are smaller and probably doesn't help as much. But the patches should be meaningful chunks to review here. There have been quite a few changes, so this does probably warrant a re-review from folks. Sorry for that.

@dyung -- as always, appreciate help verifying that this isn't going to break MSVC builds for you. The top of this PR should be up to date and ready to test.

@chandlerc chandlerc requested a review from erichkeane January 28, 2025 09:18
@dyung
Copy link
Collaborator

dyung commented Jan 28, 2025

All of the dependent changes have landed, and I've re-structured this patch series to hopefully be easier to review. I still don't have a good way to land this incrementally, as the tools to work around MSVC limitations are only available when using TableGen-ed string tables, and those aren't available at first.

I could split out some of the later patches, but those are smaller and probably doesn't help as much. But the patches should be meaningful chunks to review here. There have been quite a few changes, so this does probably warrant a re-review from folks. Sorry for that.

@dyung -- as always, appreciate help verifying that this isn't going to break MSVC builds for you. The top of this PR should be up to date and ready to test.

Happy to do so! Some updates on our end, I think that we have finished updating our internal machines with a newer build of Visual Studio 2019, but I need to confirm it. Our upstream bot has not yet been updated though, but I can do that pretty quickly/easily. I will test your changes on both versions tomorrow and let you know the results.

@dyung
Copy link
Collaborator

dyung commented Jan 29, 2025

All of the dependent changes have landed, and I've re-structured this patch series to hopefully be easier to review. I still don't have a good way to land this incrementally, as the tools to work around MSVC limitations are only available when using TableGen-ed string tables, and those aren't available at first.
I could split out some of the later patches, but those are smaller and probably doesn't help as much. But the patches should be meaningful chunks to review here. There have been quite a few changes, so this does probably warrant a re-review from folks. Sorry for that.
@dyung -- as always, appreciate help verifying that this isn't going to break MSVC builds for you. The top of this PR should be up to date and ready to test.

Happy to do so! Some updates on our end, I think that we have finished updating our internal machines with a newer build of Visual Studio 2019, but I need to confirm it. Our upstream bot has not yet been updated though, but I can do that pretty quickly/easily. I will test your changes on both versions tomorrow and let you know the results.

I ran the most recent changes in this PR (2b85083) and can confirm that all tests passed on both the original and newer version of Visual Studio 2019 that we are upgrading to.

@chandlerc
Copy link
Member Author

I know it's only been a few days, but pinging in the hope of landing this week... This seems to finally be in a good state and is somewhat hard to keep rebasing. Happy to do anything I can to help make review easier.

@Andarwinux
Copy link
Contributor

Will LLVM 20 miss this?

@h-vetinari
Copy link
Contributor

You'd have to merge and request a backport pretty quickly. Historically features could still be argued to land between rc1 (which is imminent) and rc2, but the windows is definitely closing fast

@chandlerc
Copy link
Member Author

Given the benefit to binary size and compile time in things like configure scripts, I'd certainly like to see it land...

Erich already reviewed an earlier version. Maybe @rnk can help with reviews?

@Andarwinux
Copy link
Contributor

#125367

RC1 coming soon

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

Thanks for optimizing the builtins! I feel like builtins have grown significantly since adding RISC-V and ARM MVE intrinsics, and few people until now have stopped to re-evaluate how we represent these things.

While I was watching these clearly generated Intrinsic files land in Clang, I was thinking, these are clearly generated from a data model using a script. Wouldn't it be more efficient to encode the underlying data model for these operations using a more normalized representation? I proposed this at one point, and someone from Intel assured me that these long repetitive instructions and intrinsics are hand-maintained. It must be true at some level. Regardless, this change seems like a positive step towards renormalizing the otherwise denormalized builtins databse.

There's a lot of code and I'm sure there are more nits I could pick out, but I think it makes sense to reland this with the more sharded approach and get some feedback in tree.

collectBuiltins(BuiltinRecord, Builtins);
}

// Now collect (and count) the prefixed builtins.
unsigned NumPrefixedBuiltins = Builtins.size();
const Record *first_prefix = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Super nit, just to prove I read all the code: Why the change in style here? FirstPrefix?

Copy link
Member Author

Choose a reason for hiding this comment

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

Doh, just an accident, sorry. Out of the habits of LLVM style, fixed.

@chandlerc
Copy link
Member Author

Thanks @rnk !

I've fixed the one style nit (doh!) and a few surrounding variables.

I'm working on rebasing everything now.

But one specific question: would you prefer me to land as a series of commits or a single squashed commit for the entire PR? I'm happy either way. My mild preference is to prefer the series of commits, but open to suggestions here.

This both reapplies llvm#118734, the initial attempt at this, and updates it
significantly.

First, it uses the newly added `StringTable` abstraction for string
tables, and simplifies the construction to build the string table and
info arrays separately. This should reduce any `constexpr` compile time
memory or CPU cost of the original PR while significantly improving the
APIs throughout.

It also restructures the builtins to support sharding across several
independent tables. This accomplishes two improvements from the
original PR:

1) It improves the APIs used significantly.

2) When builtins are defined from different sources (like SVE vs MVE in
   AArch64), this allows each of them to build their own string table
   independently rather than having to merge the string tables and info
   structures.

3) It allows each shard to factor out a common prefix, often cutting the
   size of the strings needed for the builtins by a factor two.

The second point is important both to allow different mechanisms of
construction (for example a `.def` file and a tablegen'ed `.inc` file,
or different tablegen'ed `.inc files), it also simply reduces the sizes
of these tables which is valuable given how large they are in some
cases. The third builds on that size reduction.

Initially, we use this new sharding rather than merging tables in
AArch64, LoongArch, RISCV, and X86. Mostly this helps ensure the system
works, as without further changes these still push scaling limits.
Subsequent commits will more deeply leverage the new structure,
including using the prefix capabilities which cannot be easily factored
out here and requires deep changes to the targets.
…leGen

This lets the TableGen-ed code be much cleaner, directly building an
efficient string table without duplicates and without the repeated
prefix.
… tables

This leverages the sharded structure of the builtins to make it easy to
directly tablegen most of the AArch64 and ARM builtins while still using
X-macros for a few edge cases. It also extracts common prefixes as part
of that.

This makes the string tables for these targets dramatically smaller.
This is especially important as the SVE builtins represent (by far) the
largest string table and largest builtin table across all the targets in
Clang.
…and info

This moves the main builtins and several targets to use nice generated
string tables and info structures rather than X-macros. Even without
obvious prefixes factored out, the resulting tables are significantly
smaller and much cheaper to compile with out all the X-macro overhead.

This leaves the X-macros in place for atomic builtins which have a wide
range of uses that don't seem reasonable to fold into TableGen.

As future work, these should move to their own file (whether as X-macros
or just generated patterns) so the AST headers don't have to include all
the data for other builtins.
This requires adding support to the general builtins emission for
producing prefixed builtin infos separately from un-prefixed which is
a bit crufty. But we don't currently have any good way of having a more
refined model than a single hard-coded prefix string per TableGen
emission. Something more powerful and/or elegant is possible, but this
is a fairly minimal first step that at least allows factoring out the
builtin prefix for something like X86.
This target's builtins have an especially long prefix and so we get over
2x reduction in string table size required with this change.
@phoebewang
Copy link
Contributor

I proposed this at one point, and someone from Intel assured me that these long repetitive instructions and intrinsics are hand-maintained. It must be true at some level.

I believe it's not me, though I don't like the changes at the beginning either. Anyway, with @chandlerc 's great script, we have migrated our internal bultins. Hope this won't affect them much.

@rnk
Copy link
Collaborator

rnk commented Feb 4, 2025

But one specific question: would you prefer me to land as a series of commits or a single squashed commit for the entire PR? I'm happy either way. My mild preference is to prefer the series of commits, but open to suggestions here.

I would land it as the six-patch stack in this case. I ended up using that to review, since mainly the first patch is the interesting one and the target-specific migration ones are more mechanical and more incremental.

@chandlerc
Copy link
Member Author

But one specific question: would you prefer me to land as a series of commits or a single squashed commit for the entire PR? I'm happy either way. My mild preference is to prefer the series of commits, but open to suggestions here.

I would land it as the six-patch stack in this case. I ended up using that to review, since mainly the first patch is the interesting one and the target-specific migration ones are more mechanical and more incremental.

SGTM, I'll rebase again and merge manually. Thanks again for the review!

@chandlerc
Copy link
Member Author

Merged:

@chandlerc chandlerc closed this Feb 4, 2025
@h-vetinari
Copy link
Contributor

If you want to backport this, you need to add the PR to the milestone and comment

/cherry-pick <hash1> <hash2> <hash3> ...

Then the bot should open a PR (or it'll tell you if it failed to cherry-pick, and how to proceed from there).

@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2025

If you want to backport this, you need to add the PR to the milestone and comment

/cherry-pick <hash1> <hash2> <hash3> ...

Then the bot should open a PR (or it'll tell you if it failed to cherry-pick, and how to proceed from there).

Error: Command failed due to missing milestone.

github-merge-queue bot pushed a commit to carbon-language/carbon-lang that referenced this pull request Feb 5, 2025
This is primarily to get commits involved in
llvm/llvm-project#120534
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

9 participants