================ @@ -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 ---------------- andykaylor wrote:
```suggestion // llvm::DiagnosticInfo::AlwaysPrint or if the -Rpass-analysis flag has a ``` https://github.com/llvm/llvm-project/pull/225966 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
