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