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 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 (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); 66 } else { 67 mapRemarkHeader(io, Remark->PassName, Remark->RemarkName, Remark->Loc, 68 Remark->FunctionName, Remark->Hotness, Remark->Args); 69 } 70 } 71 }; 72 73 template <> struct MappingTraits<RemarkLocation> { 74 static void mapping(IO &io, RemarkLocation &RL) { 75 assert(io.outputting() && "input not yet implemented"); 76 77 StringRef File = RL.SourceFilePath; 78 unsigned Line = RL.SourceLine; 79 unsigned Col = RL.SourceColumn; 80 81 if (Optional<StringTable> &StrTab = 82 reinterpret_cast<YAMLRemarkSerializer *>(io.getContext())->StrTab) { 83 unsigned FileID = StrTab->add(File).first; 84 io.mapRequired("File", FileID); 85 } else { 86 io.mapRequired("File", File); 87 } 88 89 io.mapRequired("Line", Line); 90 io.mapRequired("Column", Col); 91 } 92 93 static const bool flow = true; 94 }; 95 96 /// Helper struct for multiline string block literals. Use this type to preserve 97 /// newlines in strings. 98 struct StringBlockVal { 99 StringRef Value; 100 StringBlockVal(const std::string &Value) : Value(Value) {} 101 }; 102 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); 106 } 107 108 static StringRef input(StringRef Scalar, void *Ctx, StringBlockVal &S) { 109 return ScalarTraits<StringRef>::input(Scalar, Ctx, S.Value); 110 } 111 }; 112 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]); 125 } 126 }; 127 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"); 132 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); 140 } else { 141 io.mapRequired(A.Key.data(), A.Val); 142 } 143 io.mapOptional("DebugLoc", A.Loc); 144 } 145 }; 146 147 } // end namespace yaml 148 } // end namespace llvm 149 150 LLVM_YAML_IS_SEQUENCE_VECTOR(Argument) 151 152 YAMLRemarkSerializer::YAMLRemarkSerializer(raw_ostream &OS, SerializerMode Mode) 153 : RemarkSerializer(OS, Mode), YAMLOutput(OS, reinterpret_cast<void *>(this)) {} 154 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(); 162 DidEmitMeta = true; 163 } 164 165 // Again, YAMLTraits expect a non-const object for inputting, but we're not 166 // using that here. 167 auto R = const_cast<remarks::Remark *>(&Remark); 168 YAMLOutput << R; 169 } 170 171 std::unique_ptr<MetaSerializer> 172 YAMLRemarkSerializer::metaSerializer(raw_ostream &OS, 173 Optional<StringRef> ExternalFilename) { 174 return llvm::make_unique<YAMLMetaSerializer>(OS, ExternalFilename); 175 } 176 177 std::unique_ptr<MetaSerializer> YAMLStrTabRemarkSerializer::metaSerializer( 178 raw_ostream &OS, Optional<StringRef> ExternalFilename) { 179 assert(StrTab); 180 return llvm::make_unique<YAMLStrTabMetaSerializer>(OS, ExternalFilename, 181 std::move(*StrTab)); 182 } 183 184 static void emitMagic(raw_ostream &OS) { 185 // Emit the magic number. 186 OS << remarks::Magic; 187 // Explicitly emit a '\0'. 188 OS.write('\0'); 189 } 190 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()); 196 } 197 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()); 207 if (StrTab) 208 StrTab->serialize(OS); 209 } 210 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()); 217 OS.write('\0'); 218 } 219 220 void YAMLMetaSerializer::emit() { 221 emitMagic(OS); 222 emitVersion(OS); 223 emitStrTab(OS, None); 224 if (ExternalFilename) 225 emitExternalFile(OS, *ExternalFilename); 226 } 227 228 void YAMLStrTabMetaSerializer::emit() { 229 emitMagic(OS); 230 emitVersion(OS); 231 emitStrTab(OS, std::move(StrTab)); 232 if (ExternalFilename) 233 emitExternalFile(OS, *ExternalFilename); 234 } 235