1 //===- BitstreamRemarkSerializer.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 LLVM bitstream remark serializer 10 // using LLVM's bitstream writer. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Remarks/BitstreamRemarkSerializer.h" 15 16 using namespace llvm; 17 using namespace llvm::remarks; 18 19 BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper( 20 BitstreamRemarkContainerType ContainerType) 21 : Encoded(), R(), Bitstream(Encoded), ContainerType(ContainerType) {} 22 23 static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) { 24 append_range(R, Str); 25 } 26 27 static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream, 28 SmallVectorImpl<uint64_t> &R, StringRef Str) { 29 R.clear(); 30 R.push_back(RecordID); 31 push(R, Str); 32 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, R); 33 } 34 35 static void initBlock(unsigned BlockID, BitstreamWriter &Bitstream, 36 SmallVectorImpl<uint64_t> &R, StringRef Str) { 37 R.clear(); 38 R.push_back(BlockID); 39 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, R); 40 41 R.clear(); 42 push(R, Str); 43 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, R); 44 } 45 46 void BitstreamRemarkSerializerHelper::setupMetaBlockInfo() { 47 // Setup the metadata block. 48 initBlock(META_BLOCK_ID, Bitstream, R, MetaBlockName); 49 50 // The container information. 51 setRecordName(RECORD_META_CONTAINER_INFO, Bitstream, R, 52 MetaContainerInfoName); 53 54 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 55 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_CONTAINER_INFO)); 56 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version. 57 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Type. 58 RecordMetaContainerInfoAbbrevID = 59 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev); 60 } 61 62 void BitstreamRemarkSerializerHelper::setupMetaRemarkVersion() { 63 setRecordName(RECORD_META_REMARK_VERSION, Bitstream, R, 64 MetaRemarkVersionName); 65 66 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 67 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_REMARK_VERSION)); 68 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version. 69 RecordMetaRemarkVersionAbbrevID = 70 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev); 71 } 72 73 void BitstreamRemarkSerializerHelper::emitMetaRemarkVersion( 74 uint64_t RemarkVersion) { 75 // The remark version is emitted only if we emit remarks. 76 R.clear(); 77 R.push_back(RECORD_META_REMARK_VERSION); 78 R.push_back(RemarkVersion); 79 Bitstream.EmitRecordWithAbbrev(RecordMetaRemarkVersionAbbrevID, R); 80 } 81 82 void BitstreamRemarkSerializerHelper::setupMetaStrTab() { 83 setRecordName(RECORD_META_STRTAB, Bitstream, R, MetaStrTabName); 84 85 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 86 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_STRTAB)); 87 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Raw table. 88 RecordMetaStrTabAbbrevID = 89 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev); 90 } 91 92 void BitstreamRemarkSerializerHelper::emitMetaStrTab( 93 const StringTable &StrTab) { 94 // The string table is not emitted if we emit remarks separately. 95 R.clear(); 96 R.push_back(RECORD_META_STRTAB); 97 98 // Serialize to a blob. 99 std::string Buf; 100 raw_string_ostream OS(Buf); 101 StrTab.serialize(OS); 102 StringRef Blob = OS.str(); 103 Bitstream.EmitRecordWithBlob(RecordMetaStrTabAbbrevID, R, Blob); 104 } 105 106 void BitstreamRemarkSerializerHelper::setupMetaExternalFile() { 107 setRecordName(RECORD_META_EXTERNAL_FILE, Bitstream, R, MetaExternalFileName); 108 109 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 110 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_EXTERNAL_FILE)); 111 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename. 112 RecordMetaExternalFileAbbrevID = 113 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev); 114 } 115 116 void BitstreamRemarkSerializerHelper::emitMetaExternalFile(StringRef Filename) { 117 // The external file is emitted only if we emit the separate metadata. 118 R.clear(); 119 R.push_back(RECORD_META_EXTERNAL_FILE); 120 Bitstream.EmitRecordWithBlob(RecordMetaExternalFileAbbrevID, R, Filename); 121 } 122 123 void BitstreamRemarkSerializerHelper::setupRemarkBlockInfo() { 124 // Setup the remark block. 125 initBlock(REMARK_BLOCK_ID, Bitstream, R, RemarkBlockName); 126 127 // The header of a remark. 128 { 129 setRecordName(RECORD_REMARK_HEADER, Bitstream, R, RemarkHeaderName); 130 131 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 132 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HEADER)); 133 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Type 134 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Remark Name 135 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Pass name 136 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Function name 137 RecordRemarkHeaderAbbrevID = 138 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev); 139 } 140 141 // The location of a remark. 142 { 143 setRecordName(RECORD_REMARK_DEBUG_LOC, Bitstream, R, RemarkDebugLocName); 144 145 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 146 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_DEBUG_LOC)); 147 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File 148 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line 149 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column 150 RecordRemarkDebugLocAbbrevID = 151 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev); 152 } 153 154 // The hotness of a remark. 155 { 156 setRecordName(RECORD_REMARK_HOTNESS, Bitstream, R, RemarkHotnessName); 157 158 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 159 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HOTNESS)); 160 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Hotness 161 RecordRemarkHotnessAbbrevID = 162 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev); 163 } 164 165 // An argument entry with a debug location attached. 166 { 167 setRecordName(RECORD_REMARK_ARG_WITH_DEBUGLOC, Bitstream, R, 168 RemarkArgWithDebugLocName); 169 170 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 171 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITH_DEBUGLOC)); 172 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key 173 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value 174 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File 175 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line 176 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column 177 RecordRemarkArgWithDebugLocAbbrevID = 178 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev); 179 } 180 181 // An argument entry with no debug location attached. 182 { 183 setRecordName(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, Bitstream, R, 184 RemarkArgWithoutDebugLocName); 185 186 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 187 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC)); 188 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key 189 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value 190 RecordRemarkArgWithoutDebugLocAbbrevID = 191 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev); 192 } 193 } 194 195 void BitstreamRemarkSerializerHelper::setupBlockInfo() { 196 // Emit magic number. 197 for (const char C : ContainerMagic) 198 Bitstream.Emit(static_cast<unsigned>(C), 8); 199 200 Bitstream.EnterBlockInfoBlock(); 201 202 // Setup the main metadata. Depending on the container type, we'll setup the 203 // required records next. 204 setupMetaBlockInfo(); 205 206 switch (ContainerType) { 207 case BitstreamRemarkContainerType::SeparateRemarksMeta: 208 // Needs a string table that the separate remark file is using. 209 setupMetaStrTab(); 210 // Needs to know where the external remarks file is. 211 setupMetaExternalFile(); 212 break; 213 case BitstreamRemarkContainerType::SeparateRemarksFile: 214 // Contains remarks: emit the version. 215 setupMetaRemarkVersion(); 216 // Contains remarks: emit the remark abbrevs. 217 setupRemarkBlockInfo(); 218 break; 219 case BitstreamRemarkContainerType::Standalone: 220 // Contains remarks: emit the version. 221 setupMetaRemarkVersion(); 222 // Needs a string table. 223 setupMetaStrTab(); 224 // Contains remarks: emit the remark abbrevs. 225 setupRemarkBlockInfo(); 226 break; 227 } 228 229 Bitstream.ExitBlock(); 230 } 231 232 void BitstreamRemarkSerializerHelper::emitMetaBlock( 233 uint64_t ContainerVersion, Optional<uint64_t> RemarkVersion, 234 Optional<const StringTable *> StrTab, Optional<StringRef> Filename) { 235 // Emit the meta block 236 Bitstream.EnterSubblock(META_BLOCK_ID, 3); 237 238 // The container version and type. 239 R.clear(); 240 R.push_back(RECORD_META_CONTAINER_INFO); 241 R.push_back(ContainerVersion); 242 R.push_back(static_cast<uint64_t>(ContainerType)); 243 Bitstream.EmitRecordWithAbbrev(RecordMetaContainerInfoAbbrevID, R); 244 245 switch (ContainerType) { 246 case BitstreamRemarkContainerType::SeparateRemarksMeta: 247 assert(StrTab != None && *StrTab != nullptr); 248 emitMetaStrTab(**StrTab); 249 assert(Filename != None); 250 emitMetaExternalFile(*Filename); 251 break; 252 case BitstreamRemarkContainerType::SeparateRemarksFile: 253 assert(RemarkVersion != None); 254 emitMetaRemarkVersion(*RemarkVersion); 255 break; 256 case BitstreamRemarkContainerType::Standalone: 257 assert(RemarkVersion != None); 258 emitMetaRemarkVersion(*RemarkVersion); 259 assert(StrTab != None && *StrTab != nullptr); 260 emitMetaStrTab(**StrTab); 261 break; 262 } 263 264 Bitstream.ExitBlock(); 265 } 266 267 void BitstreamRemarkSerializerHelper::emitRemarkBlock(const Remark &Remark, 268 StringTable &StrTab) { 269 Bitstream.EnterSubblock(REMARK_BLOCK_ID, 4); 270 271 R.clear(); 272 R.push_back(RECORD_REMARK_HEADER); 273 R.push_back(static_cast<uint64_t>(Remark.RemarkType)); 274 R.push_back(StrTab.add(Remark.RemarkName).first); 275 R.push_back(StrTab.add(Remark.PassName).first); 276 R.push_back(StrTab.add(Remark.FunctionName).first); 277 Bitstream.EmitRecordWithAbbrev(RecordRemarkHeaderAbbrevID, R); 278 279 if (const Optional<RemarkLocation> &Loc = Remark.Loc) { 280 R.clear(); 281 R.push_back(RECORD_REMARK_DEBUG_LOC); 282 R.push_back(StrTab.add(Loc->SourceFilePath).first); 283 R.push_back(Loc->SourceLine); 284 R.push_back(Loc->SourceColumn); 285 Bitstream.EmitRecordWithAbbrev(RecordRemarkDebugLocAbbrevID, R); 286 } 287 288 if (Optional<uint64_t> Hotness = Remark.Hotness) { 289 R.clear(); 290 R.push_back(RECORD_REMARK_HOTNESS); 291 R.push_back(*Hotness); 292 Bitstream.EmitRecordWithAbbrev(RecordRemarkHotnessAbbrevID, R); 293 } 294 295 for (const Argument &Arg : Remark.Args) { 296 R.clear(); 297 unsigned Key = StrTab.add(Arg.Key).first; 298 unsigned Val = StrTab.add(Arg.Val).first; 299 bool HasDebugLoc = Arg.Loc != None; 300 R.push_back(HasDebugLoc ? RECORD_REMARK_ARG_WITH_DEBUGLOC 301 : RECORD_REMARK_ARG_WITHOUT_DEBUGLOC); 302 R.push_back(Key); 303 R.push_back(Val); 304 if (HasDebugLoc) { 305 R.push_back(StrTab.add(Arg.Loc->SourceFilePath).first); 306 R.push_back(Arg.Loc->SourceLine); 307 R.push_back(Arg.Loc->SourceColumn); 308 } 309 Bitstream.EmitRecordWithAbbrev(HasDebugLoc 310 ? RecordRemarkArgWithDebugLocAbbrevID 311 : RecordRemarkArgWithoutDebugLocAbbrevID, 312 R); 313 } 314 Bitstream.ExitBlock(); 315 } 316 317 void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) { 318 OS.write(Encoded.data(), Encoded.size()); 319 Encoded.clear(); 320 } 321 322 StringRef BitstreamRemarkSerializerHelper::getBuffer() { 323 return StringRef(Encoded.data(), Encoded.size()); 324 } 325 326 BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS, 327 SerializerMode Mode) 328 : RemarkSerializer(Format::Bitstream, OS, Mode), 329 Helper(BitstreamRemarkContainerType::SeparateRemarksFile) { 330 assert(Mode == SerializerMode::Separate && 331 "For SerializerMode::Standalone, a pre-filled string table needs to " 332 "be provided."); 333 // We always use a string table with bitstream. 334 StrTab.emplace(); 335 } 336 337 BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS, 338 SerializerMode Mode, 339 StringTable StrTabIn) 340 : RemarkSerializer(Format::Bitstream, OS, Mode), 341 Helper(Mode == SerializerMode::Separate 342 ? BitstreamRemarkContainerType::SeparateRemarksFile 343 : BitstreamRemarkContainerType::Standalone) { 344 StrTab = std::move(StrTabIn); 345 } 346 347 void BitstreamRemarkSerializer::emit(const Remark &Remark) { 348 if (!DidSetUp) { 349 // Emit the metadata that is embedded in the remark file. 350 // If we're in standalone mode, serialize the string table as well. 351 bool IsStandalone = 352 Helper.ContainerType == BitstreamRemarkContainerType::Standalone; 353 BitstreamMetaSerializer MetaSerializer( 354 OS, Helper, 355 IsStandalone ? &*StrTab : Optional<const StringTable *>(None)); 356 MetaSerializer.emit(); 357 DidSetUp = true; 358 } 359 360 assert(DidSetUp && 361 "The Block info block and the meta block were not emitted yet."); 362 Helper.emitRemarkBlock(Remark, *StrTab); 363 364 Helper.flushToStream(OS); 365 } 366 367 std::unique_ptr<MetaSerializer> BitstreamRemarkSerializer::metaSerializer( 368 raw_ostream &OS, Optional<StringRef> ExternalFilename) { 369 assert(Helper.ContainerType != 370 BitstreamRemarkContainerType::SeparateRemarksMeta); 371 bool IsStandalone = 372 Helper.ContainerType == BitstreamRemarkContainerType::Standalone; 373 return std::make_unique<BitstreamMetaSerializer>( 374 OS, 375 IsStandalone ? BitstreamRemarkContainerType::Standalone 376 : BitstreamRemarkContainerType::SeparateRemarksMeta, 377 &*StrTab, ExternalFilename); 378 } 379 380 void BitstreamMetaSerializer::emit() { 381 Helper->setupBlockInfo(); 382 Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab, 383 ExternalFilename); 384 Helper->flushToStream(OS); 385 } 386