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/Support/CommandLine.h"
18 using namespace llvm::remarks
;
20 // Use the same keys whether we use a string table or not (respectively, T is an
21 // unsigned or a StringRef).
23 static void mapRemarkHeader(yaml::IO
&io
, T PassName
, T RemarkName
,
24 Optional
<RemarkLocation
> RL
, T FunctionName
,
25 Optional
<uint64_t> Hotness
,
26 ArrayRef
<Argument
> Args
) {
27 io
.mapRequired("Pass", PassName
);
28 io
.mapRequired("Name", RemarkName
);
29 io
.mapOptional("DebugLoc", RL
);
30 io
.mapRequired("Function", FunctionName
);
31 io
.mapOptional("Hotness", Hotness
);
32 io
.mapOptional("Args", Args
);
38 template <> struct MappingTraits
<remarks::Remark
*> {
39 static void mapping(IO
&io
, remarks::Remark
*&Remark
) {
40 assert(io
.outputting() && "input not yet implemented");
42 if (io
.mapTag("!Passed", (Remark
->RemarkType
== Type::Passed
)))
44 else if (io
.mapTag("!Missed", (Remark
->RemarkType
== Type::Missed
)))
46 else if (io
.mapTag("!Analysis", (Remark
->RemarkType
== Type::Analysis
)))
48 else if (io
.mapTag("!AnalysisFPCommute",
49 (Remark
->RemarkType
== Type::AnalysisFPCommute
)))
51 else if (io
.mapTag("!AnalysisAliasing",
52 (Remark
->RemarkType
== Type::AnalysisAliasing
)))
54 else if (io
.mapTag("!Failure", (Remark
->RemarkType
== Type::Failure
)))
57 llvm_unreachable("Unknown remark type");
59 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
60 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
61 assert(Serializer
->StrTab
.hasValue() &&
62 "YAMLStrTabSerializer with no StrTab.");
63 StringTable
&StrTab
= *Serializer
->StrTab
;
64 unsigned PassID
= StrTab
.add(Remark
->PassName
).first
;
65 unsigned NameID
= StrTab
.add(Remark
->RemarkName
).first
;
66 unsigned FunctionID
= StrTab
.add(Remark
->FunctionName
).first
;
67 mapRemarkHeader(io
, PassID
, NameID
, Remark
->Loc
, FunctionID
,
68 Remark
->Hotness
, Remark
->Args
);
70 mapRemarkHeader(io
, Remark
->PassName
, Remark
->RemarkName
, Remark
->Loc
,
71 Remark
->FunctionName
, Remark
->Hotness
, Remark
->Args
);
76 template <> struct MappingTraits
<RemarkLocation
> {
77 static void mapping(IO
&io
, RemarkLocation
&RL
) {
78 assert(io
.outputting() && "input not yet implemented");
80 StringRef File
= RL
.SourceFilePath
;
81 unsigned Line
= RL
.SourceLine
;
82 unsigned Col
= RL
.SourceColumn
;
84 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
85 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
86 assert(Serializer
->StrTab
.hasValue() &&
87 "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(const std::string
&Value
) : Value(Value
) {}
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
.hasValue() &&
142 "YAMLStrTabSerializer with no StrTab.");
143 StringTable
&StrTab
= *Serializer
->StrTab
;
144 auto ValueID
= StrTab
.add(A
.Val
).first
;
145 io
.mapRequired(A
.Key
.data(), ValueID
);
146 } else if (StringRef(A
.Val
).count('\n') > 1) {
147 StringBlockVal
S(A
.Val
);
148 io
.mapRequired(A
.Key
.data(), S
);
150 io
.mapRequired(A
.Key
.data(), A
.Val
);
152 io
.mapOptional("DebugLoc", A
.Loc
);
156 } // end namespace yaml
157 } // end namespace llvm
159 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument
)
161 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream
&OS
, SerializerMode Mode
,
162 Optional
<StringTable
> StrTabIn
)
163 : YAMLRemarkSerializer(Format::YAML
, OS
, Mode
, std::move(StrTabIn
)) {}
165 YAMLRemarkSerializer::YAMLRemarkSerializer(Format SerializerFormat
,
166 raw_ostream
&OS
, SerializerMode Mode
,
167 Optional
<StringTable
> StrTabIn
)
168 : RemarkSerializer(SerializerFormat
, OS
, Mode
),
169 YAMLOutput(OS
, reinterpret_cast<void *>(this)) {
170 StrTab
= std::move(StrTabIn
);
173 void YAMLRemarkSerializer::emit(const Remark
&Remark
) {
174 // Again, YAMLTraits expect a non-const object for inputting, but we're not
176 auto R
= const_cast<remarks::Remark
*>(&Remark
);
180 std::unique_ptr
<MetaSerializer
>
181 YAMLRemarkSerializer::metaSerializer(raw_ostream
&OS
,
182 Optional
<StringRef
> ExternalFilename
) {
183 return std::make_unique
<YAMLMetaSerializer
>(OS
, ExternalFilename
);
186 void YAMLStrTabRemarkSerializer::emit(const Remark
&Remark
) {
187 // In standalone mode, for the serializer with a string table, emit the
188 // metadata first and set DidEmitMeta to avoid emitting it again.
189 if (Mode
== SerializerMode::Standalone
&& !DidEmitMeta
) {
190 std::unique_ptr
<MetaSerializer
> MetaSerializer
=
191 metaSerializer(OS
, /*ExternalFilename=*/None
);
192 MetaSerializer
->emit();
196 // Then do the usual remark emission.
197 YAMLRemarkSerializer::emit(Remark
);
200 std::unique_ptr
<MetaSerializer
> YAMLStrTabRemarkSerializer::metaSerializer(
201 raw_ostream
&OS
, Optional
<StringRef
> ExternalFilename
) {
203 return std::make_unique
<YAMLStrTabMetaSerializer
>(OS
, ExternalFilename
,
207 static void emitMagic(raw_ostream
&OS
) {
208 // Emit the magic number.
209 OS
<< remarks::Magic
;
210 // Explicitly emit a '\0'.
214 static void emitVersion(raw_ostream
&OS
) {
215 // Emit the version number: little-endian uint64_t.
216 std::array
<char, 8> Version
;
217 support::endian::write64le(Version
.data(), remarks::CurrentRemarkVersion
);
218 OS
.write(Version
.data(), Version
.size());
221 static void emitStrTab(raw_ostream
&OS
, Optional
<const StringTable
*> StrTab
) {
222 // Emit the string table in the section.
223 uint64_t StrTabSize
= StrTab
? (*StrTab
)->SerializedSize
: 0;
224 // Emit the total size of the string table (the size itself excluded):
225 // little-endian uint64_t.
226 // Note: even if no string table is used, emit 0.
227 std::array
<char, 8> StrTabSizeBuf
;
228 support::endian::write64le(StrTabSizeBuf
.data(), StrTabSize
);
229 OS
.write(StrTabSizeBuf
.data(), StrTabSizeBuf
.size());
231 (*StrTab
)->serialize(OS
);
234 static void emitExternalFile(raw_ostream
&OS
, StringRef Filename
) {
235 // Emit the null-terminated absolute path to the remark file.
236 SmallString
<128> FilenameBuf
= Filename
;
237 sys::fs::make_absolute(FilenameBuf
);
238 assert(!FilenameBuf
.empty() && "The filename can't be empty.");
239 OS
.write(FilenameBuf
.data(), FilenameBuf
.size());
243 void YAMLMetaSerializer::emit() {
246 emitStrTab(OS
, None
);
247 if (ExternalFilename
)
248 emitExternalFile(OS
, *ExternalFilename
);
251 void YAMLStrTabMetaSerializer::emit() {
254 emitStrTab(OS
, &StrTab
);
255 if (ExternalFilename
)
256 emitExternalFile(OS
, *ExternalFilename
);