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 (Optional
<StringTable
> &StrTab
=
60 reinterpret_cast<YAMLRemarkSerializer
*>(io
.getContext())->StrTab
) {
61 unsigned PassID
= StrTab
->add(Remark
->PassName
).first
;
62 unsigned NameID
= StrTab
->add(Remark
->RemarkName
).first
;
63 unsigned FunctionID
= StrTab
->add(Remark
->FunctionName
).first
;
64 mapRemarkHeader(io
, PassID
, NameID
, Remark
->Loc
, FunctionID
,
65 Remark
->Hotness
, Remark
->Args
);
67 mapRemarkHeader(io
, Remark
->PassName
, Remark
->RemarkName
, Remark
->Loc
,
68 Remark
->FunctionName
, Remark
->Hotness
, Remark
->Args
);
73 template <> struct MappingTraits
<RemarkLocation
> {
74 static void mapping(IO
&io
, RemarkLocation
&RL
) {
75 assert(io
.outputting() && "input not yet implemented");
77 StringRef File
= RL
.SourceFilePath
;
78 unsigned Line
= RL
.SourceLine
;
79 unsigned Col
= RL
.SourceColumn
;
81 if (Optional
<StringTable
> &StrTab
=
82 reinterpret_cast<YAMLRemarkSerializer
*>(io
.getContext())->StrTab
) {
83 unsigned FileID
= StrTab
->add(File
).first
;
84 io
.mapRequired("File", FileID
);
86 io
.mapRequired("File", File
);
89 io
.mapRequired("Line", Line
);
90 io
.mapRequired("Column", Col
);
93 static const bool flow
= true;
96 /// Helper struct for multiline string block literals. Use this type to preserve
97 /// newlines in strings.
98 struct StringBlockVal
{
100 StringBlockVal(const std::string
&Value
) : Value(Value
) {}
103 template <> struct BlockScalarTraits
<StringBlockVal
> {
104 static void output(const StringBlockVal
&S
, void *Ctx
, raw_ostream
&OS
) {
105 return ScalarTraits
<StringRef
>::output(S
.Value
, Ctx
, OS
);
108 static StringRef
input(StringRef Scalar
, void *Ctx
, StringBlockVal
&S
) {
109 return ScalarTraits
<StringRef
>::input(Scalar
, Ctx
, S
.Value
);
113 /// ArrayRef is not really compatible with the YAMLTraits. Everything should be
114 /// immutable in an ArrayRef, while the SequenceTraits expect a mutable version
115 /// for inputting, but we're only using the outputting capabilities here.
116 /// This is a hack, but still nicer than having to manually call the YAMLIO
117 /// internal methods.
118 /// Keep this in this file so that it doesn't get misused from YAMLTraits.h.
119 template <typename T
> struct SequenceTraits
<ArrayRef
<T
>> {
120 static size_t size(IO
&io
, ArrayRef
<T
> &seq
) { return seq
.size(); }
121 static Argument
&element(IO
&io
, ArrayRef
<T
> &seq
, size_t index
) {
122 assert(io
.outputting() && "input not yet implemented");
123 // The assert above should make this "safer" to satisfy the YAMLTraits.
124 return const_cast<T
&>(seq
[index
]);
128 /// Implement this as a mapping for now to get proper quotation for the value.
129 template <> struct MappingTraits
<Argument
> {
130 static void mapping(IO
&io
, Argument
&A
) {
131 assert(io
.outputting() && "input not yet implemented");
133 if (Optional
<StringTable
> &StrTab
=
134 reinterpret_cast<YAMLRemarkSerializer
*>(io
.getContext())->StrTab
) {
135 auto ValueID
= StrTab
->add(A
.Val
).first
;
136 io
.mapRequired(A
.Key
.data(), ValueID
);
137 } else if (StringRef(A
.Val
).count('\n') > 1) {
138 StringBlockVal
S(A
.Val
);
139 io
.mapRequired(A
.Key
.data(), S
);
141 io
.mapRequired(A
.Key
.data(), A
.Val
);
143 io
.mapOptional("DebugLoc", A
.Loc
);
147 } // end namespace yaml
148 } // end namespace llvm
150 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument
)
152 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream
&OS
, SerializerMode Mode
)
153 : RemarkSerializer(OS
, Mode
), YAMLOutput(OS
, reinterpret_cast<void *>(this)) {}
155 void YAMLRemarkSerializer::emit(const Remark
&Remark
) {
156 // Again, YAMLTraits expect a non-const object for inputting, but we're not
158 auto R
= const_cast<remarks::Remark
*>(&Remark
);
162 std::unique_ptr
<MetaSerializer
>
163 YAMLRemarkSerializer::metaSerializer(raw_ostream
&OS
,
164 Optional
<StringRef
> ExternalFilename
) {
165 return std::make_unique
<YAMLMetaSerializer
>(OS
, ExternalFilename
);
168 void YAMLStrTabRemarkSerializer::emit(const Remark
&Remark
) {
169 // In standalone mode, for the serializer with a string table, emit the
170 // metadata first and set DidEmitMeta to avoid emitting it again.
171 if (Mode
== SerializerMode::Standalone
&& !DidEmitMeta
) {
172 std::unique_ptr
<MetaSerializer
> MetaSerializer
=
173 metaSerializer(OS
, /*ExternalFilename=*/None
);
174 MetaSerializer
->emit();
178 // Then do the usual remark emission.
179 YAMLRemarkSerializer::emit(Remark
);
182 std::unique_ptr
<MetaSerializer
> YAMLStrTabRemarkSerializer::metaSerializer(
183 raw_ostream
&OS
, Optional
<StringRef
> ExternalFilename
) {
185 return std::make_unique
<YAMLStrTabMetaSerializer
>(OS
, ExternalFilename
,
189 static void emitMagic(raw_ostream
&OS
) {
190 // Emit the magic number.
191 OS
<< remarks::Magic
;
192 // Explicitly emit a '\0'.
196 static void emitVersion(raw_ostream
&OS
) {
197 // Emit the version number: little-endian uint64_t.
198 std::array
<char, 8> Version
;
199 support::endian::write64le(Version
.data(), remarks::CurrentRemarkVersion
);
200 OS
.write(Version
.data(), Version
.size());
203 static void emitStrTab(raw_ostream
&OS
, const Optional
<StringTable
> &StrTab
) {
204 // Emit the string table in the section.
205 uint64_t StrTabSize
= StrTab
? StrTab
->SerializedSize
: 0;
206 // Emit the total size of the string table (the size itself excluded):
207 // little-endian uint64_t.
208 // Note: even if no string table is used, emit 0.
209 std::array
<char, 8> StrTabSizeBuf
;
210 support::endian::write64le(StrTabSizeBuf
.data(), StrTabSize
);
211 OS
.write(StrTabSizeBuf
.data(), StrTabSizeBuf
.size());
213 StrTab
->serialize(OS
);
216 static void emitExternalFile(raw_ostream
&OS
, StringRef Filename
) {
217 // Emit the null-terminated absolute path to the remark file.
218 SmallString
<128> FilenameBuf
= Filename
;
219 sys::fs::make_absolute(FilenameBuf
);
220 assert(!FilenameBuf
.empty() && "The filename can't be empty.");
221 OS
.write(FilenameBuf
.data(), FilenameBuf
.size());
225 void YAMLMetaSerializer::emit() {
228 emitStrTab(OS
, None
);
229 if (ExternalFilename
)
230 emitExternalFile(OS
, *ExternalFilename
);
233 void YAMLStrTabMetaSerializer::emit() {
236 emitStrTab(OS
, std::move(StrTab
));
237 if (ExternalFilename
)
238 emitExternalFile(OS
, *ExternalFilename
);