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