| 1 | //===--- BackendDiagnosticHandler.cpp - Shared Backend Diagnostics ------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "clang/CodeGenUtils/BackendDiagnosticHandler.h" |
| 10 | #include "clang/Basic/CodeGenOptions.h" |
| 11 | #include "clang/Basic/Diagnostic.h" |
| 12 | #include "clang/Basic/DiagnosticFrontend.h" |
| 13 | #include "clang/Basic/FileManager.h" |
| 14 | #include "clang/Basic/SourceManager.h" |
| 15 | #include "llvm/ADT/Hashing.h" |
| 16 | #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" |
| 17 | #include "llvm/Demangle/Demangle.h" |
| 18 | #include "llvm/IR/DiagnosticInfo.h" |
| 19 | #include "llvm/IR/DiagnosticPrinter.h" |
| 20 | #include "llvm/IR/Function.h" |
| 21 | #include "llvm/IR/Module.h" |
| 22 | #include "llvm/Support/raw_ostream.h" |
| 23 | |
| 24 | using namespace clang; |
| 25 | using namespace llvm; |
| 26 | |
| 27 | namespace { |
| 28 | /// Forwards llvm::DiagnosticHandler callbacks into a BackendDiagnosticConsumer. |
| 29 | class BackendDiagnosticHandlerImpl final : public DiagnosticHandler { |
| 30 | public: |
| 31 | BackendDiagnosticHandlerImpl(const CodeGenOptions &CGOpts, |
| 32 | BackendDiagnosticConsumer *Consumer) |
| 33 | : CodeGenOpts(CGOpts), Consumer(Consumer) {} |
| 34 | |
| 35 | bool handleDiagnostics(const DiagnosticInfo &DI) override { |
| 36 | Consumer->handleDiagnostics(DI); |
| 37 | return true; |
| 38 | } |
| 39 | |
| 40 | bool isAnalysisRemarkEnabled(StringRef PassName) const override { |
| 41 | return CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(String: PassName); |
| 42 | } |
| 43 | bool isMissedOptRemarkEnabled(StringRef PassName) const override { |
| 44 | return CodeGenOpts.OptimizationRemarkMissed.patternMatches(String: PassName); |
| 45 | } |
| 46 | bool isPassedOptRemarkEnabled(StringRef PassName) const override { |
| 47 | return CodeGenOpts.OptimizationRemark.patternMatches(String: PassName); |
| 48 | } |
| 49 | |
| 50 | bool isAnyRemarkEnabled() const override { |
| 51 | return CodeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() || |
| 52 | CodeGenOpts.OptimizationRemarkMissed.hasValidPattern() || |
| 53 | CodeGenOpts.OptimizationRemark.hasValidPattern(); |
| 54 | } |
| 55 | |
| 56 | private: |
| 57 | const CodeGenOptions &CodeGenOpts; |
| 58 | BackendDiagnosticConsumer *Consumer; |
| 59 | }; |
| 60 | |
| 61 | /// Convert a location in a temporary llvm::SourceMgr buffer to be a valid |
| 62 | /// FullSourceLoc. |
| 63 | FullSourceLoc ConvertBackendLocation(const llvm::SMDiagnostic &D, |
| 64 | SourceManager &CSM) { |
| 65 | // Get both the clang and llvm source managers. The location is relative to |
| 66 | // a memory buffer that the LLVM Source Manager is handling, we need to add |
| 67 | // a copy to the Clang source manager. |
| 68 | const llvm::SourceMgr &LSM = *D.getSourceMgr(); |
| 69 | |
| 70 | // We need to copy the underlying LLVM memory buffer because llvm::SourceMgr |
| 71 | // already owns its one and clang::SourceManager wants to own its one. |
| 72 | const MemoryBuffer *LBuf = |
| 73 | LSM.getMemoryBuffer(i: LSM.FindBufferContainingLoc(Loc: D.getLoc())); |
| 74 | |
| 75 | // Create the copy and transfer ownership to clang::SourceManager. |
| 76 | // TODO: Avoid copying files into memory. |
| 77 | std::unique_ptr<llvm::MemoryBuffer> CBuf = |
| 78 | llvm::MemoryBuffer::getMemBufferCopy(InputData: LBuf->getBuffer(), |
| 79 | BufferName: LBuf->getBufferIdentifier()); |
| 80 | // FIXME: Keep a file ID map instead of creating new IDs for each location. |
| 81 | FileID FID = CSM.createFileID(Buffer: std::move(CBuf)); |
| 82 | |
| 83 | // Translate the offset into the file. |
| 84 | unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); |
| 85 | SourceLocation NewLoc = |
| 86 | CSM.getLocForStartOfFile(FID).getLocWithOffset(Offset); |
| 87 | return FullSourceLoc(NewLoc, CSM); |
| 88 | } |
| 89 | |
| 90 | } // namespace |
| 91 | |
| 92 | #define ComputeDiagID(Severity, GroupName, DiagID) \ |
| 93 | do { \ |
| 94 | switch (Severity) { \ |
| 95 | case llvm::DS_Error: \ |
| 96 | DiagID = diag::err_fe_##GroupName; \ |
| 97 | break; \ |
| 98 | case llvm::DS_Warning: \ |
| 99 | DiagID = diag::warn_fe_##GroupName; \ |
| 100 | break; \ |
| 101 | case llvm::DS_Remark: \ |
| 102 | llvm_unreachable("'remark' severity not expected"); \ |
| 103 | break; \ |
| 104 | case llvm::DS_Note: \ |
| 105 | DiagID = diag::note_fe_##GroupName; \ |
| 106 | break; \ |
| 107 | } \ |
| 108 | } while (false) |
| 109 | |
| 110 | #define (Severity, GroupName, DiagID) \ |
| 111 | do { \ |
| 112 | switch (Severity) { \ |
| 113 | case llvm::DS_Error: \ |
| 114 | DiagID = diag::err_fe_##GroupName; \ |
| 115 | break; \ |
| 116 | case llvm::DS_Warning: \ |
| 117 | DiagID = diag::warn_fe_##GroupName; \ |
| 118 | break; \ |
| 119 | case llvm::DS_Remark: \ |
| 120 | DiagID = diag::remark_fe_##GroupName; \ |
| 121 | break; \ |
| 122 | case llvm::DS_Note: \ |
| 123 | DiagID = diag::note_fe_##GroupName; \ |
| 124 | break; \ |
| 125 | } \ |
| 126 | } while (false) |
| 127 | |
| 128 | std::unique_ptr<llvm::DiagnosticHandler> |
| 129 | BackendDiagnosticConsumer::createDiagnosticHandler() { |
| 130 | return std::make_unique<BackendDiagnosticHandlerImpl>(args: CodeGenOpts, args: this); |
| 131 | } |
| 132 | |
| 133 | void BackendDiagnosticConsumer::addFunctionSourceLocation(StringRef MangledName, |
| 134 | FullSourceLoc Loc) { |
| 135 | // TODO: use a fast content hash when available. |
| 136 | auto NameHash = llvm::hash_value(S: MangledName); |
| 137 | ManglingFullSourceLocs.push_back(x: std::make_pair(x&: NameHash, y&: Loc)); |
| 138 | } |
| 139 | |
| 140 | std::optional<FullSourceLoc> |
| 141 | BackendDiagnosticConsumer::getFunctionSourceLocation(const Function &F) const { |
| 142 | auto Hash = llvm::hash_value(S: F.getName()); |
| 143 | for (const auto &Pair : ManglingFullSourceLocs) { |
| 144 | if (Pair.first == Hash) |
| 145 | return Pair.second; |
| 146 | } |
| 147 | return std::nullopt; |
| 148 | } |
| 149 | |
| 150 | const FullSourceLoc BackendDiagnosticConsumer::getBestLocationFromDebugLoc( |
| 151 | const llvm::DiagnosticInfoWithLocationBase &D, bool &BadDebugInfo, |
| 152 | StringRef &Filename, unsigned &Line, unsigned &Column) const { |
| 153 | SourceManager &SourceMgr = *SM; |
| 154 | FileManager &FileMgr = SourceMgr.getFileManager(); |
| 155 | SourceLocation DILoc; |
| 156 | |
| 157 | if (D.isLocationAvailable()) { |
| 158 | D.getLocation(RelativePath&: Filename, Line, Column); |
| 159 | if (Line > 0) { |
| 160 | auto FE = FileMgr.getOptionalFileRef(Filename); |
| 161 | if (!FE) |
| 162 | FE = FileMgr.getOptionalFileRef(Filename: D.getAbsolutePath()); |
| 163 | if (FE) { |
| 164 | // If -gcolumn-info was not used, Column will be 0. This upsets the |
| 165 | // source manager, so pass 1 if Column is not set. |
| 166 | DILoc = SourceMgr.translateFileLineCol(SourceFile: *FE, Line, Col: Column ? Column : 1); |
| 167 | } |
| 168 | } |
| 169 | BadDebugInfo = DILoc.isInvalid(); |
| 170 | } |
| 171 | |
| 172 | // If a location isn't available, try to approximate it using the associated |
| 173 | // function definition. We use the definition's right brace to differentiate |
| 174 | // from diagnostics that genuinely relate to the function itself. |
| 175 | FullSourceLoc Loc(DILoc, SourceMgr); |
| 176 | if (Loc.isInvalid()) { |
| 177 | if (auto MaybeLoc = getFunctionSourceLocation(F: D.getFunction())) |
| 178 | Loc = *MaybeLoc; |
| 179 | } |
| 180 | |
| 181 | if (DILoc.isInvalid() && D.isLocationAvailable()) |
| 182 | // If we were not able to translate the file:line:col information |
| 183 | // back to a SourceLocation, at least emit a note stating that |
| 184 | // we could not translate this location. This can happen in the |
| 185 | // case of #line directives. |
| 186 | Diags.Report(Loc, DiagID: diag::note_fe_backend_invalid_loc) |
| 187 | << Filename << Line << Column; |
| 188 | |
| 189 | return Loc; |
| 190 | } |
| 191 | |
| 192 | void BackendDiagnosticConsumer::SrcMgrDiagHandler( |
| 193 | const llvm::DiagnosticInfoSrcMgr &DI) { |
| 194 | const llvm::SMDiagnostic &D = DI.getSMDiag(); |
| 195 | |
| 196 | unsigned DiagID; |
| 197 | if (DI.isInlineAsmDiag()) |
| 198 | ComputeDiagID(DI.getSeverity(), inline_asm, DiagID); |
| 199 | else |
| 200 | ComputeDiagID(DI.getSeverity(), source_mgr, DiagID); |
| 201 | |
| 202 | // This is for the empty consumer that uses the clang diagnostic |
| 203 | // handler for IR input files. |
| 204 | if (!SM) { |
| 205 | D.print(ProgName: nullptr, S&: llvm::errs()); |
| 206 | Diags.Report(DiagID).AddString(V: "cannot compile inline asm" ); |
| 207 | return; |
| 208 | } |
| 209 | |
| 210 | // There are a couple of different kinds of errors we could get here. |
| 211 | // First, we re-format the SMDiagnostic in terms of a clang diagnostic. |
| 212 | |
| 213 | // Strip "error: " off the start of the message string. |
| 214 | StringRef Message = D.getMessage(); |
| 215 | (void)Message.consume_front(Prefix: "error: " ); |
| 216 | |
| 217 | // If the SMDiagnostic has an inline asm source location, translate it. |
| 218 | FullSourceLoc Loc; |
| 219 | if (D.getLoc() != SMLoc()) |
| 220 | Loc = ConvertBackendLocation(D, CSM&: *SM); |
| 221 | |
| 222 | // If this problem has clang-level source location information, report the |
| 223 | // issue in the source with a note showing the instantiated |
| 224 | // code. |
| 225 | if (DI.isInlineAsmDiag()) { |
| 226 | SourceLocation LocCookie = |
| 227 | SourceLocation::getFromRawEncoding(Encoding: DI.getLocCookie()); |
| 228 | if (LocCookie.isValid()) { |
| 229 | Diags.Report(Loc: LocCookie, DiagID).AddString(V: Message); |
| 230 | |
| 231 | if (D.getLoc().isValid()) { |
| 232 | DiagnosticBuilder B = Diags.Report(Loc, DiagID: diag::note_fe_inline_asm_here); |
| 233 | // Convert the SMDiagnostic ranges into SourceRange and attach them |
| 234 | // to the diagnostic. |
| 235 | for (const std::pair<unsigned, unsigned> &Range : D.getRanges()) { |
| 236 | unsigned Column = D.getColumnNo(); |
| 237 | B << SourceRange(Loc.getLocWithOffset(Offset: Range.first - Column), |
| 238 | Loc.getLocWithOffset(Offset: Range.second - Column)); |
| 239 | } |
| 240 | } |
| 241 | return; |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | // Otherwise, report the backend issue as occurring in the generated .s file. |
| 246 | // If Loc is invalid, we still need to report the issue, it just gets no |
| 247 | // location info. |
| 248 | Diags.Report(Loc, DiagID).AddString(V: Message); |
| 249 | } |
| 250 | |
| 251 | bool BackendDiagnosticConsumer::InlineAsmDiagHandler( |
| 252 | const llvm::DiagnosticInfoInlineAsm &D) { |
| 253 | unsigned DiagID; |
| 254 | ComputeDiagID(D.getSeverity(), inline_asm, DiagID); |
| 255 | std::string Message = D.getMsgStr().str(); |
| 256 | |
| 257 | // If this problem has clang-level source location information, report the |
| 258 | // issue as being a problem in the source with a note showing the instantiated |
| 259 | // code. |
| 260 | SourceLocation LocCookie = |
| 261 | SourceLocation::getFromRawEncoding(Encoding: D.getLocCookie()); |
| 262 | if (LocCookie.isValid()) |
| 263 | Diags.Report(Loc: LocCookie, DiagID).AddString(V: Message); |
| 264 | else { |
| 265 | // Otherwise, report the backend diagnostic as occurring in the generated |
| 266 | // .s file. |
| 267 | // If Loc is invalid, we still need to report the diagnostic, it just gets |
| 268 | // no location info. |
| 269 | FullSourceLoc Loc; |
| 270 | Diags.Report(Loc, DiagID).AddString(V: Message); |
| 271 | } |
| 272 | // We handled all the possible severities. |
| 273 | return true; |
| 274 | } |
| 275 | |
| 276 | bool BackendDiagnosticConsumer::StackSizeDiagHandler( |
| 277 | const llvm::DiagnosticInfoStackSize &D) { |
| 278 | if (D.getSeverity() != llvm::DS_Warning) |
| 279 | // For now, the only support we have for StackSize diagnostic is warning. |
| 280 | // We do not know how to format other severities. |
| 281 | return false; |
| 282 | |
| 283 | auto Loc = getFunctionSourceLocation(F: D.getFunction()); |
| 284 | if (!Loc) |
| 285 | return false; |
| 286 | |
| 287 | Diags.Report(Loc: *Loc, DiagID: diag::warn_fe_frame_larger_than) |
| 288 | << D.getStackSize() << D.getStackLimit() |
| 289 | << llvm::demangle(MangledName: D.getFunction().getName()); |
| 290 | return true; |
| 291 | } |
| 292 | |
| 293 | bool BackendDiagnosticConsumer::ResourceLimitDiagHandler( |
| 294 | const llvm::DiagnosticInfoResourceLimit &D) { |
| 295 | auto Loc = getFunctionSourceLocation(F: D.getFunction()); |
| 296 | if (!Loc) |
| 297 | return false; |
| 298 | unsigned DiagID = diag::err_fe_backend_resource_limit; |
| 299 | ComputeDiagID(D.getSeverity(), backend_resource_limit, DiagID); |
| 300 | |
| 301 | Diags.Report(Loc: *Loc, DiagID) |
| 302 | << D.getResourceName() << D.getResourceSize() << D.getResourceLimit() |
| 303 | << llvm::demangle(MangledName: D.getFunction().getName()); |
| 304 | return true; |
| 305 | } |
| 306 | |
| 307 | void BackendDiagnosticConsumer::UnsupportedDiagHandler( |
| 308 | const llvm::DiagnosticInfoUnsupported &D) { |
| 309 | // We only support warnings or errors. |
| 310 | assert(D.getSeverity() == llvm::DS_Error || |
| 311 | D.getSeverity() == llvm::DS_Warning); |
| 312 | |
| 313 | StringRef Filename; |
| 314 | unsigned Line, Column; |
| 315 | bool BadDebugInfo = false; |
| 316 | FullSourceLoc Loc; |
| 317 | std::string Msg; |
| 318 | raw_string_ostream MsgStream(Msg); |
| 319 | |
| 320 | // SM will be null for IR input files, we will construct the diag |
| 321 | // message from llvm::DiagnosticInfoUnsupported. |
| 322 | if (SM != nullptr) { |
| 323 | Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column); |
| 324 | MsgStream << D.getMessage(); |
| 325 | } else { |
| 326 | DiagnosticPrinterRawOStream DP(MsgStream); |
| 327 | D.print(DP); |
| 328 | } |
| 329 | |
| 330 | auto DiagType = D.getSeverity() == llvm::DS_Error |
| 331 | ? diag::err_fe_backend_unsupported |
| 332 | : diag::warn_fe_backend_unsupported; |
| 333 | Diags.Report(Loc, DiagID: DiagType) << Msg; |
| 334 | |
| 335 | if (BadDebugInfo) |
| 336 | // If we were not able to translate the file:line:col information |
| 337 | // back to a SourceLocation, at least emit a note stating that |
| 338 | // we could not translate this location. This can happen in the |
| 339 | // case of #line directives. |
| 340 | Diags.Report(Loc, DiagID: diag::note_fe_backend_invalid_loc) |
| 341 | << Filename << Line << Column; |
| 342 | } |
| 343 | |
| 344 | void BackendDiagnosticConsumer::UnsupportedTargetIntrinsicDiagHandler( |
| 345 | const llvm::DiagnosticInfoUnsupportedTargetIntrinsic &D) { |
| 346 | assert(D.getSeverity() == llvm::DS_Error && |
| 347 | "unsupported target intrinsic diagnostic should be an error" ); |
| 348 | |
| 349 | StringRef Filename; |
| 350 | unsigned Line, Column; |
| 351 | bool BadDebugInfo = false; |
| 352 | FullSourceLoc Loc; |
| 353 | std::string Msg; |
| 354 | raw_string_ostream MsgStream(Msg); |
| 355 | |
| 356 | // SM will be null for IR input files, so construct the diagnostic |
| 357 | // message from llvm::DiagnosticInfoUnsupportedTargetIntrinsic. |
| 358 | if (SM != nullptr) { |
| 359 | Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column); |
| 360 | MsgStream << D.getMessage(); |
| 361 | } else { |
| 362 | DiagnosticPrinterRawOStream DP(MsgStream); |
| 363 | D.print(DP); |
| 364 | } |
| 365 | |
| 366 | Diags.Report(Loc, DiagID: diag::err_fe_backend_unsupported) << Msg; |
| 367 | |
| 368 | if (BadDebugInfo) { |
| 369 | // If we were not able to translate the file:line:col information |
| 370 | // back to a SourceLocation, at least emit a note stating that |
| 371 | // we could not translate this location. This can happen in the |
| 372 | // case of #line directives. |
| 373 | Diags.Report(Loc, DiagID: diag::note_fe_backend_invalid_loc) |
| 374 | << Filename << Line << Column; |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | void BackendDiagnosticConsumer::EmitOptimizationMessage( |
| 379 | const llvm::DiagnosticInfoOptimizationBase &D, unsigned DiagID) { |
| 380 | // We only support warnings and remarks. |
| 381 | assert(D.getSeverity() == llvm::DS_Remark || |
| 382 | D.getSeverity() == llvm::DS_Warning); |
| 383 | |
| 384 | StringRef Filename; |
| 385 | unsigned Line, Column; |
| 386 | bool BadDebugInfo = false; |
| 387 | FullSourceLoc Loc; |
| 388 | std::string Msg; |
| 389 | raw_string_ostream MsgStream(Msg); |
| 390 | |
| 391 | // SM will be null for IR input files, we will construct the remark |
| 392 | // message from llvm::DiagnosticInfoOptimizationBase. |
| 393 | if (SM != nullptr) { |
| 394 | Loc = getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column); |
| 395 | MsgStream << D.getMsg(); |
| 396 | } else { |
| 397 | DiagnosticPrinterRawOStream DP(MsgStream); |
| 398 | D.print(DP); |
| 399 | } |
| 400 | |
| 401 | if (D.getHotness()) |
| 402 | MsgStream << " (hotness: " << *D.getHotness() << ")" ; |
| 403 | |
| 404 | Diags.Report(Loc, DiagID) << AddFlagValue(D.getPassName()) << Msg; |
| 405 | |
| 406 | if (BadDebugInfo) |
| 407 | // If we were not able to translate the file:line:col information |
| 408 | // back to a SourceLocation, at least emit a note stating that |
| 409 | // we could not translate this location. This can happen in the |
| 410 | // case of #line directives. |
| 411 | Diags.Report(Loc, DiagID: diag::note_fe_backend_invalid_loc) |
| 412 | << Filename << Line << Column; |
| 413 | } |
| 414 | |
| 415 | void BackendDiagnosticConsumer::OptimizationRemarkHandler( |
| 416 | const llvm::DiagnosticInfoOptimizationBase &D) { |
| 417 | // Without hotness information, don't show noisy remarks. |
| 418 | if (D.isVerbose() && !D.getHotness()) |
| 419 | return; |
| 420 | |
| 421 | if (D.isPassed()) { |
| 422 | // Optimization remarks are active only if the -Rpass flag has a regular |
| 423 | // expression that matches the name of the pass name in \p D. |
| 424 | if (CodeGenOpts.OptimizationRemark.patternMatches(String: D.getPassName())) |
| 425 | EmitOptimizationMessage(D, DiagID: diag::remark_fe_backend_optimization_remark); |
| 426 | } else if (D.isMissed()) { |
| 427 | // Missed optimization remarks are active only if the -Rpass-missed |
| 428 | // flag has a regular expression that matches the name of the pass |
| 429 | // name in \p D. |
| 430 | if (CodeGenOpts.OptimizationRemarkMissed.patternMatches(String: D.getPassName())) |
| 431 | EmitOptimizationMessage( |
| 432 | D, DiagID: diag::remark_fe_backend_optimization_remark_missed); |
| 433 | } else { |
| 434 | assert(D.isAnalysis() && "Unknown remark type" ); |
| 435 | |
| 436 | bool ShouldAlwaysPrint = false; |
| 437 | if (auto *ORA = dyn_cast<llvm::OptimizationRemarkAnalysis>(Val: &D)) |
| 438 | ShouldAlwaysPrint = ORA->shouldAlwaysPrint(); |
| 439 | |
| 440 | if (ShouldAlwaysPrint || |
| 441 | CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(String: D.getPassName())) |
| 442 | EmitOptimizationMessage( |
| 443 | D, DiagID: diag::remark_fe_backend_optimization_remark_analysis); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | void BackendDiagnosticConsumer::OptimizationRemarkHandler( |
| 448 | const llvm::OptimizationRemarkAnalysisFPCommute &D) { |
| 449 | // Optimization analysis remarks are active if the pass name is set to |
| 450 | // llvm::DiagnosticInfo::AlwaysPrint or if the -Rpass-analysis flag has a |
| 451 | // regular expression that matches the name of the pass name in \p D. |
| 452 | |
| 453 | if (D.shouldAlwaysPrint() || |
| 454 | CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(String: D.getPassName())) |
| 455 | EmitOptimizationMessage( |
| 456 | D, DiagID: diag::remark_fe_backend_optimization_remark_analysis_fpcommute); |
| 457 | } |
| 458 | |
| 459 | void BackendDiagnosticConsumer::OptimizationRemarkHandler( |
| 460 | const llvm::OptimizationRemarkAnalysisAliasing &D) { |
| 461 | // Optimization analysis remarks are active if the pass name is set to |
| 462 | // llvm::DiagnosticInfo::AlwaysPrint or if the -Rpass-analysis flag has a |
| 463 | // regular expression that matches the name of the pass name in \p D. |
| 464 | |
| 465 | if (D.shouldAlwaysPrint() || |
| 466 | CodeGenOpts.OptimizationRemarkAnalysis.patternMatches(String: D.getPassName())) |
| 467 | EmitOptimizationMessage( |
| 468 | D, DiagID: diag::remark_fe_backend_optimization_remark_analysis_aliasing); |
| 469 | } |
| 470 | |
| 471 | void BackendDiagnosticConsumer::OptimizationFailureHandler( |
| 472 | const llvm::DiagnosticInfoOptimizationFailure &D) { |
| 473 | EmitOptimizationMessage(D, DiagID: diag::warn_fe_backend_optimization_failure); |
| 474 | } |
| 475 | |
| 476 | void BackendDiagnosticConsumer::DontCallDiagHandler( |
| 477 | const DiagnosticInfoDontCall &D) { |
| 478 | SourceLocation LocCookie = |
| 479 | SourceLocation::getFromRawEncoding(Encoding: D.getLocCookie()); |
| 480 | |
| 481 | // FIXME: we can't yet diagnose indirect calls. When/if we can, we |
| 482 | // should instead assert that LocCookie.isValid(). |
| 483 | if (!LocCookie.isValid()) |
| 484 | return; |
| 485 | |
| 486 | Diags.Report(Loc: LocCookie, DiagID: D.getSeverity() == DiagnosticSeverity::DS_Error |
| 487 | ? diag::err_fe_backend_error_attr |
| 488 | : diag::warn_fe_backend_warning_attr) |
| 489 | << llvm::demangle(MangledName: D.getFunctionName()) << D.getNote(); |
| 490 | |
| 491 | if (!CodeGenOpts.ShowInliningChain) |
| 492 | return; |
| 493 | |
| 494 | auto EmitNote = [&](SourceLocation Loc, StringRef FuncName, bool IsFirst) { |
| 495 | if (!Loc.isValid()) |
| 496 | Loc = LocCookie; |
| 497 | unsigned DiagID = |
| 498 | IsFirst ? diag::note_fe_backend_in : diag::note_fe_backend_inlined; |
| 499 | Diags.Report(Loc, DiagID) << llvm::demangle(MangledName: FuncName.str()); |
| 500 | }; |
| 501 | |
| 502 | // Try debug info first for accurate source locations. |
| 503 | if (!D.getDebugInlineChain().empty()) { |
| 504 | SourceManager &SrcMgr = *SM; |
| 505 | FileManager &FM = SrcMgr.getFileManager(); |
| 506 | for (const auto &[I, Info] : llvm::enumerate(First: D.getDebugInlineChain())) { |
| 507 | SourceLocation Loc; |
| 508 | if (Info.Line > 0) |
| 509 | if (auto FE = FM.getOptionalFileRef(Filename: Info.Filename)) |
| 510 | Loc = SrcMgr.translateFileLineCol(SourceFile: *FE, Line: Info.Line, |
| 511 | Col: Info.Column ? Info.Column : 1); |
| 512 | EmitNote(Loc, Info.FuncName, I == 0); |
| 513 | } |
| 514 | return; |
| 515 | } |
| 516 | |
| 517 | // Fall back to heuristic (srcloc metadata) when debug info is unavailable. |
| 518 | auto InliningDecisions = D.getInliningDecisions(); |
| 519 | if (InliningDecisions.empty()) |
| 520 | return; |
| 521 | |
| 522 | for (const auto &[I, Entry] : llvm::enumerate(First&: InliningDecisions)) { |
| 523 | SourceLocation Loc = |
| 524 | I == 0 ? LocCookie : SourceLocation::getFromRawEncoding(Encoding: Entry.second); |
| 525 | EmitNote(Loc, Entry.first, I == 0); |
| 526 | } |
| 527 | |
| 528 | // Suggest enabling debug info (at least -gline-directives-only) for more |
| 529 | // accurate locations. |
| 530 | Diags.Report(Loc: LocCookie, DiagID: diag::note_fe_backend_inlining_debug_info); |
| 531 | } |
| 532 | |
| 533 | void BackendDiagnosticConsumer::MisExpectDiagHandler( |
| 534 | const llvm::DiagnosticInfoMisExpect &D) { |
| 535 | StringRef Filename; |
| 536 | unsigned Line, Column; |
| 537 | bool BadDebugInfo = false; |
| 538 | FullSourceLoc Loc = |
| 539 | getBestLocationFromDebugLoc(D, BadDebugInfo, Filename, Line, Column); |
| 540 | |
| 541 | Diags.Report(Loc, DiagID: diag::warn_profile_data_misexpect) << D.getMsg().str(); |
| 542 | |
| 543 | if (BadDebugInfo) |
| 544 | // If we were not able to translate the file:line:col information |
| 545 | // back to a SourceLocation, at least emit a note stating that |
| 546 | // we could not translate this location. This can happen in the |
| 547 | // case of #line directives. |
| 548 | Diags.Report(Loc, DiagID: diag::note_fe_backend_invalid_loc) |
| 549 | << Filename << Line << Column; |
| 550 | } |
| 551 | |
| 552 | void BackendDiagnosticConsumer::handleDiagnostics(const DiagnosticInfo &DI) { |
| 553 | unsigned DiagID = diag::err_fe_inline_asm; |
| 554 | llvm::DiagnosticSeverity Severity = DI.getSeverity(); |
| 555 | // Get the diagnostic ID based. |
| 556 | switch (DI.getKind()) { |
| 557 | case llvm::DK_InlineAsm: |
| 558 | if (InlineAsmDiagHandler(D: cast<DiagnosticInfoInlineAsm>(Val: DI))) |
| 559 | return; |
| 560 | ComputeDiagID(Severity, inline_asm, DiagID); |
| 561 | break; |
| 562 | case llvm::DK_SrcMgr: |
| 563 | SrcMgrDiagHandler(DI: cast<DiagnosticInfoSrcMgr>(Val: DI)); |
| 564 | return; |
| 565 | case llvm::DK_StackSize: |
| 566 | if (StackSizeDiagHandler(D: cast<DiagnosticInfoStackSize>(Val: DI))) |
| 567 | return; |
| 568 | ComputeDiagID(Severity, backend_frame_larger_than, DiagID); |
| 569 | break; |
| 570 | case llvm::DK_ResourceLimit: |
| 571 | if (ResourceLimitDiagHandler(D: cast<DiagnosticInfoResourceLimit>(Val: DI))) |
| 572 | return; |
| 573 | ComputeDiagID(Severity, backend_resource_limit, DiagID); |
| 574 | break; |
| 575 | case DK_Linker: |
| 576 | ComputeDiagID(Severity, linking_module, DiagID); |
| 577 | break; |
| 578 | case llvm::DK_OptimizationRemark: |
| 579 | // Optimization remarks are always handled completely by this |
| 580 | // handler. There is no generic way of emitting them. |
| 581 | OptimizationRemarkHandler(D: cast<OptimizationRemark>(Val: DI)); |
| 582 | return; |
| 583 | case llvm::DK_OptimizationRemarkMissed: |
| 584 | // Optimization remarks are always handled completely by this |
| 585 | // handler. There is no generic way of emitting them. |
| 586 | OptimizationRemarkHandler(D: cast<OptimizationRemarkMissed>(Val: DI)); |
| 587 | return; |
| 588 | case llvm::DK_OptimizationRemarkAnalysis: |
| 589 | // Optimization remarks are always handled completely by this |
| 590 | // handler. There is no generic way of emitting them. |
| 591 | OptimizationRemarkHandler(D: cast<OptimizationRemarkAnalysis>(Val: DI)); |
| 592 | return; |
| 593 | case llvm::DK_OptimizationRemarkAnalysisFPCommute: |
| 594 | // Optimization remarks are always handled completely by this |
| 595 | // handler. There is no generic way of emitting them. |
| 596 | OptimizationRemarkHandler(D: cast<OptimizationRemarkAnalysisFPCommute>(Val: DI)); |
| 597 | return; |
| 598 | case llvm::DK_OptimizationRemarkAnalysisAliasing: |
| 599 | // Optimization remarks are always handled completely by this |
| 600 | // handler. There is no generic way of emitting them. |
| 601 | OptimizationRemarkHandler(D: cast<OptimizationRemarkAnalysisAliasing>(Val: DI)); |
| 602 | return; |
| 603 | case llvm::DK_MachineOptimizationRemark: |
| 604 | // Optimization remarks are always handled completely by this |
| 605 | // handler. There is no generic way of emitting them. |
| 606 | OptimizationRemarkHandler(D: cast<MachineOptimizationRemark>(Val: DI)); |
| 607 | return; |
| 608 | case llvm::DK_MachineOptimizationRemarkMissed: |
| 609 | // Optimization remarks are always handled completely by this |
| 610 | // handler. There is no generic way of emitting them. |
| 611 | OptimizationRemarkHandler(D: cast<MachineOptimizationRemarkMissed>(Val: DI)); |
| 612 | return; |
| 613 | case llvm::DK_MachineOptimizationRemarkAnalysis: |
| 614 | // Optimization remarks are always handled completely by this |
| 615 | // handler. There is no generic way of emitting them. |
| 616 | OptimizationRemarkHandler(D: cast<MachineOptimizationRemarkAnalysis>(Val: DI)); |
| 617 | return; |
| 618 | case llvm::DK_OptimizationFailure: |
| 619 | // Optimization failures are always handled completely by this |
| 620 | // handler. |
| 621 | OptimizationFailureHandler(D: cast<DiagnosticInfoOptimizationFailure>(Val: DI)); |
| 622 | return; |
| 623 | case llvm::DK_Unsupported: |
| 624 | UnsupportedDiagHandler(D: cast<DiagnosticInfoUnsupported>(Val: DI)); |
| 625 | return; |
| 626 | case llvm::DK_UnsupportedTargetIntrinsic: |
| 627 | UnsupportedTargetIntrinsicDiagHandler( |
| 628 | D: cast<DiagnosticInfoUnsupportedTargetIntrinsic>(Val: DI)); |
| 629 | return; |
| 630 | case llvm::DK_DontCall: |
| 631 | DontCallDiagHandler(D: cast<DiagnosticInfoDontCall>(Val: DI)); |
| 632 | return; |
| 633 | case llvm::DK_MisExpect: |
| 634 | MisExpectDiagHandler(D: cast<DiagnosticInfoMisExpect>(Val: DI)); |
| 635 | return; |
| 636 | default: |
| 637 | // Plugin IDs are not bound to any value as they are set dynamically. |
| 638 | ComputeDiagRemarkID(Severity, backend_plugin, DiagID); |
| 639 | break; |
| 640 | } |
| 641 | std::string MsgStorage; |
| 642 | { |
| 643 | raw_string_ostream Stream(MsgStorage); |
| 644 | DiagnosticPrinterRawOStream DP(Stream); |
| 645 | DI.print(DP); |
| 646 | } |
| 647 | |
| 648 | if (DI.getKind() == DK_Linker) { |
| 649 | assert(CurLinkModule && "CurLinkModule must be set for linker diagnostics" ); |
| 650 | Diags.Report(DiagID) << CurLinkModule->getModuleIdentifier() << MsgStorage; |
| 651 | return; |
| 652 | } |
| 653 | |
| 654 | // Report the backend message using the usual diagnostic mechanism. |
| 655 | FullSourceLoc Loc; |
| 656 | Diags.Report(Loc, DiagID).AddString(V: MsgStorage); |
| 657 | } |
| 658 | |
| 659 | #undef ComputeDiagID |
| 660 | #undef ComputeDiagRemarkID |
| 661 | |