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 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 && "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 && "YAMLStrTabSerializer with no StrTab."); 87 StringTable &StrTab = *Serializer->StrTab; 88 unsigned FileID = StrTab.add(File).first; 89 io.mapRequired("File", FileID); 90 } else { 91 io.mapRequired("File", File); 92 } 93 94 io.mapRequired("Line", Line); 95 io.mapRequired("Column", Col); 96 } 97 98 static const bool flow = true; 99 }; 100 101 /// Helper struct for multiline string block literals. Use this type to preserve 102 /// newlines in strings. 103 struct StringBlockVal { 104 StringRef Value; 105 StringBlockVal(StringRef R) : Value(R) {} 106 }; 107 108 template <> struct BlockScalarTraits<StringBlockVal> { 109 static void output(const StringBlockVal &S, void *Ctx, raw_ostream &OS) { 110 return ScalarTraits<StringRef>::output(S.Value, Ctx, OS); 111 } 112 113 static StringRef input(StringRef Scalar, void *Ctx, StringBlockVal &S) { 114 return ScalarTraits<StringRef>::input(Scalar, Ctx, S.Value); 115 } 116 }; 117 118 /// ArrayRef is not really compatible with the YAMLTraits. Everything should be 119 /// immutable in an ArrayRef, while the SequenceTraits expect a mutable version 120 /// for inputting, but we're only using the outputting capabilities here. 121 /// This is a hack, but still nicer than having to manually call the YAMLIO 122 /// internal methods. 123 /// Keep this in this file so that it doesn't get misused from YAMLTraits.h. 124 template <typename T> struct SequenceTraits<ArrayRef<T>> { 125 static size_t size(IO &io, ArrayRef<T> &seq) { return seq.size(); } 126 static Argument &element(IO &io, ArrayRef<T> &seq, size_t index) { 127 assert(io.outputting() && "input not yet implemented"); 128 // The assert above should make this "safer" to satisfy the YAMLTraits. 129 return const_cast<T &>(seq[index]); 130 } 131 }; 132 133 /// Implement this as a mapping for now to get proper quotation for the value. 134 template <> struct MappingTraits<Argument> { 135 static void mapping(IO &io, Argument &A) { 136 assert(io.outputting() && "input not yet implemented"); 137 138 if (auto *Serializer = dyn_cast<YAMLStrTabRemarkSerializer>( 139 reinterpret_cast<RemarkSerializer *>(io.getContext()))) { 140 assert(Serializer->StrTab && "YAMLStrTabSerializer with no StrTab."); 141 StringTable &StrTab = *Serializer->StrTab; 142 auto ValueID = StrTab.add(A.Val).first; 143 io.mapRequired(A.Key.data(), ValueID); 144 } else if (StringRef(A.Val).count('\n') > 1) { 145 StringBlockVal S(A.Val); 146 io.mapRequired(A.Key.data(), S); 147 } else { 148 io.mapRequired(A.Key.data(), A.Val); 149 } 150 io.mapOptional("DebugLoc", A.Loc); 151 } 152 }; 153 154 } // end namespace yaml 155 } // end namespace llvm 156 157 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument) 158 159 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS, SerializerMode Mode, 160 Optional<StringTable> StrTabIn) 161 : YAMLRemarkSerializer(Format::YAML, OS, Mode, std::move(StrTabIn)) {} 162 163 YAMLRemarkSerializer::YAMLRemarkSerializer(Format SerializerFormat, 164 raw_ostream &OS, SerializerMode Mode, 165 Optional<StringTable> StrTabIn) 166 : RemarkSerializer(SerializerFormat, OS, Mode), 167 YAMLOutput(OS, reinterpret_cast<void *>(this)) { 168 StrTab = std::move(StrTabIn); 169 } 170 171 void YAMLRemarkSerializer::emit(const Remark &Remark) { 172 // Again, YAMLTraits expect a non-const object for inputting, but we're not 173 // using that here. 174 auto R = const_cast<remarks::Remark *>(&Remark); 175 YAMLOutput << R; 176 } 177 178 std::unique_ptr<MetaSerializer> 179 YAMLRemarkSerializer::metaSerializer(raw_ostream &OS, 180 Optional<StringRef> ExternalFilename) { 181 return std::make_unique<YAMLMetaSerializer>(OS, ExternalFilename); 182 } 183 184 void YAMLStrTabRemarkSerializer::emit(const Remark &Remark) { 185 // In standalone mode, for the serializer with a string table, emit the 186 // metadata first and set DidEmitMeta to avoid emitting it again. 187 if (Mode == SerializerMode::Standalone && !DidEmitMeta) { 188 std::unique_ptr<MetaSerializer> MetaSerializer = 189 metaSerializer(OS, /*ExternalFilename=*/None); 190 MetaSerializer->emit(); 191 DidEmitMeta = true; 192 } 193 194 // Then do the usual remark emission. 195 YAMLRemarkSerializer::emit(Remark); 196 } 197 198 std::unique_ptr<MetaSerializer> YAMLStrTabRemarkSerializer::metaSerializer( 199 raw_ostream &OS, Optional<StringRef> ExternalFilename) { 200 assert(StrTab); 201 return std::make_unique<YAMLStrTabMetaSerializer>(OS, ExternalFilename, 202 *StrTab); 203 } 204 205 static void emitMagic(raw_ostream &OS) { 206 // Emit the magic number. 207 OS << remarks::Magic; 208 // Explicitly emit a '\0'. 209 OS.write('\0'); 210 } 211 212 static void emitVersion(raw_ostream &OS) { 213 // Emit the version number: little-endian uint64_t. 214 std::array<char, 8> Version; 215 support::endian::write64le(Version.data(), remarks::CurrentRemarkVersion); 216 OS.write(Version.data(), Version.size()); 217 } 218 219 static void emitStrTab(raw_ostream &OS, Optional<const StringTable *> StrTab) { 220 // Emit the string table in the section. 221 uint64_t StrTabSize = StrTab ? (*StrTab)->SerializedSize : 0; 222 // Emit the total size of the string table (the size itself excluded): 223 // little-endian uint64_t. 224 // Note: even if no string table is used, emit 0. 225 std::array<char, 8> StrTabSizeBuf; 226 support::endian::write64le(StrTabSizeBuf.data(), StrTabSize); 227 OS.write(StrTabSizeBuf.data(), StrTabSizeBuf.size()); 228 if (StrTab) 229 (*StrTab)->serialize(OS); 230 } 231 232 static void emitExternalFile(raw_ostream &OS, StringRef Filename) { 233 // Emit the null-terminated absolute path to the remark file. 234 SmallString<128> FilenameBuf = Filename; 235 sys::fs::make_absolute(FilenameBuf); 236 assert(!FilenameBuf.empty() && "The filename can't be empty."); 237 OS.write(FilenameBuf.data(), FilenameBuf.size()); 238 OS.write('\0'); 239 } 240 241 void YAMLMetaSerializer::emit() { 242 emitMagic(OS); 243 emitVersion(OS); 244 emitStrTab(OS, None); 245 if (ExternalFilename) 246 emitExternalFile(OS, *ExternalFilename); 247 } 248 249 void YAMLStrTabMetaSerializer::emit() { 250 emitMagic(OS); 251 emitVersion(OS); 252 emitStrTab(OS, &StrTab); 253 if (ExternalFilename) 254 emitExternalFile(OS, *ExternalFilename); 255 } 256