| Index: include/llvm/Bitcode/NaCl/NaClBitstreamReader.h
|
| diff --git a/include/llvm/Bitcode/NaCl/NaClBitstreamReader.h b/include/llvm/Bitcode/NaCl/NaClBitstreamReader.h
|
| index 1a981ae117dab156c27e4b3058b089af9e6eea92..550c8c8f5bccd4d1f4eab60b0a569c9f6dfe9fe5 100644
|
| --- a/include/llvm/Bitcode/NaCl/NaClBitstreamReader.h
|
| +++ b/include/llvm/Bitcode/NaCl/NaClBitstreamReader.h
|
| @@ -21,7 +21,10 @@
|
| #include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h"
|
| #include "llvm/Support/Endian.h"
|
| #include "llvm/Support/StreamingMemoryObject.h"
|
| +#include <atomic>
|
| #include <climits>
|
| +#include <mutex>
|
| +#include <unordered_map>
|
| #include <vector>
|
|
|
| namespace llvm {
|
| @@ -130,15 +133,98 @@ public:
|
| AbbrevList Abbrevs;
|
| };
|
|
|
| + class BlockInfoRecordsMap;
|
| + using SharedBlockInfoMap = std::shared_ptr<BlockInfoRecordsMap>;
|
| +
|
| + // Holds the global abbreviations in the BlockInfo block of the bitcode file.
|
| + // Sharing is used to allow parallel parses. Share by using std::share_ptr's
|
| + // and std::shared_from_this().
|
| + //
|
| + // Note: The BlockInfo block must be parsed before sharing of the
|
| + // BlockInfoRecordsMap. Therefore, before changing to a parallel parse, the
|
| + // BlockInfoRecordsMap must be frozen. Failure to do so, can lead to
|
| + // unexpected behaviour.
|
| + //
|
| + // In practice, this means that only function blocks can be parsed in
|
| + // parallel.
|
| + class BlockInfoRecordsMap :
|
| + public std::enable_shared_from_this<BlockInfoRecordsMap> {
|
| + friend class NaClBitstreamReader;
|
| + BlockInfoRecordsMap(const BlockInfoRecordsMap&) = delete;
|
| + BlockInfoRecordsMap &operator=(const BlockInfoRecordsMap&) = delete;
|
| + public:
|
| + using InfosMap = std::unordered_map<unsigned, std::unique_ptr<BlockInfo>>;
|
| +
|
| + static SharedBlockInfoMap create() {
|
| + return SharedBlockInfoMap(new BlockInfoRecordsMap());
|
| + }
|
| + ~BlockInfoRecordsMap() = default;
|
| +
|
| + bool isFrozen() const {
|
| + return IsFrozen.load();
|
| + }
|
| +
|
| + // Returns true if already frozen.
|
| + bool freeze() {
|
| + return IsFrozen.exchange(true);
|
| + }
|
| +
|
| + BlockInfo *getBlockInfo(unsigned BlockID) {
|
| + auto Pos = KnownInfos.find(BlockID);
|
| + if (Pos != KnownInfos.end())
|
| + return Pos->second.get();
|
| + return getOrCreateUnknownBlockInfo(BlockID);
|
| + }
|
| +
|
| + // Locks the BlockInfoRecordsMap for the lifetime of the UpdateLock. Used
|
| + // to allow the parsing of a BlockInfo block, and install global
|
| + // abbreviations.
|
| + //
|
| + // Verifies that the BlockInfoRecordsMap didn't get frozen during the
|
| + // instance's lifetime as a safety precaution. That is, it checks that no
|
| + // bitstream reader was created to share the global abbreviations before the
|
| + // global abbreviations are defined.
|
| + class UpdateLock {
|
| + UpdateLock() = delete;
|
| + UpdateLock(const UpdateLock&) = delete;
|
| + UpdateLock &operator=(const UpdateLock&) = delete;
|
| + public:
|
| + explicit UpdateLock(BlockInfoRecordsMap &BlockInfoRecords);
|
| + ~UpdateLock();
|
| + private:
|
| + // The BlockInfoRecordsMap to update.
|
| + BlockInfoRecordsMap &BlockInfoRecords;
|
| + // The locked mutex from BlockInfoRecordsMap;
|
| + std::unique_lock<std::mutex> Lock;
|
| + };
|
| +
|
| + private:
|
| + // The set of known BlockInfo's. This map is prepopulated so that fast
|
| + // lookup can be performed thread safe (i.e. without using a lock).
|
| + InfosMap KnownInfos;
|
| + // The set of unknown BlockInfo's. This map is to handle unknown (and hence,
|
| + // invalid) PNaCl bitcode files. This map is updated incrementally, and uses
|
| + // UnknownBlockInfoLock to make it thread safe.
|
| + InfosMap UnknownInfos;
|
| + // True if the known BlockInfo blocks are frozen (i.e. the bitstream reader
|
| + // will ignore the BlockInfo block).
|
| + std::atomic_bool IsFrozen;
|
| + // Lock to use to update this data structure.
|
| + std::mutex UpdateRecordsLock;
|
| + // Lock to get/create an unknonw block info.
|
| + std::mutex UnknownBlockInfoLock;
|
| +
|
| + BlockInfoRecordsMap();
|
| +
|
| + BlockInfo *getOrCreateUnknownBlockInfo(unsigned BlockID);
|
| + };
|
| +
|
| private:
|
| friend class NaClBitstreamCursor;
|
|
|
| std::unique_ptr<MemoryObject> BitcodeBytes;
|
|
|
| - std::vector<BlockInfo> BlockInfoRecords;
|
| -
|
| - // True if the BlockInfo block has been read.
|
| - bool HasReadBlockInfoBlock = false;
|
| + SharedBlockInfoMap BlockInfoRecords;
|
|
|
| /// \brief Holds the offset of the first byte after the header.
|
| size_t InitialAddress;
|
| @@ -159,22 +245,33 @@ public:
|
| /// the given bitcode header.
|
| NaClBitstreamReader(const unsigned char *Start, const unsigned char *End,
|
| NaClBitcodeHeader &Header)
|
| - : BitcodeBytes(getNonStreamedMemoryObject(Start, End)) {
|
| + : BitcodeBytes(getNonStreamedMemoryObject(Start, End)),
|
| + BlockInfoRecords(BlockInfoRecordsMap::create()) {
|
| initFromHeader(Header);
|
| }
|
|
|
| /// Read stream from Bytes, after parsing the given bitcode header.
|
| NaClBitstreamReader(MemoryObject *Bytes, NaClBitcodeHeader &Header)
|
| - : BitcodeBytes(Bytes) {
|
| + : BitcodeBytes(Bytes), BlockInfoRecords(BlockInfoRecordsMap::create()) {
|
| initFromHeader(Header);
|
| }
|
|
|
| /// Read stream from bytes, starting at the given initial address.
|
| /// Provides simple API for unit testing.
|
| NaClBitstreamReader(MemoryObject *Bytes, size_t InitialAddress)
|
| - : BitcodeBytes(Bytes), InitialAddress(InitialAddress) {
|
| + : BitcodeBytes(Bytes), BlockInfoRecords(BlockInfoRecordsMap::create()),
|
| + InitialAddress(InitialAddress) {
|
| }
|
|
|
| + /// Read stream from sequence of bytes [Start .. End), using the global
|
| + /// abbreviations of the given bitstream reader. Assumes that [Start .. End)
|
| + /// is copied from Reader's memory object.
|
| + NaClBitstreamReader(const unsigned char *Start,
|
| + const unsigned char *End, NaClBitstreamReader *Reader)
|
| + : BitcodeBytes(getNonStreamedMemoryObject(Start, End)),
|
| + BlockInfoRecords(Reader->BlockInfoRecords), InitialAddress(0)
|
| + { BlockInfoRecords->freeze(); }
|
| +
|
| // Returns the memory object that is being read.
|
| MemoryObject &getBitcodeBytes() { return *BitcodeBytes; }
|
|
|
| @@ -189,28 +286,8 @@ public:
|
| // Block Manipulation
|
| //===--------------------------------------------------------------------===//
|
|
|
| - /// If there is block info for the specified ID, return it, otherwise return
|
| - /// null.
|
| - const BlockInfo *getBlockInfo(unsigned BlockID) const {
|
| - // Common case, the most recent entry matches BlockID.
|
| - if (!BlockInfoRecords.empty() &&
|
| - BlockInfoRecords.back().getBlockID() == BlockID)
|
| - return &BlockInfoRecords.back();
|
| -
|
| - for (unsigned i = 0, e = static_cast<unsigned>(BlockInfoRecords.size());
|
| - i != e; ++i)
|
| - if (BlockInfoRecords[i].getBlockID() == BlockID)
|
| - return &BlockInfoRecords[i];
|
| - return nullptr;
|
| - }
|
| -
|
| - BlockInfo &getOrCreateBlockInfo(unsigned BlockID) {
|
| - if (const BlockInfo *BI = getBlockInfo(BlockID))
|
| - return *const_cast<BlockInfo*>(BI);
|
| -
|
| - // Otherwise, add a new record.
|
| - BlockInfoRecords.push_back(BlockInfo(BlockID));
|
| - return BlockInfoRecords.back();
|
| + BlockInfo *getBlockInfo(unsigned BlockID) {
|
| + return BlockInfoRecords->getBlockInfo(BlockID);
|
| }
|
| };
|
|
|
| @@ -413,7 +490,7 @@ public:
|
| BitsInCurWord = 0;
|
| if (BitStream) {
|
| BlockScope.push_back(
|
| - Block(&BitStream->getOrCreateBlockInfo(naclbitc::TOP_LEVEL_BLOCKID)));
|
| + Block(BitStream->getBlockInfo(naclbitc::TOP_LEVEL_BLOCKID)));
|
| }
|
| }
|
|
|
| @@ -523,10 +600,35 @@ public:
|
| }
|
| }
|
|
|
| + /// Returns the starting byte of the word containing BitNo.
|
| + uintptr_t getStartWordByteForBit(uint64_t BitNo) const {
|
| + return uintptr_t(BitNo/CHAR_BIT) & ~(sizeof(word_t)-1);
|
| + }
|
| +
|
| + /// Returns the index of BitNo within the word it appears in.
|
| + unsigned getWordBitNo(uint64_t BitNo) const {
|
| + return unsigned(BitNo & (sizeof(word_t)*CHAR_BIT-1));
|
| + }
|
| +
|
| + /// Returns the ending byte of the word containing BitNo.
|
| + uintptr_t getEndWordByteForBit(uint64_t BitNo) const {
|
| + return getStartWordByteForBit(BitNo) +
|
| + (getWordBitNo(BitNo)
|
| + ? sizeof(word_t)
|
| + : 0);
|
| + }
|
| +
|
| + /// Fills Buffer[Size] using bytes at Address (in the memory object being
|
| + /// read). Returns number of bytes filled (less than Size if at end of memory
|
| + /// object).
|
| + uint64_t fillBuffer(uint8_t *Buffer, size_t Size, size_t Address) const {
|
| + return BitStream->getBitcodeBytes().readBytes(Buffer, Size, Address);
|
| + }
|
| +
|
| /// Reset the stream to the specified bit number.
|
| void JumpToBit(uint64_t BitNo) {
|
| - uintptr_t ByteNo = uintptr_t(BitNo/8) & ~(sizeof(word_t)-1);
|
| - unsigned WordBitNo = unsigned(BitNo & (sizeof(word_t)*8-1));
|
| + const uintptr_t ByteNo = getStartWordByteForBit(BitNo);
|
| + const unsigned WordBitNo = getWordBitNo(BitNo);
|
| if (!canSkipToPos(ByteNo))
|
| reportInvalidJumpToBit(BitNo);
|
|
|
| @@ -545,8 +647,7 @@ public:
|
| // Read the next word from the stream.
|
| uint8_t Array[sizeof(word_t)] = {0};
|
|
|
| - uint64_t BytesRead =
|
| - BitStream->getBitcodeBytes().readBytes(Array, sizeof(Array), NextChar);
|
| + uint64_t BytesRead = fillBuffer(Array, sizeof(Array), NextChar);
|
|
|
| // If we run out of data, stop at the end of the stream.
|
| if (BytesRead == 0) {
|
| @@ -558,11 +659,11 @@ public:
|
| support::endian::read<word_t, support::little, support::unaligned>(
|
| Array);
|
| NextChar += BytesRead;
|
| - BitsInCurWord = BytesRead * 8;
|
| + BitsInCurWord = BytesRead * CHAR_BIT;
|
| }
|
|
|
| word_t Read(unsigned NumBits) {
|
| - static const unsigned BitsInWord = sizeof(word_t) * 8;
|
| + static const unsigned BitsInWord = sizeof(word_t) * CHAR_BIT;
|
|
|
| assert(NumBits && NumBits <= BitsInWord &&
|
| "Cannot return zero or more than BitsInWord bits!");
|
| @@ -694,8 +795,8 @@ public:
|
|
|
| // Check that the block wasn't partially defined, and that the offset isn't
|
| // bogus.
|
| - size_t SkipTo = GetCurrentBitNo() + NumFourBytes*4*8;
|
| - if (AtEndOfStream() || !canSkipToPos(SkipTo/8))
|
| + size_t SkipTo = GetCurrentBitNo() + NumFourBytes*4*CHAR_BIT;
|
| + if (AtEndOfStream() || !canSkipToPos(SkipTo/CHAR_BIT))
|
| return true;
|
|
|
| JumpToBit(SkipTo);
|
|
|