1 | //===- YAMLRemarkSerializer.cpp -------------------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file provides the implementation of the YAML remark serializer using |
10 | // LLVM's YAMLTraits. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Remarks/YAMLRemarkSerializer.h" |
15 | #include "llvm/Remarks/Remark.h" |
16 | #include "llvm/Support/FileSystem.h" |
17 | #include <optional> |
18 | |
19 | using namespace llvm; |
20 | using namespace llvm::remarks; |
21 | |
22 | // Use the same keys whether we use a string table or not (respectively, T is an |
23 | // unsigned or a StringRef). |
24 | static void |
25 | (yaml::IO &io, StringRef PassName, StringRef , |
26 | std::optional<RemarkLocation> RL, StringRef FunctionName, |
27 | std::optional<uint64_t> Hotness, ArrayRef<Argument> Args) { |
28 | io.mapRequired(Key: "Pass" , Val&: PassName); |
29 | io.mapRequired(Key: "Name" , Val&: RemarkName); |
30 | io.mapOptional(Key: "DebugLoc" , Val&: RL); |
31 | io.mapRequired(Key: "Function" , Val&: FunctionName); |
32 | io.mapOptional(Key: "Hotness" , Val&: Hotness); |
33 | io.mapOptional(Key: "Args" , Val&: Args); |
34 | } |
35 | |
36 | namespace llvm { |
37 | namespace yaml { |
38 | |
39 | template <> struct MappingTraits<remarks::Remark *> { |
40 | static void (IO &io, remarks::Remark *&) { |
41 | assert(io.outputting() && "input not yet implemented" ); |
42 | |
43 | if (io.mapTag(Tag: "!Passed" , Default: (Remark->RemarkType == Type::Passed))) |
44 | ; |
45 | else if (io.mapTag(Tag: "!Missed" , Default: (Remark->RemarkType == Type::Missed))) |
46 | ; |
47 | else if (io.mapTag(Tag: "!Analysis" , Default: (Remark->RemarkType == Type::Analysis))) |
48 | ; |
49 | else if (io.mapTag(Tag: "!AnalysisFPCommute" , |
50 | Default: (Remark->RemarkType == Type::AnalysisFPCommute))) |
51 | ; |
52 | else if (io.mapTag(Tag: "!AnalysisAliasing" , |
53 | Default: (Remark->RemarkType == Type::AnalysisAliasing))) |
54 | ; |
55 | else if (io.mapTag(Tag: "!Failure" , Default: (Remark->RemarkType == Type::Failure))) |
56 | ; |
57 | else |
58 | llvm_unreachable("Unknown remark type" ); |
59 | |
60 | mapRemarkHeader(io, PassName: Remark->PassName, RemarkName: Remark->RemarkName, RL: Remark->Loc, |
61 | FunctionName: Remark->FunctionName, Hotness: Remark->Hotness, Args: Remark->Args); |
62 | } |
63 | }; |
64 | |
65 | template <> struct MappingTraits<RemarkLocation> { |
66 | static void (IO &io, RemarkLocation &RL) { |
67 | assert(io.outputting() && "input not yet implemented" ); |
68 | |
69 | StringRef File = RL.SourceFilePath; |
70 | unsigned Line = RL.SourceLine; |
71 | unsigned Col = RL.SourceColumn; |
72 | |
73 | io.mapRequired(Key: "File" , Val&: File); |
74 | |
75 | io.mapRequired(Key: "Line" , Val&: Line); |
76 | io.mapRequired(Key: "Column" , Val&: Col); |
77 | } |
78 | |
79 | static const bool = true; |
80 | }; |
81 | |
82 | /// Helper struct for multiline string block literals. Use this type to preserve |
83 | /// newlines in strings. |
84 | struct StringBlockVal { |
85 | StringRef Value; |
86 | StringBlockVal(StringRef R) : Value(R) {} |
87 | }; |
88 | |
89 | template <> struct BlockScalarTraits<StringBlockVal> { |
90 | static void output(const StringBlockVal &S, void *Ctx, raw_ostream &OS) { |
91 | return ScalarTraits<StringRef>::output(S.Value, Ctx, OS); |
92 | } |
93 | |
94 | static StringRef input(StringRef Scalar, void *Ctx, StringBlockVal &S) { |
95 | return ScalarTraits<StringRef>::input(Scalar, Ctx, S.Value); |
96 | } |
97 | }; |
98 | |
99 | /// ArrayRef is not really compatible with the YAMLTraits. Everything should be |
100 | /// immutable in an ArrayRef, while the SequenceTraits expect a mutable version |
101 | /// for inputting, but we're only using the outputting capabilities here. |
102 | /// This is a hack, but still nicer than having to manually call the YAMLIO |
103 | /// internal methods. |
104 | /// Keep this in this file so that it doesn't get misused from YAMLTraits.h. |
105 | template <typename T> struct SequenceTraits<ArrayRef<T>> { |
106 | static size_t size(IO &io, ArrayRef<T> &seq) { return seq.size(); } |
107 | static Argument &element(IO &io, ArrayRef<T> &seq, size_t index) { |
108 | assert(io.outputting() && "input not yet implemented" ); |
109 | // The assert above should make this "safer" to satisfy the YAMLTraits. |
110 | return const_cast<T &>(seq[index]); |
111 | } |
112 | }; |
113 | |
114 | /// Implement this as a mapping for now to get proper quotation for the value. |
115 | template <> struct MappingTraits<Argument> { |
116 | static void (IO &io, Argument &A) { |
117 | assert(io.outputting() && "input not yet implemented" ); |
118 | |
119 | if (StringRef(A.Val).count(C: '\n') > 1) { |
120 | StringBlockVal S(A.Val); |
121 | io.mapRequired(Key: A.Key.data(), Val&: S); |
122 | } else { |
123 | io.mapRequired(Key: A.Key.data(), Val&: A.Val); |
124 | } |
125 | io.mapOptional(Key: "DebugLoc" , Val&: A.Loc); |
126 | } |
127 | }; |
128 | |
129 | } // end namespace yaml |
130 | } // end namespace llvm |
131 | |
132 | LLVM_YAML_IS_SEQUENCE_VECTOR(Argument) |
133 | |
134 | YAMLRemarkSerializer::(raw_ostream &OS, SerializerMode Mode, |
135 | std::optional<StringTable> StrTabIn) |
136 | : RemarkSerializer(Format::YAML, OS, Mode), |
137 | YAMLOutput(OS, reinterpret_cast<void *>(this)) { |
138 | StrTab = std::move(StrTabIn); |
139 | } |
140 | |
141 | void YAMLRemarkSerializer::(const Remark &) { |
142 | // Again, YAMLTraits expect a non-const object for inputting, but we're not |
143 | // using that here. |
144 | auto *R = const_cast<remarks::Remark *>(&Remark); |
145 | YAMLOutput << R; |
146 | } |
147 | |
148 | std::unique_ptr<MetaSerializer> YAMLRemarkSerializer::( |
149 | raw_ostream &OS, std::optional<StringRef> ExternalFilename) { |
150 | return std::make_unique<YAMLMetaSerializer>(args&: OS, args&: ExternalFilename); |
151 | } |
152 | |
153 | static void emitMagic(raw_ostream &OS) { |
154 | // Emit the magic number. |
155 | OS << remarks::Magic; |
156 | // Explicitly emit a '\0'. |
157 | OS.write(C: '\0'); |
158 | } |
159 | |
160 | static void emitVersion(raw_ostream &OS) { |
161 | // Emit the version number: little-endian uint64_t. |
162 | std::array<char, 8> Version; |
163 | support::endian::write64le(P: Version.data(), V: remarks::CurrentRemarkVersion); |
164 | OS.write(Ptr: Version.data(), Size: Version.size()); |
165 | } |
166 | |
167 | static void emitExternalFile(raw_ostream &OS, StringRef Filename) { |
168 | // Emit the null-terminated absolute path to the remark file. |
169 | SmallString<128> FilenameBuf = Filename; |
170 | sys::fs::make_absolute(path&: FilenameBuf); |
171 | assert(!FilenameBuf.empty() && "The filename can't be empty." ); |
172 | OS.write(Ptr: FilenameBuf.data(), Size: FilenameBuf.size()); |
173 | OS.write(C: '\0'); |
174 | } |
175 | |
176 | void YAMLMetaSerializer::() { |
177 | emitMagic(OS); |
178 | emitVersion(OS); |
179 | |
180 | // Emit StringTable with size 0. This is left over after removing StringTable |
181 | // support from the YAML format. For now, don't unnecessarily change how the |
182 | // the metadata is serialized. When changing the format, we should think about |
183 | // just reusing the bitstream remark meta for this. |
184 | uint64_t StrTabSize = 0; |
185 | std::array<char, 8> StrTabSizeBuf; |
186 | support::endian::write64le(P: StrTabSizeBuf.data(), V: StrTabSize); |
187 | |
188 | OS.write(Ptr: StrTabSizeBuf.data(), Size: StrTabSizeBuf.size()); |
189 | if (ExternalFilename) |
190 | emitExternalFile(OS, Filename: *ExternalFilename); |
191 | } |
192 | |