[clang] Correctly link and optimize device libraries with -mlink-builtin-bitcode (PR #69371)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 17 12:21:02 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Jacob Lambert (lamb-j)
<details>
<summary>Changes</summary>
This set of patches updates device library linking and optimization to do the following:
Link
Optimize
Link (New)
This handles the edge case where optimization introduces new device library functions, such as a fused sincos() from separate sin() and cos() calls. The second link step ensures the sincos() definition is also linked in from the device libraries.
---
Patch is 51.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/69371.diff
6 Files Affected:
- (added) clang/lib/CodeGen/BackendConsumer.h (+166)
- (modified) clang/lib/CodeGen/BackendUtil.cpp (+42-30)
- (modified) clang/lib/CodeGen/CMakeLists.txt (+1)
- (modified) clang/lib/CodeGen/CodeGenAction.cpp (+303-361)
- (added) clang/lib/CodeGen/LinkInModulesPass.cpp (+29)
- (added) clang/lib/CodeGen/LinkInModulesPass.h (+43)
``````````diff
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
new file mode 100644
index 000000000000000..72a814cd43d738d
--- /dev/null
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -0,0 +1,166 @@
+//===--- BackendConsumer.h - LLVM BackendConsumer Header File -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CODEGEN_BACKENDCONSUMER_H
+#define LLVM_CLANG_LIB_CODEGEN_BACKENDCONSUMER_H
+
+#include "clang/CodeGen/BackendUtil.h"
+#include "clang/CodeGen/CodeGenAction.h"
+
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/Support/Timer.h"
+
+namespace llvm {
+ class DiagnosticInfoDontCall;
+}
+
+namespace clang {
+class ASTContext;
+class CodeGenAction;
+class CoverageSourceInfo;
+
+class BackendConsumer : public ASTConsumer {
+ using LinkModule = CodeGenAction::LinkModule;
+
+ virtual void anchor();
+ DiagnosticsEngine &Diags;
+ BackendAction Action;
+ const HeaderSearchOptions &HeaderSearchOpts;
+ const CodeGenOptions &CodeGenOpts;
+ const TargetOptions &TargetOpts;
+ const LangOptions &LangOpts;
+ std::unique_ptr<raw_pwrite_stream> AsmOutStream;
+ ASTContext *Context;
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
+
+ llvm::Timer LLVMIRGeneration;
+ unsigned LLVMIRGenerationRefCount;
+
+ /// True if we've finished generating IR. This prevents us from generating
+ /// additional LLVM IR after emitting output in HandleTranslationUnit. This
+ /// can happen when Clang plugins trigger additional AST deserialization.
+ bool IRGenFinished = false;
+
+ bool TimerIsEnabled = false;
+
+ std::unique_ptr<CodeGenerator> Gen;
+
+ SmallVector<LinkModule, 4> LinkModules;
+
+ // A map from mangled names to their function's source location, used for
+ // backend diagnostics as the Clang AST may be unavailable. We actually use
+ // the mangled name's hash as the key because mangled names can be very
+ // long and take up lots of space. Using a hash can cause name collision,
+ // but that is rare and the consequences are pointing to a wrong source
+ // location which is not severe. This is a vector instead of an actual map
+ // because we optimize for time building this map rather than time
+ // retrieving an entry, as backend diagnostics are uncommon.
+ std::vector<std::pair<llvm::hash_code, FullSourceLoc>>
+ ManglingFullSourceLocs;
+
+
+ // This is here so that the diagnostic printer knows the module a diagnostic
+ // refers to.
+ llvm::Module *CurLinkModule = nullptr;
+
+public:
+ BackendConsumer(BackendAction Action, DiagnosticsEngine &Diags,
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ const HeaderSearchOptions &HeaderSearchOpts,
+ const PreprocessorOptions &PPOpts,
+ const CodeGenOptions &CodeGenOpts,
+ const TargetOptions &TargetOpts,
+ const LangOptions &LangOpts, const std::string &InFile,
+ SmallVector<LinkModule, 4> LinkModules,
+ std::unique_ptr<raw_pwrite_stream> OS, llvm::LLVMContext &C,
+ CoverageSourceInfo *CoverageInfo = nullptr);
+
+ // This constructor is used in installing an empty BackendConsumer
+ // to use the clang diagnostic handler for IR input files. It avoids
+ // initializing the OS field.
+ BackendConsumer(BackendAction Action, DiagnosticsEngine &Diags,
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ const HeaderSearchOptions &HeaderSearchOpts,
+ const PreprocessorOptions &PPOpts,
+ const CodeGenOptions &CodeGenOpts,
+ const TargetOptions &TargetOpts,
+ const LangOptions &LangOpts, llvm::Module *Module,
+ SmallVector<LinkModule, 4> LinkModules, llvm::LLVMContext &C,
+ CoverageSourceInfo *CoverageInfo = nullptr);
+
+ llvm::Module *getModule() const;
+ std::unique_ptr<llvm::Module> takeModule();
+
+ CodeGenerator *getCodeGenerator();
+
+ void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override;
+ void Initialize(ASTContext &Ctx) override;
+ bool HandleTopLevelDecl(DeclGroupRef D) override;
+ void HandleInlineFunctionDefinition(FunctionDecl *D) override;
+ void HandleInterestingDecl(DeclGroupRef D) override;
+ void HandleTranslationUnit(ASTContext &C) override;
+ void HandleTagDeclDefinition(TagDecl *D) override;
+ void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
+ void CompleteTentativeDefinition(VarDecl *D) override;
+ void CompleteExternalDeclaration(VarDecl *D) override;
+ void AssignInheritanceModel(CXXRecordDecl *RD) override;
+ void HandleVTable(CXXRecordDecl *RD) override;
+
+
+ // Links each entry in LinkModules into our module. Returns true on error.
+ bool LinkInModules(llvm::Module *M, bool ShouldLinkFiles = true);
+
+ /// Get the best possible source location to represent a diagnostic that
+ /// may have associated debug info.
+ const FullSourceLoc getBestLocationFromDebugLoc(
+ const llvm::DiagnosticInfoWithLocationBase &D,
+ bool &BadDebugInfo, StringRef &Filename,
+ unsigned &Line, unsigned &Column) const;
+
+ std::optional<FullSourceLoc> getFunctionSourceLocation(
+ const llvm::Function &F) const;
+
+ void DiagnosticHandlerImpl(const llvm::DiagnosticInfo &DI);
+ /// Specialized handler for InlineAsm diagnostic.
+ /// \return True if the diagnostic has been successfully reported, false
+ /// otherwise.
+ bool InlineAsmDiagHandler(const llvm::DiagnosticInfoInlineAsm &D);
+ /// Specialized handler for diagnostics reported using SMDiagnostic.
+ void SrcMgrDiagHandler(const llvm::DiagnosticInfoSrcMgr &D);
+ /// Specialized handler for StackSize diagnostic.
+ /// \return True if the diagnostic has been successfully reported, false
+ /// otherwise.
+ bool StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D);
+ /// Specialized handler for ResourceLimit diagnostic.
+ /// \return True if the diagnostic has been successfully reported, false
+ /// otherwise.
+ bool ResourceLimitDiagHandler(const llvm::DiagnosticInfoResourceLimit &D);
+
+ /// Specialized handler for unsupported backend feature diagnostic.
+ void UnsupportedDiagHandler(const llvm::DiagnosticInfoUnsupported &D);
+ /// Specialized handlers for optimization remarks.
+ /// Note that these handlers only accept remarks and they always handle
+ /// them.
+ void EmitOptimizationMessage(const llvm::DiagnosticInfoOptimizationBase &D,
+ unsigned DiagID);
+ void
+ OptimizationRemarkHandler(const llvm::DiagnosticInfoOptimizationBase &D);
+ void OptimizationRemarkHandler(
+ const llvm::OptimizationRemarkAnalysisFPCommute &D);
+ void OptimizationRemarkHandler(
+ const llvm::OptimizationRemarkAnalysisAliasing &D);
+ void OptimizationFailureHandler(
+ const llvm::DiagnosticInfoOptimizationFailure &D);
+ void DontCallDiagHandler(const llvm::DiagnosticInfoDontCall &D);
+ /// Specialized handler for misexpect warnings.
+ /// Note that misexpect remarks are emitted through ORE
+ void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D);
+};
+
+} // namespace clang
+#endif
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index d066819871dfde3..894daced377fb55 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//
+#include "BackendConsumer.h"
+#include "LinkInModulesPass.h"
#include "clang/CodeGen/BackendUtil.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/Diagnostic.h"
@@ -112,7 +114,7 @@ class EmitAssemblyHelper {
const CodeGenOptions &CodeGenOpts;
const clang::TargetOptions &TargetOpts;
const LangOptions &LangOpts;
- Module *TheModule;
+ llvm::Module *TheModule;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
Timer CodeGenerationTime;
@@ -155,10 +157,10 @@ class EmitAssemblyHelper {
return F;
}
- void
- RunOptimizationPipeline(BackendAction Action,
+ void RunOptimizationPipeline(BackendAction Action,
std::unique_ptr<raw_pwrite_stream> &OS,
- std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS);
+ std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS,
+ BackendConsumer *BC);
void RunCodegenPipeline(BackendAction Action,
std::unique_ptr<raw_pwrite_stream> &OS,
std::unique_ptr<llvm::ToolOutputFile> &DwoOS);
@@ -178,7 +180,7 @@ class EmitAssemblyHelper {
const HeaderSearchOptions &HeaderSearchOpts,
const CodeGenOptions &CGOpts,
const clang::TargetOptions &TOpts,
- const LangOptions &LOpts, Module *M,
+ const LangOptions &LOpts, llvm::Module *M,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
: Diags(_Diags), HSOpts(HeaderSearchOpts), CodeGenOpts(CGOpts),
TargetOpts(TOpts), LangOpts(LOpts), TheModule(M), VFS(std::move(VFS)),
@@ -194,7 +196,8 @@ class EmitAssemblyHelper {
// Emit output using the new pass manager for the optimization pipeline.
void EmitAssembly(BackendAction Action,
- std::unique_ptr<raw_pwrite_stream> OS);
+ std::unique_ptr<raw_pwrite_stream> OS,
+ BackendConsumer *BC);
};
}
@@ -690,7 +693,7 @@ static void addSanitizers(const Triple &TargetTriple,
// the logic of the original code, but operates on "shadow" values. It
// can benefit from re-running some general purpose optimization
// passes.
- MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
+ MPM.addPass(RequireAnalysisPass<GlobalsAA, llvm::Module>());
FunctionPassManager FPM;
FPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
FPM.addPass(InstCombinePass());
@@ -749,7 +752,7 @@ static void addSanitizers(const Triple &TargetTriple,
SanitizersCallback(NewMPM, Level);
if (!NewMPM.isEmpty()) {
// Sanitizers can abandon<GlobalsAA>.
- NewMPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
+ NewMPM.addPass(RequireAnalysisPass<GlobalsAA, llvm::Module>());
MPM.addPass(std::move(NewMPM));
}
});
@@ -761,7 +764,7 @@ static void addSanitizers(const Triple &TargetTriple,
void EmitAssemblyHelper::RunOptimizationPipeline(
BackendAction Action, std::unique_ptr<raw_pwrite_stream> &OS,
- std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS) {
+ std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS, BackendConsumer *BC) {
std::optional<PGOOptions> PGOOpt;
if (CodeGenOpts.hasProfileIRInstr())
@@ -1035,6 +1038,12 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
}
}
+ // Re-link against any bitcodes supplied via the -mlink-builtin-bitcode option
+ // Some optimizations may generate new function calls that would not have
+ // been linked pre-optimization (i.e. fused sincos calls generated by
+ // AMDGPULibCalls::fold_sincos.)
+ MPM.addPass(LinkInModulesPass(BC, false));
+
// Add a verifier pass if requested. We don't have to do this if the action
// requires code generation because there will already be a verifier pass in
// the code-generation pipeline.
@@ -1046,7 +1055,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
if (Action == Backend_EmitBC || Action == Backend_EmitLL) {
if (CodeGenOpts.PrepareForThinLTO && !CodeGenOpts.DisableLLVMPasses) {
if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
- TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit",
+ TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit",
CodeGenOpts.EnableSplitLTOUnit);
if (Action == Backend_EmitBC) {
if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) {
@@ -1055,7 +1064,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
return;
}
if (CodeGenOpts.UnifiedLTO)
- TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1));
+ TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1));
MPM.addPass(ThinLTOBitcodeWriterPass(
*OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr));
} else {
@@ -1069,12 +1078,12 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
bool EmitLTOSummary = shouldEmitRegularLTOSummary();
if (EmitLTOSummary) {
if (!TheModule->getModuleFlag("ThinLTO") && !CodeGenOpts.UnifiedLTO)
- TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0));
+ TheModule->addModuleFlag(llvm::Module::Error, "ThinLTO", uint32_t(0));
if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
- TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit",
+ TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit",
uint32_t(1));
if (CodeGenOpts.UnifiedLTO)
- TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1));
+ TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1));
}
if (Action == Backend_EmitBC)
MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists,
@@ -1088,13 +1097,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
// Set module flags, like EnableSplitLTOUnit and UnifiedLTO, since FatLTO
// uses a different action than Backend_EmitBC or Backend_EmitLL.
if (!TheModule->getModuleFlag("ThinLTO"))
- TheModule->addModuleFlag(Module::Error, "ThinLTO",
+ TheModule->addModuleFlag(llvm::Module::Error, "ThinLTO",
uint32_t(CodeGenOpts.PrepareForThinLTO));
if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
- TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit",
+ TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit",
uint32_t(CodeGenOpts.EnableSplitLTOUnit));
if (CodeGenOpts.UnifiedLTO && !TheModule->getModuleFlag("UnifiedLTO"))
- TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1));
+ TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1));
}
// Print a textual, '-passes=' compatible, representation of pipeline if
@@ -1160,7 +1169,8 @@ void EmitAssemblyHelper::RunCodegenPipeline(
}
void EmitAssemblyHelper::EmitAssembly(BackendAction Action,
- std::unique_ptr<raw_pwrite_stream> OS) {
+ std::unique_ptr<raw_pwrite_stream> OS,
+ BackendConsumer *BC) {
TimeRegion Region(CodeGenOpts.TimePasses ? &CodeGenerationTime : nullptr);
setCommandLineOpts(CodeGenOpts);
@@ -1176,7 +1186,7 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action,
cl::PrintOptionValues();
std::unique_ptr<llvm::ToolOutputFile> ThinLinkOS, DwoOS;
- RunOptimizationPipeline(Action, OS, ThinLinkOS);
+ RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
RunCodegenPipeline(Action, OS, DwoOS);
if (ThinLinkOS)
@@ -1186,11 +1196,12 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action,
}
static void runThinLTOBackend(
- DiagnosticsEngine &Diags, ModuleSummaryIndex *CombinedIndex, Module *M,
- const HeaderSearchOptions &HeaderOpts, const CodeGenOptions &CGOpts,
- const clang::TargetOptions &TOpts, const LangOptions &LOpts,
- std::unique_ptr<raw_pwrite_stream> OS, std::string SampleProfile,
- std::string ProfileRemapping, BackendAction Action) {
+ DiagnosticsEngine &Diags, ModuleSummaryIndex *CombinedIndex,
+ llvm::Module *M, const HeaderSearchOptions &HeaderOpts,
+ const CodeGenOptions &CGOpts, const clang::TargetOptions &TOpts,
+ const LangOptions &LOpts, std::unique_ptr<raw_pwrite_stream> OS,
+ std::string SampleProfile, std::string ProfileRemapping,
+ BackendAction Action) {
DenseMap<StringRef, DenseMap<GlobalValue::GUID, GlobalValueSummary *>>
ModuleToDefinedGVSummaries;
CombinedIndex->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
@@ -1259,18 +1270,18 @@ static void runThinLTOBackend(
Conf.SplitDwarfOutput = CGOpts.SplitDwarfOutput;
switch (Action) {
case Backend_EmitNothing:
- Conf.PreCodeGenModuleHook = [](size_t Task, const Module &Mod) {
+ Conf.PreCodeGenModuleHook = [](size_t Task, const llvm::Module &Mod) {
return false;
};
break;
case Backend_EmitLL:
- Conf.PreCodeGenModuleHook = [&](size_t Task, const Module &Mod) {
+ Conf.PreCodeGenModuleHook = [&](size_t Task, const llvm::Module &Mod) {
M->print(*OS, nullptr, CGOpts.EmitLLVMUseLists);
return false;
};
break;
case Backend_EmitBC:
- Conf.PreCodeGenModuleHook = [&](size_t Task, const Module &Mod) {
+ Conf.PreCodeGenModuleHook = [&](size_t Task, const llvm::Module &Mod) {
WriteBitcodeToFile(*M, *OS, CGOpts.EmitLLVMUseLists);
return false;
};
@@ -1294,9 +1305,10 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
const CodeGenOptions &CGOpts,
const clang::TargetOptions &TOpts,
const LangOptions &LOpts, StringRef TDesc,
- Module *M, BackendAction Action,
+ llvm::Module *M, BackendAction Action,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
- std::unique_ptr<raw_pwrite_stream> OS) {
+ std::unique_ptr<raw_pwrite_stream> OS,
+ BackendConsumer *BC) {
llvm::TimeTraceScope TimeScope("Backend");
@@ -1339,7 +1351,7 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
}
EmitAssemblyHelper AsmHelper(Diags, HeaderOpts, CGOpts, TOpts, LOpts, M, VFS);
- AsmHelper.EmitAssembly(Action, std::move(OS));
+ AsmHelper.EmitAssembly(Action, std::move(OS), BC);
// Verify clang's TargetInfo DataLayout against the LLVM TargetMachine's
// DataLayout.
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 1debeb6d9cce9e0..41e31c12a16e641 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -81,6 +81,7 @@ add_clang_library(clangCodeGen
ConstantInitBuilder.cpp
CoverageMappingGen.cpp
ItaniumCXXABI.cpp
+ LinkInModulesPass.cpp
MacroPPCallbacks.cpp
MicrosoftCXXABI.cpp
ModuleBuilder.cpp
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index a3b72381d73fc54..f4586a776c0fefd 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/CodeGen/CodeGenAction.h"
+#include "BackendConsumer.h"
#include "CGCall.h"
#include "CodeGenModule.h"
#include "CoverageMappingGen.h"
@@ -48,8 +49,8 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/Utils/Cloning.h"
-#include <memory>
#include <optional>
using namespace clang;
using namespace llvm;
@@ -57,419 +58,360 @@ using namespace llvm;
#define DEBUG_TYPE "codegenaction"
namespace clang {
- class BackendConsumer;
- class ClangDiagnosticHandler final : public DiagnosticHandler {
- public:
- ClangDiagnosticHandler(const CodeGenOptions &CGOpts, BackendConsumer *BCon)
- : CodeGenOpts(CGOpts), BackendCon(BCon) {}
+class BackendConsumer;
+class ClangDiagnosticHandler final : public DiagnosticHandler {
+public:
+ ClangDiagnosticHandler(const CodeGenOptions &CGOpts, BackendConsumer *BCon)
+ : CodeGenOpts(CGOpts), BackendCon(BCon) {}
- bool handleDiagnostics(const DiagnosticInfo &DI) override;
+ bool handleDiagnostics(const DiagnosticInfo &DI) over...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/69371
More information about the cfe-commits
mailing list