1 //===- llvm/IR/RemarkStreamer.cpp - Remark Streamer -*- C++ -------------*-===//
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 // This file contains the implementation of the remark outputting as part of
10 // LLVMContext.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/IR/RemarkStreamer.h"
15 #include "llvm/IR/DiagnosticInfo.h"
16 #include "llvm/IR/Function.h"
17 #include "llvm/IR/GlobalValue.h"
18 #include "llvm/Remarks/BitstreamRemarkSerializer.h"
19 #include "llvm/Remarks/RemarkFormat.h"
20 #include "llvm/Remarks/RemarkSerializer.h"
21 #include "llvm/Support/CommandLine.h"
22 
23 using namespace llvm;
24 
25 static cl::opt<cl::boolOrDefault> EnableRemarksSection(
26     "remarks-section",
27     cl::desc(
28         "Emit a section containing remark diagnostics metadata. By default, "
29         "this is enabled for the following formats: yaml-strtab, bitstream."),
30     cl::init(cl::BOU_UNSET), cl::Hidden);
31 
RemarkStreamer(std::unique_ptr<remarks::RemarkSerializer> RemarkSerializer,Optional<StringRef> FilenameIn)32 RemarkStreamer::RemarkStreamer(
33     std::unique_ptr<remarks::RemarkSerializer> RemarkSerializer,
34     Optional<StringRef> FilenameIn)
35     : PassFilter(), RemarkSerializer(std::move(RemarkSerializer)),
36       Filename(FilenameIn ? Optional<std::string>(FilenameIn->str()) : None) {}
37 
setFilter(StringRef Filter)38 Error RemarkStreamer::setFilter(StringRef Filter) {
39   Regex R = Regex(Filter);
40   std::string RegexError;
41   if (!R.isValid(RegexError))
42     return createStringError(std::make_error_code(std::errc::invalid_argument),
43                              RegexError.data());
44   PassFilter = std::move(R);
45   return Error::success();
46 }
47 
48 /// DiagnosticKind -> remarks::Type
toRemarkType(enum DiagnosticKind Kind)49 static remarks::Type toRemarkType(enum DiagnosticKind Kind) {
50   switch (Kind) {
51   default:
52     return remarks::Type::Unknown;
53   case DK_OptimizationRemark:
54   case DK_MachineOptimizationRemark:
55     return remarks::Type::Passed;
56   case DK_OptimizationRemarkMissed:
57   case DK_MachineOptimizationRemarkMissed:
58     return remarks::Type::Missed;
59   case DK_OptimizationRemarkAnalysis:
60   case DK_MachineOptimizationRemarkAnalysis:
61     return remarks::Type::Analysis;
62   case DK_OptimizationRemarkAnalysisFPCommute:
63     return remarks::Type::AnalysisFPCommute;
64   case DK_OptimizationRemarkAnalysisAliasing:
65     return remarks::Type::AnalysisAliasing;
66   case DK_OptimizationFailure:
67     return remarks::Type::Failure;
68   }
69 }
70 
71 /// DiagnosticLocation -> remarks::RemarkLocation.
72 static Optional<remarks::RemarkLocation>
toRemarkLocation(const DiagnosticLocation & DL)73 toRemarkLocation(const DiagnosticLocation &DL) {
74   if (!DL.isValid())
75     return None;
76   StringRef File = DL.getRelativePath();
77   unsigned Line = DL.getLine();
78   unsigned Col = DL.getColumn();
79   return remarks::RemarkLocation{File, Line, Col};
80 }
81 
82 /// LLVM Diagnostic -> Remark
83 remarks::Remark
toRemark(const DiagnosticInfoOptimizationBase & Diag)84 RemarkStreamer::toRemark(const DiagnosticInfoOptimizationBase &Diag) {
85   remarks::Remark R; // The result.
86   R.RemarkType = toRemarkType(static_cast<DiagnosticKind>(Diag.getKind()));
87   R.PassName = Diag.getPassName();
88   R.RemarkName = Diag.getRemarkName();
89   R.FunctionName =
90       GlobalValue::dropLLVMManglingEscape(Diag.getFunction().getName());
91   R.Loc = toRemarkLocation(Diag.getLocation());
92   R.Hotness = Diag.getHotness();
93 
94   for (const DiagnosticInfoOptimizationBase::Argument &Arg : Diag.getArgs()) {
95     R.Args.emplace_back();
96     R.Args.back().Key = Arg.Key;
97     R.Args.back().Val = Arg.Val;
98     R.Args.back().Loc = toRemarkLocation(Arg.Loc);
99   }
100 
101   return R;
102 }
103 
emit(const DiagnosticInfoOptimizationBase & Diag)104 void RemarkStreamer::emit(const DiagnosticInfoOptimizationBase &Diag) {
105   if (Optional<Regex> &Filter = PassFilter)
106     if (!Filter->match(Diag.getPassName()))
107       return;
108 
109   // First, convert the diagnostic to a remark.
110   remarks::Remark R = toRemark(Diag);
111   // Then, emit the remark through the serializer.
112   RemarkSerializer->emit(R);
113 }
114 
needsSection() const115 bool RemarkStreamer::needsSection() const {
116   if (EnableRemarksSection == cl::BOU_TRUE)
117     return true;
118 
119   if (EnableRemarksSection == cl::BOU_FALSE)
120     return false;
121 
122   assert(EnableRemarksSection == cl::BOU_UNSET);
123 
124   // We only need a section if we're in separate mode.
125   if (RemarkSerializer->Mode != remarks::SerializerMode::Separate)
126     return false;
127 
128   // Only some formats need a section:
129   // * bitstream
130   // * yaml-strtab
131   switch (RemarkSerializer->SerializerFormat) {
132   case remarks::Format::YAMLStrTab:
133   case remarks::Format::Bitstream:
134     return true;
135   default:
136     return false;
137   }
138 }
139 
140 char RemarkSetupFileError::ID = 0;
141 char RemarkSetupPatternError::ID = 0;
142 char RemarkSetupFormatError::ID = 0;
143 
144 Expected<std::unique_ptr<ToolOutputFile>>
setupOptimizationRemarks(LLVMContext & Context,StringRef RemarksFilename,StringRef RemarksPasses,StringRef RemarksFormat,bool RemarksWithHotness,unsigned RemarksHotnessThreshold)145 llvm::setupOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename,
146                                StringRef RemarksPasses, StringRef RemarksFormat,
147                                bool RemarksWithHotness,
148                                unsigned RemarksHotnessThreshold) {
149   if (RemarksWithHotness)
150     Context.setDiagnosticsHotnessRequested(true);
151 
152   if (RemarksHotnessThreshold)
153     Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold);
154 
155   if (RemarksFilename.empty())
156     return nullptr;
157 
158   Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat);
159   if (Error E = Format.takeError())
160     return make_error<RemarkSetupFormatError>(std::move(E));
161 
162   std::error_code EC;
163   auto Flags = *Format == remarks::Format::YAML ? sys::fs::OF_Text
164                                                 : sys::fs::OF_None;
165   auto RemarksFile =
166       std::make_unique<ToolOutputFile>(RemarksFilename, EC, Flags);
167   // We don't use llvm::FileError here because some diagnostics want the file
168   // name separately.
169   if (EC)
170     return make_error<RemarkSetupFileError>(errorCodeToError(EC));
171 
172   Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
173       remarks::createRemarkSerializer(
174           *Format, remarks::SerializerMode::Separate, RemarksFile->os());
175   if (Error E = RemarkSerializer.takeError())
176     return make_error<RemarkSetupFormatError>(std::move(E));
177 
178   Context.setRemarkStreamer(std::make_unique<RemarkStreamer>(
179       std::move(*RemarkSerializer), RemarksFilename));
180 
181   if (!RemarksPasses.empty())
182     if (Error E = Context.getRemarkStreamer()->setFilter(RemarksPasses))
183       return make_error<RemarkSetupPatternError>(std::move(E));
184 
185   return std::move(RemarksFile);
186 }
187 
setupOptimizationRemarks(LLVMContext & Context,raw_ostream & OS,StringRef RemarksPasses,StringRef RemarksFormat,bool RemarksWithHotness,unsigned RemarksHotnessThreshold)188 Error llvm::setupOptimizationRemarks(LLVMContext &Context, raw_ostream &OS,
189                                      StringRef RemarksPasses,
190                                      StringRef RemarksFormat,
191                                      bool RemarksWithHotness,
192                                      unsigned RemarksHotnessThreshold) {
193   if (RemarksWithHotness)
194     Context.setDiagnosticsHotnessRequested(true);
195 
196   if (RemarksHotnessThreshold)
197     Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold);
198 
199   Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat);
200   if (Error E = Format.takeError())
201     return make_error<RemarkSetupFormatError>(std::move(E));
202 
203   Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
204       remarks::createRemarkSerializer(*Format,
205                                       remarks::SerializerMode::Separate, OS);
206   if (Error E = RemarkSerializer.takeError())
207     return make_error<RemarkSetupFormatError>(std::move(E));
208 
209   Context.setRemarkStreamer(
210       std::make_unique<RemarkStreamer>(std::move(*RemarkSerializer)));
211 
212   if (!RemarksPasses.empty())
213     if (Error E = Context.getRemarkStreamer()->setFilter(RemarksPasses))
214       return make_error<RemarkSetupPatternError>(std::move(E));
215 
216   return Error::success();
217 }
218