[clang] [CodeGen][CIR] Split out Backend Diags to utils, impl CIR error-attr (PR #225966)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 28 06:48:11 PDT 2026


https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/225966

>From 8ca3efd4f06b4c83aa8801d4a1a0aa14b784d8a9 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 23 Sep 2026 14:36:30 -0700
Subject: [PATCH 1/2] [CodeGen][CIR] Split out Backend Diags to utils, impl CIR
 error-attr

So first, the motivation for this is the `error` attribute, which
applies to a function and diagnoses if it 'survives' into the backend as
a call.  In order to do this, we need to add the
dontcall-error/dontcall-warn attributes to functions.

The call operation ALSO needs the srcloc to be present, so this threads
that through as well.  Note this is pretty fragile in classic-codegen,
so we inherit some fragility from that source location as well.

HOWEVER, in order to get diagnostics to work properly in the backend, we
need to consume them.  This patch ALSO extracts the handling for that
from CodeGenAction.cpp into CodeGenUtils, plus uses it from both sides.

Disclaimer: I ended up using Claude to do a lot of the refactoring.  It
was mostly copy/paste with some minor changes to generalize it, but
Cladue helped.
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |   3 +
 .../CodeGenUtils/BackendDiagnosticHandler.h   | 139 ++++
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          |   7 +
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  13 +
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp |  25 +-
 clang/lib/CIR/FrontendAction/CMakeLists.txt   |   1 +
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  10 +-
 .../Lowering/DirectToLLVM/LowerToLLVMIR.cpp   |  23 +
 clang/lib/CodeGen/BackendConsumer.h           |  71 +-
 clang/lib/CodeGen/CodeGenAction.cpp           | 635 +----------------
 .../CodeGenUtils/BackendDiagnosticHandler.cpp | 660 ++++++++++++++++++
 clang/lib/CodeGenUtils/CMakeLists.txt         |   4 +
 clang/test/CIR/CodeGen/attr-error.c           |  31 +
 clang/test/CIR/Diagnostics/attr-error.c       |  16 +
 .../inline-asm-backend-diagnostic.c           |   8 +
 15 files changed, 954 insertions(+), 692 deletions(-)
 create mode 100644 clang/include/clang/CodeGenUtils/BackendDiagnosticHandler.h
 create mode 100644 clang/lib/CodeGenUtils/BackendDiagnosticHandler.cpp
 create mode 100644 clang/test/CIR/CodeGen/attr-error.c
 create mode 100644 clang/test/CIR/Diagnostics/attr-error.c
 create mode 100644 clang/test/CIR/Diagnostics/inline-asm-backend-diagnostic.c

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 6f88d3a2b547fe..e83a36b2158a9a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -56,6 +56,9 @@ def CIR_Dialect : Dialect {
     static llvm::StringRef getNoRecurseAttrName() { return "norecurse"; }
     static llvm::StringRef getMustProgressAttrName() { return "mustprogress"; }
     static llvm::StringRef getSYCLModuleIdAttrName() { return "sycl-module-id"; }
+    static llvm::StringRef getDontCallErrorAttrName() { return "dontcall-error"; }
+    static llvm::StringRef getDontCallWarnAttrName() { return "dontcall-warn"; }
+    static llvm::StringRef getSrcLocAttrName() { return "cir.srcloc"; }
     static llvm::StringRef getNoUnwindAttrName() { return "nounwind"; }
     static llvm::StringRef getWillReturnAttrName() { return "willreturn"; }
     static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
diff --git a/clang/include/clang/CodeGenUtils/BackendDiagnosticHandler.h b/clang/include/clang/CodeGenUtils/BackendDiagnosticHandler.h
new file mode 100644
index 00000000000000..5d8ef7270d2a90
--- /dev/null
+++ b/clang/include/clang/CodeGenUtils/BackendDiagnosticHandler.h
@@ -0,0 +1,139 @@
+//===--- BackendDiagnosticHandler.h - Shared Backend Diagnostics -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 declares BackendDiagnosticConsumer, which turns LLVM backend
+// diagnostics (llvm::DiagnosticInfo) into clang::DiagnosticsEngine
+// diagnostics. It is shared between classic CodeGen (BackendConsumer) and
+// CIR (CIRGenConsumer) so that both LLVM-emitting pipelines report backend
+// diagnostics (optimization remarks, inline-asm errors, unsupported
+// features, etc.) through the same clang diagnostics machinery instead of
+// falling back to LLVM's default stderr-printing handler.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CODEGENUTILS_BACKENDDIAGNOSTICHANDLER_H
+#define LLVM_CLANG_CODEGENUTILS_BACKENDDIAGNOSTICHANDLER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/DiagnosticHandler.h"
+#include <memory>
+#include <optional>
+#include <vector>
+
+namespace llvm {
+class DiagnosticInfo;
+class DiagnosticInfoDontCall;
+class DiagnosticInfoInlineAsm;
+class DiagnosticInfoMisExpect;
+class DiagnosticInfoOptimizationBase;
+class DiagnosticInfoOptimizationFailure;
+class DiagnosticInfoResourceLimit;
+class DiagnosticInfoSrcMgr;
+class DiagnosticInfoStackSize;
+class DiagnosticInfoUnsupported;
+class DiagnosticInfoUnsupportedTargetIntrinsic;
+class DiagnosticInfoWithLocationBase;
+class Function;
+class Module;
+class OptimizationRemarkAnalysisAliasing;
+class OptimizationRemarkAnalysisFPCommute;
+} // namespace llvm
+
+namespace clang {
+class CodeGenOptions;
+class DiagnosticsEngine;
+class SourceManager;
+
+/// Owns the state needed to translate LLVM backend diagnostics into clang
+/// diagnostics, and implements the translation itself. This is not itself an
+/// llvm::DiagnosticHandler so that it can be installed/updated independently
+/// of the llvm::LLVMContext's handler lifetime; use
+/// BackendDiagnosticConsumer::createDiagnosticHandler() to get an
+/// llvm::DiagnosticHandler that forwards into it.
+class BackendDiagnosticConsumer {
+public:
+  BackendDiagnosticConsumer(DiagnosticsEngine &Diags,
+                            const CodeGenOptions &CodeGenOpts)
+      : Diags(Diags), CodeGenOpts(CodeGenOpts) {}
+
+  /// The SourceManager used to translate backend-reported file:line:col
+  /// locations back into clang SourceLocations. May be null (e.g. when
+  /// compiling a raw LLVM IR input file with no clang AST), in which case
+  /// diagnostics are still reported, just without clang-level source
+  /// locations.
+  void setSourceManager(SourceManager *SM) { this->SM = SM; }
+
+  /// Set the module currently being linked in, used to name DK_Linker
+  /// diagnostics.
+  void setCurLinkModule(llvm::Module *M) { CurLinkModule = M; }
+
+  /// Record the source location of the function with the given mangled
+  /// name, used to approximate a location for backend diagnostics (e.g.
+  /// stack-size warnings) that don't carry debug-info locations of their
+  /// own.
+  void addFunctionSourceLocation(StringRef MangledName, FullSourceLoc Loc);
+
+  std::optional<FullSourceLoc>
+  getFunctionSourceLocation(const llvm::Function &F) const;
+
+  /// Get the best possible source location to represent a diagnostic that
+  /// may have associated debug info.
+  FullSourceLoc
+  getBestLocationFromDebugLoc(const llvm::DiagnosticInfoWithLocationBase &D,
+                              bool &BadDebugInfo, StringRef &Filename,
+                              unsigned &Line, unsigned &Column) const;
+
+  /// Create an llvm::DiagnosticHandler that forwards diagnostics to this
+  /// consumer. The returned handler must not outlive this consumer.
+  std::unique_ptr<llvm::DiagnosticHandler> createDiagnosticHandler();
+
+  /// This is invoked when the backend needs to report something to the
+  /// user.
+  void handleDiagnostics(const llvm::DiagnosticInfo &DI);
+
+private:
+  bool InlineAsmDiagHandler(const llvm::DiagnosticInfoInlineAsm &D);
+  void SrcMgrDiagHandler(const llvm::DiagnosticInfoSrcMgr &D);
+  bool StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D);
+  bool ResourceLimitDiagHandler(const llvm::DiagnosticInfoResourceLimit &D);
+  void UnsupportedDiagHandler(const llvm::DiagnosticInfoUnsupported &D);
+  void UnsupportedTargetIntrinsicDiagHandler(
+      const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D);
+  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);
+  void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D);
+
+  DiagnosticsEngine &Diags;
+  const CodeGenOptions &CodeGenOpts;
+  SourceManager *SM = nullptr;
+  llvm::Module *CurLinkModule = nullptr;
+
+  // 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;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_CODEGENUTILS_BACKENDDIAGNOSTICHANDLER_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 226bc0a0ed0d5a..7dde1c17711048 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1404,6 +1404,13 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
   if (callOp)
     *callOp = theCall;
 
+  // If we have the error-attr we need the source location info so that the
+  // backend diagnostics pick it up.
+  if (calleeDecl && calleeDecl->hasAttr<ErrorAttr>())
+    theCall->setAttr(
+        cir::CIRDialect::getSrcLocAttrName(),
+        builder.getI64IntegerAttr(clangLoc.getBegin().getRawEncoding()));
+
   // Sema/emitAttributedStmt (see
   // https://github.com/llvm/llvm-project/issues/214764) should one-day enforce
   // that only one of these is valid at a time. For now, we have the same 'bug'
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 61d5b03f393b01..3386d2cc5ac405 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3318,6 +3318,19 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
   if (!isIncompleteFunction && func.isDeclaration())
     getTargetCIRGenInfo().setTargetAttributes(funcDecl, func, *this);
 
+  // Diagnose calls to this function at the backend level, mirroring
+  // CodeGenModule::SetFunctionAttributes's "dontcall-error"/"dontcall-warn".
+  if (const auto *errorAttr = funcDecl->getAttr<ErrorAttr>()) {
+    if (errorAttr->isError())
+      func->setAttr(cir::CIRDialect::getDontCallErrorAttrName(),
+                    mlir::StringAttr::get(&getMLIRContext(),
+                                          errorAttr->getUserDiagnostic()));
+    else if (errorAttr->isWarning())
+      func->setAttr(cir::CIRDialect::getDontCallWarnAttrName(),
+                    mlir::StringAttr::get(&getMLIRContext(),
+                                          errorAttr->getUserDiagnostic()));
+  }
+
   // Mirrors setLinkageForGV in CodeGenModule::SetFunctionAttributes.
   setLinkageForFunction(*this, func, funcDecl);
 
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 240601f9834e5d..72b52ea06e8b9c 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -18,6 +18,7 @@
 #include "clang/CIR/LowerToLLVM.h"
 #include "clang/CodeGen/BackendUtil.h"
 #include "clang/CodeGen/ModuleLinker.h"
+#include "clang/CodeGenUtils/BackendDiagnosticHandler.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
@@ -90,6 +91,11 @@ class CIRGenConsumer : public clang::ASTConsumer {
 
   std::optional<CIRDiagnosticHandler> MLIRDiagHandler;
 
+  // Translates LLVM backend diagnostics (raised while lowering CIR to LLVM
+  // IR and while running emitBackendOutput) into clang diagnostics; shared
+  // with classic CodeGen's BackendConsumer.
+  BackendDiagnosticConsumer DiagConsumer;
+
 public:
   CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI,
                  CodeGenOptions &CGO, std::unique_ptr<raw_pwrite_stream> OS,
@@ -100,11 +106,13 @@ class CIRGenConsumer : public clang::ASTConsumer {
         Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS),
                                            CI.getCodeGenOpts())),
         FEOptions(CI.getFrontendOpts()), CGO(CGO), LLVMCtx(LLVMCtx),
