[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