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 // In standalone mode, emit the metadata first and set DidEmitMeta to avoid
157 // emitting it again.
158 if (Mode
== SerializerMode::Standalone
) {
159 std::unique_ptr
<MetaSerializer
> MetaSerializer
=
160 metaSerializer(OS
, /*ExternalFilename=*/None
);
161 MetaSerializer
->emit();
165 // Again, YAMLTraits expect a non-const object for inputting, but we're not
167 auto R
= const_cast<remarks::Remark
*>(&Remark
);
171 std::unique_ptr
<MetaSerializer
>
172 YAMLRemarkSerializer::metaSerializer(raw_ostream
&OS
,
173 Optional
<StringRef
> ExternalFilename
) {
174 return std::make_unique
<YAMLMetaSerializer
>(OS
, ExternalFilename
);
177 std::unique_ptr
<MetaSerializer
> YAMLStrTabRemarkSerializer::metaSerializer(
178 raw_ostream
&OS
, Optional
<StringRef
> ExternalFilename
) {
180 return std::make_unique
<YAMLStrTabMetaSerializer
>(OS
, ExternalFilename
,
184 static void emitMagic(raw_ostream
&OS
) {
185 // Emit the magic number.
186 OS
<< remarks::Magic
;
187 // Explicitly emit a '\0'.
191 static void emitVersion(raw_ostream
&OS
) {
192 // Emit the version number: little-endian uint64_t.
193 std::array
<char, 8> Version
;
194 support::endian::write64le(Version
.data(), remarks::CurrentRemarkVersion
);
195 OS
.write(Version
.data(), Version
.size());
198 static void emitStrTab(raw_ostream
&OS
, const Optional
<StringTable
> &StrTab
) {
199 // Emit the string table in the section.
200 uint64_t StrTabSize
= StrTab
? StrTab
->SerializedSize
: 0;
201 // Emit the total size of the string table (the size itself excluded):
202 // little-endian uint64_t.
203 // Note: even if no string table is used, emit 0.
204 std::array
<char, 8> StrTabSizeBuf
;
205 support::endian::write64le(StrTabSizeBuf
.data(), StrTabSize
);
206 OS
.write(StrTabSizeBuf
.data(), StrTabSizeBuf
.size());
208 StrTab
->serialize(OS
);
211 static void emitExternalFile(raw_ostream
&OS
, StringRef Filename
) {
212 // Emit the null-terminated absolute path to the remark file.
213 SmallString
<128> FilenameBuf
= Filename
;
214 sys::fs::make_absolute(FilenameBuf
);
215 assert(!FilenameBuf
.empty() && "The filename can't be empty.");
216 OS
.write(FilenameBuf
.data(), FilenameBuf
.size());
220 void YAMLMetaSerializer::emit() {
223 emitStrTab(OS
, None
);
224 if (ExternalFilename
)
225 emitExternalFile(OS
, *ExternalFilename
);
228 void YAMLStrTabMetaSerializer::emit() {
231 emitStrTab(OS
, std::move(StrTab
));
232 if (ExternalFilename
)
233 emitExternalFile(OS
, *ExternalFilename
);