OLD | NEW |
1 //===- NaClBitstreamReader.h -----------------------------------*- C++ -*-===// | 1 //===- NaClBitstreamReader.h -----------------------------------*- C++ -*-===// |
2 // Low-level bitstream reader interface | 2 // Low-level bitstream reader interface |
3 // | 3 // |
4 // The LLVM Compiler Infrastructure | 4 // The LLVM Compiler Infrastructure |
5 // | 5 // |
6 // This file is distributed under the University of Illinois Open Source | 6 // This file is distributed under the University of Illinois Open Source |
7 // License. See LICENSE.TXT for details. | 7 // License. See LICENSE.TXT for details. |
8 // | 8 // |
9 //===----------------------------------------------------------------------===// | 9 //===----------------------------------------------------------------------===// |
10 // | 10 // |
11 // This header defines the BitstreamReader class. This class can be used to | 11 // This header defines the BitstreamReader class. This class can be used to |
12 // read an arbitrary bitstream, regardless of its contents. | 12 // read an arbitrary bitstream, regardless of its contents. |
13 // | 13 // |
14 //===----------------------------------------------------------------------===// | 14 //===----------------------------------------------------------------------===// |
15 | 15 |
16 #ifndef LLVM_BITCODE_NACL_NACLBITSTREAMREADER_H | 16 #ifndef LLVM_BITCODE_NACL_NACLBITSTREAMREADER_H |
17 #define LLVM_BITCODE_NACL_NACLBITSTREAMREADER_H | 17 #define LLVM_BITCODE_NACL_NACLBITSTREAMREADER_H |
18 | 18 |
19 #include "llvm/ADT/SmallVector.h" | 19 #include "llvm/ADT/SmallVector.h" |
20 #include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" | 20 #include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" |
21 #include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" | 21 #include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" |
22 #include "llvm/Support/Endian.h" | 22 #include "llvm/Support/Endian.h" |
23 #include "llvm/Support/StreamingMemoryObject.h" | 23 #include "llvm/Support/StreamingMemoryObject.h" |
| 24 #include <atomic> |
24 #include <climits> | 25 #include <climits> |
| 26 #include <mutex> |
| 27 #include <unordered_map> |
25 #include <vector> | 28 #include <vector> |
26 | 29 |
27 namespace llvm { | 30 namespace llvm { |
28 | 31 |
29 class Deserializer; | 32 class Deserializer; |
30 class NaClBitstreamCursor; | 33 class NaClBitstreamCursor; |
31 | 34 |
32 namespace naclbitc { | 35 namespace naclbitc { |
33 | 36 |
34 /// Returns the Bit as a Byte:BitInByte string. | 37 /// Returns the Bit as a Byte:BitInByte string. |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 BlockInfo(const BlockInfo&) = default; | 126 BlockInfo(const BlockInfo&) = default; |
124 unsigned getBlockID() const { return BlockID; } | 127 unsigned getBlockID() const { return BlockID; } |
125 void setBlockID(unsigned ID) { BlockID = ID; } | 128 void setBlockID(unsigned ID) { BlockID = ID; } |
126 AbbrevList &getAbbrevs() { return Abbrevs; } | 129 AbbrevList &getAbbrevs() { return Abbrevs; } |
127 ~BlockInfo() {} | 130 ~BlockInfo() {} |
128 private: | 131 private: |
129 unsigned BlockID; | 132 unsigned BlockID; |
130 AbbrevList Abbrevs; | 133 AbbrevList Abbrevs; |
131 }; | 134 }; |
132 | 135 |
| 136 class BlockInfoRecordsMap; |
| 137 using SharedBlockInfoMap = std::shared_ptr<BlockInfoRecordsMap>; |
| 138 |
| 139 // Holds the global abbreviations in the BlockInfo block of the bitcode file. |
| 140 // Sharing is used to allow parallel parses. Share by using std::share_ptr's |
| 141 // and std::shared_from_this(). |
| 142 // |
| 143 // Note: The BlockInfo block must be parsed before sharing of the |
| 144 // BlockInfoRecordsMap. Therefore, before changing to a parallel parse, the |
| 145 // BlockInfoRecordsMap must be frozen. Failure to do so, can lead to |
| 146 // unexpected behaviour. |
| 147 // |
| 148 // In practice, this means that only function blocks can be parsed in |
| 149 // parallel. |
| 150 class BlockInfoRecordsMap : |
| 151 public std::enable_shared_from_this<BlockInfoRecordsMap> { |
| 152 friend class NaClBitstreamReader; |
| 153 BlockInfoRecordsMap(const BlockInfoRecordsMap&) = delete; |
| 154 BlockInfoRecordsMap &operator=(const BlockInfoRecordsMap&) = delete; |
| 155 public: |
| 156 using InfosMap = std::unordered_map<unsigned, std::unique_ptr<BlockInfo>>; |
| 157 |
| 158 static SharedBlockInfoMap create() { |
| 159 return SharedBlockInfoMap(new BlockInfoRecordsMap()); |
| 160 } |
| 161 ~BlockInfoRecordsMap() = default; |
| 162 |
| 163 bool isFrozen() const { |
| 164 return IsFrozen.load(); |
| 165 } |
| 166 |
| 167 // Returns true if already frozen. |
| 168 bool freeze() { |
| 169 return IsFrozen.exchange(true); |
| 170 } |
| 171 |
| 172 BlockInfo *getBlockInfo(unsigned BlockID) { |
| 173 auto Pos = KnownInfos.find(BlockID); |
| 174 if (Pos != KnownInfos.end()) |
| 175 return Pos->second.get(); |
| 176 return getOrCreateUnknownBlockInfo(BlockID); |
| 177 } |
| 178 |
| 179 // Locks the BlockInfoRecordsMap for the lifetime of the UpdateLock. Used |
| 180 // to allow the parsing of a BlockInfo block, and install global |
| 181 // abbreviations. |
| 182 // |
| 183 // Verifies that the BlockInfoRecordsMap didn't get frozen during the |
| 184 // instance's lifetime as a safety precaution. That is, it checks that no |
| 185 // bitstream reader was created to share the global abbreviations before the |
| 186 // global abbreviations are defined. |
| 187 class UpdateLock { |
| 188 UpdateLock() = delete; |
| 189 UpdateLock(const UpdateLock&) = delete; |
| 190 UpdateLock &operator=(const UpdateLock&) = delete; |
| 191 public: |
| 192 explicit UpdateLock(BlockInfoRecordsMap &BlockInfoRecords); |
| 193 ~UpdateLock(); |
| 194 private: |
| 195 // The BlockInfoRecordsMap to update. |
| 196 BlockInfoRecordsMap &BlockInfoRecords; |
| 197 // The locked mutex from BlockInfoRecordsMap; |
| 198 std::unique_lock<std::mutex> Lock; |
| 199 }; |
| 200 |
| 201 private: |
| 202 // The set of known BlockInfo's. This map is prepopulated so that fast |
| 203 // lookup can be performed thread safe (i.e. without using a lock). |
| 204 InfosMap KnownInfos; |
| 205 // The set of unknown BlockInfo's. This map is to handle unknown (and hence, |
| 206 // invalid) PNaCl bitcode files. This map is updated incrementally, and uses |
| 207 // UnknownBlockInfoLock to make it thread safe. |
| 208 InfosMap UnknownInfos; |
| 209 // True if the known BlockInfo blocks are frozen (i.e. the bitstream reader |
| 210 // will ignore the BlockInfo block). |
| 211 std::atomic_bool IsFrozen; |
| 212 // Lock to use to update this data structure. |
| 213 std::mutex UpdateRecordsLock; |
| 214 // Lock to get/create an unknonw block info. |
| 215 std::mutex UnknownBlockInfoLock; |
| 216 |
| 217 BlockInfoRecordsMap(); |
| 218 |
| 219 BlockInfo *getOrCreateUnknownBlockInfo(unsigned BlockID); |
| 220 }; |
| 221 |
133 private: | 222 private: |
134 friend class NaClBitstreamCursor; | 223 friend class NaClBitstreamCursor; |
135 | 224 |
136 std::unique_ptr<MemoryObject> BitcodeBytes; | 225 std::unique_ptr<MemoryObject> BitcodeBytes; |
137 | 226 |
138 std::vector<BlockInfo> BlockInfoRecords; | 227 SharedBlockInfoMap BlockInfoRecords; |
139 | |
140 // True if the BlockInfo block has been read. | |
141 bool HasReadBlockInfoBlock = false; | |
142 | 228 |
143 /// \brief Holds the offset of the first byte after the header. | 229 /// \brief Holds the offset of the first byte after the header. |
144 size_t InitialAddress; | 230 size_t InitialAddress; |
145 | 231 |
146 // True if filler should be added to byte align records. | 232 // True if filler should be added to byte align records. |
147 bool AlignBitcodeRecords = false; | 233 bool AlignBitcodeRecords = false; |
148 NaClBitstreamReader(const NaClBitstreamReader&) = delete; | 234 NaClBitstreamReader(const NaClBitstreamReader&) = delete; |
149 void operator=(const NaClBitstreamReader&) = delete; | 235 void operator=(const NaClBitstreamReader&) = delete; |
150 | 236 |
151 | 237 |
152 void initFromHeader(NaClBitcodeHeader &Header) { | 238 void initFromHeader(NaClBitcodeHeader &Header) { |
153 InitialAddress = Header.getHeaderSize(); | 239 InitialAddress = Header.getHeaderSize(); |
154 AlignBitcodeRecords = Header.getAlignBitcodeRecords(); | 240 AlignBitcodeRecords = Header.getAlignBitcodeRecords(); |
155 } | 241 } |
156 | 242 |
157 public: | 243 public: |
158 /// Read stream from sequence of bytes [Start .. End) after parsing | 244 /// Read stream from sequence of bytes [Start .. End) after parsing |
159 /// the given bitcode header. | 245 /// the given bitcode header. |
160 NaClBitstreamReader(const unsigned char *Start, const unsigned char *End, | 246 NaClBitstreamReader(const unsigned char *Start, const unsigned char *End, |
161 NaClBitcodeHeader &Header) | 247 NaClBitcodeHeader &Header) |
162 : BitcodeBytes(getNonStreamedMemoryObject(Start, End)) { | 248 : BitcodeBytes(getNonStreamedMemoryObject(Start, End)), |
| 249 BlockInfoRecords(BlockInfoRecordsMap::create()) { |
163 initFromHeader(Header); | 250 initFromHeader(Header); |
164 } | 251 } |
165 | 252 |
166 /// Read stream from Bytes, after parsing the given bitcode header. | 253 /// Read stream from Bytes, after parsing the given bitcode header. |
167 NaClBitstreamReader(MemoryObject *Bytes, NaClBitcodeHeader &Header) | 254 NaClBitstreamReader(MemoryObject *Bytes, NaClBitcodeHeader &Header) |
168 : BitcodeBytes(Bytes) { | 255 : BitcodeBytes(Bytes), BlockInfoRecords(BlockInfoRecordsMap::create()) { |
169 initFromHeader(Header); | 256 initFromHeader(Header); |
170 } | 257 } |
171 | 258 |
172 /// Read stream from bytes, starting at the given initial address. | 259 /// Read stream from bytes, starting at the given initial address. |
173 /// Provides simple API for unit testing. | 260 /// Provides simple API for unit testing. |
174 NaClBitstreamReader(MemoryObject *Bytes, size_t InitialAddress) | 261 NaClBitstreamReader(MemoryObject *Bytes, size_t InitialAddress) |
175 : BitcodeBytes(Bytes), InitialAddress(InitialAddress) { | 262 : BitcodeBytes(Bytes), BlockInfoRecords(BlockInfoRecordsMap::create()), |
| 263 InitialAddress(InitialAddress) { |
176 } | 264 } |
177 | 265 |
| 266 /// Read stream from sequence of bytes [Start .. End), using the global |
| 267 /// abbreviations of the given bitstream reader. Assumes that [Start .. End) |
| 268 /// is copied from Reader's memory object. |
| 269 NaClBitstreamReader(const unsigned char *Start, |
| 270 const unsigned char *End, NaClBitstreamReader *Reader) |
| 271 : BitcodeBytes(getNonStreamedMemoryObject(Start, End)), |
| 272 BlockInfoRecords(Reader->BlockInfoRecords), InitialAddress(0) |
| 273 { BlockInfoRecords->freeze(); } |
| 274 |
178 // Returns the memory object that is being read. | 275 // Returns the memory object that is being read. |
179 MemoryObject &getBitcodeBytes() { return *BitcodeBytes; } | 276 MemoryObject &getBitcodeBytes() { return *BitcodeBytes; } |
180 | 277 |
181 ~NaClBitstreamReader() {} | 278 ~NaClBitstreamReader() {} |
182 | 279 |
183 /// \brief Returns the initial address (after the header) of the input stream. | 280 /// \brief Returns the initial address (after the header) of the input stream. |
184 size_t getInitialAddress() const { | 281 size_t getInitialAddress() const { |
185 return InitialAddress; | 282 return InitialAddress; |
186 } | 283 } |
187 | 284 |
188 //===--------------------------------------------------------------------===// | 285 //===--------------------------------------------------------------------===// |
189 // Block Manipulation | 286 // Block Manipulation |
190 //===--------------------------------------------------------------------===// | 287 //===--------------------------------------------------------------------===// |
191 | 288 |
192 /// If there is block info for the specified ID, return it, otherwise return | 289 BlockInfo *getBlockInfo(unsigned BlockID) { |
193 /// null. | 290 return BlockInfoRecords->getBlockInfo(BlockID); |
194 const BlockInfo *getBlockInfo(unsigned BlockID) const { | |
195 // Common case, the most recent entry matches BlockID. | |
196 if (!BlockInfoRecords.empty() && | |
197 BlockInfoRecords.back().getBlockID() == BlockID) | |
198 return &BlockInfoRecords.back(); | |
199 | |
200 for (unsigned i = 0, e = static_cast<unsigned>(BlockInfoRecords.size()); | |
201 i != e; ++i) | |
202 if (BlockInfoRecords[i].getBlockID() == BlockID) | |
203 return &BlockInfoRecords[i]; | |
204 return nullptr; | |
205 } | |
206 | |
207 BlockInfo &getOrCreateBlockInfo(unsigned BlockID) { | |
208 if (const BlockInfo *BI = getBlockInfo(BlockID)) | |
209 return *const_cast<BlockInfo*>(BI); | |
210 | |
211 // Otherwise, add a new record. | |
212 BlockInfoRecords.push_back(BlockInfo(BlockID)); | |
213 return BlockInfoRecords.back(); | |
214 } | 291 } |
215 }; | 292 }; |
216 | 293 |
217 /// When advancing through a bitstream cursor, each advance can discover a few | 294 /// When advancing through a bitstream cursor, each advance can discover a few |
218 /// different kinds of entries: | 295 /// different kinds of entries: |
219 struct NaClBitstreamEntry { | 296 struct NaClBitstreamEntry { |
220 enum { | 297 enum { |
221 Error, // Malformed bitcode was found. | 298 Error, // Malformed bitcode was found. |
222 EndBlock, // We've reached the end of the current block, (or the end of the | 299 EndBlock, // We've reached the end of the current block, (or the end of the |
223 // file, which is treated like a series of EndBlock records. | 300 // file, which is treated like a series of EndBlock records. |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 : ErrHandler(new ErrorHandler(*this)) { init(&R); } | 483 : ErrHandler(new ErrorHandler(*this)) { init(&R); } |
407 | 484 |
408 void init(NaClBitstreamReader *R) { | 485 void init(NaClBitstreamReader *R) { |
409 freeState(); | 486 freeState(); |
410 BitStream = R; | 487 BitStream = R; |
411 NextChar = (BitStream == nullptr) ? 0 : BitStream->getInitialAddress(); | 488 NextChar = (BitStream == nullptr) ? 0 : BitStream->getInitialAddress(); |
412 Size = 0; | 489 Size = 0; |
413 BitsInCurWord = 0; | 490 BitsInCurWord = 0; |
414 if (BitStream) { | 491 if (BitStream) { |
415 BlockScope.push_back( | 492 BlockScope.push_back( |
416 Block(&BitStream->getOrCreateBlockInfo(naclbitc::TOP_LEVEL_BLOCKID))); | 493 Block(BitStream->getBlockInfo(naclbitc::TOP_LEVEL_BLOCKID))); |
417 } | 494 } |
418 } | 495 } |
419 | 496 |
420 ~NaClBitstreamCursor() { | 497 ~NaClBitstreamCursor() { |
421 freeState(); | 498 freeState(); |
422 } | 499 } |
423 | 500 |
424 void freeState() { | 501 void freeState() { |
425 while (!BlockScope.empty()) | 502 while (!BlockScope.empty()) |
426 BlockScope.pop_back(); | 503 BlockScope.pop_back(); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
516 NaClBitstreamEntry Entry = advance(Flags, 0); | 593 NaClBitstreamEntry Entry = advance(Flags, 0); |
517 if (Entry.Kind != NaClBitstreamEntry::SubBlock) | 594 if (Entry.Kind != NaClBitstreamEntry::SubBlock) |
518 return Entry; | 595 return Entry; |
519 | 596 |
520 // If we found a sub-block, just skip over it and check the next entry. | 597 // If we found a sub-block, just skip over it and check the next entry. |
521 if (SkipBlock()) | 598 if (SkipBlock()) |
522 return NaClBitstreamEntry::getError(); | 599 return NaClBitstreamEntry::getError(); |
523 } | 600 } |
524 } | 601 } |
525 | 602 |
| 603 /// Returns the starting byte of the word containing BitNo. |
| 604 uintptr_t getStartWordByteForBit(uint64_t BitNo) const { |
| 605 return uintptr_t(BitNo/CHAR_BIT) & ~(sizeof(word_t)-1); |
| 606 } |
| 607 |
| 608 /// Returns the index of BitNo within the word it appears in. |
| 609 unsigned getWordBitNo(uint64_t BitNo) const { |
| 610 return unsigned(BitNo & (sizeof(word_t)*CHAR_BIT-1)); |
| 611 } |
| 612 |
| 613 /// Returns the ending byte of the word containing BitNo. |
| 614 uintptr_t getEndWordByteForBit(uint64_t BitNo) const { |
| 615 return getStartWordByteForBit(BitNo) + |
| 616 (getWordBitNo(BitNo) |
| 617 ? sizeof(word_t) |
| 618 : 0); |
| 619 } |
| 620 |
| 621 /// Fills Buffer[Size] using bytes at Address (in the memory object being |
| 622 /// read). Returns number of bytes filled (less than Size if at end of memory |
| 623 /// object). |
| 624 uint64_t fillBuffer(uint8_t *Buffer, size_t Size, size_t Address) const { |
| 625 return BitStream->getBitcodeBytes().readBytes(Buffer, Size, Address); |
| 626 } |
| 627 |
526 /// Reset the stream to the specified bit number. | 628 /// Reset the stream to the specified bit number. |
527 void JumpToBit(uint64_t BitNo) { | 629 void JumpToBit(uint64_t BitNo) { |
528 uintptr_t ByteNo = uintptr_t(BitNo/8) & ~(sizeof(word_t)-1); | 630 const uintptr_t ByteNo = getStartWordByteForBit(BitNo); |
529 unsigned WordBitNo = unsigned(BitNo & (sizeof(word_t)*8-1)); | 631 const unsigned WordBitNo = getWordBitNo(BitNo); |
530 if (!canSkipToPos(ByteNo)) | 632 if (!canSkipToPos(ByteNo)) |
531 reportInvalidJumpToBit(BitNo); | 633 reportInvalidJumpToBit(BitNo); |
532 | 634 |
533 // Move the cursor to the right word. | 635 // Move the cursor to the right word. |
534 NextChar = ByteNo; | 636 NextChar = ByteNo; |
535 BitsInCurWord = 0; | 637 BitsInCurWord = 0; |
536 | 638 |
537 // Skip over any bits that are already consumed. | 639 // Skip over any bits that are already consumed. |
538 if (WordBitNo) | 640 if (WordBitNo) |
539 Read(WordBitNo); | 641 Read(WordBitNo); |
540 } | 642 } |
541 | 643 |
542 void fillCurWord() { | 644 void fillCurWord() { |
543 assert(Size == 0 || NextChar < (unsigned)Size); | 645 assert(Size == 0 || NextChar < (unsigned)Size); |
544 | 646 |
545 // Read the next word from the stream. | 647 // Read the next word from the stream. |
546 uint8_t Array[sizeof(word_t)] = {0}; | 648 uint8_t Array[sizeof(word_t)] = {0}; |
547 | 649 |
548 uint64_t BytesRead = | 650 uint64_t BytesRead = fillBuffer(Array, sizeof(Array), NextChar); |
549 BitStream->getBitcodeBytes().readBytes(Array, sizeof(Array), NextChar); | |
550 | 651 |
551 // If we run out of data, stop at the end of the stream. | 652 // If we run out of data, stop at the end of the stream. |
552 if (BytesRead == 0) { | 653 if (BytesRead == 0) { |
553 Size = NextChar; | 654 Size = NextChar; |
554 return; | 655 return; |
555 } | 656 } |
556 | 657 |
557 CurWord = | 658 CurWord = |
558 support::endian::read<word_t, support::little, support::unaligned>( | 659 support::endian::read<word_t, support::little, support::unaligned>( |
559 Array); | 660 Array); |
560 NextChar += BytesRead; | 661 NextChar += BytesRead; |
561 BitsInCurWord = BytesRead * 8; | 662 BitsInCurWord = BytesRead * CHAR_BIT; |
562 } | 663 } |
563 | 664 |
564 word_t Read(unsigned NumBits) { | 665 word_t Read(unsigned NumBits) { |
565 static const unsigned BitsInWord = sizeof(word_t) * 8; | 666 static const unsigned BitsInWord = sizeof(word_t) * CHAR_BIT; |
566 | 667 |
567 assert(NumBits && NumBits <= BitsInWord && | 668 assert(NumBits && NumBits <= BitsInWord && |
568 "Cannot return zero or more than BitsInWord bits!"); | 669 "Cannot return zero or more than BitsInWord bits!"); |
569 | 670 |
570 static const unsigned Mask = sizeof(word_t) > 4 ? 0x3f : 0x1f; | 671 static const unsigned Mask = sizeof(word_t) > 4 ? 0x3f : 0x1f; |
571 | 672 |
572 // If the field is fully contained by CurWord, return it quickly. | 673 // If the field is fully contained by CurWord, return it quickly. |
573 if (BitsInCurWord >= NumBits) { | 674 if (BitsInCurWord >= NumBits) { |
574 word_t R = CurWord & (~word_t(0) >> (BitsInWord - NumBits)); | 675 word_t R = CurWord & (~word_t(0) >> (BitsInWord - NumBits)); |
575 | 676 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 /// of this block. If the block record is malformed, return true. | 788 /// of this block. If the block record is malformed, return true. |
688 bool SkipBlock() { | 789 bool SkipBlock() { |
689 // Read and ignore the codelen value. Since we are skipping this block, we | 790 // Read and ignore the codelen value. Since we are skipping this block, we |
690 // don't care what code widths are used inside of it. | 791 // don't care what code widths are used inside of it. |
691 ReadVBR(naclbitc::CodeLenWidth); | 792 ReadVBR(naclbitc::CodeLenWidth); |
692 SkipToFourByteBoundary(); | 793 SkipToFourByteBoundary(); |
693 unsigned NumFourBytes = Read(naclbitc::BlockSizeWidth); | 794 unsigned NumFourBytes = Read(naclbitc::BlockSizeWidth); |
694 | 795 |
695 // Check that the block wasn't partially defined, and that the offset isn't | 796 // Check that the block wasn't partially defined, and that the offset isn't |
696 // bogus. | 797 // bogus. |
697 size_t SkipTo = GetCurrentBitNo() + NumFourBytes*4*8; | 798 size_t SkipTo = GetCurrentBitNo() + NumFourBytes*4*CHAR_BIT; |
698 if (AtEndOfStream() || !canSkipToPos(SkipTo/8)) | 799 if (AtEndOfStream() || !canSkipToPos(SkipTo/CHAR_BIT)) |
699 return true; | 800 return true; |
700 | 801 |
701 JumpToBit(SkipTo); | 802 JumpToBit(SkipTo); |
702 return false; | 803 return false; |
703 } | 804 } |
704 | 805 |
705 /// Having read the ENTER_SUBBLOCK abbrevid, enter the block, and return true | 806 /// Having read the ENTER_SUBBLOCK abbrevid, enter the block, and return true |
706 /// if the block has an error. | 807 /// if the block has an error. |
707 bool EnterSubBlock(unsigned BlockID, unsigned *NumWordsP = nullptr); | 808 bool EnterSubBlock(unsigned BlockID, unsigned *NumWordsP = nullptr); |
708 | 809 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
785 // Skips over an abbreviation record. Duplicates code of ReadAbbrevRecord, | 886 // Skips over an abbreviation record. Duplicates code of ReadAbbrevRecord, |
786 // except that no abbreviation is built. | 887 // except that no abbreviation is built. |
787 void SkipAbbrevRecord(); | 888 void SkipAbbrevRecord(); |
788 | 889 |
789 bool ReadBlockInfoBlock(NaClAbbrevListener *Listener); | 890 bool ReadBlockInfoBlock(NaClAbbrevListener *Listener); |
790 }; | 891 }; |
791 | 892 |
792 } // End llvm namespace | 893 } // End llvm namespace |
793 | 894 |
794 #endif | 895 #endif |
OLD | NEW |