1//===- llvm/IR/LLVMRemarkStreamer.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 conversion between IR
10// Diagnostics and serializable remarks::Remark objects.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/IR/LLVMRemarkStreamer.h"
15#include "llvm/IR/DiagnosticInfo.h"
16#include "llvm/IR/Function.h"
17#include "llvm/IR/GlobalValue.h"
18#include "llvm/Remarks/RemarkStreamer.h"
19#include "llvm/Support/FileSystem.h"
20#include "llvm/Support/ToolOutputFile.h"
21#include "llvm/Support/VirtualFileSystem.h"
22#include <optional>
23
24using namespace llvm;
25
26/// DiagnosticKind -> remarks::Type
27static remarks::Type toRemarkType(enum DiagnosticKind Kind) {
28 switch (Kind) {
29 default:
30 return remarks::Type::Unknown;
31 case DK_OptimizationRemark:
32 case DK_MachineOptimizationRemark:
33 return remarks::Type::Passed;
34 case DK_OptimizationRemarkMissed:
35 case DK_MachineOptimizationRemarkMissed:
36 return remarks::Type::Missed;
37 case DK_OptimizationRemarkAnalysis:
38 case DK_MachineOptimizationRemarkAnalysis:
39 return remarks::Type::Analysis;
40 case DK_OptimizationRemarkAnalysisFPCommute:
41 return remarks::Type::AnalysisFPCommute;
42 case DK_OptimizationRemarkAnalysisAliasing:
43 return remarks::Type::AnalysisAliasing;
44 case DK_OptimizationFailure:
45 return remarks::Type::Failure;
46 }
47}
48
49/// DiagnosticLocation -> remarks::RemarkLocation.
50static std::optional<remarks::RemarkLocation>
51toRemarkLocation(const DiagnosticLocation &DL) {
52 if (!DL.isValid())
53 return std::nullopt;
54 StringRef File = DL.getRelativePath();
55 unsigned Line = DL.getLine();
56 unsigned Col = DL.getColumn();
57 return remarks::RemarkLocation{.SourceFilePath: File, .SourceLine: Line, .SourceColumn: Col};
58}
59
60/// LLVM Diagnostic -> Remark
61remarks::Remark
62LLVMRemarkStreamer::toRemark(const DiagnosticInfoOptimizationBase &Diag) const {
63 remarks::Remark R; // The result.
64 R.RemarkType = toRemarkType(Kind: static_cast<DiagnosticKind>(Diag.getKind()));
65 R.PassName = Diag.getPassName();
66 R.RemarkName = Diag.getRemarkName();
67 R.FunctionName =
68 GlobalValue::dropLLVMManglingEscape(Name: Diag.getFunction().getName());
69 R.Loc = toRemarkLocation(DL: Diag.getLocation());
70 R.Hotness = Diag.getHotness();
71
72 for (const DiagnosticInfoOptimizationBase::Argument &Arg : Diag.getArgs()) {
73 R.Args.emplace_back();
74 R.Args.back().Key = Arg.Key;
75 R.Args.back().Val = Arg.Val;
76 R.Args.back().Loc = toRemarkLocation(DL: Arg.Loc);
77 }
78
79 return R;
80}
81
82void LLVMRemarkStreamer::emit(const DiagnosticInfoOptimizationBase &Diag) {
83 if (!RS.matchesFilter(Str: Diag.getPassName()))
84 return;
85
86 // First, convert the diagnostic to a remark.
87 remarks::Remark R = toRemark(Diag);
88 // Then, emit the remark through the serializer.
89 RS.getSerializer().emit(Remark: R);
90}
91
92char LLVMRemarkSetupFileError::ID = 0;
93char LLVMRemarkSetupPatternError::ID = 0;
94char LLVMRemarkSetupFormatError::ID = 0;
95
96Expected<LLVMRemarkFileHandle> llvm::setupLLVMOptimizationRemarks(
97 LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses,
98 StringRef RemarksFormat, bool RemarksWithHotness,
99 std::optional<uint64_t> RemarksHotnessThreshold) {
100 if (RemarksWithHotness || RemarksHotnessThreshold.value_or(u: 1))
101 Context.setDiagnosticsHotnessRequested(true);
102
103 Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold);
104
105 if (RemarksFilename.empty())
106 return LLVMRemarkFileHandle();
107
108 Expected<remarks::Format> Format = remarks::parseFormat(FormatStr: RemarksFormat);
109 if (Error E = Format.takeError())
110 return make_error<LLVMRemarkSetupFormatError>(Args: std::move(E));
111
112 // Resolve the filename up front, so that the path recorded in the remarks
113 // section always denotes the file that gets created below.
114 SmallString<128> AbsRemarksFilename(RemarksFilename);
115 if (RemarksFilename != "-") {
116 if (std::error_code EC =
117 Context.getVirtualFileSystem().makeAbsolute(Path&: AbsRemarksFilename))
118 return make_error<LLVMRemarkSetupFileError>(Args: errorCodeToError(EC));
119 RemarksFilename = AbsRemarksFilename;
120 }
121
122 std::error_code EC;
123 auto Flags = *Format == remarks::Format::YAML ? sys::fs::OF_TextWithCRLF
124 : sys::fs::OF_None;
125 auto RemarksFile =
126 std::make_unique<ToolOutputFile>(args&: RemarksFilename, args&: EC, args&: Flags);
127 // We don't use llvm::FileError here because some diagnostics want the file
128 // name separately.
129 if (EC)
130 return make_error<LLVMRemarkSetupFileError>(Args: errorCodeToError(EC));
131
132 Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
133 remarks::createRemarkSerializer(RemarksFormat: *Format, OS&: RemarksFile->os());
134 if (Error E = RemarkSerializer.takeError())
135 return make_error<LLVMRemarkSetupFormatError>(Args: std::move(E));
136
137 auto RS = std::make_unique<remarks::RemarkStreamer>(
138 args: std::move(*RemarkSerializer), args&: RemarksFilename);
139
140 if (!RemarksPasses.empty())
141 if (Error E = RS->setFilter(RemarksPasses)) {
142 RS->releaseSerializer();
143 return make_error<LLVMRemarkSetupPatternError>(Args: std::move(E));
144 }
145
146 // Install the main remark streamer. Only install this after setting the
147 // filter, because this might fail.
148 Context.setMainRemarkStreamer(std::move(RS));
149
150 // Create LLVM's optimization remarks streamer.
151 Context.setLLVMRemarkStreamer(
152 std::make_unique<LLVMRemarkStreamer>(args&: *Context.getMainRemarkStreamer()));
153
154 return LLVMRemarkFileHandle{std::move(RemarksFile), Context};
155}
156
157void LLVMRemarkFileHandle::Finalizer::finalize() {
158 if (!Context)
159 return;
160 finalizeLLVMOptimizationRemarks(Context&: *Context);
161 Context = nullptr;
162}
163
164Error llvm::setupLLVMOptimizationRemarks(
165 LLVMContext &Context, raw_ostream &OS, StringRef RemarksPasses,
166 StringRef RemarksFormat, bool RemarksWithHotness,
167 std::optional<uint64_t> RemarksHotnessThreshold) {
168 if (RemarksWithHotness || RemarksHotnessThreshold.value_or(u: 1))
169 Context.setDiagnosticsHotnessRequested(true);
170
171 Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold);
172
173 Expected<remarks::Format> Format = remarks::parseFormat(FormatStr: RemarksFormat);
174 if (Error E = Format.takeError())
175 return make_error<LLVMRemarkSetupFormatError>(Args: std::move(E));
176
177 Expected<std::unique_ptr<remarks::RemarkSerializer>> RemarkSerializer =
178 remarks::createRemarkSerializer(RemarksFormat: *Format, OS);
179 if (Error E = RemarkSerializer.takeError())
180 return make_error<LLVMRemarkSetupFormatError>(Args: std::move(E));
181
182 auto RS =
183 std::make_unique<remarks::RemarkStreamer>(args: std::move(*RemarkSerializer));
184
185 if (!RemarksPasses.empty())
186 if (Error E = RS->setFilter(RemarksPasses)) {
187 RS->releaseSerializer();
188 return make_error<LLVMRemarkSetupPatternError>(Args: std::move(E));
189 }
190
191 // Install the main remark streamer. Only install this after setting the
192 // filter, because this might fail.
193 Context.setMainRemarkStreamer(std::move(RS));
194
195 // Create LLVM's optimization remarks streamer.
196 Context.setLLVMRemarkStreamer(
197 std::make_unique<LLVMRemarkStreamer>(args&: *Context.getMainRemarkStreamer()));
198
199 return Error::success();
200}
201
202void llvm::finalizeLLVMOptimizationRemarks(LLVMContext &Context) {
203 Context.setLLVMRemarkStreamer(nullptr);
204 if (auto *RS = Context.getMainRemarkStreamer()) {
205 RS->releaseSerializer();
206 Context.setMainRemarkStreamer(nullptr);
207 }
208}
209