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