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/Support/FileSystem.h" 16 17 using namespace llvm; 18 using namespace llvm::remarks; 19 20 // Use the same keys whether we use a string table or not (respectively, T is an 21 // unsigned or a StringRef). 22 template <typename T> 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); 33 } 34 35 namespace llvm { 36 namespace yaml { 37 38 template <> struct MappingTraits<remarks::Remark *> { 39 static void mapping(IO &io, remarks::Remark *&Remark) { 40 assert(io.outputting() && "input not yet implemented"); 41 42 if (io.mapTag("!Passed", (Remark->RemarkType == Type::Passed))) 43 ; 44 else if (io.mapTag("!Missed", (Remark->RemarkType == Type::Missed))) 45 ; 46 else if (io.mapTag("!Analysis", (Remark->RemarkType == Type::Analysis))) 47 ; 48 else if (io.mapTag("!AnalysisFPCommute", 49 (Remark->RemarkType == Type::AnalysisFPCommute))) 50 ; 51 else if (io.mapTag("!AnalysisAliasing", 52 (Remark->RemarkType == Type::AnalysisAliasing))) 53 ; 54 else if (io.mapTag("!Failure", (Remark->RemarkType == Type::Failure))) 55 ; 56 else 57 llvm_unreachable("Unknown remark type"); 58 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); 69 } else { 70 mapRemarkHeader(io, Remark->PassName, Remark->RemarkName, Remark->Loc, 71 Remark->FunctionName, Remark->Hotness, Remark->Args); 72 } 73 } 74 }; 75 76 template <> struct MappingTraits<RemarkLocation> { 77 static void mapping(IO &io, RemarkLocation &RL) { 78 assert(io.outputting() && "input not yet implemented"); 79 80 StringRef File = RL.SourceFilePath; 81 unsigned Line = RL.SourceLine; 82 unsigned Col = RL.SourceColumn; 83 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); 91 } else { 92 io.mapRequired("File", File); 93 } 94 95 io.mapRequired("Line", Line); 96 io.mapRequired("Column", Col); 97 } 98 99 static const bool flow = true; 100 }; 101 102 /// Helper struct for multiline string block literals. Use this type to preserve 103 /// newlines in strings. 104 struct StringBlockVal { 105 StringRef Value; 106 StringBlockVal(StringRef R) : Value(R) {} 107 }; 108 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); 112 } 113 114 static StringRef input(StringRef Scalar, void *Ctx, StringBlockVal &S) { 115 return ScalarTraits<StringRef>::input(Scalar, Ctx, S.Value); 116 } 117 }; 118 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]); 131 } 132 }; 133 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"); 138 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); 149 } else { 150 io.mapRequired(A.Key.data(), A.Val); 151 } 152 io.mapOptional("DebugLoc", A.Loc); 153 } 154 }; 155 156 } // end namespace yaml 157 } // end namespace llvm 158 159 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument) 160 161 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS, SerializerMode Mode, 162 Optional<StringTable> StrTabIn) 163 : YAMLRemarkSerializer(Format::YAML, OS, Mode, std::move(StrTabIn)) {} 164 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); 171 } 172 173 void YAMLRemarkSerializer::emit(const Remark &Remark) { 174 // Again, YAMLTraits expect a non-const object for inputting, but we're not 175 // using that here. 176 auto R = const_cast<remarks::Remark *>(&Remark); 177 YAMLOutput << R; 178 } 179 180 std::unique_ptr<MetaSerializer> 181 YAMLRemarkSerializer::metaSerializer(raw_ostream &OS, 182 Optional<StringRef> ExternalFilename) { 183 return std::make_unique<YAMLMetaSerializer>(OS, ExternalFilename); 184 } 185 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(); 193 DidEmitMeta = true; 194 } 195 196 // Then do the usual remark emission. 197 YAMLRemarkSerializer::emit(Remark); 198 } 199 200 std::unique_ptr<MetaSerializer> YAMLStrTabRemarkSerializer::metaSerializer( 201 raw_ostream &OS, Optional<StringRef> ExternalFilename) { 202 assert(StrTab); 203 return std::make_unique<YAMLStrTabMetaSerializer>(OS, ExternalFilename, 204 *StrTab); 205 } 206 207 static void emitMagic(raw_ostream &OS) { 208 // Emit the magic number. 209 OS << remarks::Magic; 210 // Explicitly emit a '\0'. 211 OS.write('\0'); 212 } 213 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()); 219 } 220 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()); 230 if (StrTab) 231 (*StrTab)->serialize(OS); 232 } 233 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()); 240 OS.write('\0'); 241 } 242 243 void YAMLMetaSerializer::emit() { 244 emitMagic(OS); 245 emitVersion(OS); 246 emitStrTab(OS, None); 247 if (ExternalFilename) 248 emitExternalFile(OS, *ExternalFilename); 249 } 250 251 void YAMLStrTabMetaSerializer::emit() { 252 emitMagic(OS); 253 emitVersion(OS); 254 emitStrTab(OS, &StrTab); 255 if (ExternalFilename) 256 emitExternalFile(OS, *ExternalFilename); 257 } 258