10b57cec5SDimitry Andric //===- BitstreamWriter.h - Low-level bitstream writer interface -*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This header defines the BitstreamWriter class. This class can be used to 100b57cec5SDimitry Andric // write an arbitrary bitstream, regardless of its contents. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #ifndef LLVM_BITSTREAM_BITSTREAMWRITER_H 150b57cec5SDimitry Andric #define LLVM_BITSTREAM_BITSTREAMWRITER_H 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 180b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 190b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 200b57cec5SDimitry Andric #include "llvm/Bitstream/BitCodes.h" 210b57cec5SDimitry Andric #include "llvm/Support/Endian.h" 22fe6060f1SDimitry Andric #include "llvm/Support/MathExtras.h" 23e8d8bef9SDimitry Andric #include "llvm/Support/raw_ostream.h" 24e8d8bef9SDimitry Andric #include <algorithm> 25bdd1243dSDimitry Andric #include <optional> 260b57cec5SDimitry Andric #include <vector> 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric namespace llvm { 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric class BitstreamWriter { 31e8d8bef9SDimitry Andric /// Out - The buffer that keeps unflushed bytes. 320b57cec5SDimitry Andric SmallVectorImpl<char> &Out; 330b57cec5SDimitry Andric 34e8d8bef9SDimitry Andric /// FS - The file stream that Out flushes to. If FS is nullptr, it does not 35e8d8bef9SDimitry Andric /// support read or seek, Out cannot be flushed until all data are written. 36e8d8bef9SDimitry Andric raw_fd_stream *FS; 37e8d8bef9SDimitry Andric 38e8d8bef9SDimitry Andric /// FlushThreshold - If FS is valid, this is the threshold (unit B) to flush 39e8d8bef9SDimitry Andric /// FS. 40e8d8bef9SDimitry Andric const uint64_t FlushThreshold; 41e8d8bef9SDimitry Andric 420b57cec5SDimitry Andric /// CurBit - Always between 0 and 31 inclusive, specifies the next bit to use. 430b57cec5SDimitry Andric unsigned CurBit; 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric /// CurValue - The current value. Only bits < CurBit are valid. 460b57cec5SDimitry Andric uint32_t CurValue; 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric /// CurCodeSize - This is the declared size of code values used for the 490b57cec5SDimitry Andric /// current block, in bits. 500b57cec5SDimitry Andric unsigned CurCodeSize; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric /// BlockInfoCurBID - When emitting a BLOCKINFO_BLOCK, this is the currently 530b57cec5SDimitry Andric /// selected BLOCK ID. 540b57cec5SDimitry Andric unsigned BlockInfoCurBID; 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric /// CurAbbrevs - Abbrevs installed at in this block. 570b57cec5SDimitry Andric std::vector<std::shared_ptr<BitCodeAbbrev>> CurAbbrevs; 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric struct Block { 600b57cec5SDimitry Andric unsigned PrevCodeSize; 610b57cec5SDimitry Andric size_t StartSizeWord; 620b57cec5SDimitry Andric std::vector<std::shared_ptr<BitCodeAbbrev>> PrevAbbrevs; BlockBlock630b57cec5SDimitry Andric Block(unsigned PCS, size_t SSW) : PrevCodeSize(PCS), StartSizeWord(SSW) {} 640b57cec5SDimitry Andric }; 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric /// BlockScope - This tracks the current blocks that we have entered. 670b57cec5SDimitry Andric std::vector<Block> BlockScope; 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric /// BlockInfo - This contains information emitted to BLOCKINFO_BLOCK blocks. 700b57cec5SDimitry Andric /// These describe abbreviations that all blocks of the specified ID inherit. 710b57cec5SDimitry Andric struct BlockInfo { 720b57cec5SDimitry Andric unsigned BlockID; 730b57cec5SDimitry Andric std::vector<std::shared_ptr<BitCodeAbbrev>> Abbrevs; 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric std::vector<BlockInfo> BlockInfoRecords; 760b57cec5SDimitry Andric WriteWord(unsigned Value)770b57cec5SDimitry Andric void WriteWord(unsigned Value) { 785f757f3fSDimitry Andric Value = 795f757f3fSDimitry Andric support::endian::byte_swap<uint32_t, llvm::endianness::little>(Value); 800b57cec5SDimitry Andric Out.append(reinterpret_cast<const char *>(&Value), 810b57cec5SDimitry Andric reinterpret_cast<const char *>(&Value + 1)); 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric GetNumOfFlushedBytes()84e8d8bef9SDimitry Andric uint64_t GetNumOfFlushedBytes() const { return FS ? FS->tell() : 0; } 85e8d8bef9SDimitry Andric GetBufferOffset()86e8d8bef9SDimitry Andric size_t GetBufferOffset() const { return Out.size() + GetNumOfFlushedBytes(); } 870b57cec5SDimitry Andric GetWordIndex()880b57cec5SDimitry Andric size_t GetWordIndex() const { 890b57cec5SDimitry Andric size_t Offset = GetBufferOffset(); 900b57cec5SDimitry Andric assert((Offset & 3) == 0 && "Not 32-bit aligned"); 910b57cec5SDimitry Andric return Offset / 4; 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 94e8d8bef9SDimitry Andric /// If the related file stream supports reading, seeking and writing, flush 95e8d8bef9SDimitry Andric /// the buffer if its size is above a threshold. FlushToFile()96e8d8bef9SDimitry Andric void FlushToFile() { 97e8d8bef9SDimitry Andric if (!FS) 98e8d8bef9SDimitry Andric return; 99e8d8bef9SDimitry Andric if (Out.size() < FlushThreshold) 100e8d8bef9SDimitry Andric return; 101e8d8bef9SDimitry Andric FS->write((char *)&Out.front(), Out.size()); 102e8d8bef9SDimitry Andric Out.clear(); 103e8d8bef9SDimitry Andric } 104e8d8bef9SDimitry Andric 1050b57cec5SDimitry Andric public: 106e8d8bef9SDimitry Andric /// Create a BitstreamWriter that writes to Buffer \p O. 107e8d8bef9SDimitry Andric /// 108e8d8bef9SDimitry Andric /// \p FS is the file stream that \p O flushes to incrementally. If \p FS is 109e8d8bef9SDimitry Andric /// null, \p O does not flush incrementially, but writes to disk at the end. 110e8d8bef9SDimitry Andric /// 111e8d8bef9SDimitry Andric /// \p FlushThreshold is the threshold (unit M) to flush \p O if \p FS is 11281ad6265SDimitry Andric /// valid. Flushing only occurs at (sub)block boundaries. 113e8d8bef9SDimitry Andric BitstreamWriter(SmallVectorImpl<char> &O, raw_fd_stream *FS = nullptr, 114e8d8bef9SDimitry Andric uint32_t FlushThreshold = 512) Out(O)1155f757f3fSDimitry Andric : Out(O), FS(FS), FlushThreshold(uint64_t(FlushThreshold) << 20), CurBit(0), 116e8d8bef9SDimitry Andric CurValue(0), CurCodeSize(2) {} 1170b57cec5SDimitry Andric ~BitstreamWriter()1180b57cec5SDimitry Andric ~BitstreamWriter() { 1190b57cec5SDimitry Andric assert(CurBit == 0 && "Unflushed data remaining"); 1200b57cec5SDimitry Andric assert(BlockScope.empty() && CurAbbrevs.empty() && "Block imbalance"); 1210b57cec5SDimitry Andric } 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric /// Retrieve the current position in the stream, in bits. GetCurrentBitNo()1240b57cec5SDimitry Andric uint64_t GetCurrentBitNo() const { return GetBufferOffset() * 8 + CurBit; } 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric /// Retrieve the number of bits currently used to encode an abbrev ID. GetAbbrevIDWidth()1270b57cec5SDimitry Andric unsigned GetAbbrevIDWidth() const { return CurCodeSize; } 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 1300b57cec5SDimitry Andric // Basic Primitives for emitting bits to the stream. 1310b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 1320b57cec5SDimitry Andric 1335f757f3fSDimitry Andric /// Backpatch a byte in the output at the given bit offset with the specified 1345f757f3fSDimitry Andric /// value. BackpatchByte(uint64_t BitNo,uint8_t NewByte)1355f757f3fSDimitry Andric void BackpatchByte(uint64_t BitNo, uint8_t NewByte) { 1360b57cec5SDimitry Andric using namespace llvm::support; 137e8d8bef9SDimitry Andric uint64_t ByteNo = BitNo / 8; 138e8d8bef9SDimitry Andric uint64_t StartBit = BitNo & 7; 139e8d8bef9SDimitry Andric uint64_t NumOfFlushedBytes = GetNumOfFlushedBytes(); 140e8d8bef9SDimitry Andric 141e8d8bef9SDimitry Andric if (ByteNo >= NumOfFlushedBytes) { 1425f757f3fSDimitry Andric assert((!endian::readAtBitAlignment<uint8_t, llvm::endianness::little, 1435f757f3fSDimitry Andric unaligned>( 144e8d8bef9SDimitry Andric &Out[ByteNo - NumOfFlushedBytes], StartBit)) && 1450b57cec5SDimitry Andric "Expected to be patching over 0-value placeholders"); 1465f757f3fSDimitry Andric endian::writeAtBitAlignment<uint8_t, llvm::endianness::little, unaligned>( 1475f757f3fSDimitry Andric &Out[ByteNo - NumOfFlushedBytes], NewByte, StartBit); 148e8d8bef9SDimitry Andric return; 149e8d8bef9SDimitry Andric } 150e8d8bef9SDimitry Andric 151e8d8bef9SDimitry Andric // If the byte offset to backpatch is flushed, use seek to backfill data. 152e8d8bef9SDimitry Andric // First, save the file position to restore later. 153e8d8bef9SDimitry Andric uint64_t CurPos = FS->tell(); 154e8d8bef9SDimitry Andric 155e8d8bef9SDimitry Andric // Copy data to update into Bytes from the file FS and the buffer Out. 1565f757f3fSDimitry Andric char Bytes[3]; // Use one more byte to silence a warning from Visual C++. 1575f757f3fSDimitry Andric size_t BytesNum = StartBit ? 2 : 1; 158e8d8bef9SDimitry Andric size_t BytesFromDisk = std::min(static_cast<uint64_t>(BytesNum), NumOfFlushedBytes - ByteNo); 159e8d8bef9SDimitry Andric size_t BytesFromBuffer = BytesNum - BytesFromDisk; 160e8d8bef9SDimitry Andric 161e8d8bef9SDimitry Andric // When unaligned, copy existing data into Bytes from the file FS and the 162e8d8bef9SDimitry Andric // buffer Out so that it can be updated before writing. For debug builds 163e8d8bef9SDimitry Andric // read bytes unconditionally in order to check that the existing value is 0 164e8d8bef9SDimitry Andric // as expected. 165e8d8bef9SDimitry Andric #ifdef NDEBUG 166e8d8bef9SDimitry Andric if (StartBit) 167e8d8bef9SDimitry Andric #endif 168e8d8bef9SDimitry Andric { 169e8d8bef9SDimitry Andric FS->seek(ByteNo); 170e8d8bef9SDimitry Andric ssize_t BytesRead = FS->read(Bytes, BytesFromDisk); 171e8d8bef9SDimitry Andric (void)BytesRead; // silence warning 172e8d8bef9SDimitry Andric assert(BytesRead >= 0 && static_cast<size_t>(BytesRead) == BytesFromDisk); 173e8d8bef9SDimitry Andric for (size_t i = 0; i < BytesFromBuffer; ++i) 174e8d8bef9SDimitry Andric Bytes[BytesFromDisk + i] = Out[i]; 1755f757f3fSDimitry Andric assert((!endian::readAtBitAlignment<uint8_t, llvm::endianness::little, 1765f757f3fSDimitry Andric unaligned>(Bytes, StartBit)) && 177e8d8bef9SDimitry Andric "Expected to be patching over 0-value placeholders"); 178e8d8bef9SDimitry Andric } 179e8d8bef9SDimitry Andric 180e8d8bef9SDimitry Andric // Update Bytes in terms of bit offset and value. 1815f757f3fSDimitry Andric endian::writeAtBitAlignment<uint8_t, llvm::endianness::little, unaligned>( 1825f757f3fSDimitry Andric Bytes, NewByte, StartBit); 183e8d8bef9SDimitry Andric 184e8d8bef9SDimitry Andric // Copy updated data back to the file FS and the buffer Out. 185e8d8bef9SDimitry Andric FS->seek(ByteNo); 186e8d8bef9SDimitry Andric FS->write(Bytes, BytesFromDisk); 187e8d8bef9SDimitry Andric for (size_t i = 0; i < BytesFromBuffer; ++i) 188e8d8bef9SDimitry Andric Out[i] = Bytes[BytesFromDisk + i]; 189e8d8bef9SDimitry Andric 190e8d8bef9SDimitry Andric // Restore the file position. 191e8d8bef9SDimitry Andric FS->seek(CurPos); 1920b57cec5SDimitry Andric } 1930b57cec5SDimitry Andric BackpatchHalfWord(uint64_t BitNo,uint16_t Val)1945f757f3fSDimitry Andric void BackpatchHalfWord(uint64_t BitNo, uint16_t Val) { 1955f757f3fSDimitry Andric BackpatchByte(BitNo, (uint8_t)Val); 1965f757f3fSDimitry Andric BackpatchByte(BitNo + 8, (uint8_t)(Val >> 8)); 1975f757f3fSDimitry Andric } 1985f757f3fSDimitry Andric BackpatchWord(uint64_t BitNo,unsigned Val)1995f757f3fSDimitry Andric void BackpatchWord(uint64_t BitNo, unsigned Val) { 2005f757f3fSDimitry Andric BackpatchHalfWord(BitNo, (uint16_t)Val); 2015f757f3fSDimitry Andric BackpatchHalfWord(BitNo + 16, (uint16_t)(Val >> 16)); 2025f757f3fSDimitry Andric } 2035f757f3fSDimitry Andric BackpatchWord64(uint64_t BitNo,uint64_t Val)2040b57cec5SDimitry Andric void BackpatchWord64(uint64_t BitNo, uint64_t Val) { 2050b57cec5SDimitry Andric BackpatchWord(BitNo, (uint32_t)Val); 2060b57cec5SDimitry Andric BackpatchWord(BitNo + 32, (uint32_t)(Val >> 32)); 2070b57cec5SDimitry Andric } 2080b57cec5SDimitry Andric Emit(uint32_t Val,unsigned NumBits)2090b57cec5SDimitry Andric void Emit(uint32_t Val, unsigned NumBits) { 2100b57cec5SDimitry Andric assert(NumBits && NumBits <= 32 && "Invalid value size!"); 2110b57cec5SDimitry Andric assert((Val & ~(~0U >> (32-NumBits))) == 0 && "High bits set!"); 2120b57cec5SDimitry Andric CurValue |= Val << CurBit; 2130b57cec5SDimitry Andric if (CurBit + NumBits < 32) { 2140b57cec5SDimitry Andric CurBit += NumBits; 2150b57cec5SDimitry Andric return; 2160b57cec5SDimitry Andric } 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric // Add the current word. 2190b57cec5SDimitry Andric WriteWord(CurValue); 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric if (CurBit) 2220b57cec5SDimitry Andric CurValue = Val >> (32-CurBit); 2230b57cec5SDimitry Andric else 2240b57cec5SDimitry Andric CurValue = 0; 2250b57cec5SDimitry Andric CurBit = (CurBit+NumBits) & 31; 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric FlushToWord()2280b57cec5SDimitry Andric void FlushToWord() { 2290b57cec5SDimitry Andric if (CurBit) { 2300b57cec5SDimitry Andric WriteWord(CurValue); 2310b57cec5SDimitry Andric CurBit = 0; 2320b57cec5SDimitry Andric CurValue = 0; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric EmitVBR(uint32_t Val,unsigned NumBits)2360b57cec5SDimitry Andric void EmitVBR(uint32_t Val, unsigned NumBits) { 2370b57cec5SDimitry Andric assert(NumBits <= 32 && "Too many bits to emit!"); 2380b57cec5SDimitry Andric uint32_t Threshold = 1U << (NumBits-1); 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric // Emit the bits with VBR encoding, NumBits-1 bits at a time. 2410b57cec5SDimitry Andric while (Val >= Threshold) { 2421db9f3b2SDimitry Andric Emit((Val & ((1U << (NumBits - 1)) - 1)) | (1U << (NumBits - 1)), 2431db9f3b2SDimitry Andric NumBits); 2440b57cec5SDimitry Andric Val >>= NumBits-1; 2450b57cec5SDimitry Andric } 2460b57cec5SDimitry Andric 2470b57cec5SDimitry Andric Emit(Val, NumBits); 2480b57cec5SDimitry Andric } 2490b57cec5SDimitry Andric EmitVBR64(uint64_t Val,unsigned NumBits)2500b57cec5SDimitry Andric void EmitVBR64(uint64_t Val, unsigned NumBits) { 2510b57cec5SDimitry Andric assert(NumBits <= 32 && "Too many bits to emit!"); 2520b57cec5SDimitry Andric if ((uint32_t)Val == Val) 2530b57cec5SDimitry Andric return EmitVBR((uint32_t)Val, NumBits); 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric uint32_t Threshold = 1U << (NumBits-1); 2560b57cec5SDimitry Andric 2570b57cec5SDimitry Andric // Emit the bits with VBR encoding, NumBits-1 bits at a time. 2580b57cec5SDimitry Andric while (Val >= Threshold) { 2591db9f3b2SDimitry Andric Emit(((uint32_t)Val & ((1U << (NumBits - 1)) - 1)) | 2601db9f3b2SDimitry Andric (1U << (NumBits - 1)), 26181ad6265SDimitry Andric NumBits); 2620b57cec5SDimitry Andric Val >>= NumBits-1; 2630b57cec5SDimitry Andric } 2640b57cec5SDimitry Andric 2650b57cec5SDimitry Andric Emit((uint32_t)Val, NumBits); 2660b57cec5SDimitry Andric } 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric /// EmitCode - Emit the specified code. EmitCode(unsigned Val)2690b57cec5SDimitry Andric void EmitCode(unsigned Val) { 2700b57cec5SDimitry Andric Emit(Val, CurCodeSize); 2710b57cec5SDimitry Andric } 2720b57cec5SDimitry Andric 2730b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 2740b57cec5SDimitry Andric // Block Manipulation 2750b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric /// getBlockInfo - If there is block info for the specified ID, return it, 2780b57cec5SDimitry Andric /// otherwise return null. getBlockInfo(unsigned BlockID)2790b57cec5SDimitry Andric BlockInfo *getBlockInfo(unsigned BlockID) { 2800b57cec5SDimitry Andric // Common case, the most recent entry matches BlockID. 2810b57cec5SDimitry Andric if (!BlockInfoRecords.empty() && BlockInfoRecords.back().BlockID == BlockID) 2820b57cec5SDimitry Andric return &BlockInfoRecords.back(); 2830b57cec5SDimitry Andric 284bdd1243dSDimitry Andric for (BlockInfo &BI : BlockInfoRecords) 285bdd1243dSDimitry Andric if (BI.BlockID == BlockID) 286bdd1243dSDimitry Andric return &BI; 2870b57cec5SDimitry Andric return nullptr; 2880b57cec5SDimitry Andric } 2890b57cec5SDimitry Andric EnterSubblock(unsigned BlockID,unsigned CodeLen)2900b57cec5SDimitry Andric void EnterSubblock(unsigned BlockID, unsigned CodeLen) { 2910b57cec5SDimitry Andric // Block header: 2920b57cec5SDimitry Andric // [ENTER_SUBBLOCK, blockid, newcodelen, <align4bytes>, blocklen] 2930b57cec5SDimitry Andric EmitCode(bitc::ENTER_SUBBLOCK); 2940b57cec5SDimitry Andric EmitVBR(BlockID, bitc::BlockIDWidth); 2950b57cec5SDimitry Andric EmitVBR(CodeLen, bitc::CodeLenWidth); 2960b57cec5SDimitry Andric FlushToWord(); 2970b57cec5SDimitry Andric 2980b57cec5SDimitry Andric size_t BlockSizeWordIndex = GetWordIndex(); 2990b57cec5SDimitry Andric unsigned OldCodeSize = CurCodeSize; 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric // Emit a placeholder, which will be replaced when the block is popped. 3020b57cec5SDimitry Andric Emit(0, bitc::BlockSizeWidth); 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric CurCodeSize = CodeLen; 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric // Push the outer block's abbrev set onto the stack, start out with an 3070b57cec5SDimitry Andric // empty abbrev set. 3080b57cec5SDimitry Andric BlockScope.emplace_back(OldCodeSize, BlockSizeWordIndex); 3090b57cec5SDimitry Andric BlockScope.back().PrevAbbrevs.swap(CurAbbrevs); 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric // If there is a blockinfo for this BlockID, add all the predefined abbrevs 3120b57cec5SDimitry Andric // to the abbrev list. 313fe6060f1SDimitry Andric if (BlockInfo *Info = getBlockInfo(BlockID)) 314fe6060f1SDimitry Andric append_range(CurAbbrevs, Info->Abbrevs); 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric ExitBlock()3170b57cec5SDimitry Andric void ExitBlock() { 3180b57cec5SDimitry Andric assert(!BlockScope.empty() && "Block scope imbalance!"); 3190b57cec5SDimitry Andric const Block &B = BlockScope.back(); 3200b57cec5SDimitry Andric 3210b57cec5SDimitry Andric // Block tail: 3220b57cec5SDimitry Andric // [END_BLOCK, <align4bytes>] 3230b57cec5SDimitry Andric EmitCode(bitc::END_BLOCK); 3240b57cec5SDimitry Andric FlushToWord(); 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric // Compute the size of the block, in words, not counting the size field. 3270b57cec5SDimitry Andric size_t SizeInWords = GetWordIndex() - B.StartSizeWord - 1; 3280b57cec5SDimitry Andric uint64_t BitNo = uint64_t(B.StartSizeWord) * 32; 3290b57cec5SDimitry Andric 3300b57cec5SDimitry Andric // Update the block size field in the header of this sub-block. 3310b57cec5SDimitry Andric BackpatchWord(BitNo, SizeInWords); 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric // Restore the inner block's code size and abbrev table. 3340b57cec5SDimitry Andric CurCodeSize = B.PrevCodeSize; 3350b57cec5SDimitry Andric CurAbbrevs = std::move(B.PrevAbbrevs); 3360b57cec5SDimitry Andric BlockScope.pop_back(); 33781ad6265SDimitry Andric FlushToFile(); 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 3410b57cec5SDimitry Andric // Record Emission 3420b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 3430b57cec5SDimitry Andric 3440b57cec5SDimitry Andric private: 3450b57cec5SDimitry Andric /// EmitAbbreviatedLiteral - Emit a literal value according to its abbrev 3460b57cec5SDimitry Andric /// record. This is a no-op, since the abbrev specifies the literal to use. 3470b57cec5SDimitry Andric template<typename uintty> EmitAbbreviatedLiteral(const BitCodeAbbrevOp & Op,uintty V)3480b57cec5SDimitry Andric void EmitAbbreviatedLiteral(const BitCodeAbbrevOp &Op, uintty V) { 3490b57cec5SDimitry Andric assert(Op.isLiteral() && "Not a literal"); 3500b57cec5SDimitry Andric // If the abbrev specifies the literal value to use, don't emit 3510b57cec5SDimitry Andric // anything. 3520b57cec5SDimitry Andric assert(V == Op.getLiteralValue() && 3530b57cec5SDimitry Andric "Invalid abbrev for record!"); 3540b57cec5SDimitry Andric } 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric /// EmitAbbreviatedField - Emit a single scalar field value with the specified 3570b57cec5SDimitry Andric /// encoding. 3580b57cec5SDimitry Andric template<typename uintty> EmitAbbreviatedField(const BitCodeAbbrevOp & Op,uintty V)3590b57cec5SDimitry Andric void EmitAbbreviatedField(const BitCodeAbbrevOp &Op, uintty V) { 3600b57cec5SDimitry Andric assert(!Op.isLiteral() && "Literals should use EmitAbbreviatedLiteral!"); 3610b57cec5SDimitry Andric 3620b57cec5SDimitry Andric // Encode the value as we are commanded. 3630b57cec5SDimitry Andric switch (Op.getEncoding()) { 3640b57cec5SDimitry Andric default: llvm_unreachable("Unknown encoding!"); 3650b57cec5SDimitry Andric case BitCodeAbbrevOp::Fixed: 3660b57cec5SDimitry Andric if (Op.getEncodingData()) 3670b57cec5SDimitry Andric Emit((unsigned)V, (unsigned)Op.getEncodingData()); 3680b57cec5SDimitry Andric break; 3690b57cec5SDimitry Andric case BitCodeAbbrevOp::VBR: 3700b57cec5SDimitry Andric if (Op.getEncodingData()) 3710b57cec5SDimitry Andric EmitVBR64(V, (unsigned)Op.getEncodingData()); 3720b57cec5SDimitry Andric break; 3730b57cec5SDimitry Andric case BitCodeAbbrevOp::Char6: 3740b57cec5SDimitry Andric Emit(BitCodeAbbrevOp::EncodeChar6((char)V), 6); 3750b57cec5SDimitry Andric break; 3760b57cec5SDimitry Andric } 3770b57cec5SDimitry Andric } 3780b57cec5SDimitry Andric 3790b57cec5SDimitry Andric /// EmitRecordWithAbbrevImpl - This is the core implementation of the record 3800b57cec5SDimitry Andric /// emission code. If BlobData is non-null, then it specifies an array of 3810b57cec5SDimitry Andric /// data that should be emitted as part of the Blob or Array operand that is 3820b57cec5SDimitry Andric /// known to exist at the end of the record. If Code is specified, then 3830b57cec5SDimitry Andric /// it is the record code to emit before the Vals, which must not contain 3840b57cec5SDimitry Andric /// the code. 3850b57cec5SDimitry Andric template <typename uintty> EmitRecordWithAbbrevImpl(unsigned Abbrev,ArrayRef<uintty> Vals,StringRef Blob,std::optional<unsigned> Code)3860b57cec5SDimitry Andric void EmitRecordWithAbbrevImpl(unsigned Abbrev, ArrayRef<uintty> Vals, 387bdd1243dSDimitry Andric StringRef Blob, std::optional<unsigned> Code) { 3880b57cec5SDimitry Andric const char *BlobData = Blob.data(); 3890b57cec5SDimitry Andric unsigned BlobLen = (unsigned) Blob.size(); 3900b57cec5SDimitry Andric unsigned AbbrevNo = Abbrev-bitc::FIRST_APPLICATION_ABBREV; 3910b57cec5SDimitry Andric assert(AbbrevNo < CurAbbrevs.size() && "Invalid abbrev #!"); 3920b57cec5SDimitry Andric const BitCodeAbbrev *Abbv = CurAbbrevs[AbbrevNo].get(); 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric EmitCode(Abbrev); 3950b57cec5SDimitry Andric 3960b57cec5SDimitry Andric unsigned i = 0, e = static_cast<unsigned>(Abbv->getNumOperandInfos()); 3970b57cec5SDimitry Andric if (Code) { 3980b57cec5SDimitry Andric assert(e && "Expected non-empty abbreviation"); 3990b57cec5SDimitry Andric const BitCodeAbbrevOp &Op = Abbv->getOperandInfo(i++); 4000b57cec5SDimitry Andric 4010b57cec5SDimitry Andric if (Op.isLiteral()) 402bdd1243dSDimitry Andric EmitAbbreviatedLiteral(Op, *Code); 4030b57cec5SDimitry Andric else { 4040b57cec5SDimitry Andric assert(Op.getEncoding() != BitCodeAbbrevOp::Array && 4050b57cec5SDimitry Andric Op.getEncoding() != BitCodeAbbrevOp::Blob && 4060b57cec5SDimitry Andric "Expected literal or scalar"); 407bdd1243dSDimitry Andric EmitAbbreviatedField(Op, *Code); 4080b57cec5SDimitry Andric } 4090b57cec5SDimitry Andric } 4100b57cec5SDimitry Andric 4110b57cec5SDimitry Andric unsigned RecordIdx = 0; 4120b57cec5SDimitry Andric for (; i != e; ++i) { 4130b57cec5SDimitry Andric const BitCodeAbbrevOp &Op = Abbv->getOperandInfo(i); 4140b57cec5SDimitry Andric if (Op.isLiteral()) { 4150b57cec5SDimitry Andric assert(RecordIdx < Vals.size() && "Invalid abbrev/record"); 4160b57cec5SDimitry Andric EmitAbbreviatedLiteral(Op, Vals[RecordIdx]); 4170b57cec5SDimitry Andric ++RecordIdx; 4180b57cec5SDimitry Andric } else if (Op.getEncoding() == BitCodeAbbrevOp::Array) { 4190b57cec5SDimitry Andric // Array case. 4200b57cec5SDimitry Andric assert(i + 2 == e && "array op not second to last?"); 4210b57cec5SDimitry Andric const BitCodeAbbrevOp &EltEnc = Abbv->getOperandInfo(++i); 4220b57cec5SDimitry Andric 4230b57cec5SDimitry Andric // If this record has blob data, emit it, otherwise we must have record 4240b57cec5SDimitry Andric // entries to encode this way. 4250b57cec5SDimitry Andric if (BlobData) { 4260b57cec5SDimitry Andric assert(RecordIdx == Vals.size() && 4270b57cec5SDimitry Andric "Blob data and record entries specified for array!"); 4280b57cec5SDimitry Andric // Emit a vbr6 to indicate the number of elements present. 4290b57cec5SDimitry Andric EmitVBR(static_cast<uint32_t>(BlobLen), 6); 4300b57cec5SDimitry Andric 4310b57cec5SDimitry Andric // Emit each field. 4320b57cec5SDimitry Andric for (unsigned i = 0; i != BlobLen; ++i) 4330b57cec5SDimitry Andric EmitAbbreviatedField(EltEnc, (unsigned char)BlobData[i]); 4340b57cec5SDimitry Andric 4350b57cec5SDimitry Andric // Know that blob data is consumed for assertion below. 4360b57cec5SDimitry Andric BlobData = nullptr; 4370b57cec5SDimitry Andric } else { 4380b57cec5SDimitry Andric // Emit a vbr6 to indicate the number of elements present. 4390b57cec5SDimitry Andric EmitVBR(static_cast<uint32_t>(Vals.size()-RecordIdx), 6); 4400b57cec5SDimitry Andric 4410b57cec5SDimitry Andric // Emit each field. 4420b57cec5SDimitry Andric for (unsigned e = Vals.size(); RecordIdx != e; ++RecordIdx) 4430b57cec5SDimitry Andric EmitAbbreviatedField(EltEnc, Vals[RecordIdx]); 4440b57cec5SDimitry Andric } 4450b57cec5SDimitry Andric } else if (Op.getEncoding() == BitCodeAbbrevOp::Blob) { 4460b57cec5SDimitry Andric // If this record has blob data, emit it, otherwise we must have record 4470b57cec5SDimitry Andric // entries to encode this way. 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric if (BlobData) { 4500b57cec5SDimitry Andric assert(RecordIdx == Vals.size() && 4510b57cec5SDimitry Andric "Blob data and record entries specified for blob operand!"); 4520b57cec5SDimitry Andric 4530b57cec5SDimitry Andric assert(Blob.data() == BlobData && "BlobData got moved"); 4540b57cec5SDimitry Andric assert(Blob.size() == BlobLen && "BlobLen got changed"); 4550b57cec5SDimitry Andric emitBlob(Blob); 4560b57cec5SDimitry Andric BlobData = nullptr; 4570b57cec5SDimitry Andric } else { 4580b57cec5SDimitry Andric emitBlob(Vals.slice(RecordIdx)); 4590b57cec5SDimitry Andric } 4600b57cec5SDimitry Andric } else { // Single scalar field. 4610b57cec5SDimitry Andric assert(RecordIdx < Vals.size() && "Invalid abbrev/record"); 4620b57cec5SDimitry Andric EmitAbbreviatedField(Op, Vals[RecordIdx]); 4630b57cec5SDimitry Andric ++RecordIdx; 4640b57cec5SDimitry Andric } 4650b57cec5SDimitry Andric } 4660b57cec5SDimitry Andric assert(RecordIdx == Vals.size() && "Not all record operands emitted!"); 4670b57cec5SDimitry Andric assert(BlobData == nullptr && 4680b57cec5SDimitry Andric "Blob data specified for record that doesn't use it!"); 4690b57cec5SDimitry Andric } 4700b57cec5SDimitry Andric 4710b57cec5SDimitry Andric public: 4720b57cec5SDimitry Andric /// Emit a blob, including flushing before and tail-padding. 4730b57cec5SDimitry Andric template <class UIntTy> 4740b57cec5SDimitry Andric void emitBlob(ArrayRef<UIntTy> Bytes, bool ShouldEmitSize = true) { 4750b57cec5SDimitry Andric // Emit a vbr6 to indicate the number of elements present. 4760b57cec5SDimitry Andric if (ShouldEmitSize) 4770b57cec5SDimitry Andric EmitVBR(static_cast<uint32_t>(Bytes.size()), 6); 4780b57cec5SDimitry Andric 4790b57cec5SDimitry Andric // Flush to a 32-bit alignment boundary. 4800b57cec5SDimitry Andric FlushToWord(); 4810b57cec5SDimitry Andric 4820b57cec5SDimitry Andric // Emit literal bytes. 48381ad6265SDimitry Andric assert(llvm::all_of(Bytes, [](UIntTy B) { return isUInt<8>(B); })); 48481ad6265SDimitry Andric Out.append(Bytes.begin(), Bytes.end()); 4850b57cec5SDimitry Andric 4860b57cec5SDimitry Andric // Align end to 32-bits. 4870b57cec5SDimitry Andric while (GetBufferOffset() & 3) 48881ad6265SDimitry Andric Out.push_back(0); 4890b57cec5SDimitry Andric } 4900b57cec5SDimitry Andric void emitBlob(StringRef Bytes, bool ShouldEmitSize = true) { 491bdd1243dSDimitry Andric emitBlob(ArrayRef((const uint8_t *)Bytes.data(), Bytes.size()), 4920b57cec5SDimitry Andric ShouldEmitSize); 4930b57cec5SDimitry Andric } 4940b57cec5SDimitry Andric 4950b57cec5SDimitry Andric /// EmitRecord - Emit the specified record to the stream, using an abbrev if 4960b57cec5SDimitry Andric /// we have one to compress the output. 4970b57cec5SDimitry Andric template <typename Container> 4980b57cec5SDimitry Andric void EmitRecord(unsigned Code, const Container &Vals, unsigned Abbrev = 0) { 4990b57cec5SDimitry Andric if (!Abbrev) { 5000b57cec5SDimitry Andric // If we don't have an abbrev to use, emit this in its fully unabbreviated 5010b57cec5SDimitry Andric // form. 502bdd1243dSDimitry Andric auto Count = static_cast<uint32_t>(std::size(Vals)); 5030b57cec5SDimitry Andric EmitCode(bitc::UNABBREV_RECORD); 5040b57cec5SDimitry Andric EmitVBR(Code, 6); 5050b57cec5SDimitry Andric EmitVBR(Count, 6); 5060b57cec5SDimitry Andric for (unsigned i = 0, e = Count; i != e; ++i) 5070b57cec5SDimitry Andric EmitVBR64(Vals[i], 6); 5080b57cec5SDimitry Andric return; 5090b57cec5SDimitry Andric } 5100b57cec5SDimitry Andric 511bdd1243dSDimitry Andric EmitRecordWithAbbrevImpl(Abbrev, ArrayRef(Vals), StringRef(), Code); 5120b57cec5SDimitry Andric } 5130b57cec5SDimitry Andric 5140b57cec5SDimitry Andric /// EmitRecordWithAbbrev - Emit a record with the specified abbreviation. 5150b57cec5SDimitry Andric /// Unlike EmitRecord, the code for the record should be included in Vals as 5160b57cec5SDimitry Andric /// the first entry. 5170b57cec5SDimitry Andric template <typename Container> EmitRecordWithAbbrev(unsigned Abbrev,const Container & Vals)5180b57cec5SDimitry Andric void EmitRecordWithAbbrev(unsigned Abbrev, const Container &Vals) { 519bdd1243dSDimitry Andric EmitRecordWithAbbrevImpl(Abbrev, ArrayRef(Vals), StringRef(), std::nullopt); 5200b57cec5SDimitry Andric } 5210b57cec5SDimitry Andric 5220b57cec5SDimitry Andric /// EmitRecordWithBlob - Emit the specified record to the stream, using an 5230b57cec5SDimitry Andric /// abbrev that includes a blob at the end. The blob data to emit is 5240b57cec5SDimitry Andric /// specified by the pointer and length specified at the end. In contrast to 5250b57cec5SDimitry Andric /// EmitRecord, this routine expects that the first entry in Vals is the code 5260b57cec5SDimitry Andric /// of the record. 5270b57cec5SDimitry Andric template <typename Container> EmitRecordWithBlob(unsigned Abbrev,const Container & Vals,StringRef Blob)5280b57cec5SDimitry Andric void EmitRecordWithBlob(unsigned Abbrev, const Container &Vals, 5290b57cec5SDimitry Andric StringRef Blob) { 530bdd1243dSDimitry Andric EmitRecordWithAbbrevImpl(Abbrev, ArrayRef(Vals), Blob, std::nullopt); 5310b57cec5SDimitry Andric } 5320b57cec5SDimitry Andric template <typename Container> EmitRecordWithBlob(unsigned Abbrev,const Container & Vals,const char * BlobData,unsigned BlobLen)5330b57cec5SDimitry Andric void EmitRecordWithBlob(unsigned Abbrev, const Container &Vals, 5340b57cec5SDimitry Andric const char *BlobData, unsigned BlobLen) { 535bdd1243dSDimitry Andric return EmitRecordWithAbbrevImpl(Abbrev, ArrayRef(Vals), 536bdd1243dSDimitry Andric StringRef(BlobData, BlobLen), std::nullopt); 5370b57cec5SDimitry Andric } 5380b57cec5SDimitry Andric 5390b57cec5SDimitry Andric /// EmitRecordWithArray - Just like EmitRecordWithBlob, works with records 5400b57cec5SDimitry Andric /// that end with an array. 5410b57cec5SDimitry Andric template <typename Container> EmitRecordWithArray(unsigned Abbrev,const Container & Vals,StringRef Array)5420b57cec5SDimitry Andric void EmitRecordWithArray(unsigned Abbrev, const Container &Vals, 5430b57cec5SDimitry Andric StringRef Array) { 544bdd1243dSDimitry Andric EmitRecordWithAbbrevImpl(Abbrev, ArrayRef(Vals), Array, std::nullopt); 5450b57cec5SDimitry Andric } 5460b57cec5SDimitry Andric template <typename Container> EmitRecordWithArray(unsigned Abbrev,const Container & Vals,const char * ArrayData,unsigned ArrayLen)5470b57cec5SDimitry Andric void EmitRecordWithArray(unsigned Abbrev, const Container &Vals, 5480b57cec5SDimitry Andric const char *ArrayData, unsigned ArrayLen) { 549bdd1243dSDimitry Andric return EmitRecordWithAbbrevImpl( 550bdd1243dSDimitry Andric Abbrev, ArrayRef(Vals), StringRef(ArrayData, ArrayLen), std::nullopt); 5510b57cec5SDimitry Andric } 5520b57cec5SDimitry Andric 5530b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 5540b57cec5SDimitry Andric // Abbrev Emission 5550b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 5560b57cec5SDimitry Andric 5570b57cec5SDimitry Andric private: 5580b57cec5SDimitry Andric // Emit the abbreviation as a DEFINE_ABBREV record. EncodeAbbrev(const BitCodeAbbrev & Abbv)5590b57cec5SDimitry Andric void EncodeAbbrev(const BitCodeAbbrev &Abbv) { 5600b57cec5SDimitry Andric EmitCode(bitc::DEFINE_ABBREV); 5610b57cec5SDimitry Andric EmitVBR(Abbv.getNumOperandInfos(), 5); 5620b57cec5SDimitry Andric for (unsigned i = 0, e = static_cast<unsigned>(Abbv.getNumOperandInfos()); 5630b57cec5SDimitry Andric i != e; ++i) { 5640b57cec5SDimitry Andric const BitCodeAbbrevOp &Op = Abbv.getOperandInfo(i); 5650b57cec5SDimitry Andric Emit(Op.isLiteral(), 1); 5660b57cec5SDimitry Andric if (Op.isLiteral()) { 5670b57cec5SDimitry Andric EmitVBR64(Op.getLiteralValue(), 8); 5680b57cec5SDimitry Andric } else { 5690b57cec5SDimitry Andric Emit(Op.getEncoding(), 3); 5700b57cec5SDimitry Andric if (Op.hasEncodingData()) 5710b57cec5SDimitry Andric EmitVBR64(Op.getEncodingData(), 5); 5720b57cec5SDimitry Andric } 5730b57cec5SDimitry Andric } 5740b57cec5SDimitry Andric } 5750b57cec5SDimitry Andric public: 5760b57cec5SDimitry Andric 5770b57cec5SDimitry Andric /// Emits the abbreviation \p Abbv to the stream. EmitAbbrev(std::shared_ptr<BitCodeAbbrev> Abbv)5780b57cec5SDimitry Andric unsigned EmitAbbrev(std::shared_ptr<BitCodeAbbrev> Abbv) { 5790b57cec5SDimitry Andric EncodeAbbrev(*Abbv); 5800b57cec5SDimitry Andric CurAbbrevs.push_back(std::move(Abbv)); 5810b57cec5SDimitry Andric return static_cast<unsigned>(CurAbbrevs.size())-1 + 5820b57cec5SDimitry Andric bitc::FIRST_APPLICATION_ABBREV; 5830b57cec5SDimitry Andric } 5840b57cec5SDimitry Andric 5850b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 5860b57cec5SDimitry Andric // BlockInfo Block Emission 5870b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 5880b57cec5SDimitry Andric 5890b57cec5SDimitry Andric /// EnterBlockInfoBlock - Start emitting the BLOCKINFO_BLOCK. EnterBlockInfoBlock()5900b57cec5SDimitry Andric void EnterBlockInfoBlock() { 5910b57cec5SDimitry Andric EnterSubblock(bitc::BLOCKINFO_BLOCK_ID, 2); 5920b57cec5SDimitry Andric BlockInfoCurBID = ~0U; 5930b57cec5SDimitry Andric BlockInfoRecords.clear(); 5940b57cec5SDimitry Andric } 5950b57cec5SDimitry Andric private: 5960b57cec5SDimitry Andric /// SwitchToBlockID - If we aren't already talking about the specified block 5970b57cec5SDimitry Andric /// ID, emit a BLOCKINFO_CODE_SETBID record. SwitchToBlockID(unsigned BlockID)5980b57cec5SDimitry Andric void SwitchToBlockID(unsigned BlockID) { 5990b57cec5SDimitry Andric if (BlockInfoCurBID == BlockID) return; 6000b57cec5SDimitry Andric SmallVector<unsigned, 2> V; 6010b57cec5SDimitry Andric V.push_back(BlockID); 6020b57cec5SDimitry Andric EmitRecord(bitc::BLOCKINFO_CODE_SETBID, V); 6030b57cec5SDimitry Andric BlockInfoCurBID = BlockID; 6040b57cec5SDimitry Andric } 6050b57cec5SDimitry Andric getOrCreateBlockInfo(unsigned BlockID)6060b57cec5SDimitry Andric BlockInfo &getOrCreateBlockInfo(unsigned BlockID) { 6070b57cec5SDimitry Andric if (BlockInfo *BI = getBlockInfo(BlockID)) 6080b57cec5SDimitry Andric return *BI; 6090b57cec5SDimitry Andric 6100b57cec5SDimitry Andric // Otherwise, add a new record. 6110b57cec5SDimitry Andric BlockInfoRecords.emplace_back(); 6120b57cec5SDimitry Andric BlockInfoRecords.back().BlockID = BlockID; 6130b57cec5SDimitry Andric return BlockInfoRecords.back(); 6140b57cec5SDimitry Andric } 6150b57cec5SDimitry Andric 6160b57cec5SDimitry Andric public: 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric /// EmitBlockInfoAbbrev - Emit a DEFINE_ABBREV record for the specified 6190b57cec5SDimitry Andric /// BlockID. EmitBlockInfoAbbrev(unsigned BlockID,std::shared_ptr<BitCodeAbbrev> Abbv)6200b57cec5SDimitry Andric unsigned EmitBlockInfoAbbrev(unsigned BlockID, std::shared_ptr<BitCodeAbbrev> Abbv) { 6210b57cec5SDimitry Andric SwitchToBlockID(BlockID); 6220b57cec5SDimitry Andric EncodeAbbrev(*Abbv); 6230b57cec5SDimitry Andric 6240b57cec5SDimitry Andric // Add the abbrev to the specified block record. 6250b57cec5SDimitry Andric BlockInfo &Info = getOrCreateBlockInfo(BlockID); 6260b57cec5SDimitry Andric Info.Abbrevs.push_back(std::move(Abbv)); 6270b57cec5SDimitry Andric 6280b57cec5SDimitry Andric return Info.Abbrevs.size()-1+bitc::FIRST_APPLICATION_ABBREV; 6290b57cec5SDimitry Andric } 6300b57cec5SDimitry Andric }; 6310b57cec5SDimitry Andric 6320b57cec5SDimitry Andric 6330b57cec5SDimitry Andric } // End llvm namespace 6340b57cec5SDimitry Andric 6350b57cec5SDimitry Andric #endif 636