-        LinkModules(LinkModules) {}
+        LinkModules(LinkModules),
+        DiagConsumer(CI.getDiagnostics(), CI.getCodeGenOpts()) {}
 
   void Initialize(ASTContext &Ctx) override {
     assert(!Context && "initialized multiple times");
     Context = &Ctx;
+    DiagConsumer.setSourceManager(&Ctx.getSourceManager());
     Gen->Initialize(Ctx);
     // Install the MLIR diagnostic handler now that CIRGenerator owns its
     // MLIRContext. Lifetime is tied to this consumer, which spans CIRGen,
@@ -202,6 +210,16 @@ class CIRGenConsumer : public clang::ASTConsumer {
       if (CI.getDiagnostics().hasErrorOccurred())
         return;
 
+      // Route LLVM backend diagnostics (optimization remarks, unsupported
+      // features, inline-asm errors, etc.) through clang diagnostics for
+      // the remainder of the LLVM-emitting pipeline.
+      std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler =
+          LLVMCtx.getDiagnosticHandler();
+      llvm::scope_exit RestoreDiagnosticHandler([&]() {
+        LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
+      });
+      LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler());
+
       std::unique_ptr<llvm::Module> LLVMModule = lowerFromCIRToLLVMIR(
           MlirModule, LLVMCtx, C.getLangOpts().OpenMP, mlirSaveTempsOutFile,
           &CI.getVirtualFileSystem());
@@ -223,8 +241,8 @@ class CIRGenConsumer : public clang::ASTConsumer {
     }
   }
 
-  // TODO: share with BackendConsumer::LinkInModules once OG's CurLinkModule
-  // diagnostic-handler indirection is abstracted behind a callback for CIR.
+  // TODO: share with BackendConsumer::LinkInModules once the rest of the
+  // linking logic (not just diagnostics) is unified.
   bool linkInModules(llvm::Module &M) {
     for (auto &LM : LinkModules) {
       assert(LM.Module && "LinkModule does not actually have a module");
@@ -237,6 +255,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
               F, CGO, CI.getLangOpts(), CI.getTargetOpts(), LM.Internalize);
         }
 
+      DiagConsumer.setCurLinkModule(LM.Module.get());
       bool Err;
       if (LM.Internalize) {
         Err = llvm::Linker::linkModules(
diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt
index b6b1e8b2bbbe2c..4fd0e01f71ed03 100644
--- a/clang/lib/CIR/FrontendAction/CMakeLists.txt
+++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt
@@ -23,6 +23,7 @@ add_clang_library(clangCIRFrontendAction
   clangCIRLoweringCommon
   clangCIRLoweringDirectToLLVM
   clangCodeGen
+  clangCodeGenUtils
   MLIRCIR
   MLIRIR
   )
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 1b1423862ba7bb..d2b762d3f70a38 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2715,7 +2715,9 @@ static bool isHandledDiscardableFuncAttr(mlir::NamedAttribute attr) {
          attr.getName() == CIRDialect::getNoRecurseAttrName() ||
          attr.getName() == CIRDialect::getMustProgressAttrName() ||
          attr.getName() == CIRDialect::getNoBuiltinAttrName() ||
-         attr.getName() == CIRDialect::getSYCLModuleIdAttrName();
+         attr.getName() == CIRDialect::getSYCLModuleIdAttrName() ||
+         attr.getName() == CIRDialect::getDontCallErrorAttrName() ||
+         attr.getName() == CIRDialect::getDontCallWarnAttrName();
 }
 
 /// Lower `cir.func` attributes for an `LLVMFuncOp` or `LLVM::AliasOp`.
@@ -2867,6 +2869,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
         {rewriter.getStringAttr(CIRDialect::getSYCLModuleIdAttrName()),
          moduleId}));
 
+  for (llvm::StringRef dontCallAttr : {CIRDialect::getDontCallErrorAttrName(),
+                                       CIRDialect::getDontCallWarnAttrName()})
+    if (auto diagnostic = op->getAttrOfType<mlir::StringAttr>(dontCallAttr))
+      passthrough.push_back(rewriter.getArrayAttr(
+          {rewriter.getStringAttr(dontCallAttr), diagnostic}));
+
   if (!passthrough.empty())
     fn.setPassthroughAttr(rewriter.getArrayAttr(passthrough));
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
index dd95f09ee77aeb..213f80f0defb01 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
@@ -65,11 +65,34 @@ class CIRDialectLLVMIRTranslationInterface
       if (mlir::failed(amendRISCVNontemporalDomain(op, instructions, attribute,
                                                    moduleTranslation)))
         return mlir::failure();
+    } else if (attribute.getName() == cir::CIRDialect::getSrcLocAttrName()) {
+      if (mlir::failed(amendSrcLoc(instructions, attribute, moduleTranslation)))
+        return mlir::failure();
     }
     return mlir::success();
   }
 
 private:
