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"
16 #include "llvm/Support/FileSystem.h"
19 using namespace llvm::remarks
;
21 // Use the same keys whether we use a string table or not (respectively, T is an
22 // unsigned or a StringRef).
24 static void mapRemarkHeader(yaml::IO
&io
, T PassName
, T RemarkName
,
25 Optional
<RemarkLocation
> RL
, T FunctionName
,
26 Optional
<uint64_t> Hotness
,
27 ArrayRef
<Argument
> Args
) {
28 io
.mapRequired("Pass", PassName
);
29 io
.mapRequired("Name", RemarkName
);
30 io
.mapOptional("DebugLoc", RL
);
31 io
.mapRequired("Function", FunctionName
);
32 io
.mapOptional("Hotness", Hotness
);
33 io
.mapOptional("Args", Args
);
39 template <> struct MappingTraits
<remarks::Remark
*> {
40 static void mapping(IO
&io
, remarks::Remark
*&Remark
) {
41 assert(io
.outputting() && "input not yet implemented");
43 if (io
.mapTag("!Passed", (Remark
->RemarkType
== Type::Passed
)))
45 else if (io
.mapTag("!Missed", (Remark
->RemarkType
== Type::Missed
)))
47 else if (io
.mapTag("!Analysis", (Remark
->RemarkType
== Type::Analysis
)))
49 else if (io
.mapTag("!AnalysisFPCommute",
50 (Remark
->RemarkType
== Type::AnalysisFPCommute
)))
52 else if (io
.mapTag("!AnalysisAliasing",
53 (Remark
->RemarkType
== Type::AnalysisAliasing
)))
55 else if (io
.mapTag("!Failure", (Remark
->RemarkType
== Type::Failure
)))
58 llvm_unreachable("Unknown remark type");
60 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
61 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
62 assert(Serializer
->StrTab
.hasValue() &&
63 "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
.hasValue() &&
88 "YAMLStrTabSerializer with no StrTab.");
89 StringTable
&StrTab
= *Serializer
->StrTab
;
90 unsigned FileID
= StrTab
.add(File
).first
;
91 io
.mapRequired("File", FileID
);
93 io
.mapRequired("File", File
);
96 io
.mapRequired("Line", Line
);
97 io
.mapRequired("Column", Col
);
100 static const bool flow
= true;
103 /// Helper struct for multiline string block literals. Use this type to preserve
104 /// newlines in strings.
105 struct StringBlockVal
{
107 StringBlockVal(StringRef R
) : Value(R
) {}
110 template <> struct BlockScalarTraits
<StringBlockVal
> {
111 static void output(const StringBlockVal
&S
, void *Ctx
, raw_ostream
&OS
) {
112 return ScalarTraits
<StringRef
>::output(S
.Value
, Ctx
, OS
);
115 static StringRef
input(StringRef Scalar
, void *Ctx
, StringBlockVal
&S
) {
116 return ScalarTraits
<StringRef
>::input(Scalar
, Ctx
, S
.Value
);
120 /// ArrayRef is not really compatible with the YAMLTraits. Everything should be
121 /// immutable in an ArrayRef, while the SequenceTraits expect a mutable version
122 /// for inputting, but we're only using the outputting capabilities here.
123 /// This is a hack, but still nicer than having to manually call the YAMLIO
124 /// internal methods.
125 /// Keep this in this file so that it doesn't get misused from YAMLTraits.h.
126 template <typename T
> struct SequenceTraits
<ArrayRef
<T
>> {
127 static size_t size(IO
&io
, ArrayRef
<T
> &seq
) { return seq
.size(); }
128 static Argument
&element(IO
&io
, ArrayRef
<T
> &seq
, size_t index
) {
129 assert(io
.outputting() && "input not yet implemented");
130 // The assert above should make this "safer" to satisfy the YAMLTraits.
131 return const_cast<T
&>(seq
[index
]);
135 /// Implement this as a mapping for now to get proper quotation for the value.
136 template <> struct MappingTraits
<Argument
> {
137 static void mapping(IO
&io
, Argument
&A
) {
138 assert(io
.outputting() && "input not yet implemented");
140 if (auto *Serializer
= dyn_cast
<YAMLStrTabRemarkSerializer
>(
141 reinterpret_cast<RemarkSerializer
*>(io
.getContext()))) {
142 assert(Serializer
->StrTab
.hasValue() &&
143 "YAMLStrTabSerializer with no StrTab.");
144 StringTable
&StrTab
= *Serializer
->StrTab
;
145 auto ValueID
= StrTab
.add(A
.Val
).first
;
146 io
.mapRequired(A
.Key
.data(), ValueID
);
147 } else if (StringRef(A
.Val
).count('\n') > 1) {
148 StringBlockVal
S(A
.Val
);
149 io
.mapRequired(A
.Key
.data(), S
);
151 io
.mapRequired(A
.Key
.data(), A
.Val
);
153 io
.mapOptional("DebugLoc", A
.Loc
);
157 } // end namespace yaml
158 } // end namespace llvm
160 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument
)
162 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream
&OS
, SerializerMode Mode
,
163 Optional
<StringTable
> StrTabIn
)
164 : YAMLRemarkSerializer(Format::YAML
, OS
, Mode
, std::move(StrTabIn
)) {}
166 YAMLRemarkSerializer::YAMLRemarkSerializer(Format SerializerFormat
,
167 raw_ostream
&OS
, SerializerMode Mode
,
168 Optional
<StringTable
> StrTabIn
)
169 : RemarkSerializer(SerializerFormat
, OS
, Mode
),
170 YAMLOutput(OS
, reinterpret_cast<void *>(this)) {
171 StrTab
= std::move(StrTabIn
);
174 void YAMLRemarkSerializer::emit(const Remark
&Remark
) {
175 // Again, YAMLTraits expect a non-const object for inputting, but we're not
177 auto R
= const_cast<remarks::Remark
*>(&Remark
);
181 std::unique_ptr
<MetaSerializer
>
182 YAMLRemarkSerializer::metaSerializer(raw_ostream
&OS
,
183 Optional
<StringRef
> ExternalFilename
) {
184 return std::make_unique
<YAMLMetaSerializer
>(OS
, ExternalFilename
);
187 void YAMLStrTabRemarkSerializer::emit(const Remark
&Remark
) {
188 // In standalone mode, for the serializer with a string table, emit the
189 // metadata first and set DidEmitMeta to avoid emitting it again.
190 if (Mode
== SerializerMode::Standalone
&& !DidEmitMeta
) {
191 std::unique_ptr
<MetaSerializer
> MetaSerializer
=
192 metaSerializer(OS
, /*ExternalFilename=*/None
);
193 MetaSerializer
->emit();
197 // Then do the usual remark emission.
198 YAMLRemarkSerializer::emit(Remark
);
201 std::unique_ptr
<MetaSerializer
> YAMLStrTabRemarkSerializer::metaSerializer(
202 raw_ostream
&OS
, Optional
<StringRef
> ExternalFilename
) {
204 return std::make_unique
<YAMLStrTabMetaSerializer
>(OS
, ExternalFilename
,
208 static void emitMagic(raw_ostream
&OS
) {
209 // Emit the magic number.
210 OS
<< remarks::Magic
;
211 // Explicitly emit a '\0'.
215 static void emitVersion(raw_ostream
&OS
) {
216 // Emit the version number: little-endian uint64_t.
217 std::array
<char, 8> Version
;
218 support::endian::write64le(Version
.data(), remarks::CurrentRemarkVersion
);
219 OS
.write(Version
.data(), Version
.size());
222 static void emitStrTab(raw_ostream
&OS
, Optional
<const StringTable
*> StrTab
) {
223 // Emit the string table in the section.
224 uint64_t StrTabSize
= StrTab
? (*StrTab
)->SerializedSize
: 0;
225 // Emit the total size of the string table (the size itself excluded):
226 // little-endian uint64_t.
227 // Note: even if no string table is used, emit 0.
228 std::array
<char, 8> StrTabSizeBuf
;
229 support::endian::write64le(StrTabSizeBuf
.data(), StrTabSize
);
230 OS
.write(StrTabSizeBuf
.data(), StrTabSizeBuf
.size());
232 (*StrTab
)->serialize(OS
);
235 static void emitExternalFile(raw_ostream
&OS
, StringRef Filename
) {
236 // Emit the null-terminated absolute path to the remark file.
237 SmallString
<128> FilenameBuf
= Filename
;
238 sys::fs::make_absolute(FilenameBuf
);
239 assert(!FilenameBuf
.empty() && "The filename can't be empty.");
240 OS
.write(FilenameBuf
.data(), FilenameBuf
.size());
244 void YAMLMetaSerializer::emit() {
247 emitStrTab(OS
, None
);
248 if (ExternalFilename
)
249 emitExternalFile(OS
, *ExternalFilename
);
252 void YAMLStrTabMetaSerializer::emit() {
255 emitStrTab(OS
, &StrTab
);
256 if (ExternalFilename
)
257 emitExternalFile(OS
, *ExternalFilename
);