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