+  // Convert the raw-encoded clang::SourceLocation into !srcloc, so that
+  // dontcall-error/warn works.
+  mlir::LogicalResult
+  amendSrcLoc(llvm::ArrayRef<llvm::Instruction *> instructions,
+              mlir::NamedAttribute attribute,
+              mlir::LLVM::ModuleTranslation &moduleTranslation) const {
+    auto srcLoc = mlir::dyn_cast<mlir::IntegerAttr>(attribute.getValue());
+    if (!srcLoc)
+      return mlir::failure();
+
+    llvm::LLVMContext &llvmContext = moduleTranslation.getLLVMContext();
+    llvm::MDNode *node = llvm::MDNode::get(
+        llvmContext, llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                         llvm::Type::getInt64Ty(llvmContext),
+                         srcLoc.getValue().getZExtValue())));
+    for (llvm::Instruction *inst : instructions)
+      inst->setMetadata("srcloc", node);
+    return mlir::success();
+  }
+
   mlir::LogicalResult amendRISCVNontemporalDomain(
       mlir::Operation *op, llvm::ArrayRef<llvm::Instruction *> instructions,
       mlir::NamedAttribute attribute,
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index 708658d206bafa..95494d6c27a664 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -12,6 +12,7 @@
 #include "clang/CodeGen/BackendUtil.h"
 #include "clang/CodeGen/CodeGenAction.h"
 #include "clang/CodeGen/ModuleLinker.h"
+#include "clang/CodeGenUtils/BackendDiagnosticHandler.h"
 
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/Support/Timer.h"
@@ -47,21 +48,10 @@ class BackendConsumer : public ASTConsumer {
 
   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;
+  // Translates LLVM backend diagnostics into clang diagnostics; shared with
+  // CIR so that both LLVM-emitting pipelines report backend diagnostics
+  // through the same mechanism.
+  BackendDiagnosticConsumer DiagConsumer;
 
 public:
   BackendConsumer(CompilerInstance &CI, BackendAction Action,
@@ -92,54 +82,9 @@ class BackendConsumer : public ASTConsumer {
   // Links each entry in LinkModules into our module.  Returns true on error.
   bool LinkInModules(llvm::Module *M);
 
-  /// 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 handler for unsupported target intrinsic diagnostic.
-  void UnsupportedTargetIntrinsicDiagHandler(
-      const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &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);
+  /// Create an llvm::DiagnosticHandler that routes LLVM backend diagnostics
+  /// through this consumer's clang diagnostics.
+  std::unique_ptr<llvm::DiagnosticHandler> createDiagnosticHandler();
 };
 
 } // namespace clang
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index 21e58c4aea8c81..d398e79533ae1b 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -67,34 +67,6 @@ llvm::ManagedStatic<llvm::sys::SmartMutex<true>> TimePassesMutex;
 }
 
 namespace clang {
-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 isAnalysisRemarkEnabled(StringRef PassName) const override {
-    return CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(PassName);
-  }
-  bool isMissedOptRemarkEnabled(StringRef PassName) const override {
-    return CodeGenOpts.OptimizationRemarkMissed.patternMatches(PassName);
-  }
-  bool isPassedOptRemarkEnabled(StringRef PassName) const override {
-    return CodeGenOpts.OptimizationRemark.patternMatches(PassName);
-  }
-
-  bool isAnyRemarkEnabled() const override {
-    return CodeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() ||
-           CodeGenOpts.OptimizationRemarkMissed.hasValidPattern() ||
-           CodeGenOpts.OptimizationRemark.hasValidPattern();
-  }
-
-private:
-  const CodeGenOptions &CodeGenOpts;
-  BackendConsumer *BackendCon;
-};
 
 static void reportOptRecordError(Error E, DiagnosticsEngine &Diags,
                                  const CodeGenOptions &CodeGenOpts) {
@@ -126,7 +98,8 @@ BackendConsumer::BackendConsumer(CompilerInstance &CI, BackendAction Action,
       TargetOpts(CI.getTargetOpts()), LangOpts(CI.getLangOpts()),
       AsmOutStream(std::move(OS)), FS(VFS), Action(Action),
       Gen(CreateLLVMCodeGen(CI, InFile, C, CoverageInfo)),
-      LinkModules(std::move(LinkModules)), CurLinkModule(CurLinkModule) {
+      LinkModules(std::move(LinkModules)), DiagConsumer(Diags, CodeGenOpts) {
+  DiagConsumer.setCurLinkModule(CurLinkModule);
   TimerIsEnabled = CodeGenOpts.TimePasses;
   {
     llvm::sys::SmartScopedLock<true> Lock(*TimePassesMutex);
@@ -157,6 +130,7 @@ void BackendConsumer::Initialize(ASTContext &Ctx) {
   assert(!Context && "initialized multiple times");
 
   Context = &Ctx;
+  DiagConsumer.setSourceManager(&Ctx.getSourceManager());
 
   if (TimerIsEnabled)
     LLVMIRGeneration.startTimer();
@@ -216,7 +190,7 @@ bool BackendConsumer::LinkInModules(llvm::Module *M) {
           F, CodeGenOpts, LangOpts, TargetOpts, LM.Internalize);
       }
 
-    CurLinkModule = LM.Module.get();
+    DiagConsumer.setCurLinkModule(LM.Module.get());
     bool Err;
 
     if (LM.Internalize) {
@@ -260,8 +234,7 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
     Ctx.getDiagnosticHandler();
   llvm::scope_exit RestoreDiagnosticHandler(
       [&]() { Ctx.setDiagnosticHandler(std::move(OldDiagnosticHandler)); });
-  Ctx.setDiagnosticHandler(std::make_unique<ClangDiagnosticHandler>(
-      CodeGenOpts, this));
+  Ctx.setDiagnosticHandler(createDiagnosticHandler());
 
   Ctx.setDefaultTargetCPU(TargetOpts.CPU);
   Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
@@ -299,9 +272,7 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
   for (auto &F : getModule()->functions()) {
     if (const Decl *FD = Gen->GetDeclForMangledName(F.getName())) {
       auto Loc = FD->getASTContext().getFullLoc(FD->getLocation());
-      // TODO: use a fast content hash when available.
-      auto NameHash = llvm::hash_value(F.getName());
-      ManglingFullSourceLocs.push_back(std::make_pair(NameHash, Loc));
+      DiagConsumer.addFunctionSourceLocation(F.getName(), Loc);
     }
   }
 
@@ -357,595 +328,10 @@ void BackendConsumer::anchor() { }
 
 } // namespace clang
 
-bool ClangDiagnosticHandler::handleDiagnostics(const DiagnosticInfo &DI) {
-  BackendCon->DiagnosticHandlerImpl(DI);
-  return true;
-}
-
-/// ConvertBackendLocation - Convert a location in a temporary llvm::SourceMgr
-/// buffer to be a valid FullSourceLoc.
-static FullSourceLoc ConvertBackendLocation(const llvm::SMDiagnostic &D,
-                                            SourceManager &CSM) {
-  // Get both the clang and llvm source managers.  The location is relative to
-  // a memory buffer that the LLVM Source Manager is handling, we need to add
-  // a copy to the Clang source manager.
-  const llvm::SourceMgr &LSM = *D.getSourceMgr();
-
-  // We need to copy the underlying LLVM memory buffer because llvm::SourceMgr
-  // already owns its one and clang::SourceManager wants to own its one.
-  const MemoryBuffer *LBuf =
-  LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc()));
-
-  // Create the copy and transfer ownership to clang::SourceManager.
-  // TODO: Avoid copying files into memory.
-  std::unique_ptr<llvm::MemoryBuffer> CBuf =
-      llvm::MemoryBuffer::getMemBufferCopy(LBuf->getBuffer(),
-                                           LBuf->getBufferIdentifier());
-  // FIXME: Keep a file ID map instead of creating new IDs for each location.
-  FileID FID = CSM.createFileID(std::move(CBuf));
-
-  // Translate the offset into the file.
-  unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart();
-  SourceLocation NewLoc =
-  CSM.getLocForStartOfFile(FID).getLocWithOffset(Offset);
-  return FullSourceLoc(NewLoc, CSM);
-}
-
-#define ComputeDiagID(Severity, GroupName, DiagID)                             \
-  do {                                                                         \
-    switch (Severity) {                                                        \
-    case llvm::DS_Error:                                                       \
-      DiagID = diag::err_fe_##GroupName;                                       \
-      break;                                                                   \
-    case llvm::DS_Warning:                                                     \
-      DiagID = diag::warn_fe_##GroupName;                                      \
-      break;                                                                   \
-    case llvm::DS_Remark:                                                      \
-      llvm_unreachable("'remark' severity not expected");                      \
-      break;                                                                   \
-    case llvm::DS_Note:                                                        \
-      DiagID = diag::note_fe_##GroupName;                                      \
-      break;                                                                   \
-    }                                                                          \
-  } while (false)
-
-#define ComputeDiagRemarkID(Severity, GroupName, DiagID)                       \
-  do {                                                                         \
-    switch (Severity) {                                                        \
-    case llvm::DS_Error:                                                       \
-      DiagID = diag::err_fe_##GroupName;                                       \
-      break;                                                                   \
-    case llvm::DS_Warning:                                                     \
-      DiagID = diag::warn_fe_##GroupName;                                      \
-      break;                                                                   \
-    case llvm::DS_Remark:                                                      \
-      DiagID = diag::remark_fe_##GroupName;                                    \
-      break;                                                                   \
-    case llvm::DS_Note:                                                        \
-      DiagID = diag::note_fe_##GroupName;                                      \
-      break;                                                                   \
-    }                                                                          \
-  } while (false)
-
-void BackendConsumer::SrcMgrDiagHandler(const llvm::DiagnosticInfoSrcMgr &DI) {
-  const llvm::SMDiagnostic &D = DI.getSMDiag();
-
-  unsigned DiagID;
-  if (DI.isInlineAsmDiag())
-    ComputeDiagID(DI.getSeverity(), inline_asm, DiagID);
-  else
-    ComputeDiagID(DI.getSeverity(), source_mgr, DiagID);
-
-  // This is for the empty BackendConsumer that uses the clang diagnostic
-  // handler for IR input files.
-  if (!Context) {
-    D.print(nullptr, llvm::errs());
-    Diags.Report(DiagID).AddString("cannot compile inline asm");
-    return;
-  }
-
-  // There are a couple of different kinds of errors we could get here.
-  // First, we re-format the SMDiagnostic in terms of a clang diagnostic.
-
-  // Strip "error: " off the start of the message string.
-  StringRef Message = D.getMessage();
-  (void)Message.consume_front("error: ");
-
-  // If the SMDiagnostic has an inline asm source location, translate it.
-  FullSourceLoc Loc;
-  if (D.getLoc() != SMLoc())
-    Loc = ConvertBackendLocation(D, Context->getSourceManager());
-
-  // If this problem has clang-level source location information, report the
-  // issue in the source with a note showing the instantiated
-  // code.
-  if (DI.isInlineAsmDiag()) {
-    SourceLocation LocCookie =
-        SourceLocation::getFromRawEncoding(DI.getLocCookie());
-    if (LocCookie.isValid()) {
-      Diags.Report(LocCookie, DiagID).AddString(Message);
-
-      if (D.getLoc().isValid()) {
-        DiagnosticBuilder B = Diags.Report(Loc, diag::note_fe_inline_asm_here);
-        // Convert the SMDiagnostic ranges into SourceRange and attach them
-        // to the diagnostic.
-        for (const std::pair<unsigned, unsigned> &Range : D.getRanges()) {
-          unsigned Column = D.getColumnNo();
-          B << SourceRange(Loc.getLocWithOffset(Range.first - Column),
-                           Loc.getLocWithOffset(Range.second - Column));
-        }
-      }
-      return;
-    }
-  }
-
-  // Otherwise, report the backend issue as occurring in the generated .s file.
-  // If Loc is invalid, we still need to report the issue, it just gets no
-  // location info.
-  Diags.Report(Loc, DiagID).AddString(Message);
-}
-
-bool
-BackendConsumer::InlineAsmDiagHandler(const llvm::DiagnosticInfoInlineAsm &D) {
-  unsigned DiagID;
-  ComputeDiagID(D.getSeverity(), inline_asm, DiagID);
-  std::string Message = D.getMsgStr().str();
-
-  // If this problem has clang-level source location information, report the
-  // issue as being a problem in the source with a note showing the instantiated
-  // code.
-  SourceLocation LocCookie =
-      SourceLocation::getFromRawEncoding(D.getLocCookie());
-  if (LocCookie.isValid())
-    Diags.Report(LocCookie, DiagID).AddString(Message);
-  else {
-    // Otherwise, report the backend diagnostic as occurring in the generated
-    // .s file.
-    // If Loc is invalid, we still need to report the diagnostic, it just gets
-    // no location info.
-    FullSourceLoc Loc;
-    Diags.Report(Loc, DiagID).AddString(Message);
-  }
-  // We handled all the possible severities.
-  return true;
-}
-
-bool
-BackendConsumer::StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D) {
-  if (D.getSeverity() != llvm::DS_Warning)
-    // For now, the only support we have for StackSize diagnostic is warning.
-    // We do not know how to format other severities.
-    return false;
-
-  auto Loc = getFunctionSourceLocation(D.getFunction());
-  if (!Loc)
-    return false;
-
-  Diags.Report(*Loc, diag::warn_fe_frame_larger_than)
-      << D.getStackSize() << D.getStackLimit()
-      << llvm::demangle(D.getFunction().getName());
-  return true;
-}
-
-bool BackendConsumer::ResourceLimitDiagHandler(
-    const llvm::DiagnosticInfoResourceLimit &D) {
-  auto Loc = getFunctionSourceLocation(D.getFunction());
-  if (!Loc)
-    return false;
-  unsigned DiagID = diag::err_fe_backend_resource_limit;
-  ComputeDiagID(D.getSeverity(), backend_resource_limit, DiagID);
-
-  Diags.Report(*Loc, DiagID)
-      << D.getResourceName() << D.getResourceSize() << D.getResourceLimit()
-      << llvm::demangle(D.getFunction().getName());
-  return true;
-}
-
-const FullSourceLoc BackendConsumer::getBestLocationFromDebugLoc(
-    const llvm::DiagnosticInfoWithLocationBase &D, bool &BadDebugInfo,
-    StringRef &Filename, unsigned &Line, unsigned &Column) const {
-  SourceManager &SourceMgr = Context->getSourceManager();
-  FileManager &FileMgr = SourceMgr.getFileManager();
-  SourceLocation DILoc;
-
-  if (D.isLocationAvailable()) {
-    D.getLocation(Filename, Line, Column);
-    if (Line > 0) {
-      auto FE = FileMgr.getOptionalFileRef(Filename);
-      if (!FE)
-        FE = FileMgr.getOptionalFileRef(D.getAbsolutePath());
-      if (FE) {
-        // If -gcolumn-info was not used, Column will be 0. This upsets the
-        // source manager, so pass 1 if Column is not set.
-        DILoc = SourceMgr.translateFileLineCol(*FE, Line, Column ? Column : 1);
-      }
-    }
-    BadDebugInfo = DILoc.isInvalid();
-  }
-
-  // If a location isn't available, try to approximate it using the associated
-  // function definition. We use the definition's right brace to differentiate
-  // from diagnostics that genuinely relate to the function itself.
-  FullSourceLoc Loc(DILoc, SourceMgr);
-  if (Loc.isInvalid()) {
-    if (auto MaybeLoc = getFunctionSourceLocation(D.getFunction()))
-      Loc = *MaybeLoc;
-  }
-
-  if (DILoc.isInvalid() && D.isLocationAvailable())
-    // If we were not able to translate the file:line:col information
-    // back to a SourceLocation, at least emit a note stating that
-    // we could not translate this location. This can happen in the
-    // case of #line directives.
-    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
-        << Filename << Line << Column;
-
-  return Loc;
-}
-
-std::optional<FullSourceLoc>
-BackendConsumer::getFunctionSourceLocation(const Function &F) const {
-  auto Hash = llvm::hash_value(F.getName());
-  for (const auto &Pair : ManglingFullSourceLocs) {
-    if (Pair.first == Hash)
-      return Pair.second;
-  }
-  return std::nullopt;
-}
-
-void BackendConsumer::UnsupportedDiagHandler(
-    const llvm::DiagnosticInfoUnsupported &D) {
-  // We only support warnings or errors.
-  assert(D.getSeverity() == llvm::DS_Error ||
-         D.getSeverity() == llvm::DS_Warning);
-
-  StringRef Filename;
-  unsigned Line, Column;
-  bool BadDebugInfo = false;
-  FullSourceLoc Loc;
-  std::string Msg;
-  raw_string_ostream MsgStream(Msg);
-
-  // Context will be nullptr for IR input files, we will construct the diag
-  // message from llvm::DiagnosticInfoUnsupported.
-  if (Context != nullptr) {
-    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
-    MsgStream << D.getMessage();
-  } else {
-    DiagnosticPrinterRawOStream DP(MsgStream);
-    D.print(DP);
-  }
-
-  auto DiagType = D.getSeverity() == llvm::DS_Error
-                      ? diag::err_fe_backend_unsupported
-                      : diag::warn_fe_backend_unsupported;
-  Diags.Report(Loc, DiagType) << Msg;
-
-  if (BadDebugInfo)
-    // If we were not able to translate the file:line:col information
-    // back to a SourceLocation, at least emit a note stating that
-    // we could not translate this location. This can happen in the
-    // case of #line directives.
-    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
-        << Filename << Line << Column;
-}
-
-void BackendConsumer::UnsupportedTargetIntrinsicDiagHandler(
-    const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D) {
-  assert(D.getSeverity() == llvm::DS_Error &&
-         "unsupported target intrinsic diagnostic should be an error");
-
-  StringRef Filename;
-  unsigned Line, Column;
-  bool BadDebugInfo = false;
-  FullSourceLoc Loc;
-  std::string Msg;
-  raw_string_ostream MsgStream(Msg);
-
-  // Context will be nullptr for IR input files, so construct the diagnostic
-  // message from llvm::DiagnosticInfoUnsupportedTargetIntrinsic.
-  if (Context != nullptr) {
-    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
-    MsgStream << D.getMessage();
-  } else {
-    DiagnosticPrinterRawOStream DP(MsgStream);
-    D.print(DP);
-  }
-
-  Diags.Report(Loc, diag::err_fe_backend_unsupported) << Msg;
-
-  if (BadDebugInfo) {
-    // If we were not able to translate the file:line:col information
-    // back to a SourceLocation, at least emit a note stating that
-    // we could not translate this location. This can happen in the
-    // case of #line directives.
-    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
-        << Filename << Line << Column;
-  }
-}
-
-void BackendConsumer::EmitOptimizationMessage(
-    const llvm::DiagnosticInfoOptimizationBase &D, unsigned DiagID) {
-  // We only support warnings and remarks.
-  assert(D.getSeverity() == llvm::DS_Remark ||
-         D.getSeverity() == llvm::DS_Warning);
-
-  StringRef Filename;
-  unsigned Line, Column;
-  bool BadDebugInfo = false;
-  FullSourceLoc Loc;
-  std::string Msg;
-  raw_string_ostream MsgStream(Msg);
-
-  // Context will be nullptr for IR input files, we will construct the remark
-  // message from llvm::DiagnosticInfoOptimizationBase.
-  if (Context != nullptr) {
-    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
-    MsgStream << D.getMsg();
-  } else {
-    DiagnosticPrinterRawOStream DP(MsgStream);
-    D.print(DP);
-  }
-
-  if (D.getHotness())
-    MsgStream << " (hotness: " << *D.getHotness() << ")";
-
-  Diags.Report(Loc, DiagID) << AddFlagValue(D.getPassName()) << Msg;
-
-  if (BadDebugInfo)
-    // If we were not able to translate the file:line:col information
-    // back to a SourceLocation, at least emit a note stating that
-    // we could not translate this location. This can happen in the
-    // case of #line directives.
-    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
-        << Filename << Line << Column;
-}
-
-void BackendConsumer::OptimizationRemarkHandler(
-    const llvm::DiagnosticInfoOptimizationBase &D) {
-  // Without hotness information, don't show noisy remarks.
-  if (D.isVerbose() && !D.getHotness())
-    return;
-
-  if (D.isPassed()) {
-    // Optimization remarks are active only if the -Rpass flag has a regular
-    // expression that matches the name of the pass name in \p D.
-    if (CodeGenOpts.OptimizationRemark.patternMatches(D.getPassName()))
-      EmitOptimizationMessage(D, diag::remark_fe_backend_optimization_remark);
-  } else if (D.isMissed()) {
-    // Missed optimization remarks are active only if the -Rpass-missed
-    // flag has a regular expression that matches the name of the pass
-    // name in \p D.
-    if (CodeGenOpts.OptimizationRemarkMissed.patternMatches(D.getPassName()))
-      EmitOptimizationMessage(
-          D, diag::remark_fe_backend_optimization_remark_missed);
-  } else {
-    assert(D.isAnalysis() && "Unknown remark type");
-
-    bool ShouldAlwaysPrint = false;
-    if (auto *ORA = dyn_cast<llvm::OptimizationRemarkAnalysis>(&D))
-      ShouldAlwaysPrint = ORA->shouldAlwaysPrint();
-
-    if (ShouldAlwaysPrint ||
-        CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
-      EmitOptimizationMessage(
-          D, diag::remark_fe_backend_optimization_remark_analysis);
-  }
-}
-
-void BackendConsumer::OptimizationRemarkHandler(
-    const llvm::OptimizationRemarkAnalysisFPCommute &D) {
-  // Optimization analysis remarks are active if the pass name is set to
-  // llvm::DiagnosticInfo::AlwasyPrint or if the -Rpass-analysis flag has a
-  // regular expression that matches the name of the pass name in \p D.
-
-  if (D.shouldAlwaysPrint() ||
-      CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
-    EmitOptimizationMessage(
-        D, diag::remark_fe_backend_optimization_remark_analysis_fpcommute);
-}
-
-void BackendConsumer::OptimizationRemarkHandler(
-    const llvm::OptimizationRemarkAnalysisAliasing &D) {
-  // Optimization analysis remarks are active if the pass name is set to
-  // llvm::DiagnosticInfo::AlwasyPrint or if the -Rpass-analysis flag has a
-  // regular expression that matches the name of the pass name in \p D.
-
-  if (D.shouldAlwaysPrint() ||
-      CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
-    EmitOptimizationMessage(
-        D, diag::remark_fe_backend_optimization_remark_analysis_aliasing);
-}
-
-void BackendConsumer::OptimizationFailureHandler(
-    const llvm::DiagnosticInfoOptimizationFailure &D) {
-  EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure);
-}
-
-void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
-  SourceLocation LocCookie =
-      SourceLocation::getFromRawEncoding(D.getLocCookie());
-
-  // FIXME: we can't yet diagnose indirect calls. When/if we can, we
-  // should instead assert that LocCookie.isValid().
-  if (!LocCookie.isValid())
-    return;
-
-  Diags.Report(LocCookie, D.getSeverity() == DiagnosticSeverity::DS_Error
-                              ? diag::err_fe_backend_error_attr
-                              : diag::warn_fe_backend_warning_attr)
-      << llvm::demangle(D.getFunctionName()) << D.getNote();
-
-  if (!CodeGenOpts.ShowInliningChain)
-    return;
-
-  auto EmitNote = [&](SourceLocation Loc, StringRef FuncName, bool IsFirst) {
-    if (!Loc.isValid())
-      Loc = LocCookie;
-    unsigned DiagID =
-        IsFirst ? diag::note_fe_backend_in : diag::note_fe_backend_inlined;
-    Diags.Report(Loc, DiagID) << llvm::demangle(FuncName.str());
-  };
-
-  // Try debug info first for accurate source locations.
-  if (!D.getDebugInlineChain().empty()) {
-    SourceManager &SM = Context->getSourceManager();
-    FileManager &FM = SM.getFileManager();
-    for (const auto &[I, Info] : llvm::enumerate(D.getDebugInlineChain())) {
-      SourceLocation Loc;
-      if (Info.Line > 0)
-        if (auto FE = FM.getOptionalFileRef(Info.Filename))
-          Loc = SM.translateFileLineCol(*FE, Info.Line,
-                                        Info.Column ? Info.Column : 1);
-      EmitNote(Loc, Info.FuncName, I == 0);
-    }
-    return;
-  }
-
-  // Fall back to heuristic (srcloc metadata) when debug info is unavailable.
-  auto InliningDecisions = D.getInliningDecisions();
-  if (InliningDecisions.empty())
-    return;
-
-  for (const auto &[I, Entry] : llvm::enumerate(InliningDecisions)) {
-    SourceLocation Loc =
-        I == 0 ? LocCookie : SourceLocation::getFromRawEncoding(Entry.second);
-    EmitNote(Loc, Entry.first, I == 0);
-  }
-
-  // Suggest enabling debug info (at least -gline-directives-only) for more
-  // accurate locations.
-  Diags.Report(LocCookie, diag::note_fe_backend_inlining_debug_info);
-}
-
-void BackendConsumer::MisExpectDiagHandler(
-    const llvm::DiagnosticInfoMisExpect &D) {
-  StringRef Filename;
-  unsigned Line, Column;
-  bool BadDebugInfo = false;
-  FullSourceLoc Loc =
-      getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
-
-  Diags.Report(Loc, diag::warn_profile_data_misexpect) << D.getMsg().str();
-
-  if (BadDebugInfo)
-    // If we were not able to translate the file:line:col information
-    // back to a SourceLocation, at least emit a note stating that
-    // we could not translate this location. This can happen in the
-    // case of #line directives.
-    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
-        << Filename << Line << Column;
-}
-
-/// This function is invoked when the backend needs
-/// to report something to the user.
-void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
-  unsigned DiagID = diag::err_fe_inline_asm;
-  llvm::DiagnosticSeverity Severity = DI.getSeverity();
-  // Get the diagnostic ID based.
-  switch (DI.getKind()) {
-  case llvm::DK_InlineAsm:
-    if (InlineAsmDiagHandler(cast<DiagnosticInfoInlineAsm>(DI)))
-      return;
-    ComputeDiagID(Severity, inline_asm, DiagID);
-    break;
-  case llvm::DK_SrcMgr:
-    SrcMgrDiagHandler(cast<DiagnosticInfoSrcMgr>(DI));
-    return;
-  case llvm::DK_StackSize:
-    if (StackSizeDiagHandler(cast<DiagnosticInfoStackSize>(DI)))
-      return;
-    ComputeDiagID(Severity, backend_frame_larger_than, DiagID);
-    break;
-  case llvm::DK_ResourceLimit:
-    if (ResourceLimitDiagHandler(cast<DiagnosticInfoResourceLimit>(DI)))
-      return;
-    ComputeDiagID(Severity, backend_resource_limit, DiagID);
-    break;
-  case DK_Linker:
-    ComputeDiagID(Severity, linking_module, DiagID);
-    break;
-  case llvm::DK_OptimizationRemark:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<OptimizationRemark>(DI));
-    return;
-  case llvm::DK_OptimizationRemarkMissed:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<OptimizationRemarkMissed>(DI));
-    return;
-  case llvm::DK_OptimizationRemarkAnalysis:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysis>(DI));
-    return;
-  case llvm::DK_OptimizationRemarkAnalysisFPCommute:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysisFPCommute>(DI));
-    return;
-  case llvm::DK_OptimizationRemarkAnalysisAliasing:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysisAliasing>(DI));
-    return;
-  case llvm::DK_MachineOptimizationRemark:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<MachineOptimizationRemark>(DI));
-    return;
-  case llvm::DK_MachineOptimizationRemarkMissed:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<MachineOptimizationRemarkMissed>(DI));
-    return;
-  case llvm::DK_MachineOptimizationRemarkAnalysis:
-    // Optimization remarks are always handled completely by this
-    // handler. There is no generic way of emitting them.
-    OptimizationRemarkHandler(cast<MachineOptimizationRemarkAnalysis>(DI));
-    return;
-  case llvm::DK_OptimizationFailure:
-    // Optimization failures are always handled completely by this
-    // handler.
-    OptimizationFailureHandler(cast<DiagnosticInfoOptimizationFailure>(DI));
-    return;
-  case llvm::DK_Unsupported:
-    UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
-    return;
-  case llvm::DK_UnsupportedTargetIntrinsic:
-    UnsupportedTargetIntrinsicDiagHandler(
-        cast<DiagnosticInfoUnsupportedTargetIntrinsic>(DI));
-    return;
-  case llvm::DK_DontCall:
-    DontCallDiagHandler(cast<DiagnosticInfoDontCall>(DI));
-    return;
-  case llvm::DK_MisExpect:
-    MisExpectDiagHandler(cast<DiagnosticInfoMisExpect>(DI));
-    return;
-  default:
-    // Plugin IDs are not bound to any value as they are set dynamically.
-    ComputeDiagRemarkID(Severity, backend_plugin, DiagID);
-    break;
-  }
-  std::string MsgStorage;
-  {
-    raw_string_ostream Stream(MsgStorage);
-    DiagnosticPrinterRawOStream DP(Stream);
-    DI.print(DP);
-  }
-
-  if (DI.getKind() == DK_Linker) {
-    assert(CurLinkModule && "CurLinkModule must be set for linker diagnostics");
-    Diags.Report(DiagID) << CurLinkModule->getModuleIdentifier() << MsgStorage;
-    return;
-  }
-
-  // Report the backend message using the usual diagnostic mechanism.
-  FullSourceLoc Loc;
-  Diags.Report(Loc, DiagID).AddString(MsgStorage);
+std::unique_ptr<llvm::DiagnosticHandler>
+BackendConsumer::createDiagnosticHandler() {
+  return DiagConsumer.createDiagnosticHandler();
 }
