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

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 29 13:51:48 PDT 2026


================
@@ -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
----------------
andykaylor wrote:

```suggestion
  // llvm::DiagnosticInfo::AlwaysPrint or if the -Rpass-analysis flag has a
```

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


More information about the cfe-commits mailing list