| //===- llvm/IR/RemarkStreamer.cpp - Remark Streamer -*- 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 contains the implementation of the remark outputting as part of | |
| // LLVMContext. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/IR/RemarkStreamer.h" | |
| #include "llvm/IR/DiagnosticInfo.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/GlobalValue.h" | |
| #include "llvm/Remarks/BitstreamRemarkSerializer.h" | |
| #include "llvm/Remarks/RemarkFormat.h" | |
| #include "llvm/Remarks/RemarkSerializer.h" | |
| #include "llvm/Support/CommandLine.h" | |
| using namespace llvm; | |
| static cl::opt<cl::boolOrDefault> EnableRemarksSection( | |
| "remarks-section", | |
| cl::desc( | |
| "Emit a section containing remark diagnostics metadata. By default, " | |
| "this is enabled for the following formats: yaml-strtab, bitstream."), | |
| cl::init(cl::BOU_UNSET), cl::Hidden); | |
| RemarkStreamer::RemarkStreamer( | |
| std::unique_ptr<remarks::RemarkSerializer> RemarkSerializer, | |
| Optional<StringRef> FilenameIn) | |
| : PassFilter(), RemarkSerializer(std::move(RemarkSerializer)), | |
| Filename(FilenameIn ? Optional<std::string>(FilenameIn->str()) : None) {} | |
| Error RemarkStreamer::setFilter(StringRef Filter) { | |
| Regex R = Regex(Filter); | |
| std::string RegexError; | |
| if (!R.isValid(RegexError)) | |
| return createStringError(std::make_error_code(std::errc::invalid_argument), | |
| RegexError.data()); | |
| PassFilter = std::move(R); | |
| return Error::success(); | |
| } | |
| /// DiagnosticKind -> remarks::Type | |
| static remarks::Type toRemarkType(enum DiagnosticKind Kind) { | |
| switch (Kind) { | |
| default: | |
| return remarks::Type::Unknown; | |
| case DK_OptimizationRemark: | |
| case DK_MachineOptimizationRemark: | |
| return remarks::Type::Passed; | |
| case DK_OptimizationRemarkMissed: | |
| case DK_MachineOptimizationRemarkMissed: | |
| return remarks::Type::Missed; | |
| case DK_OptimizationRemarkAnalysis: | |
| case DK_MachineOptimizationRemarkAnalysis: | |
| return remarks::Type::Analysis; | |
| case DK_OptimizationRemarkAnalysisFPCommute: | |
| return remarks::Type::AnalysisFPCommute; | |
| case DK_OptimizationRemarkAnalysisAliasing: | |
| return remarks::Type::AnalysisAliasing; | |
| case DK_OptimizationFailure: | |
| return remarks::Type::Failure; | |
| } | |
| } | |
| /// DiagnosticLocation -> remarks::RemarkLocation. | |
| static Optional<remarks::RemarkLocation> | |
| toRemarkLocation(const DiagnosticLocation &DL) { | |
| if (!DL.isValid()) | |
| return None; | |
| StringRef File = DL.getRelativePath(); | |
| unsigned Line = DL.getLine(); | |
| unsigned Col = DL.getColumn(); | |
| return remarks::RemarkLocation{File, Line, Col}; | |
| } | |
| /// LLVM Diagnostic -> Remark | |
| remarks::Remark | |
| RemarkStreamer::toRemark(const DiagnosticInfoOptimizationBase &Diag) { | |
| remarks::Remark R; // The result. | |
| R.RemarkType = toRemarkType(static_cast<DiagnosticKind>(Diag.getKind())); | |
| R.PassName = Diag.getPassName(); | |
| R.RemarkName = Diag.getRemarkName(); | |
| R.FunctionName = | |
| GlobalValue::dropLLVMManglingEscape(Diag.getFunction().getName()); | |
| R.Loc = toRemarkLocation(Diag.getLocation()); | |
| R.Hotness = Diag.getHotness(); | |
| for (const DiagnosticInfoOptimizationBase::Argument &Arg : Diag.getArgs()) { | |
| R.Args.emplace_back(); | |
| R.Args.back().Key = Arg.Key; | |
| R.Args.back().Val = Arg.Val; | |
| R.Args.back().Loc = toRemarkLocation(Arg.Loc); | |
| } | |
| return R; | |
| } | |
| void RemarkStreamer::emit(const DiagnosticInfoOptimizationBase &Diag) { | |
| if (Optional<Regex> &Filter = PassFilter) | |
| if (!Filter->match(Diag.getPassName())) | |
| return; | |
| // First, convert the diagnostic to a remark. | |
| remarks::Remark R = toRemark(Diag); | |
| // Then, emit the remark through the serializer. | |
| RemarkSerializer->emit(R); | |
| } | |
| bool RemarkStreamer::needsSection() const { | |
| if (EnableRemarksSection == cl::BOU_TRUE) | |
| return true; | |
| if (EnableRemarksSection == cl::BOU_FALSE) | |
| return false; | |
| assert(EnableRemarksSection == cl::BOU_UNSET); | |
| // We only need a section if we're in separate mode. | |
| if (RemarkSerializer->Mode != remarks::SerializerMode::Separate) | |
| return false; | |
| // Only some formats need a section: | |
| // * bitstream | |
| // * yaml-strtab | |
| switch (RemarkSerializer->SerializerFormat) { | |
| case remarks::Format::YAMLStrTab: | |
| case remarks::Format::Bitstream: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| char RemarkSetupFileError::ID = 0; | |
| char RemarkSetupPatternError::ID = 0; | |
| char RemarkSetupFormatError::ID = 0; | |
| Expected<std::unique_ptr<ToolOutputFile>> | |
| llvm::setupOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename, | |
| StringRef RemarksPasses, StringRef RemarksFormat, | |
| bool RemarksWithHotness, | |
| unsigned RemarksHotnessThreshold) { | |
| if (RemarksWithHotness) | |
| Context.setDiagnosticsHotnessRequested(true); | |
| if (RemarksHotnessThreshold) | |
| Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); | |
| if (RemarksFilename.empty()) | |
| return nullptr; | |
| Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat); | |
| if (Error E = Format.takeError()) | |
| return make_error<RemarkSetupFormatError>(std::move(E)); | |
| std::error_code EC; | |
| auto Flags = *Format == remarks::Format::YAML ? sys::fs::OF_Text | |
| : sys::fs::OF_None; | |
| auto RemarksFile = | |
| std::make_unique<ToolOutputFile>(RemarksFilename, EC, Flags); | |
| // We don't use llvm::FileError here because some diagnostics want the file | |
| // name separately. | |
| if (EC) | |
| return make_error<RemarkSetupFileError>(errorCodeToError(EC)); | |
| Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer = | |
| remarks::createRemarkSerializer( | |
| *Format, remarks::SerializerMode::Separate, RemarksFile->os()); | |
| if (Error E = RemarkSerializer.takeError()) | |
| return make_error<RemarkSetupFormatError>(std::move(E)); | |
| Context.setRemarkStreamer(std::make_unique<RemarkStreamer>( | |
| std::move(*RemarkSerializer), RemarksFilename)); | |
| if (!RemarksPasses.empty()) | |
| if (Error E = Context.getRemarkStreamer()->setFilter(RemarksPasses)) | |
| return make_error<RemarkSetupPatternError>(std::move(E)); | |
| return std::move(RemarksFile); | |
| } | |
| Error llvm::setupOptimizationRemarks(LLVMContext &Context, raw_ostream &OS, | |
| StringRef RemarksPasses, | |
| StringRef RemarksFormat, | |
| bool RemarksWithHotness, | |
| unsigned RemarksHotnessThreshold) { | |
| if (RemarksWithHotness) | |
| Context.setDiagnosticsHotnessRequested(true); | |
| if (RemarksHotnessThreshold) | |
| Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); | |
| Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat); | |
| if (Error E = Format.takeError()) | |
| return make_error<RemarkSetupFormatError>(std::move(E)); | |
| Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer = | |
| remarks::createRemarkSerializer(*Format, | |
| remarks::SerializerMode::Separate, OS); | |
| if (Error E = RemarkSerializer.takeError()) | |
| return make_error<RemarkSetupFormatError>(std::move(E)); | |
| Context.setRemarkStreamer( | |
| std::make_unique<RemarkStreamer>(std::move(*RemarkSerializer))); | |
| if (!RemarksPasses.empty()) | |
| if (Error E = Context.getRemarkStreamer()->setFilter(RemarksPasses)) | |
| return make_error<RemarkSetupPatternError>(std::move(E)); | |
| return Error::success(); | |
| } |