1 //===- YAMLRemarkSerializer.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file provides the implementation of the YAML remark serializer using
12 //===----------------------------------------------------------------------===//
14 #include "llvm/Remarks/YAMLRemarkSerializer.h"
15 #include "llvm/Remarks/Remark.h"
16 #include "llvm/Support/FileSystem.h"
20 using namespace llvm::remarks
;
22 // Use the same keys whether we use a string table or not (respectively, T is an
23 // unsigned or a StringRef).
25 static void mapRemarkHeader(yaml::IO
&io
, T PassName
, T RemarkName
,
26 std::optional
<RemarkLocation
> RL
, T FunctionName
,
27 std::optional
<uint64_t> Hotness
,
28 ArrayRef
<Argument
> Args
) {
29 io
.mapRequired("Pass", PassName
);
30 io
.mapRequired("Name", RemarkName
);
31 io
.mapOptional("DebugLoc", RL
);
32 io
.mapRequired("Function", FunctionName
);
33 io
.mapOptional("Hotness", Hotness
);
34 io
.mapOptional("Args", Args
);
40 template <> struct MappingTraits
<remarks::Remark
*> {
41 static void mapping(IO
&io
, remarks::Remark
*&Remark
) {
42 assert(io
.outputting() && "input not yet implemented");
44 if (io
.mapTag("!Passed", (Remark
->RemarkType
== Type::Passed
)))
46 else if (io
.mapTag("!Missed", (Remark
->RemarkType
== Type::Missed
)))
48 else if (io
.mapTag("!Analysis", (Remark
->RemarkType
== Type::Analysis
)))
50 else if (io
.mapTag("!AnalysisFPCommute",
51 (Remark
->RemarkType
== Type::AnalysisFPCommute
)))
53 else if (io
.mapTag("!AnalysisAliasing",
54 (Remark
->RemarkType
== Type::AnalysisAliasing
)))
56 else if (io
.mapTag("!Failure", (Remark
->RemarkType
== Type::Failure
)))
59 llvm_unreachable("Unknown remark type");
61 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
62 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
63 assert(Serializer
->StrTab
&& "YAMLStrTabSerializer with no StrTab.");
64 StringTable
&StrTab
= *Serializer
->StrTab
;
65 unsigned PassID
= StrTab
.add(Remark
->PassName
).first
;
66 unsigned NameID
= StrTab
.add(Remark
->RemarkName
).first
;
67 unsigned FunctionID
= StrTab
.add(Remark
->FunctionName
).first
;
68 mapRemarkHeader(io
, PassID
, NameID
, Remark
->Loc
, FunctionID
,
69 Remark
->Hotness
, Remark
->Args
);
71 mapRemarkHeader(io
, Remark
->PassName
, Remark
->RemarkName
, Remark
->Loc
,
72 Remark
->FunctionName
, Remark
->Hotness
, Remark
->Args
);
77 template <> struct MappingTraits
<RemarkLocation
> {
78 static void mapping(IO
&io
, RemarkLocation
&RL
) {
79 assert(io
.outputting() && "input not yet implemented");
81 StringRef File
= RL
.SourceFilePath
;
82 unsigned Line
= RL
.SourceLine
;
83 unsigned Col
= RL
.SourceColumn
;
85 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
86 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
87 assert(Serializer
->StrTab
&& "YAMLStrTabSerializer with no StrTab.");
88 StringTable
&StrTab
= *Serializer
->StrTab
;
89 unsigned FileID
= StrTab
.add(File
).first
;
90 io
.mapRequired("File", FileID
);
92 io
.mapRequired("File", File
);
95 io
.mapRequired("Line", Line
);
96 io
.mapRequired("Column", Col
);
99 static const bool flow
= true;
102 /// Helper struct for multiline string block literals. Use this type to preserve
103 /// newlines in strings.
104 struct StringBlockVal
{
106 StringBlockVal(StringRef R
) : Value(R
) {}
109 template <> struct BlockScalarTraits
<StringBlockVal
> {
110 static void output(const StringBlockVal
&S
, void *Ctx
, raw_ostream
&OS
) {
111 return ScalarTraits
<StringRef
>::output(S
.Value
, Ctx
, OS
);
114 static StringRef
input(StringRef Scalar
, void *Ctx
, StringBlockVal
&S
) {
115 return ScalarTraits
<StringRef
>::input(Scalar
, Ctx
, S
.Value
);
119 /// ArrayRef is not really compatible with the YAMLTraits. Everything should be
120 /// immutable in an ArrayRef, while the SequenceTraits expect a mutable version
121 /// for inputting, but we're only using the outputting capabilities here.
122 /// This is a hack, but still nicer than having to manually call the YAMLIO
123 /// internal methods.
124 /// Keep this in this file so that it doesn't get misused from YAMLTraits.h.
125 template <typename T
> struct SequenceTraits
<ArrayRef
<T
>> {
126 static size_t size(IO
&io
, ArrayRef
<T
> &seq
) { return seq
.size(); }
127 static Argument
&element(IO
&io
, ArrayRef
<T
> &seq
, size_t index
) {
128 assert(io
.outputting() && "input not yet implemented");
129 // The assert above should make this "safer" to satisfy the YAMLTraits.
130 return const_cast<T
&>(seq
[index
]);
134 /// Implement this as a mapping for now to get proper quotation for the value.
135 template <> struct MappingTraits
<Argument
> {
136 static void mapping(IO
&io
, Argument
&A
) {
137 assert(io
.outputting() && "input not yet implemented");
139 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
140 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
141 assert(Serializer
->StrTab
&& "YAMLStrTabSerializer with no StrTab.");
142 StringTable
&StrTab
= *Serializer
->StrTab
;
143 auto ValueID
= StrTab
.add(A
.Val
).first
;
144 io
.mapRequired(A
.Key
.data(), ValueID
);
145 } else if (StringRef(A
.Val
).count('\n') > 1) {
146 StringBlockVal
S(A
.Val
);
147 io
.mapRequired(A
.Key
.data(), S
);
149 io
.mapRequired(A
.Key
.data(), A
.Val
);
151 io
.mapOptional("DebugLoc", A
.Loc
);
155 } // end namespace yaml
156 } // end namespace llvm
158 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument
)
160 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream
&OS
, SerializerMode Mode
,
161 std::optional
<StringTable
> StrTabIn
)
162 : YAMLRemarkSerializer(Format::YAML
, OS
, Mode
, std::move(StrTabIn
)) {}
164 YAMLRemarkSerializer::YAMLRemarkSerializer(Format SerializerFormat
,
165 raw_ostream
&OS
, SerializerMode Mode
,
166 std::optional
<StringTable
> StrTabIn
)
167 : RemarkSerializer(SerializerFormat
, OS
, Mode
),
168 YAMLOutput(OS
, reinterpret_cast<void *>(this)) {
169 StrTab
= std::move(StrTabIn
);
172 void YAMLRemarkSerializer::emit(const Remark
&Remark
) {
173 // Again, YAMLTraits expect a non-const object for inputting, but we're not
175 auto R
= const_cast<remarks::Remark
*>(&Remark
);
179 std::unique_ptr
<MetaSerializer
> YAMLRemarkSerializer::metaSerializer(
180 raw_ostream
&OS
, std::optional
<StringRef
> ExternalFilename
) {
181 return std::make_unique
<YAMLMetaSerializer
>(OS
, ExternalFilename
);
184 void YAMLStrTabRemarkSerializer::emit(const Remark
&Remark
) {
185 // In standalone mode, for the serializer with a string table, emit the
186 // metadata first and set DidEmitMeta to avoid emitting it again.
187 if (Mode
== SerializerMode::Standalone
&& !DidEmitMeta
) {
188 std::unique_ptr
<MetaSerializer
> MetaSerializer
=
189 metaSerializer(OS
, /*ExternalFilename=*/std::nullopt
);
190 MetaSerializer
->emit();
194 // Then do the usual remark emission.
195 YAMLRemarkSerializer::emit(Remark
);
198 std::unique_ptr
<MetaSerializer
> YAMLStrTabRemarkSerializer::metaSerializer(
199 raw_ostream
&OS
, std::optional
<StringRef
> ExternalFilename
) {
201 return std::make_unique
<YAMLStrTabMetaSerializer
>(OS
, ExternalFilename
,
205 static void emitMagic(raw_ostream
&OS
) {
206 // Emit the magic number.
207 OS
<< remarks::Magic
;
208 // Explicitly emit a '\0'.
212 static void emitVersion(raw_ostream
&OS
) {
213 // Emit the version number: little-endian uint64_t.
214 std::array
<char, 8> Version
;
215 support::endian::write64le(Version
.data(), remarks::CurrentRemarkVersion
);
216 OS
.write(Version
.data(), Version
.size());
219 static void emitStrTab(raw_ostream
&OS
,
220 std::optional
<const StringTable
*> StrTab
) {
221 // Emit the string table in the section.
222 uint64_t StrTabSize
= StrTab
? (*StrTab
)->SerializedSize
: 0;
223 // Emit the total size of the string table (the size itself excluded):
224 // little-endian uint64_t.
225 // Note: even if no string table is used, emit 0.
226 std::array
<char, 8> StrTabSizeBuf
;
227 support::endian::write64le(StrTabSizeBuf
.data(), StrTabSize
);
228 OS
.write(StrTabSizeBuf
.data(), StrTabSizeBuf
.size());
230 (*StrTab
)->serialize(OS
);
233 static void emitExternalFile(raw_ostream
&OS
, StringRef Filename
) {
234 // Emit the null-terminated absolute path to the remark file.
235 SmallString
<128> FilenameBuf
= Filename
;
236 sys::fs::make_absolute(FilenameBuf
);
237 assert(!FilenameBuf
.empty() && "The filename can't be empty.");
238 OS
.write(FilenameBuf
.data(), FilenameBuf
.size());
242 void YAMLMetaSerializer::emit() {
245 emitStrTab(OS
, std::nullopt
);
246 if (ExternalFilename
)
247 emitExternalFile(OS
, *ExternalFilename
);
250 void YAMLStrTabMetaSerializer::emit() {
253 emitStrTab(OS
, &StrTab
);
254 if (ExternalFilename
)
255 emitExternalFile(OS
, *ExternalFilename
);