-#undef ComputeDiagID
 
 CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext)
     : Act(_Act), VMContext(_VMContext ? _VMContext : new LLVMContext),
@@ -1220,8 +606,7 @@ void CodeGenAction::ExecuteAction() {
   // PR44896: Force DiscardValueNames as false. DiscardValueNames cannot be
   // true here because the valued names are needed for reading textual IR.
   Ctx.setDiscardValueNames(false);
-  Ctx.setDiagnosticHandler(
-      std::make_unique<ClangDiagnosticHandler>(CodeGenOpts, &Result));
+  Ctx.setDiagnosticHandler(Result.createDiagnosticHandler());
 
   Ctx.setDefaultTargetCPU(TargetOpts.CPU);
   Ctx.setDefaultTargetFeatures(llvm::join(TargetOpts.Features, ","));
diff --git a/clang/lib/CodeGenUtils/BackendDiagnosticHandler.cpp b/clang/lib/CodeGenUtils/BackendDiagnosticHandler.cpp
new file mode 100644
index 00000000000000..da7d111d76d65e
--- /dev/null
+++ b/clang/lib/CodeGenUtils/BackendDiagnosticHandler.cpp
@@ -0,0 +1,660 @@
+//===--- BackendDiagnosticHandler.cpp - Shared Backend Diagnostics ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/CodeGenUtils/BackendDiagnosticHandler.h"
+#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+/// Forwards llvm::DiagnosticHandler callbacks into a BackendDiagnosticConsumer.
+class BackendDiagnosticHandlerImpl final : public DiagnosticHandler {
+public:
+  BackendDiagnosticHandlerImpl(const CodeGenOptions &CGOpts,
+                               BackendDiagnosticConsumer *Consumer)
+      : CodeGenOpts(CGOpts), Consumer(Consumer) {}
+
+  bool handleDiagnostics(const DiagnosticInfo &DI) override {
+    Consumer->handleDiagnostics(DI);
+    return true;
+  }
+
+  bool isAnalysisRemarkEnabled(StringRef PassName) const override {
+    return CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(PassName);
+  }
+  bool isMissedOptRemarkEnabled(StringRef PassName) const override {
+    return CodeGenOpts.OptimizationRemarkMissed.patternMatches(PassName);
+  }
+  bool isPassedOptRemarkEnabled(StringRef PassName) const override {
+    return CodeGenOpts.OptimizationRemark.patternMatches(PassName);
+  }
+
+  bool isAnyRemarkEnabled() const override {
+    return CodeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() ||
+           CodeGenOpts.OptimizationRemarkMissed.hasValidPattern() ||
+           CodeGenOpts.OptimizationRemark.hasValidPattern();
+  }
+
+private:
+  const CodeGenOptions &CodeGenOpts;
+  BackendDiagnosticConsumer *Consumer;
+};
+
+/// Convert a location in a temporary llvm::SourceMgr buffer to be a valid
+/// FullSourceLoc.
+FullSourceLoc ConvertBackendLocation(const llvm::SMDiagnostic &D,
+                                     SourceManager &CSM) {
+  // Get both the clang and llvm source managers. The location is relative to
+  // a memory buffer that the LLVM Source Manager is handling, we need to add
+  // a copy to the Clang source manager.
+  const llvm::SourceMgr &LSM = *D.getSourceMgr();
+
+  // We need to copy the underlying LLVM memory buffer because llvm::SourceMgr
+  // already owns its one and clang::SourceManager wants to own its one.
+  const MemoryBuffer *LBuf =
+      LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc()));
+
+  // Create the copy and transfer ownership to clang::SourceManager.
+  // TODO: Avoid copying files into memory.
+  std::unique_ptr<llvm::MemoryBuffer> CBuf =
+      llvm::MemoryBuffer::getMemBufferCopy(LBuf->getBuffer(),
+                                           LBuf->getBufferIdentifier());
+  // FIXME: Keep a file ID map instead of creating new IDs for each location.
+  FileID FID = CSM.createFileID(std::move(CBuf));
+
+  // Translate the offset into the file.
+  unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart();
+  SourceLocation NewLoc =
+      CSM.getLocForStartOfFile(FID).getLocWithOffset(Offset);
+  return FullSourceLoc(NewLoc, CSM);
+}
+
+} // namespace
+
+#define ComputeDiagID(Severity, GroupName, DiagID)                             \
+  do {                                                                         \
+    switch (Severity) {                                                        \
+    case llvm::DS_Error:                                                       \
+      DiagID = diag::err_fe_##GroupName;                                       \
+      break;                                                                   \
+    case llvm::DS_Warning:                                                     \
+      DiagID = diag::warn_fe_##GroupName;                                      \
+      break;                                                                   \
+    case llvm::DS_Remark:                                                      \
+      llvm_unreachable("'remark' severity not expected");                      \
+      break;                                                                   \
+    case llvm::DS_Note:                                                        \
+      DiagID = diag::note_fe_##GroupName;                                      \
+      break;                                                                   \
+    }                                                                          \
+  } while (false)
+
+#define ComputeDiagRemarkID(Severity, GroupName, DiagID)                       \
+  do {                                                                         \
+    switch (Severity) {                                                        \
+    case llvm::DS_Error:                                                       \
+      DiagID = diag::err_fe_##GroupName;                                       \
+      break;                                                                   \
+    case llvm::DS_Warning:                                                     \
+      DiagID = diag::warn_fe_##GroupName;                                      \
+      break;                                                                   \
+    case llvm::DS_Remark:                                                      \
+      DiagID = diag::remark_fe_##GroupName;                                    \
+      break;                                                                   \
+    case llvm::DS_Note:                                                        \
+      DiagID = diag::note_fe_##GroupName;                                      \
+      break;                                                                   \
+    }                                                                          \
+  } while (false)
+
+std::unique_ptr<llvm::DiagnosticHandler>
+BackendDiagnosticConsumer::createDiagnosticHandler() {
+  return std::make_unique<BackendDiagnosticHandlerImpl>(CodeGenOpts, this);
+}
+
+void BackendDiagnosticConsumer::addFunctionSourceLocation(StringRef MangledName,
+                                                          FullSourceLoc Loc) {
+  // TODO: use a fast content hash when available.
+  auto NameHash = llvm::hash_value(MangledName);
+  ManglingFullSourceLocs.push_back(std::make_pair(NameHash, Loc));
+}
+
+std::optional<FullSourceLoc>
+BackendDiagnosticConsumer::getFunctionSourceLocation(const Function &F) const {
+  auto Hash = llvm::hash_value(F.getName());
+  for (const auto &Pair : ManglingFullSourceLocs) {
+    if (Pair.first == Hash)
+      return Pair.second;
+  }
+  return std::nullopt;
+}
+
+FullSourceLoc BackendDiagnosticConsumer::getBestLocationFromDebugLoc(
+    const llvm::DiagnosticInfoWithLocationBase &D, bool &BadDebugInfo,
+    StringRef &Filename, unsigned &Line, unsigned &Column) const {
+  SourceManager &SourceMgr = *SM;
+  FileManager &FileMgr = SourceMgr.getFileManager();
+  SourceLocation DILoc;
+
+  if (D.isLocationAvailable()) {
+    D.getLocation(Filename, Line, Column);
+    if (Line > 0) {
+      auto FE = FileMgr.getOptionalFileRef(Filename);
+      if (!FE)
+        FE = FileMgr.getOptionalFileRef(D.getAbsolutePath());
+      if (FE) {
+        // If -gcolumn-info was not used, Column will be 0. This upsets the
+        // source manager, so pass 1 if Column is not set.
+        DILoc = SourceMgr.translateFileLineCol(*FE, Line, Column ? Column : 1);
+      }
+    }
+    BadDebugInfo = DILoc.isInvalid();
+  }
+
+  // If a location isn't available, try to approximate it using the associated
+  // function definition. We use the definition's right brace to differentiate
+  // from diagnostics that genuinely relate to the function itself.
+  FullSourceLoc Loc(DILoc, SourceMgr);
+  if (Loc.isInvalid()) {
+    if (auto MaybeLoc = getFunctionSourceLocation(D.getFunction()))
+      Loc = *MaybeLoc;
+  }
+
+  if (DILoc.isInvalid() && D.isLocationAvailable())
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+
+  return Loc;
+}
+
+void BackendDiagnosticConsumer::SrcMgrDiagHandler(
+    const llvm::DiagnosticInfoSrcMgr &DI) {
+  const llvm::SMDiagnostic &D = DI.getSMDiag();
+
+  unsigned DiagID;
+  if (DI.isInlineAsmDiag())
+    ComputeDiagID(DI.getSeverity(), inline_asm, DiagID);
+  else
+    ComputeDiagID(DI.getSeverity(), source_mgr, DiagID);
+
+  // This is for the empty consumer that uses the clang diagnostic
+  // handler for IR input files.
+  if (!SM) {
+    D.print(nullptr, llvm::errs());
+    Diags.Report(DiagID).AddString("cannot compile inline asm");
+    return;
+  }
+
+  // There are a couple of different kinds of errors we could get here.
+  // First, we re-format the SMDiagnostic in terms of a clang diagnostic.
+
+  // Strip "error: " off the start of the message string.
+  StringRef Message = D.getMessage();
+  (void)Message.consume_front("error: ");
+
+  // If the SMDiagnostic has an inline asm source location, translate it.
+  FullSourceLoc Loc;
+  if (D.getLoc() != SMLoc())
+    Loc = ConvertBackendLocation(D, *SM);
+
+  // If this problem has clang-level source location information, report the
+  // issue in the source with a note showing the instantiated
+  // code.
+  if (DI.isInlineAsmDiag()) {
+    SourceLocation LocCookie =
+        SourceLocation::getFromRawEncoding(DI.getLocCookie());
+    if (LocCookie.isValid()) {
+      Diags.Report(LocCookie, DiagID).AddString(Message);
+
+      if (D.getLoc().isValid()) {
+        DiagnosticBuilder B = Diags.Report(Loc, diag::note_fe_inline_asm_here);
+        // Convert the SMDiagnostic ranges into SourceRange and attach them
+        // to the diagnostic.
+        for (const std::pair<unsigned, unsigned> &Range : D.getRanges()) {
+          unsigned Column = D.getColumnNo();
+          B << SourceRange(Loc.getLocWithOffset(Range.first - Column),
+                           Loc.getLocWithOffset(Range.second - Column));
+        }
+      }
+      return;
+    }
+  }
+
+  // Otherwise, report the backend issue as occurring in the generated .s file.
+  // If Loc is invalid, we still need to report the issue, it just gets no
+  // location info.
+  Diags.Report(Loc, DiagID).AddString(Message);
+}
+
+bool BackendDiagnosticConsumer::InlineAsmDiagHandler(
+    const llvm::DiagnosticInfoInlineAsm &D) {
+  unsigned DiagID;
+  ComputeDiagID(D.getSeverity(), inline_asm, DiagID);
+  std::string Message = D.getMsgStr().str();
+
+  // If this problem has clang-level source location information, report the
+  // issue as being a problem in the source with a note showing the instantiated
+  // code.
+  SourceLocation LocCookie =
+      SourceLocation::getFromRawEncoding(D.getLocCookie());
+  if (LocCookie.isValid())
+    Diags.Report(LocCookie, DiagID).AddString(Message);
+  else {
+    // Otherwise, report the backend diagnostic as occurring in the generated
+    // .s file.
+    // If Loc is invalid, we still need to report the diagnostic, it just gets
+    // no location info.
+    FullSourceLoc Loc;
+    Diags.Report(Loc, DiagID).AddString(Message);
+  }
+  // We handled all the possible severities.
+  return true;
+}
+
+bool BackendDiagnosticConsumer::StackSizeDiagHandler(
+    const llvm::DiagnosticInfoStackSize &D) {
+  if (D.getSeverity() != llvm::DS_Warning)
+    // For now, the only support we have for StackSize diagnostic is warning.
+    // We do not know how to format other severities.
+    return false;
+
+  auto Loc = getFunctionSourceLocation(D.getFunction());
+  if (!Loc)
+    return false;
+
+  Diags.Report(*Loc, diag::warn_fe_frame_larger_than)
+      << D.getStackSize() << D.getStackLimit()
+      << llvm::demangle(D.getFunction().getName());
+  return true;
+}
+
+bool BackendDiagnosticConsumer::ResourceLimitDiagHandler(
+    const llvm::DiagnosticInfoResourceLimit &D) {
+  auto Loc = getFunctionSourceLocation(D.getFunction());
+  if (!Loc)
+    return false;
+  unsigned DiagID = diag::err_fe_backend_resource_limit;
+  ComputeDiagID(D.getSeverity(), backend_resource_limit, DiagID);
+
+  Diags.Report(*Loc, DiagID)
+      << D.getResourceName() << D.getResourceSize() << D.getResourceLimit()
+      << llvm::demangle(D.getFunction().getName());
+  return true;
+}
+
+void BackendDiagnosticConsumer::UnsupportedDiagHandler(
+    const llvm::DiagnosticInfoUnsupported &D) {
+  // We only support warnings or errors.
+  assert(D.getSeverity() == llvm::DS_Error ||
+         D.getSeverity() == llvm::DS_Warning);
+
+  StringRef Filename;
+  unsigned Line, Column;
+  bool BadDebugInfo = false;
+  FullSourceLoc Loc;
+  std::string Msg;
+  raw_string_ostream MsgStream(Msg);
+
+  // SM will be null for IR input files, we will construct the diag
+  // message from llvm::DiagnosticInfoUnsupported.
+  if (SM != nullptr) {
+    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
+    MsgStream << D.getMessage();
+  } else {
+    DiagnosticPrinterRawOStream DP(MsgStream);
+    D.print(DP);
+  }
+
+  auto DiagType = D.getSeverity() == llvm::DS_Error
+                      ? diag::err_fe_backend_unsupported
+                      : diag::warn_fe_backend_unsupported;
+  Diags.Report(Loc, DiagType) << Msg;
+
+  if (BadDebugInfo)
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+}
+
+void BackendDiagnosticConsumer::UnsupportedTargetIntrinsicDiagHandler(
+    const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D) {
+  assert(D.getSeverity() == llvm::DS_Error &&
+         "unsupported target intrinsic diagnostic should be an error");
+
+  StringRef Filename;
+  unsigned Line, Column;
+  bool BadDebugInfo = false;
+  FullSourceLoc Loc;
+  std::string Msg;
+  raw_string_ostream MsgStream(Msg);
+
+  // SM will be null for IR input files, so construct the diagnostic
+  // message from llvm::DiagnosticInfoUnsupportedTargetIntrinsic.
+  if (SM != nullptr) {
+    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
+    MsgStream << D.getMessage();
+  } else {
+    DiagnosticPrinterRawOStream DP(MsgStream);
+    D.print(DP);
+  }
+
+  Diags.Report(Loc, diag::err_fe_backend_unsupported) << Msg;
+
+  if (BadDebugInfo) {
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+  }
+}
+
+void BackendDiagnosticConsumer::EmitOptimizationMessage(
+    const llvm::DiagnosticInfoOptimizationBase &D, unsigned DiagID) {
+  // We only support warnings and remarks.
+  assert(D.getSeverity() == llvm::DS_Remark ||
+         D.getSeverity() == llvm::DS_Warning);
+
+  StringRef Filename;
+  unsigned Line, Column;
+  bool BadDebugInfo = false;
+  FullSourceLoc Loc;
+  std::string Msg;
+  raw_string_ostream MsgStream(Msg);
+
+  // SM will be null for IR input files, we will construct the remark
+  // message from llvm::DiagnosticInfoOptimizationBase.
+  if (SM != nullptr) {
+    Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
+    MsgStream << D.getMsg();
+  } else {
+    DiagnosticPrinterRawOStream DP(MsgStream);
+    D.print(DP);
+  }
+
+  if (D.getHotness())
+    MsgStream << " (hotness: " << *D.getHotness() << ")";
+
+  Diags.Report(Loc, DiagID) << AddFlagValue(D.getPassName()) << Msg;
+
+  if (BadDebugInfo)
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+}
+
+void BackendDiagnosticConsumer::OptimizationRemarkHandler(
+    const llvm::DiagnosticInfoOptimizationBase &D) {
+  // Without hotness information, don't show noisy remarks.
+  if (D.isVerbose() && !D.getHotness())
+    return;
+
+  if (D.isPassed()) {
+    // Optimization remarks are active only if the -Rpass flag has a regular
+    // expression that matches the name of the pass name in \p D.
+    if (CodeGenOpts.OptimizationRemark.patternMatches(D.getPassName()))
+      EmitOptimizationMessage(D, diag::remark_fe_backend_optimization_remark);
+  } else if (D.isMissed()) {
+    // Missed optimization remarks are active only if the -Rpass-missed
+    // flag has a regular expression that matches the name of the pass
+    // name in \p D.
+    if (CodeGenOpts.OptimizationRemarkMissed.patternMatches(D.getPassName()))
+      EmitOptimizationMessage(
+          D, diag::remark_fe_backend_optimization_remark_missed);
+  } else {
+    assert(D.isAnalysis() && "Unknown remark type");
+
+    bool ShouldAlwaysPrint = false;
+    if (auto *ORA = dyn_cast<llvm::OptimizationRemarkAnalysis>(&D))
+      ShouldAlwaysPrint = ORA->shouldAlwaysPrint();
+
+    if (ShouldAlwaysPrint ||
+        CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
+      EmitOptimizationMessage(
+          D, diag::remark_fe_backend_optimization_remark_analysis);
+  }
+}
+
+void BackendDiagnosticConsumer::OptimizationRemarkHandler(
+    const llvm::OptimizationRemarkAnalysisFPCommute &D) {
+  // Optimization analysis remarks are active if the pass name is set to
+  // llvm::DiagnosticInfo::AlwasyPrint or if the -Rpass-analysis flag has a
+  // regular expression that matches the name of the pass name in \p D.
+
+  if (D.shouldAlwaysPrint() ||
+      CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
+    EmitOptimizationMessage(
+        D, diag::remark_fe_backend_optimization_remark_analysis_fpcommute);
+}
+
+void BackendDiagnosticConsumer::OptimizationRemarkHandler(
+    const llvm::OptimizationRemarkAnalysisAliasing &D) {
+  // Optimization analysis remarks are active if the pass name is set to
+  // llvm::DiagnosticInfo::AlwasyPrint or if the -Rpass-analysis flag has a
+  // regular expression that matches the name of the pass name in \p D.
+
+  if (D.shouldAlwaysPrint() ||
+      CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(D.getPassName()))
+    EmitOptimizationMessage(
+        D, diag::remark_fe_backend_optimization_remark_analysis_aliasing);
+}
+
+void BackendDiagnosticConsumer::OptimizationFailureHandler(
+    const llvm::DiagnosticInfoOptimizationFailure &D) {
+  EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure);
+}
+
+void BackendDiagnosticConsumer::DontCallDiagHandler(
+    const DiagnosticInfoDontCall &D) {
+  SourceLocation LocCookie =
+      SourceLocation::getFromRawEncoding(D.getLocCookie());
+
+  // FIXME: we can't yet diagnose indirect calls. When/if we can, we
+  // should instead assert that LocCookie.isValid().
+  if (!LocCookie.isValid())
+    return;
+
+  Diags.Report(LocCookie, D.getSeverity() == DiagnosticSeverity::DS_Error
+                              ? diag::err_fe_backend_error_attr
+                              : diag::warn_fe_backend_warning_attr)
+      << llvm::demangle(D.getFunctionName()) << D.getNote();
+
+  if (!CodeGenOpts.ShowInliningChain)
+    return;
+
+  auto EmitNote = [&](SourceLocation Loc, StringRef FuncName, bool IsFirst) {
+    if (!Loc.isValid())
+      Loc = LocCookie;
+    unsigned DiagID =
+        IsFirst ? diag::note_fe_backend_in : diag::note_fe_backend_inlined;
+    Diags.Report(Loc, DiagID) << llvm::demangle(FuncName.str());
+  };
+
+  // Try debug info first for accurate source locations.
+  if (!D.getDebugInlineChain().empty()) {
+    SourceManager &SrcMgr = *SM;
+    FileManager &FM = SrcMgr.getFileManager();
+    for (const auto &[I, Info] : llvm::enumerate(D.getDebugInlineChain())) {
+      SourceLocation Loc;
+      if (Info.Line > 0)
+        if (auto FE = FM.getOptionalFileRef(Info.Filename))
+          Loc = SrcMgr.translateFileLineCol(*FE, Info.Line,
+                                            Info.Column ? Info.Column : 1);
+      EmitNote(Loc, Info.FuncName, I == 0);
+    }
+    return;
+  }
+
+  // Fall back to heuristic (srcloc metadata) when debug info is unavailable.
+  auto InliningDecisions = D.getInliningDecisions();
+  if (InliningDecisions.empty())
+    return;
+
+  for (const auto &[I, Entry] : llvm::enumerate(InliningDecisions)) {
+    SourceLocation Loc =
+        I == 0 ? LocCookie : SourceLocation::getFromRawEncoding(Entry.second);
+    EmitNote(Loc, Entry.first, I == 0);
+  }
+
+  // Suggest enabling debug info (at least -gline-directives-only) for more
+  // accurate locations.
+  Diags.Report(LocCookie, diag::note_fe_backend_inlining_debug_info);
+}
+
+void BackendDiagnosticConsumer::MisExpectDiagHandler(
+    const llvm::DiagnosticInfoMisExpect &D) {
+  StringRef Filename;
+  unsigned Line, Column;
+  bool BadDebugInfo = false;
+  FullSourceLoc Loc =
+      getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column);
+
+  Diags.Report(Loc, diag::warn_profile_data_misexpect) << D.getMsg().str();
+
+  if (BadDebugInfo)
+    // If we were not able to translate the file:line:col information
+    // back to a SourceLocation, at least emit a note stating that
+    // we could not translate this location. This can happen in the
+    // case of #line directives.
+    Diags.Report(Loc, diag::note_fe_backend_invalid_loc)
+        << Filename << Line << Column;
+}
+
+void BackendDiagnosticConsumer::handleDiagnostics(const DiagnosticInfo &DI) {
+  unsigned DiagID = diag::err_fe_inline_asm;
+  llvm::DiagnosticSeverity Severity = DI.getSeverity();
+  // Get the diagnostic ID based.
+  switch (DI.getKind()) {
+  case llvm::DK_InlineAsm:
+    if (InlineAsmDiagHandler(cast<DiagnosticInfoInlineAsm>(DI)))
+      return;
+    ComputeDiagID(Severity, inline_asm, DiagID);
+    break;
+  case llvm::DK_SrcMgr:
+    SrcMgrDiagHandler(cast<DiagnosticInfoSrcMgr>(DI));
+    return;
+  case llvm::DK_StackSize:
+    if (StackSizeDiagHandler(cast<DiagnosticInfoStackSize>(DI)))
+      return;
+    ComputeDiagID(Severity, backend_frame_larger_than, DiagID);
+    break;
+  case llvm::DK_ResourceLimit:
+    if (ResourceLimitDiagHandler(cast<DiagnosticInfoResourceLimit>(DI)))
+      return;
+    ComputeDiagID(Severity, backend_resource_limit, DiagID);
+    break;
+  case DK_Linker:
+    ComputeDiagID(Severity, linking_module, DiagID);
+    break;
+  case llvm::DK_OptimizationRemark:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<OptimizationRemark>(DI));
+    return;
+  case llvm::DK_OptimizationRemarkMissed:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<OptimizationRemarkMissed>(DI));
+    return;
+  case llvm::DK_OptimizationRemarkAnalysis:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysis>(DI));
+    return;
+  case llvm::DK_OptimizationRemarkAnalysisFPCommute:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysisFPCommute>(DI));
+    return;
+  case llvm::DK_OptimizationRemarkAnalysisAliasing:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<OptimizationRemarkAnalysisAliasing>(DI));
+    return;
+  case llvm::DK_MachineOptimizationRemark:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<MachineOptimizationRemark>(DI));
+    return;
+  case llvm::DK_MachineOptimizationRemarkMissed:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<MachineOptimizationRemarkMissed>(DI));
+    return;
+  case llvm::DK_MachineOptimizationRemarkAnalysis:
+    // Optimization remarks are always handled completely by this
+    // handler. There is no generic way of emitting them.
+    OptimizationRemarkHandler(cast<MachineOptimizationRemarkAnalysis>(DI));
+    return;
+  case llvm::DK_OptimizationFailure:
+    // Optimization failures are always handled completely by this
+    // handler.
+    OptimizationFailureHandler(cast<DiagnosticInfoOptimizationFailure>(DI));
+    return;
+  case llvm::DK_Unsupported:
+    UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
+    return;
+  case llvm::DK_UnsupportedTargetIntrinsic:
+    UnsupportedTargetIntrinsicDiagHandler(
+        cast<DiagnosticInfoUnsupportedTargetIntrinsic>(DI));
+    return;
+  case llvm::DK_DontCall:
+    DontCallDiagHandler(cast<DiagnosticInfoDontCall>(DI));
+    return;
+  case llvm::DK_MisExpect:
+    MisExpectDiagHandler(cast<DiagnosticInfoMisExpect>(DI));
+    return;
+  default:
+    // Plugin IDs are not bound to any value as they are set dynamically.
+    ComputeDiagRemarkID(Severity, backend_plugin, DiagID);
+    break;
+  }
+  std::string MsgStorage;
+  {
+    raw_string_ostream Stream(MsgStorage);
+    DiagnosticPrinterRawOStream DP(Stream);
+    DI.print(DP);
+  }
+
+  if (DI.getKind() == DK_Linker) {
+    assert(CurLinkModule && "CurLinkModule must be set for linker diagnostics");
+    Diags.Report(DiagID) << CurLinkModule->getModuleIdentifier() << MsgStorage;
+    return;
+  }
+
+  // Report the backend message using the usual diagnostic mechanism.
+  FullSourceLoc Loc;
+  Diags.Report(Loc, DiagID).AddString(MsgStorage);
+}
+
+#undef ComputeDiagID
+#undef ComputeDiagRemarkID
diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt
index e3328aa13c3437..6586635a2fb480 100644
--- a/clang/lib/CodeGenUtils/CMakeLists.txt
+++ b/clang/lib/CodeGenUtils/CMakeLists.txt
@@ -1,8 +1,12 @@
 set(LLVM_LINK_COMPONENTS
+  CodeGen
+  Core
+  Demangle
   Support
   )
 
 add_clang_library(clangCodeGenUtils
+  BackendDiagnosticHandler.cpp
   CallUtils.cpp
   ClassUtils.cpp
   CodeGenUtils.cpp
diff --git a/clang/test/CIR/CodeGen/attr-error.c b/clang/test/CIR/CodeGen/attr-error.c
new file mode 100644
index 00000000000000..e5a8a7359e7d32
--- /dev/null
+++ b/clang/test/CIR/CodeGen/attr-error.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+__attribute__((error("don't call"))) void err(void);
+__attribute__((warning("don't call me maybe"))) void warn(void);
+
+void bar(void) {
+  err();
+  warn();
+}
+
+// CIR-LABEL: cir.func {{.*}}@bar
+// LLVM-LABEL: define{{.*}}@bar
+
+// CIR: cir.call @err() {cir.srcloc = {{[0-9]+}} : i64}
+// LLVM: call void @err(), !srcloc [[SRC_LOC_ERR:![0-9]+]]
+
+// CIR: cir.call @warn() {cir.srcloc = {{[0-9]+}} : i64}
+// LLVM: call void @warn(), !srcloc [[SRC_LOC_WARN:![0-9]+]]
+
+// CIR: cir.func private @err() attributes {{.*}}"dontcall-error" = "don't call"{{.*}}
+// LLVM: declare{{.*}} void @err() [[ATTR_ERROR:#[0-9]+]]
+
+// CIR: cir.func private @warn() attributes {{.*}}"dontcall-warn" = "don't call me maybe"{{.*}}
+// LLVM: declare{{.*}} void @warn() [[ATTR_WARN:#[0-9]+]]
+
+// LLVM-DAG: attributes [[ATTR_ERROR]] ={{.*}}"dontcall-error"="don't call"
+// LLVM-DAG: attributes [[ATTR_WARN]] ={{.*}}"dontcall-warn"="don't call me maybe"
+// LLVM-DAG: [[SRC_LOC_ERR]] = !{i64 {{[0-9]+}}}
+// LLVM-DAG: [[SRC_LOC_WARN]] = !{i64 {{[0-9]+}}}
diff --git a/clang/test/CIR/Diagnostics/attr-error.c b/clang/test/CIR/Diagnostics/attr-error.c
new file mode 100644
index 00000000000000..670dc30a2fde79
--- /dev/null
+++ b/clang/test/CIR/Diagnostics/attr-error.c
@@ -0,0 +1,16 @@
+// REQUIRES: x86-registered-target
+
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S -o - %s 2>&1 | FileCheck %s --check-prefix=ERROR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S -o - %s -DWARN 2>&1 | FileCheck %s --check-prefix=WARN
+
+#ifndef WARN
+__attribute__((error("do not call me"))) void banned(void);
+void caller(void) {
+  banned(); // ERROR: error: call to 'banned' declared with 'error' attribute: do not call me
+}
+#else
+__attribute__((warning("please do not call me"))) void warned(void);
+void caller(void) {
+  warned(); // WARN: warning: call to 'warned' declared with 'warning' attribute: pelase do not call me
+}
+#endif
diff --git a/clang/test/CIR/Diagnostics/inline-asm-backend-diagnostic.c b/clang/test/CIR/Diagnostics/inline-asm-backend-diagnostic.c
new file mode 100644
index 00000000000000..403881857f0820
--- /dev/null
+++ b/clang/test/CIR/Diagnostics/inline-asm-backend-diagnostic.c
@@ -0,0 +1,8 @@
+// REQUIRES: x86-registered-target
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S -o - %s 2>&1 | FileCheck %s
+
+// CHECK: <inline asm>:1:10: error: unexpected token in argument list
+// CHECK-NOT: LLVM ERROR
+void foo(void) {
+  __asm__ volatile("this is not an instruction");
+}

>From 654e829ac18d0245e14300c368faacb6fbd69e2c Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Mon, 28 Sep 2026 06:47:52 -0700
Subject: [PATCH 2/2] move check lines to a different line, so they don't end
 up in diags

---
 clang/test/CIR/Diagnostics/attr-error.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/test/CIR/Diagnostics/attr-error.c b/clang/test/CIR/Diagnostics/attr-error.c
index 670dc30a2fde79..a954c106f62c4e 100644
--- a/clang/test/CIR/Diagnostics/attr-error.c
+++ b/clang/test/CIR/Diagnostics/attr-error.c
@@ -4,13 +4,15 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S -o - %s -DWARN 2>&1 | FileCheck %s --check-prefix=WARN
 
 #ifndef WARN
+// ERROR: error: call to 'banned' declared with 'error' attribute: do not call me
 __attribute__((error("do not call me"))) void banned(void);
 void caller(void) {
-  banned(); // ERROR: error: call to 'banned' declared with 'error' attribute: do not call me
+  banned();
 }
 #else
+// WARN: warning: call to 'warned' declared with 'warning' attribute: please do not call me
 __attribute__((warning("please do not call me"))) void warned(void);
 void caller(void) {
-  warned(); // WARN: warning: call to 'warned' declared with 'warning' attribute: pelase do not call me
+  warned();
 }
 #endif



More information about the cfe-commits mailing list