| //===- subzero/src/PNaClTranslator.cpp - ICE from bitcode -----------------===// |
| // |
| // The Subzero Code Generator |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// \brief Implements the interface for translation from PNaCl bitcode files to |
| /// ICE to machine code. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "PNaClTranslator.h" |
| |
| #include "IceCfg.h" |
| #include "IceCfgNode.h" |
| #include "IceClFlags.h" |
| #include "IceDefs.h" |
| #include "IceGlobalInits.h" |
| #include "IceInst.h" |
| #include "IceOperand.h" |
| #include "IceRangeSpec.h" |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wunused-parameter" |
| #endif // __clang__ |
| |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Bitcode/NaCl/NaClBitcodeDecoders.h" |
| #include "llvm/Bitcode/NaCl/NaClBitcodeDefs.h" |
| #include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" |
| #include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" |
| #include "llvm/Bitcode/NaCl/NaClReaderWriter.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif // __clang__ |
| |
| #include <unordered_set> |
| |
| // Define a hash function for SmallString's, so that it can be used in hash |
| // tables. |
| namespace std { |
| template <unsigned InternalLen> struct hash<llvm::SmallString<InternalLen>> { |
| size_t operator()(const llvm::SmallString<InternalLen> &Key) const { |
| return llvm::hash_combine_range(Key.begin(), Key.end()); |
| } |
| }; |
| } // end of namespace std |
| |
| namespace { |
| using namespace llvm; |
| |
| // Models elements in the list of types defined in the types block. These |
| // elements can be undefined, a (simple) type, or a function type signature. |
| // Note that an extended type is undefined on construction. Use methods |
| // setAsSimpleType and setAsFuncSigType to define the extended type. |
| class ExtendedType { |
| ExtendedType &operator=(const ExtendedType &Ty) = delete; |
| |
| public: |
| /// Discriminator for LLVM-style RTTI. |
| enum TypeKind { Undefined, Simple, FuncSig }; |
| |
| ExtendedType() = default; |
| ExtendedType(const ExtendedType &Ty) = default; |
| |
| virtual ~ExtendedType() = default; |
| |
| ExtendedType::TypeKind getKind() const { return Kind; } |
| void dump(Ice::Ostream &Stream) const; |
| |
| /// Changes the extended type to a simple type with the given / value. |
| void setAsSimpleType(Ice::Type Ty) { |
| assert(Kind == Undefined); |
| Kind = Simple; |
| Signature.setReturnType(Ty); |
| } |
| |
| /// Changes the extended type to an (empty) function signature type. |
| void setAsFunctionType() { |
| assert(Kind == Undefined); |
| Kind = FuncSig; |
| } |
| |
| protected: |
| // Note: For simple types, the return type of the signature will be used to |
| // hold the simple type. |
| Ice::FuncSigType Signature; |
| |
| private: |
| ExtendedType::TypeKind Kind = Undefined; |
| }; |
| |
| Ice::Ostream &operator<<(Ice::Ostream &Stream, const ExtendedType &Ty) { |
| if (!Ice::BuildDefs::dump()) |
| return Stream; |
| Ty.dump(Stream); |
| return Stream; |
| } |
| |
| Ice::Ostream &operator<<(Ice::Ostream &Stream, ExtendedType::TypeKind Kind) { |
| if (!Ice::BuildDefs::dump()) |
| return Stream; |
| Stream << "ExtendedType::"; |
| switch (Kind) { |
| case ExtendedType::Undefined: |
| Stream << "Undefined"; |
| break; |
| case ExtendedType::Simple: |
| Stream << "Simple"; |
| break; |
| case ExtendedType::FuncSig: |
| Stream << "FuncSig"; |
| break; |
| } |
| return Stream; |
| } |
| |
| // Models an ICE type as an extended type. |
| class SimpleExtendedType : public ExtendedType { |
| SimpleExtendedType() = delete; |
| SimpleExtendedType(const SimpleExtendedType &) = delete; |
| SimpleExtendedType &operator=(const SimpleExtendedType &) = delete; |
| |
| public: |
| Ice::Type getType() const { return Signature.getReturnType(); } |
| |
| static bool classof(const ExtendedType *Ty) { |
| return Ty->getKind() == Simple; |
| } |
| }; |
| |
| // Models a function signature as an extended type. |
| class FuncSigExtendedType : public ExtendedType { |
| FuncSigExtendedType() = delete; |
| FuncSigExtendedType(const FuncSigExtendedType &) = delete; |
| FuncSigExtendedType &operator=(const FuncSigExtendedType &) = delete; |
| |
| public: |
| const Ice::FuncSigType &getSignature() const { return Signature; } |
| void setReturnType(Ice::Type ReturnType) { |
| Signature.setReturnType(ReturnType); |
| } |
| void appendArgType(Ice::Type ArgType) { Signature.appendArgType(ArgType); } |
| static bool classof(const ExtendedType *Ty) { |
| return Ty->getKind() == FuncSig; |
| } |
| }; |
| |
| void ExtendedType::dump(Ice::Ostream &Stream) const { |
| if (!Ice::BuildDefs::dump()) |
| return; |
| Stream << Kind; |
| switch (Kind) { |
| case Simple: { |
| Stream << " " << Signature.getReturnType(); |
| break; |
| } |
| case FuncSig: { |
| Stream << " " << Signature; |
| } |
| default: |
| break; |
| } |
| } |
| |
| // Models integer literals as a sequence of bits. Used to read integer values |
| // from bitcode files. Based on llvm::APInt. |
| class BitcodeInt { |
| BitcodeInt() = delete; |
| BitcodeInt(const BitcodeInt &) = delete; |
| BitcodeInt &operator=(const BitcodeInt &) = delete; |
| |
| public: |
| BitcodeInt(Ice::SizeT Bits, uint64_t Val) : BitWidth(Bits), Val(Val) { |
| assert(Bits && "bitwidth too small"); |
| assert(Bits <= BITS_PER_WORD && "bitwidth too big"); |
| clearUnusedBits(); |
| } |
| |
| int64_t getSExtValue() const { |
| return static_cast<int64_t>(Val << (BITS_PER_WORD - BitWidth)) >> |
| (BITS_PER_WORD - BitWidth); |
| } |
| |
| template <typename IntType, typename FpType> |
| inline FpType convertToFp() const { |
| static_assert(sizeof(IntType) == sizeof(FpType), |
| "IntType and FpType should be the same width"); |
| assert(BitWidth == sizeof(IntType) * CHAR_BIT); |
| auto V = static_cast<IntType>(Val); |
| return Ice::Utils::bitCopy<FpType>(V); |
| } |
| |
| private: |
| /// Bits in the (internal) value. |
| static const Ice::SizeT BITS_PER_WORD = sizeof(uint64_t) * CHAR_BIT; |
| |
| uint32_t BitWidth; /// The number of bits in the floating point number. |
| uint64_t Val; /// The (64-bit) equivalent integer value. |
| |
| /// Clear unused high order bits. |
| void clearUnusedBits() { |
| // If all bits are used, we want to leave the value alone. |
| if (BitWidth == BITS_PER_WORD) |
| return; |
| |
| // Mask out the high bits. |
| Val &= ~static_cast<uint64_t>(0) >> (BITS_PER_WORD - BitWidth); |
| } |
| }; |
| |
| class BlockParserBaseClass; |
| |
| // Top-level class to read PNaCl bitcode files, and translate to ICE. |
| class TopLevelParser final : public NaClBitcodeParser { |
| TopLevelParser() = delete; |
| TopLevelParser(const TopLevelParser &) = delete; |
| TopLevelParser &operator=(const TopLevelParser &) = delete; |
| |
| public: |
| TopLevelParser(Ice::Translator &Translator, NaClBitstreamCursor &Cursor, |
| Ice::ErrorCode &ErrorStatus) |
| : NaClBitcodeParser(Cursor), Translator(Translator), |
| ErrorStatus(ErrorStatus), |
| VariableDeclarations(new Ice::VariableDeclarationList()) {} |
| |
| ~TopLevelParser() override = default; |
| |
| Ice::Translator &getTranslator() const { return Translator; } |
| |
| /// Generates error with given Message, occurring at BitPosition within the |
| /// bitcode file. Always returns true. |
| bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t BitPosition, |
| const std::string &Message) override; |
| |
| /// Generates error message with respect to the current block parser. |
| bool blockError(const std::string &Message); |
| |
| /// Changes the size of the type list to the given size. |
| void resizeTypeIDValues(size_t NewSize) { TypeIDValues.resize(NewSize); } |
| |
| size_t getNumTypeIDValues() const { return TypeIDValues.size(); } |
| |
| /// Returns a pointer to the pool where globals are allocated. |
| Ice::VariableDeclarationList *getGlobalVariablesPool() { |
| return VariableDeclarations.get(); |
| } |
| |
| /// Returns the undefined type associated with type ID. Note: Returns extended |
| /// type ready to be defined. |
| ExtendedType *getTypeByIDForDefining(NaClBcIndexSize_t ID) { |
| // Get corresponding element, verifying the value is still undefined (and |
| // hence allowed to be defined). |
| ExtendedType *Ty = getTypeByIDAsKind(ID, ExtendedType::Undefined); |
| if (Ty) |
| return Ty; |
| if (ID >= TypeIDValues.size()) { |
| if (ID >= NaClBcIndexSize_t_Max) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Can't define more than " << NaClBcIndexSize_t_Max |
| << " types\n"; |
| blockError(StrBuf.str()); |
| // Recover by using existing type slot. |
| return &TypeIDValues[0]; |
| } |
| Ice::Utils::reserveAndResize(TypeIDValues, ID + 1); |
| } |
| return &TypeIDValues[ID]; |
| } |
| |
| /// Returns the type associated with the given index. |
| Ice::Type getSimpleTypeByID(NaClBcIndexSize_t ID) { |
| const ExtendedType *Ty = getTypeByIDAsKind(ID, ExtendedType::Simple); |
| if (Ty == nullptr) |
| // Return error recovery value. |
| return Ice::IceType_void; |
| return cast<SimpleExtendedType>(Ty)->getType(); |
| } |
| |
| /// Returns the type signature associated with the given index. |
| const Ice::FuncSigType &getFuncSigTypeByID(NaClBcIndexSize_t ID) { |
| const ExtendedType *Ty = getTypeByIDAsKind(ID, ExtendedType::FuncSig); |
| if (Ty == nullptr) |
| // Return error recovery value. |
| return UndefinedFuncSigType; |
| return cast<FuncSigExtendedType>(Ty)->getSignature(); |
| } |
| |
| /// Sets the next function ID to the given LLVM function. |
| void setNextFunctionID(Ice::FunctionDeclaration *Fcn) { |
| FunctionDeclarations.push_back(Fcn); |
| } |
| |
| /// Returns the value id that should be associated with the the current |
| /// function block. Increments internal counters during call so that it will |
| /// be in correct position for next function block. |
| NaClBcIndexSize_t getNextFunctionBlockValueID() { |
| size_t NumDeclaredFunctions = FunctionDeclarations.size(); |
| while (NextDefiningFunctionID < NumDeclaredFunctions && |
| FunctionDeclarations[NextDefiningFunctionID]->isProto()) |
| ++NextDefiningFunctionID; |
| if (NextDefiningFunctionID >= NumDeclaredFunctions) |
| Fatal("More function blocks than defined function addresses"); |
| return NextDefiningFunctionID++; |
| } |
| |
| /// Returns the function associated with ID. |
| Ice::FunctionDeclaration *getFunctionByID(NaClBcIndexSize_t ID) { |
| if (ID < FunctionDeclarations.size()) |
| return FunctionDeclarations[ID]; |
| return reportGetFunctionByIDError(ID); |
| } |
| |
| /// Returns the constant associated with the given global value ID. |
| Ice::Constant *getGlobalConstantByID(NaClBcIndexSize_t ID) { |
| assert(ID < ValueIDConstants.size()); |
| return ValueIDConstants[ID]; |
| } |
| |
| /// Install names for all global values without names. Called after the global |
| /// value symbol table is processed, but before any function blocks are |
| /// processed. |
| void installGlobalNames() { |
| assert(VariableDeclarations); |
| installGlobalVarNames(); |
| installFunctionNames(); |
| } |
| |
| void verifyFunctionTypeSignatures(); |
| |
| void createValueIDs() { |
| assert(VariableDeclarations); |
| ValueIDConstants.reserve(VariableDeclarations->size() + |
| FunctionDeclarations.size()); |
| createValueIDsForFunctions(); |
| createValueIDsForGlobalVars(); |
| } |
| |
| /// Returns the number of function declarations in the bitcode file. |
| size_t getNumFunctionIDs() const { return FunctionDeclarations.size(); } |
| |
| /// Returns the number of global declarations (i.e. IDs) defined in the |
| /// bitcode file. |
| size_t getNumGlobalIDs() const { |
| if (VariableDeclarations) { |
| return FunctionDeclarations.size() + VariableDeclarations->size(); |
| } else { |
| return ValueIDConstants.size(); |
| } |
| } |
| |
| /// Adds the given global declaration to the end of the list of global |
| /// declarations. |
| void addGlobalDeclaration(Ice::VariableDeclaration *Decl) { |
| assert(VariableDeclarations); |
| VariableDeclarations->push_back(Decl); |
| } |
| |
| /// Returns the global variable declaration with the given index. |
| Ice::VariableDeclaration *getGlobalVariableByID(NaClBcIndexSize_t Index) { |
| assert(VariableDeclarations); |
| if (Index < VariableDeclarations->size()) |
| return VariableDeclarations->at(Index); |
| return reportGetGlobalVariableByIDError(Index); |
| } |
| |
| /// Returns the global declaration (variable or function) with the given |
| /// Index. |
| Ice::GlobalDeclaration *getGlobalDeclarationByID(NaClBcIndexSize_t Index) { |
| size_t NumFunctionIds = FunctionDeclarations.size(); |
| if (Index < NumFunctionIds) |
| return getFunctionByID(Index); |
| else |
| return getGlobalVariableByID(Index - NumFunctionIds); |
| } |
| |
| /// Returns true if a module block has been parsed. |
| bool parsedModuleBlock() const { return ParsedModuleBlock; } |
| |
| /// Returns the list of parsed global variable declarations. Releases |
| /// ownership of the current list of global variables. Note: only returns |
| /// non-null pointer on first call. All successive calls return a null |
| /// pointer. |
| std::unique_ptr<Ice::VariableDeclarationList> getGlobalVariables() { |
| // Before returning, check that ValidIDConstants has already been built. |
| assert(!VariableDeclarations || |
| VariableDeclarations->size() <= ValueIDConstants.size()); |
| return std::move(VariableDeclarations); |
| } |
| |
| // Upper limit of alignment power allowed by LLVM |
| static constexpr uint32_t AlignPowerLimit = 29; |
| |
| // Extracts the corresponding Alignment to use, given the AlignPower (i.e. |
| // 2**(AlignPower-1), or 0 if AlignPower == 0). Parser defines the block |
| // context the alignment check appears in, and Prefix defines the context the |
| // alignment appears in. |
| uint32_t extractAlignment(NaClBitcodeParser *Parser, const char *Prefix, |
| uint32_t AlignPower) { |
| if (AlignPower <= AlignPowerLimit + 1) |
| return (1 << AlignPower) >> 1; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << Prefix << " alignment greater than 2**" << AlignPowerLimit |
| << ". Found: 2**" << (AlignPower - 1); |
| Parser->Error(StrBuf.str()); |
| // Error recover with value that is always acceptable. |
| return 1; |
| } |
| |
| private: |
| // The translator associated with the parser. |
| Ice::Translator &Translator; |
| |
| // ErrorStatus should only be updated while this lock is locked. |
| Ice::GlobalLockType ErrorReportingLock; |
| // The exit status that should be set to true if an error occurs. |
| Ice::ErrorCode &ErrorStatus; |
| |
| // The types associated with each type ID. |
| std::vector<ExtendedType> TypeIDValues; |
| // The set of functions (prototype and defined). |
| Ice::FunctionDeclarationList FunctionDeclarations; |
| // The ID of the next possible defined function ID in FunctionDeclarations. |
| // FunctionDeclarations is filled first. It's the set of functions (either |
| // defined or isproto). Then function definitions are encountered/parsed and |
| // NextDefiningFunctionID is incremented to track the next actually-defined |
| // function. |
| size_t NextDefiningFunctionID = 0; |
| // The set of global variables. |
| std::unique_ptr<Ice::VariableDeclarationList> VariableDeclarations; |
| // Relocatable constants associated with global declarations. |
| Ice::ConstantList ValueIDConstants; |
| // Error recovery value to use when getFuncSigTypeByID fails. |
| Ice::FuncSigType UndefinedFuncSigType; |
| // Defines if a module block has already been parsed. |
| bool ParsedModuleBlock = false; |
| |
| bool ParseBlock(unsigned BlockID) override; |
| |
| // Gets extended type associated with the given index, assuming the extended |
| // type is of the WantedKind. Generates error message if corresponding |
| // extended type of WantedKind can't be found, and returns nullptr. |
| ExtendedType *getTypeByIDAsKind(NaClBcIndexSize_t ID, |
| ExtendedType::TypeKind WantedKind) { |
| ExtendedType *Ty = nullptr; |
| if (ID < TypeIDValues.size()) { |
| Ty = &TypeIDValues[ID]; |
| if (Ty->getKind() == WantedKind) |
| return Ty; |
| } |
| // Generate an error message and set ErrorStatus. |
| this->reportBadTypeIDAs(ID, Ty, WantedKind); |
| return nullptr; |
| } |
| |
| // Gives Decl a name if it doesn't already have one. Prefix and NameIndex are |
| // used to generate the name. NameIndex is automatically incremented if a new |
| // name is created. DeclType is literal text describing the type of name being |
| // created. Also generates a warning if created names may conflict with named |
| // declarations. |
| void installDeclarationName(Ice::GlobalDeclaration *Decl, |
| const std::string &Prefix, const char *DeclType, |
| NaClBcIndexSize_t &NameIndex) { |
| if (Decl->hasName()) { |
| Translator.checkIfUnnamedNameSafe(Decl->getName().toString(), DeclType, |
| Prefix); |
| } else { |
| Ice::GlobalContext *Ctx = Translator.getContext(); |
| // Synthesize a dummy name if any of the following is true: |
| // - DUMP is enabled |
| // - The symbol is external |
| // - The -timing-funcs flag is enabled |
| // - Some RangeSpec is initialized with actual names |
| if (Ice::BuildDefs::dump() || !Decl->isInternal() || |
| Ice::RangeSpec::hasNames() || Ice::getFlags().getTimeEachFunction()) { |
| Decl->setName(Ctx, Translator.createUnnamedName(Prefix, NameIndex)); |
| } else { |
| Decl->setName(Ctx); |
| } |
| ++NameIndex; |
| } |
| } |
| |
| // Installs names for global variables without names. |
| void installGlobalVarNames() { |
| assert(VariableDeclarations); |
| const std::string &GlobalPrefix = Ice::getFlags().getDefaultGlobalPrefix(); |
| if (!GlobalPrefix.empty()) { |
| NaClBcIndexSize_t NameIndex = 0; |
| for (Ice::VariableDeclaration *Var : *VariableDeclarations) { |
| installDeclarationName(Var, GlobalPrefix, "global", NameIndex); |
| } |
| } |
| } |
| |
| // Installs names for functions without names. |
| void installFunctionNames() { |
| const std::string &FunctionPrefix = |
| Ice::getFlags().getDefaultFunctionPrefix(); |
| if (!FunctionPrefix.empty()) { |
| NaClBcIndexSize_t NameIndex = 0; |
| for (Ice::FunctionDeclaration *Func : FunctionDeclarations) { |
| installDeclarationName(Func, FunctionPrefix, "function", NameIndex); |
| } |
| } |
| } |
| |
| // Builds a constant symbol named Name. IsExternal is true iff the symbol is |
| // external. |
| Ice::Constant *getConstantSym(Ice::GlobalString Name, bool IsExternal) const { |
| Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| if (IsExternal) { |
| return Ctx->getConstantExternSym(Name); |
| } else { |
| const Ice::RelocOffsetT Offset = 0; |
| return Ctx->getConstantSym(Offset, Name); |
| } |
| } |
| |
| void reportLinkageError(const char *Kind, |
| const Ice::GlobalDeclaration &Decl) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << Kind << " " << Decl.getName() |
| << " has incorrect linkage: " << Decl.getLinkageName(); |
| if (Decl.isExternal()) |
| StrBuf << "\n Use flag -allow-externally-defined-symbols to override"; |
| Error(StrBuf.str()); |
| } |
| |
| // Converts function declarations into constant value IDs. |
| void createValueIDsForFunctions() { |
| Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| for (const Ice::FunctionDeclaration *Func : FunctionDeclarations) { |
| if (!Func->verifyLinkageCorrect(Ctx)) |
| reportLinkageError("Function", *Func); |
| Ice::Constant *C = getConstantSym(Func->getName(), Func->isProto()); |
| ValueIDConstants.push_back(C); |
| } |
| } |
| |
| // Converts global variable declarations into constant value IDs. |
| void createValueIDsForGlobalVars() { |
| for (const Ice::VariableDeclaration *Decl : *VariableDeclarations) { |
| if (!Decl->verifyLinkageCorrect()) |
| reportLinkageError("Global", *Decl); |
| Ice::Constant *C = |
| getConstantSym(Decl->getName(), !Decl->hasInitializer()); |
| ValueIDConstants.push_back(C); |
| } |
| } |
| |
| // Reports that type ID is undefined, or not of the WantedType. |
| void reportBadTypeIDAs(NaClBcIndexSize_t ID, const ExtendedType *Ty, |
| ExtendedType::TypeKind WantedType); |
| |
| // Reports that there is no function declaration for ID. Returns an error |
| // recovery value to use. |
| Ice::FunctionDeclaration *reportGetFunctionByIDError(NaClBcIndexSize_t ID); |
| |
| // Reports that there is not global variable declaration for ID. Returns an |
| // error recovery value to use. |
| Ice::VariableDeclaration * |
| reportGetGlobalVariableByIDError(NaClBcIndexSize_t Index); |
| |
| // Reports that there is no corresponding ICE type for LLVMTy, and returns |
| // Ice::IceType_void. |
| Ice::Type convertToIceTypeError(Type *LLVMTy); |
| }; |
| |
| bool TopLevelParser::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, |
| const std::string &Message) { |
| Ice::GlobalContext *Context = Translator.getContext(); |
| { |
| std::unique_lock<Ice::GlobalLockType> _(ErrorReportingLock); |
| ErrorStatus.assign(Ice::EC_Bitcode); |
| } |
| { // Lock while printing out error message. |
| Ice::OstreamLocker L(Context); |
| raw_ostream &OldErrStream = setErrStream(Context->getStrError()); |
| NaClBitcodeParser::ErrorAt(Level, Bit, Message); |
| setErrStream(OldErrStream); |
| } |
| if (Level >= naclbitc::Error && !Ice::getFlags().getAllowErrorRecovery()) |
| Fatal(); |
| return true; |
| } |
| |
| void TopLevelParser::reportBadTypeIDAs(NaClBcIndexSize_t ID, |
| const ExtendedType *Ty, |
| ExtendedType::TypeKind WantedType) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| if (Ty == nullptr) { |
| StrBuf << "Can't find extended type for type id: " << ID; |
| } else { |
| StrBuf << "Type id " << ID << " not " << WantedType << ". Found: " << *Ty; |
| } |
| blockError(StrBuf.str()); |
| } |
| |
| Ice::FunctionDeclaration * |
| TopLevelParser::reportGetFunctionByIDError(NaClBcIndexSize_t ID) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function index " << ID |
| << " not allowed. Out of range. Must be less than " |
| << FunctionDeclarations.size(); |
| blockError(StrBuf.str()); |
| if (!FunctionDeclarations.empty()) |
| return FunctionDeclarations[0]; |
| Fatal(); |
| } |
| |
| Ice::VariableDeclaration * |
| TopLevelParser::reportGetGlobalVariableByIDError(NaClBcIndexSize_t Index) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Global index " << Index |
| << " not allowed. Out of range. Must be less than " |
| << VariableDeclarations->size(); |
| blockError(StrBuf.str()); |
| if (!VariableDeclarations->empty()) |
| return VariableDeclarations->at(0); |
| Fatal(); |
| } |
| |
| Ice::Type TopLevelParser::convertToIceTypeError(Type *LLVMTy) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Invalid LLVM type: " << *LLVMTy; |
| Error(StrBuf.str()); |
| return Ice::IceType_void; |
| } |
| |
| void TopLevelParser::verifyFunctionTypeSignatures() { |
| const Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| for (Ice::FunctionDeclaration *FuncDecl : FunctionDeclarations) { |
| if (!FuncDecl->validateTypeSignature(Ctx)) |
| Error(FuncDecl->getTypeSignatureError(Ctx)); |
| } |
| } |
| |
| // Base class for parsing blocks within the bitcode file. Note: Because this is |
| // the base class of block parsers, we generate error messages if ParseBlock or |
| // ParseRecord is not overridden in derived classes. |
| class BlockParserBaseClass : public NaClBitcodeParser { |
| BlockParserBaseClass() = delete; |
| BlockParserBaseClass(const BlockParserBaseClass &) = delete; |
| BlockParserBaseClass &operator=(const BlockParserBaseClass &) = delete; |
| |
| public: |
| // Constructor for the top-level module block parser. |
| BlockParserBaseClass(unsigned BlockID, TopLevelParser *Context) |
| : NaClBitcodeParser(BlockID, Context), Context(Context) {} |
| |
| BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser, |
| NaClBitstreamCursor &Cursor) |
| : NaClBitcodeParser(BlockID, EnclosingParser, Cursor), |
| Context(EnclosingParser->Context) {} |
| |
| ~BlockParserBaseClass() override {} |
| |
| // Returns the printable name of the type of block being parsed. |
| virtual const char *getBlockName() const { |
| // If this class is used, it is parsing an unknown block. |
| return "unknown"; |
| } |
| |
| // Generates an error Message with the Bit address prefixed to it. |
| bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, |
| const std::string &Message) override; |
| |
| protected: |
| // The context parser that contains the decoded state. |
| TopLevelParser *Context; |
| // True if ErrorAt has been called in this block. |
| bool BlockHasError = false; |
| |
| // Constructor for nested block parsers. |
| BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser) |
| : NaClBitcodeParser(BlockID, EnclosingParser), |
| Context(EnclosingParser->Context) {} |
| |
| // Gets the translator associated with the bitcode parser. |
| Ice::Translator &getTranslator() const { return Context->getTranslator(); } |
| |
| // Default implementation. Reports that block is unknown and skips its |
| // contents. |
| bool ParseBlock(unsigned BlockID) override; |
| |
| // Default implementation. Reports that the record is not understood. |
| void ProcessRecord() override; |
| |
| // Checks if the size of the record is Size. Return true if valid. Otherwise |
| // generates an error and returns false. |
| bool isValidRecordSize(size_t Size, const char *RecordName) { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| if (Values.size() == Size) |
| return true; |
| reportRecordSizeError(Size, RecordName, nullptr); |
| return false; |
| } |
| |
| // Checks if the size of the record is at least as large as the LowerLimit. |
| // Returns true if valid. Otherwise generates an error and returns false. |
| bool isValidRecordSizeAtLeast(size_t LowerLimit, const char *RecordName) { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| if (Values.size() >= LowerLimit) |
| return true; |
| reportRecordSizeError(LowerLimit, RecordName, "at least"); |
| return false; |
| } |
| |
| // Checks if the size of the record is no larger than the |
| // UpperLimit. Returns true if valid. Otherwise generates an error and |
| // returns false. |
| bool isValidRecordSizeAtMost(size_t UpperLimit, const char *RecordName) { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| if (Values.size() <= UpperLimit) |
| return true; |
| reportRecordSizeError(UpperLimit, RecordName, "no more than"); |
| return false; |
| } |
| |
| // Checks if the size of the record is at least as large as the LowerLimit, |
| // and no larger than the UpperLimit. Returns true if valid. Otherwise |
| // generates an error and returns false. |
| bool isValidRecordSizeInRange(size_t LowerLimit, size_t UpperLimit, |
| const char *RecordName) { |
| return isValidRecordSizeAtLeast(LowerLimit, RecordName) || |
| isValidRecordSizeAtMost(UpperLimit, RecordName); |
| } |
| |
| private: |
| /// Generates a record size error. ExpectedSize is the number of elements |
| /// expected. RecordName is the name of the kind of record that has incorrect |
| /// size. ContextMessage (if not nullptr) is appended to "record expects" to |
| /// describe how ExpectedSize should be interpreted. |
| void reportRecordSizeError(size_t ExpectedSize, const char *RecordName, |
| const char *ContextMessage); |
| }; |
| |
| bool TopLevelParser::blockError(const std::string &Message) { |
| // TODO(kschimpf): Remove this method. This method used to redirect |
| // block-level errors to the block we are in, rather than the top-level |
| // block. This gave better bit location for error messages. However, with |
| // parallel parsing, we can't keep a field to redirect (there could be many |
| // and we don't know which block parser applies). Hence, This redirect can't |
| // be applied anymore. |
| return Error(Message); |
| } |
| |
| // Generates an error Message with the bit address prefixed to it. |
| bool BlockParserBaseClass::ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit, |
| const std::string &Message) { |
| BlockHasError = true; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| // Note: If dump routines have been turned off, the error messages will not |
| // be readable. Hence, replace with simple error. We also use the simple form |
| // for unit tests. |
| if (Ice::getFlags().getGenerateUnitTestMessages()) { |
| StrBuf << "Invalid " << getBlockName() << " record: <" << Record.GetCode(); |
| for (const uint64_t Val : Record.GetValues()) { |
| StrBuf << " " << Val; |
| } |
| StrBuf << ">"; |
| } else { |
| StrBuf << Message; |
| } |
| return Context->ErrorAt(Level, Record.GetCursor().getErrorBitNo(Bit), |
| StrBuf.str()); |
| } |
| |
| void BlockParserBaseClass::reportRecordSizeError(size_t ExpectedSize, |
| const char *RecordName, |
| const char *ContextMessage) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| const char *BlockName = getBlockName(); |
| const char FirstChar = toupper(*BlockName); |
| StrBuf << FirstChar << (BlockName + 1) << " " << RecordName |
| << " record expects"; |
| if (ContextMessage) |
| StrBuf << " " << ContextMessage; |
| StrBuf << " " << ExpectedSize << " argument"; |
| if (ExpectedSize > 1) |
| StrBuf << "s"; |
| StrBuf << ". Found: " << Record.GetValues().size(); |
| Error(StrBuf.str()); |
| } |
| |
| bool BlockParserBaseClass::ParseBlock(unsigned BlockID) { |
| // If called, derived class doesn't know how to handle block. Report error |
| // and skip. |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Don't know how to parse block id: " << BlockID; |
| Error(StrBuf.str()); |
| SkipBlock(); |
| return false; |
| } |
| |
| void BlockParserBaseClass::ProcessRecord() { |
| // If called, derived class doesn't know how to handle. |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Don't know how to process " << getBlockName() |
| << " record:" << Record; |
| Error(StrBuf.str()); |
| } |
| |
| // Class to parse a types block. |
| class TypesParser final : public BlockParserBaseClass { |
| TypesParser() = delete; |
| TypesParser(const TypesParser &) = delete; |
| TypesParser &operator=(const TypesParser &) = delete; |
| |
| public: |
| TypesParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser) |
| : BlockParserBaseClass(BlockID, EnclosingParser), |
| Timer(Ice::TimerStack::TT_parseTypes, getTranslator().getContext()) {} |
| |
| ~TypesParser() override { |
| if (ExpectedNumTypes != Context->getNumTypeIDValues()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Types block expected " << ExpectedNumTypes |
| << " types but found: " << NextTypeId; |
| Error(StrBuf.str()); |
| } |
| } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // The type ID that will be associated with the next type defining record in |
| // the types block. |
| NaClBcIndexSize_t NextTypeId = 0; |
| |
| // The expected number of types, based on record TYPE_CODE_NUMENTRY. |
| NaClBcIndexSize_t ExpectedNumTypes = 0; |
| |
| void ProcessRecord() override; |
| |
| const char *getBlockName() const override { return "type"; } |
| |
| void setNextTypeIDAsSimpleType(Ice::Type Ty) { |
| Context->getTypeByIDForDefining(NextTypeId++)->setAsSimpleType(Ty); |
| } |
| }; |
| |
| void TypesParser::ProcessRecord() { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| switch (Record.GetCode()) { |
| case naclbitc::TYPE_CODE_NUMENTRY: { |
| // NUMENTRY: [numentries] |
| if (!isValidRecordSize(1, "count")) |
| return; |
| uint64_t Size = Values[0]; |
| if (Size > NaClBcIndexSize_t_Max) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Size to big for count record: " << Size; |
| Error(StrBuf.str()); |
| ExpectedNumTypes = NaClBcIndexSize_t_Max; |
| } |
| // The code double checks that Expected size and the actual size at the end |
| // of the block. To reduce allocations we preallocate the space. |
| // |
| // However, if the number is large, we suspect that the number is |
| // (possibly) incorrect. In that case, we preallocate a smaller space. |
| constexpr uint64_t DefaultLargeResizeValue = 1000000; |
| Context->resizeTypeIDValues(std::min(Size, DefaultLargeResizeValue)); |
| ExpectedNumTypes = Size; |
| return; |
| } |
| case naclbitc::TYPE_CODE_VOID: |
| // VOID |
| if (!isValidRecordSize(0, "void")) |
| return; |
| setNextTypeIDAsSimpleType(Ice::IceType_void); |
| return; |
| case naclbitc::TYPE_CODE_FLOAT: |
| // FLOAT |
| if (!isValidRecordSize(0, "float")) |
| return; |
| setNextTypeIDAsSimpleType(Ice::IceType_f32); |
| return; |
| case naclbitc::TYPE_CODE_DOUBLE: |
| // DOUBLE |
| if (!isValidRecordSize(0, "double")) |
| return; |
| setNextTypeIDAsSimpleType(Ice::IceType_f64); |
| return; |
| case naclbitc::TYPE_CODE_INTEGER: |
| // INTEGER: [width] |
| if (!isValidRecordSize(1, "integer")) |
| return; |
| switch (Values[0]) { |
| case 1: |
| setNextTypeIDAsSimpleType(Ice::IceType_i1); |
| return; |
| case 8: |
| setNextTypeIDAsSimpleType(Ice::IceType_i8); |
| return; |
| case 16: |
| setNextTypeIDAsSimpleType(Ice::IceType_i16); |
| return; |
| case 32: |
| setNextTypeIDAsSimpleType(Ice::IceType_i32); |
| return; |
| case 64: |
| setNextTypeIDAsSimpleType(Ice::IceType_i64); |
| return; |
| default: |
| break; |
| } |
| { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Type integer record with invalid bitsize: " << Values[0]; |
| Error(StrBuf.str()); |
| } |
| return; |
| case naclbitc::TYPE_CODE_VECTOR: { |
| // VECTOR: [numelts, eltty] |
| if (!isValidRecordSize(2, "vector")) |
| return; |
| Ice::Type BaseTy = Context->getSimpleTypeByID(Values[1]); |
| Ice::SizeT Size = Values[0]; |
| switch (BaseTy) { |
| case Ice::IceType_i1: |
| switch (Size) { |
| case 4: |
| setNextTypeIDAsSimpleType(Ice::IceType_v4i1); |
| return; |
| case 8: |
| setNextTypeIDAsSimpleType(Ice::IceType_v8i1); |
| return; |
| case 16: |
| setNextTypeIDAsSimpleType(Ice::IceType_v16i1); |
| return; |
| default: |
| break; |
| } |
| break; |
| case Ice::IceType_i8: |
| if (Size == 16) { |
| setNextTypeIDAsSimpleType(Ice::IceType_v16i8); |
| return; |
| } |
| break; |
| case Ice::IceType_i16: |
| if (Size == 8) { |
| setNextTypeIDAsSimpleType(Ice::IceType_v8i16); |
| return; |
| } |
| break; |
| case Ice::IceType_i32: |
| if (Size == 4) { |
| setNextTypeIDAsSimpleType(Ice::IceType_v4i32); |
| return; |
| } |
| break; |
| case Ice::IceType_f32: |
| if (Size == 4) { |
| setNextTypeIDAsSimpleType(Ice::IceType_v4f32); |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Invalid type vector record: <" << Values[0] << " x " << BaseTy |
| << ">"; |
| Error(StrBuf.str()); |
| } |
| return; |
| } |
| case naclbitc::TYPE_CODE_FUNCTION: { |
| // FUNCTION: [vararg, retty, paramty x N] |
| if (!isValidRecordSizeAtLeast(2, "signature")) |
| return; |
| if (Values[0]) |
| Error("Function type can't define varargs"); |
| ExtendedType *Ty = Context->getTypeByIDForDefining(NextTypeId++); |
| Ty->setAsFunctionType(); |
| auto *FuncTy = cast<FuncSigExtendedType>(Ty); |
| FuncTy->setReturnType(Context->getSimpleTypeByID(Values[1])); |
| for (size_t i = 2, e = Values.size(); i != e; ++i) { |
| // Check that type void not used as argument type. Note: PNaCl |
| // restrictions can't be checked until we know the name, because we have |
| // to check for intrinsic signatures. |
| Ice::Type ArgTy = Context->getSimpleTypeByID(Values[i]); |
| if (ArgTy == Ice::IceType_void) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Type for parameter " << (i - 1) |
| << " not valid. Found: " << ArgTy; |
| ArgTy = Ice::IceType_i32; |
| } |
| FuncTy->appendArgType(ArgTy); |
| } |
| return; |
| } |
| default: |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| llvm_unreachable("Unknown type block record not processed!"); |
| } |
| |
| /// Parses the globals block (i.e. global variable declarations and |
| /// corresponding initializers). |
| class GlobalsParser final : public BlockParserBaseClass { |
| GlobalsParser() = delete; |
| GlobalsParser(const GlobalsParser &) = delete; |
| GlobalsParser &operator=(const GlobalsParser &) = delete; |
| |
| public: |
| GlobalsParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser) |
| : BlockParserBaseClass(BlockID, EnclosingParser), |
| Timer(Ice::TimerStack::TT_parseGlobals, getTranslator().getContext()), |
| NumFunctionIDs(Context->getNumFunctionIDs()), |
| DummyGlobalVar(Ice::VariableDeclaration::create( |
| Context->getGlobalVariablesPool())), |
| CurGlobalVar(DummyGlobalVar) { |
| Context->getGlobalVariablesPool()->willNotBeEmitted(DummyGlobalVar); |
| } |
| |
| ~GlobalsParser() override = default; |
| |
| const char *getBlockName() const override { return "globals"; } |
| |
| private: |
| using GlobalVarsMapType = |
| std::unordered_map<NaClBcIndexSize_t, Ice::VariableDeclaration *>; |
| |
| Ice::TimerMarker Timer; |
| |
| // Holds global variables generated/referenced in the global variables block. |
| GlobalVarsMapType GlobalVarsMap; |
| |
| // Holds the number of defined function IDs. |
| NaClBcIndexSize_t NumFunctionIDs; |
| |
| // Holds the specified number of global variables by the count record in the |
| // global variables block. |
| NaClBcIndexSize_t SpecifiedNumberVars = 0; |
| |
| // Keeps track of how many initializers are expected for the global variable |
| // declaration being built. |
| NaClBcIndexSize_t InitializersNeeded = 0; |
| |
| // The index of the next global variable declaration. |
| NaClBcIndexSize_t NextGlobalID = 0; |
| |
| // Dummy global variable declaration to guarantee CurGlobalVar is always |
| // defined (allowing code to not need to check if CurGlobalVar is nullptr). |
| Ice::VariableDeclaration *DummyGlobalVar; |
| |
| // Holds the current global variable declaration being built. |
| Ice::VariableDeclaration *CurGlobalVar; |
| |
| // Returns the global variable associated with the given Index. |
| Ice::VariableDeclaration *getGlobalVarByID(NaClBcIndexSize_t Index) { |
| Ice::VariableDeclaration *&Decl = GlobalVarsMap[Index]; |
| if (Decl == nullptr) |
| Decl = |
| Ice::VariableDeclaration::create(Context->getGlobalVariablesPool()); |
| return Decl; |
| } |
| |
| // Returns the global declaration associated with the given index. |
| Ice::GlobalDeclaration *getGlobalDeclByID(NaClBcIndexSize_t Index) { |
| if (Index < NumFunctionIDs) |
| return Context->getFunctionByID(Index); |
| return getGlobalVarByID(Index - NumFunctionIDs); |
| } |
| |
| // If global variables parsed correctly, install them into the top-level |
| // context. |
| void installGlobalVariables() { |
| // Verify specified number of globals matches number found. |
| size_t NumGlobals = GlobalVarsMap.size(); |
| if (SpecifiedNumberVars != NumGlobals || |
| SpecifiedNumberVars != NextGlobalID) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << getBlockName() << " block expects " << SpecifiedNumberVars |
| << " global variables. Found: " << GlobalVarsMap.size(); |
| Error(StrBuf.str()); |
| return; |
| } |
| // Install global variables into top-level context. |
| for (size_t I = 0; I < NumGlobals; ++I) |
| Context->addGlobalDeclaration(GlobalVarsMap[I]); |
| } |
| |
| void ExitBlock() override { |
| verifyNoMissingInitializers(); |
| installGlobalVariables(); |
| BlockParserBaseClass::ExitBlock(); |
| } |
| |
| void ProcessRecord() override; |
| |
| // Checks if the number of initializers for the CurGlobalVar is the same as |
| // the number found in the bitcode file. If different, and error message is |
| // generated, and the internal state of the parser is fixed so this condition |
| // is no longer violated. |
| void verifyNoMissingInitializers() { |
| size_t NumInits = CurGlobalVar->getInitializers().size(); |
| if (InitializersNeeded != NumInits) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Global variable @g" << NextGlobalID << " expected " |
| << InitializersNeeded << " initializer"; |
| if (InitializersNeeded > 1) |
| StrBuf << "s"; |
| StrBuf << ". Found: " << NumInits; |
| Error(StrBuf.str()); |
| InitializersNeeded = NumInits; |
| } |
| } |
| }; |
| |
| void GlobalsParser::ProcessRecord() { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| switch (Record.GetCode()) { |
| case naclbitc::GLOBALVAR_COUNT: |
| // COUNT: [n] |
| if (!isValidRecordSize(1, "count")) |
| return; |
| if (SpecifiedNumberVars || NextGlobalID) { |
| Error("Globals count record not first in block."); |
| return; |
| } |
| SpecifiedNumberVars = Values[0]; |
| return; |
| case naclbitc::GLOBALVAR_VAR: { |
| // VAR: [align, isconst] |
| if (!isValidRecordSize(2, "variable")) |
| return; |
| verifyNoMissingInitializers(); |
| // Always build the global variable, even if IR generation is turned off. |
| // This is needed because we need a placeholder in the top-level context |
| // when no IR is generated. |
| uint32_t Alignment = |
| Context->extractAlignment(this, "Global variable", Values[0]); |
| CurGlobalVar = getGlobalVarByID(NextGlobalID); |
| InitializersNeeded = 1; |
| CurGlobalVar->setAlignment(Alignment); |
| CurGlobalVar->setIsConstant(Values[1] != 0); |
| ++NextGlobalID; |
| return; |
| } |
| case naclbitc::GLOBALVAR_COMPOUND: |
| // COMPOUND: [size] |
| if (!isValidRecordSize(1, "compound")) |
| return; |
| if (!CurGlobalVar->getInitializers().empty()) { |
| Error("Globals compound record not first initializer"); |
| return; |
| } |
| if (Values[0] < 2) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << getBlockName() |
| << " compound record size invalid. Found: " << Values[0]; |
| Error(StrBuf.str()); |
| return; |
| } |
| InitializersNeeded = Values[0]; |
| return; |
| case naclbitc::GLOBALVAR_ZEROFILL: { |
| // ZEROFILL: [size] |
| if (!isValidRecordSize(1, "zerofill")) |
| return; |
| auto *Pool = Context->getGlobalVariablesPool(); |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::ZeroInitializer::create(Pool, Values[0])); |
| return; |
| } |
| case naclbitc::GLOBALVAR_DATA: { |
| // DATA: [b0, b1, ...] |
| if (!isValidRecordSizeAtLeast(1, "data")) |
| return; |
| auto *Pool = Context->getGlobalVariablesPool(); |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::DataInitializer::create(Pool, Values)); |
| return; |
| } |
| case naclbitc::GLOBALVAR_RELOC: { |
| // RELOC: [val, [addend]] |
| if (!isValidRecordSizeInRange(1, 2, "reloc")) |
| return; |
| NaClBcIndexSize_t Index = Values[0]; |
| NaClBcIndexSize_t IndexLimit = SpecifiedNumberVars + NumFunctionIDs; |
| if (Index >= IndexLimit) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Relocation index " << Index << " to big. Expect index < " |
| << IndexLimit; |
| Error(StrBuf.str()); |
| } |
| uint64_t Offset = 0; |
| if (Values.size() == 2) { |
| Offset = Values[1]; |
| if (Offset > std::numeric_limits<uint32_t>::max()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Addend of global reloc record too big: " << Offset; |
| Error(StrBuf.str()); |
| } |
| } |
| auto *Pool = Context->getGlobalVariablesPool(); |
| Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::RelocInitializer::create( |
| Pool, getGlobalDeclByID(Index), |
| {Ice::RelocOffset::create(Ctx, Offset)})); |
| return; |
| } |
| default: |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| } |
| |
| /// Base class for parsing a valuesymtab block in the bitcode file. |
| class ValuesymtabParser : public BlockParserBaseClass { |
| ValuesymtabParser() = delete; |
| ValuesymtabParser(const ValuesymtabParser &) = delete; |
| void operator=(const ValuesymtabParser &) = delete; |
| |
| public: |
| ValuesymtabParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser) |
| : BlockParserBaseClass(BlockID, EnclosingParser) {} |
| |
| ~ValuesymtabParser() override = default; |
| |
| const char *getBlockName() const override { return "valuesymtab"; } |
| |
| protected: |
| using StringType = SmallString<128>; |
| |
| // Returns the name to identify the kind of symbol table this is |
| // in error messages. |
| virtual const char *getTableKind() const = 0; |
| |
| // Associates Name with the value defined by the given Index. |
| virtual void setValueName(NaClBcIndexSize_t Index, StringType &Name) = 0; |
| |
| // Associates Name with the value defined by the given Index; |
| virtual void setBbName(NaClBcIndexSize_t Index, StringType &Name) = 0; |
| |
| // Reports that the assignment of Name to the value associated with |
| // index is not possible, for the given Context. |
| void reportUnableToAssign(const char *Context, NaClBcIndexSize_t Index, |
| StringType &Name); |
| |
| private: |
| using NamesSetType = std::unordered_set<StringType>; |
| NamesSetType ValueNames; |
| NamesSetType BlockNames; |
| |
| void ProcessRecord() override; |
| |
| // Extracts out ConvertedName. Returns true if unique wrt to Names. |
| bool convertToString(NamesSetType &Names, StringType &ConvertedName) { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| for (size_t i = 1, e = Values.size(); i != e; ++i) { |
| ConvertedName += static_cast<char>(Values[i]); |
| } |
| auto Pair = Names.insert(ConvertedName); |
| return Pair.second; |
| } |
| |
| void ReportDuplicateName(const char *NameCat, StringType &Name); |
| }; |
| |
| void ValuesymtabParser::reportUnableToAssign(const char *Context, |
| NaClBcIndexSize_t Index, |
| StringType &Name) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << getTableKind() << " " << getBlockName() << ": " << Context |
| << " name '" << Name << "' can't be associated with index " << Index; |
| Error(StrBuf.str()); |
| } |
| |
| void ValuesymtabParser::ReportDuplicateName(const char *NameCat, |
| StringType &Name) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << getTableKind() << " " << getBlockName() << " defines duplicate " |
| << NameCat << " name: '" << Name << "'"; |
| Error(StrBuf.str()); |
| } |
| |
| void ValuesymtabParser::ProcessRecord() { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| StringType ConvertedName; |
| switch (Record.GetCode()) { |
| case naclbitc::VST_CODE_ENTRY: { |
| // VST_ENTRY: [ValueId, namechar x N] |
| if (!isValidRecordSizeAtLeast(2, "value entry")) |
| return; |
| if (convertToString(ValueNames, ConvertedName)) |
| setValueName(Values[0], ConvertedName); |
| else |
| ReportDuplicateName("value", ConvertedName); |
| return; |
| } |
| case naclbitc::VST_CODE_BBENTRY: { |
| // VST_BBENTRY: [BbId, namechar x N] |
| if (!isValidRecordSizeAtLeast(2, "basic block entry")) |
| return; |
| if (convertToString(BlockNames, ConvertedName)) |
| setBbName(Values[0], ConvertedName); |
| else |
| ReportDuplicateName("block", ConvertedName); |
| return; |
| } |
| default: |
| break; |
| } |
| // If reached, don't know how to handle record. |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| |
| /// Parses function blocks in the bitcode file. |
| class FunctionParser final : public BlockParserBaseClass { |
| FunctionParser() = delete; |
| FunctionParser(const FunctionParser &) = delete; |
| FunctionParser &operator=(const FunctionParser &) = delete; |
| |
| public: |
| FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser, |
| NaClBcIndexSize_t FcnId) |
| : BlockParserBaseClass(BlockID, EnclosingParser), |
| Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()), |
| Func(nullptr), FuncDecl(Context->getFunctionByID(FcnId)), |
| CachedNumGlobalValueIDs(Context->getNumGlobalIDs()), |
| NextLocalInstIndex(Context->getNumGlobalIDs()) {} |
| |
| FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser, |
| NaClBcIndexSize_t FcnId, NaClBitstreamCursor &Cursor) |
| : BlockParserBaseClass(BlockID, EnclosingParser, Cursor), |
| Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()), |
| Func(nullptr), FuncDecl(Context->getFunctionByID(FcnId)), |
| CachedNumGlobalValueIDs(Context->getNumGlobalIDs()), |
| NextLocalInstIndex(Context->getNumGlobalIDs()) {} |
| |
| std::unique_ptr<Ice::Cfg> parseFunction(uint32_t SeqNumber) { |
| bool ParserResult; |
| Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| { |
| Ice::TimerMarker T(Ctx, FuncDecl->getName().toStringOrEmpty()); |
| // Note: The Cfg is created, even when IR generation is disabled. This is |
| // done to install a CfgLocalAllocator for various internal containers. |
| Ice::GlobalContext *Ctx = getTranslator().getContext(); |
| Func = Ice::Cfg::create(Ctx, SeqNumber); |
| |
| Ice::CfgLocalAllocatorScope _(Func.get()); |
| |
| // TODO(kschimpf) Clean up API to add a function signature to a CFG. |
| const Ice::FuncSigType &Signature = FuncDecl->getSignature(); |
| |
| Func->setFunctionName(FuncDecl->getName()); |
| Func->setReturnType(Signature.getReturnType()); |
| Func->setInternal(FuncDecl->getLinkage() == GlobalValue::InternalLinkage); |
| CurrentNode = installNextBasicBlock(); |
| Func->setEntryNode(CurrentNode); |
| for (Ice::Type ArgType : Signature.getArgList()) { |
| Func->addArg(getNextInstVar(ArgType)); |
| } |
| |
| ParserResult = ParseThisBlock(); |
| } |
| |
| if (ParserResult || BlockHasError) |
| Func->setError("Unable to parse function"); |
| |
| return std::move(Func); |
| } |
| |
| ~FunctionParser() override = default; |
| |
| const char *getBlockName() const override { return "function"; } |
| |
| Ice::Cfg *getFunc() const { return Func.get(); } |
| |
| size_t getNumGlobalIDs() const { return CachedNumGlobalValueIDs; } |
| |
| void setNextLocalInstIndex(Ice::Operand *Op) { |
| setOperand(NextLocalInstIndex++, Op); |
| } |
| |
| // Set the next constant ID to the given constant C. |
| void setNextConstantID(Ice::Constant *C) { setNextLocalInstIndex(C); } |
| |
| // Returns the value referenced by the given value Index. |
| Ice::Operand *getOperand(NaClBcIndexSize_t Index) { |
| if (Index < CachedNumGlobalValueIDs) { |
| return Context->getGlobalConstantByID(Index); |
| } |
| NaClBcIndexSize_t LocalIndex = Index - CachedNumGlobalValueIDs; |
| if (LocalIndex >= LocalOperands.size()) |
| reportGetOperandUndefined(Index); |
| Ice::Operand *Op = LocalOperands[LocalIndex]; |
| if (Op == nullptr) |
| reportGetOperandUndefined(Index); |
| return Op; |
| } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // The number of words in the bitstream defining the function block. |
| uint64_t NumBytesDefiningFunction = 0; |
| // Maximum number of records that can appear in the function block, based on |
| // the number of bytes defining the function block. |
| uint64_t MaxRecordsInBlock = 0; |
| // The corresponding ICE function defined by the function block. |
| std::unique_ptr<Ice::Cfg> Func; |
| // The index to the current basic block being built. |
| NaClBcIndexSize_t CurrentBbIndex = 0; |
| // The number of basic blocks declared for the function block. |
| NaClBcIndexSize_t DeclaredNumberBbs = 0; |
| // The basic block being built. |
| Ice::CfgNode *CurrentNode = nullptr; |
| // The corresponding function declaration. |
| Ice::FunctionDeclaration *FuncDecl; |
| // Holds the dividing point between local and global absolute value indices. |
| size_t CachedNumGlobalValueIDs; |
| // Holds operands local to the function block, based on indices defined in |
| // the bitcode file. |
| Ice::OperandList LocalOperands; |
| // Holds the index within LocalOperands corresponding to the next instruction |
| // that generates a value. |
| NaClBcIndexSize_t NextLocalInstIndex; |
| // True if the last processed instruction was a terminating instruction. |
| bool InstIsTerminating = false; |
| |
| bool ParseBlock(unsigned BlockID) override; |
| |
| void ProcessRecord() override; |
| |
| void EnterBlock(unsigned NumWords) override { |
| // Note: Bitstream defines words as 32-bit values. |
| NumBytesDefiningFunction = NumWords * sizeof(uint32_t); |
| // We know that all records are minimally defined by a two-bit abreviation. |
| MaxRecordsInBlock = NumBytesDefiningFunction * (CHAR_BIT >> 1); |
| } |
| |
| void ExitBlock() override; |
| |
| // Creates and appends a new basic block to the list of basic blocks. |
| Ice::CfgNode *installNextBasicBlock() { |
| Ice::CfgNode *Node = Func->makeNode(); |
| return Node; |
| } |
| |
| // Returns the Index-th basic block in the list of basic blocks. |
| Ice::CfgNode *getBasicBlock(NaClBcIndexSize_t Index) { |
| if (Index >= Func->getNumNodes()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Reference to basic block " << Index |
| << " not found. Must be less than " << Func->getNumNodes(); |
| Error(StrBuf.str()); |
| Index = 0; |
| } |
| return Func->getNodes()[Index]; |
| } |
| |
| // Returns the Index-th basic block in the list of basic blocks. Assumes |
| // Index corresponds to a branch instruction. Hence, if the branch references |
| // the entry block, it also generates a corresponding error. |
| Ice::CfgNode *getBranchBasicBlock(NaClBcIndexSize_t Index) { |
| if (Index == 0) { |
| Error("Branch to entry block not allowed"); |
| } |
| return getBasicBlock(Index); |
| } |
| |
| // Generate an instruction variable with type Ty. |
| Ice::Variable *createInstVar(Ice::Type Ty) { |
| if (Ty == Ice::IceType_void) { |
| Error("Can't define instruction value using type void"); |
| // Recover since we can't throw an exception. |
| Ty = Ice::IceType_i32; |
| } |
| return Func->makeVariable(Ty); |
| } |
| |
| // Generates the next available local variable using the given type. |
| Ice::Variable *getNextInstVar(Ice::Type Ty) { |
| assert(NextLocalInstIndex >= CachedNumGlobalValueIDs); |
| // Before creating one, see if a forwardtyperef has already defined it. |
| NaClBcIndexSize_t LocalIndex = NextLocalInstIndex - CachedNumGlobalValueIDs; |
| if (LocalIndex < LocalOperands.size()) { |
| Ice::Operand *Op = LocalOperands[LocalIndex]; |
| if (Op != nullptr) { |
| if (auto *Var = dyn_cast<Ice::Variable>(Op)) { |
| if (Var->getType() == Ty) { |
| ++NextLocalInstIndex; |
| return Var; |
| } |
| } |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Illegal forward referenced instruction (" |
| << NextLocalInstIndex << "): " << *Op; |
| Error(StrBuf.str()); |
| ++NextLocalInstIndex; |
| return createInstVar(Ty); |
| } |
| } |
| Ice::Variable *Var = createInstVar(Ty); |
| setOperand(NextLocalInstIndex++, Var); |
| return Var; |
| } |
| |
| // Converts a relative index (wrt to BaseIndex) to an absolute value index. |
| NaClBcIndexSize_t convertRelativeToAbsIndex(NaClRelBcIndexSize_t Id, |
| NaClRelBcIndexSize_t BaseIndex) { |
| if (BaseIndex < Id) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Invalid relative value id: " << Id |
| << " (must be <= " << BaseIndex << ")"; |
| Error(StrBuf.str()); |
| return 0; |
| } |
| return BaseIndex - Id; |
| } |
| |
| // Sets element Index (in the local operands list) to Op. |
| void setOperand(NaClBcIndexSize_t Index, Ice::Operand *Op) { |
| assert(Op); |
| // Check if simple push works. |
| NaClBcIndexSize_t LocalIndex = Index - CachedNumGlobalValueIDs; |
| if (LocalIndex == LocalOperands.size()) { |
| LocalOperands.push_back(Op); |
| return; |
| } |
| |
| // Must be forward reference, expand vector to accommodate. |
| if (LocalIndex >= LocalOperands.size()) { |
| if (LocalIndex > MaxRecordsInBlock) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Forward reference @" << Index << " too big. Have " |
| << CachedNumGlobalValueIDs << " globals and function contains " |
| << NumBytesDefiningFunction << " bytes"; |
| Fatal(StrBuf.str()); |
| // Recover by using index one beyond the maximal allowed. |
| LocalIndex = MaxRecordsInBlock; |
| } |
| Ice::Utils::reserveAndResize(LocalOperands, LocalIndex + 1); |
| } |
| |
| // If element not defined, set it. |
| Ice::Operand *OldOp = LocalOperands[LocalIndex]; |
| if (OldOp == nullptr) { |
| LocalOperands[LocalIndex] = Op; |
| return; |
| } |
| |
| // See if forward reference matches. |
| if (OldOp == Op) |
| return; |
| |
| // Error has occurred. |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Multiple definitions for index " << Index << ": " << *Op |
| << " and " << *OldOp; |
| Error(StrBuf.str()); |
| LocalOperands[LocalIndex] = Op; |
| } |
| |
| // Returns the relative operand (wrt to BaseIndex) referenced by the given |
| // value Index. |
| Ice::Operand *getRelativeOperand(NaClBcIndexSize_t Index, |
| NaClBcIndexSize_t BaseIndex) { |
| return getOperand(convertRelativeToAbsIndex(Index, BaseIndex)); |
| } |
| |
| // Returns the absolute index of the next value generating instruction. |
| NaClBcIndexSize_t getNextInstIndex() const { return NextLocalInstIndex; } |
| |
| // Generates type error message for binary operator Op operating on Type |
| // OpTy. |
| void reportInvalidBinaryOp(Ice::InstArithmetic::OpKind Op, Ice::Type OpTy); |
| |
| // Validates if integer logical Op, for type OpTy, is valid. Returns true if |
| // valid. Otherwise generates error message and returns false. |
| bool isValidIntegerLogicalOp(Ice::InstArithmetic::OpKind Op, Ice::Type OpTy) { |
| if (Ice::isIntegerType(OpTy)) |
| return true; |
| reportInvalidBinaryOp(Op, OpTy); |
| return false; |
| } |
| |
| // Validates if integer (or vector of integers) arithmetic Op, for type OpTy, |
| // is valid. Returns true if valid. Otherwise generates error message and |
| // returns false. |
| bool isValidIntegerArithOp(Ice::InstArithmetic::OpKind Op, Ice::Type OpTy) { |
| if (Ice::isIntegerArithmeticType(OpTy)) |
| return true; |
| reportInvalidBinaryOp(Op, OpTy); |
| return false; |
| } |
| |
| // Checks if floating arithmetic Op, for type OpTy, is valid. Returns true if |
| // valid. Otherwise generates an error message and returns false; |
| bool isValidFloatingArithOp(Ice::InstArithmetic::OpKind Op, Ice::Type OpTy) { |
| if (Ice::isFloatingType(OpTy)) |
| return true; |
| reportInvalidBinaryOp(Op, OpTy); |
| return false; |
| } |
| |
| // Checks if the type of operand Op is the valid pointer type, for the given |
| // InstructionName. Returns true if valid. Otherwise generates an error |
| // message and returns false. |
| bool isValidPointerType(Ice::Operand *Op, const char *InstructionName) { |
| Ice::Type PtrType = Ice::getPointerType(); |
| if (Op->getType() == PtrType) |
| return true; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << InstructionName << " address not " << PtrType |
| << ". Found: " << Op->getType(); |
| Error(StrBuf.str()); |
| return false; |
| } |
| |
| // Checks if loading/storing a value of type Ty is allowed. Returns true if |
| // Valid. Otherwise generates an error message and returns false. |
| bool isValidLoadStoreType(Ice::Type Ty, const char *InstructionName) { |
| if (isLoadStoreType(Ty)) |
| return true; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << InstructionName << " type not allowed: " << Ty << "*"; |
| Error(StrBuf.str()); |
| return false; |
| } |
| |
| // Checks if loading/storing a value of type Ty is allowed for the given |
| // Alignment. Otherwise generates an error message and returns false. |
| bool isValidLoadStoreAlignment(size_t Alignment, Ice::Type Ty, |
| const char *InstructionName) { |
| if (!isValidLoadStoreType(Ty, InstructionName)) |
| return false; |
| if (isAllowedAlignment(Alignment, Ty)) |
| return true; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << InstructionName << " " << Ty << "*: not allowed for alignment " |
| << Alignment; |
| Error(StrBuf.str()); |
| return false; |
| } |
| |
| // Defines if the given alignment is valid for the given type. Simplified |
| // version of PNaClABIProps::isAllowedAlignment, based on API's offered for |
| // Ice::Type. |
| bool isAllowedAlignment(size_t Alignment, Ice::Type Ty) const { |
| return Alignment == typeAlignInBytes(Ty) || |
| (Alignment == 1 && !isVectorType(Ty)); |
| } |
| |
| // Types of errors that can occur for insertelement and extractelement |
| // instructions. |
| enum VectorIndexCheckValue { |
| VectorIndexNotVector, |
| VectorIndexNotConstant, |
| VectorIndexNotInRange, |
| VectorIndexNotI32, |
| VectorIndexValid |
| }; |
| |
| void dumpVectorIndexCheckValue(raw_ostream &Stream, |
| VectorIndexCheckValue Value) const { |
| if (!Ice::BuildDefs::dump()) |
| return; |
| switch (Value) { |
| case VectorIndexNotVector: |
| Stream << "Vector index on non vector"; |
| break; |
| case VectorIndexNotConstant: |
| Stream << "Vector index not integer constant"; |
| break; |
| case VectorIndexNotInRange: |
| Stream << "Vector index not in range of vector"; |
| break; |
| case VectorIndexNotI32: |
| Stream << "Vector index not of type " << Ice::IceType_i32; |
| break; |
| case VectorIndexValid: |
| Stream << "Valid vector index"; |
| break; |
| } |
| } |
| |
| // Returns whether the given vector index (for insertelement and |
| // extractelement instructions) is valid. |
| VectorIndexCheckValue validateVectorIndex(const Ice::Operand *Vec, |
| const Ice::Operand *Index) const { |
| Ice::Type VecType = Vec->getType(); |
| if (!Ice::isVectorType(VecType)) |
| return VectorIndexNotVector; |
| const auto *C = dyn_cast<Ice::ConstantInteger32>(Index); |
| if (C == nullptr) |
| return VectorIndexNotConstant; |
| if (static_cast<size_t>(C->getValue()) >= typeNumElements(VecType)) |
| return VectorIndexNotInRange; |
| if (Index->getType() != Ice::IceType_i32) |
| return VectorIndexNotI32; |
| return VectorIndexValid; |
| } |
| |
| // Takes the PNaCl bitcode binary operator Opcode, and the opcode type Ty, |
| // and sets Op to the corresponding ICE binary opcode. Returns true if able |
| // to convert, false otherwise. |
| bool convertBinopOpcode(unsigned Opcode, Ice::Type Ty, |
| Ice::InstArithmetic::OpKind &Op) { |
| switch (Opcode) { |
| default: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Binary opcode " << Opcode << "not understood for type " << Ty; |
| Error(StrBuf.str()); |
| Op = Ice::InstArithmetic::Add; |
| return false; |
| } |
| case naclbitc::BINOP_ADD: |
| if (Ice::isIntegerType(Ty)) { |
| Op = Ice::InstArithmetic::Add; |
| return isValidIntegerArithOp(Op, Ty); |
| } else { |
| Op = Ice::InstArithmetic::Fadd; |
| return isValidFloatingArithOp(Op, Ty); |
| } |
| case naclbitc::BINOP_SUB: |
| if (Ice::isIntegerType(Ty)) { |
| Op = Ice::InstArithmetic::Sub; |
| return isValidIntegerArithOp(Op, Ty); |
| } else { |
| Op = Ice::InstArithmetic::Fsub; |
| return isValidFloatingArithOp(Op, Ty); |
| } |
| case naclbitc::BINOP_MUL: |
| if (Ice::isIntegerType(Ty)) { |
| Op = Ice::InstArithmetic::Mul; |
| return isValidIntegerArithOp(Op, Ty); |
| } else { |
| Op = Ice::InstArithmetic::Fmul; |
| return isValidFloatingArithOp(Op, Ty); |
| } |
| case naclbitc::BINOP_UDIV: |
| Op = Ice::InstArithmetic::Udiv; |
| return isValidIntegerArithOp(Op, Ty); |
| case naclbitc::BINOP_SDIV: |
| if (Ice::isIntegerType(Ty)) { |
| Op = Ice::InstArithmetic::Sdiv; |
| return isValidIntegerArithOp(Op, Ty); |
| } else { |
| Op = Ice::InstArithmetic::Fdiv; |
| return isValidFloatingArithOp(Op, Ty); |
| } |
| case naclbitc::BINOP_UREM: |
| Op = Ice::InstArithmetic::Urem; |
| return isValidIntegerArithOp(Op, Ty); |
| case naclbitc::BINOP_SREM: |
| if (Ice::isIntegerType(Ty)) { |
| Op = Ice::InstArithmetic::Srem; |
| return isValidIntegerArithOp(Op, Ty); |
| } else { |
| Op = Ice::InstArithmetic::Frem; |
| return isValidFloatingArithOp(Op, Ty); |
| } |
| case naclbitc::BINOP_SHL: |
| Op = Ice::InstArithmetic::Shl; |
| return isValidIntegerArithOp(Op, Ty); |
| case naclbitc::BINOP_LSHR: |
| Op = Ice::InstArithmetic::Lshr; |
| return isValidIntegerArithOp(Op, Ty); |
| case naclbitc::BINOP_ASHR: |
| Op = Ice::InstArithmetic::Ashr; |
| return isValidIntegerArithOp(Op, Ty); |
| case naclbitc::BINOP_AND: |
| Op = Ice::InstArithmetic::And; |
| return isValidIntegerLogicalOp(Op, Ty); |
| case naclbitc::BINOP_OR: |
| Op = Ice::InstArithmetic::Or; |
| return isValidIntegerLogicalOp(Op, Ty); |
| case naclbitc::BINOP_XOR: |
| Op = Ice::InstArithmetic::Xor; |
| return isValidIntegerLogicalOp(Op, Ty); |
| } |
| } |
| |
| /// Simplifies out vector types from Type1 and Type2, if both are vectors of |
| /// the same size. Returns true iff both are vectors of the same size, or are |
| /// both scalar types. |
| static bool simplifyOutCommonVectorType(Ice::Type &Type1, Ice::Type &Type2) { |
| bool IsType1Vector = isVectorType(Type1); |
| bool IsType2Vector = isVectorType(Type2); |
| if (IsType1Vector != IsType2Vector) |
| return false; |
| if (!IsType1Vector) |
| return true; |
| if (typeNumElements(Type1) != typeNumElements(Type2)) |
| return false; |
| Type1 = typeElementType(Type1); |
| Type2 = typeElementType(Type2); |
| return true; |
| } |
| |
| /// Returns true iff an integer truncation from SourceType to TargetType is |
| /// valid. |
| static bool isIntTruncCastValid(Ice::Type SourceType, Ice::Type TargetType) { |
| return Ice::isIntegerType(SourceType) && Ice::isIntegerType(TargetType) && |
| simplifyOutCommonVectorType(SourceType, TargetType) && |
| getScalarIntBitWidth(SourceType) > getScalarIntBitWidth(TargetType); |
| } |
| |
| /// Returns true iff a floating type truncation from SourceType to TargetType |
| /// is valid. |
| static bool isFloatTruncCastValid(Ice::Type SourceType, |
| Ice::Type TargetType) { |
| return simplifyOutCommonVectorType(SourceType, TargetType) && |
| SourceType == Ice::IceType_f64 && TargetType == Ice::IceType_f32; |
| } |
| |
| /// Returns true iff an integer extension from SourceType to TargetType is |
| /// valid. |
| static bool isIntExtCastValid(Ice::Type SourceType, Ice::Type TargetType) { |
| return isIntTruncCastValid(TargetType, SourceType); |
| } |
| |
| /// Returns true iff a floating type extension from SourceType to TargetType |
| /// is valid. |
| static bool isFloatExtCastValid(Ice::Type SourceType, Ice::Type TargetType) { |
| return isFloatTruncCastValid(TargetType, SourceType); |
| } |
| |
| /// Returns true iff a cast from floating type SourceType to integer type |
| /// TargetType is valid. |
| static bool isFloatToIntCastValid(Ice::Type SourceType, |
| Ice::Type TargetType) { |
| if (!(Ice::isFloatingType(SourceType) && Ice::isIntegerType(TargetType))) |
| return false; |
| bool IsSourceVector = isVectorType(SourceType); |
| bool IsTargetVector = isVectorType(TargetType); |
| if (IsSourceVector != IsTargetVector) |
| return false; |
| if (IsSourceVector) { |
| return typeNumElements(SourceType) == typeNumElements(TargetType); |
| } |
| return true; |
| } |
| |
| /// Returns true iff a cast from integer type SourceType to floating type |
| /// TargetType is valid. |
| static bool isIntToFloatCastValid(Ice::Type SourceType, |
| Ice::Type TargetType) { |
| return isFloatToIntCastValid(TargetType, SourceType); |
| } |
| |
| /// Returns the number of bits used to model type Ty when defining the bitcast |
| /// instruction. |
| static Ice::SizeT bitcastSizeInBits(Ice::Type Ty) { |
| if (Ice::isVectorType(Ty)) |
| return Ice::typeNumElements(Ty) * |
| bitcastSizeInBits(Ice::typeElementType(Ty)); |
| if (Ty == Ice::IceType_i1) |
| return 1; |
| return Ice::typeWidthInBytes(Ty) * CHAR_BIT; |
| } |
| |
| /// Returns true iff a bitcast from SourceType to TargetType is allowed. |
| static bool isBitcastValid(Ice::Type SourceType, Ice::Type TargetType) { |
| return bitcastSizeInBits(SourceType) == bitcastSizeInBits(TargetType); |
| } |
| |
| /// Returns true iff the NaCl bitcode Opcode is a valid cast opcode for |
| /// converting SourceType to TargetType. Updates CastKind to the corresponding |
| /// instruction cast opcode. Also generates an error message when this |
| /// function returns false. |
| bool convertCastOpToIceOp(uint64_t Opcode, Ice::Type SourceType, |
| Ice::Type TargetType, |
| Ice::InstCast::OpKind &CastKind) { |
| bool Result; |
| switch (Opcode) { |
| default: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Cast opcode " << Opcode << " not understood.\n"; |
| Error(StrBuf.str()); |
| CastKind = Ice::InstCast::Bitcast; |
| return false; |
| } |
| case naclbitc::CAST_TRUNC: |
| CastKind = Ice::InstCast::Trunc; |
| Result = isIntTruncCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_ZEXT: |
| CastKind = Ice::InstCast::Zext; |
| Result = isIntExtCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_SEXT: |
| CastKind = Ice::InstCast::Sext; |
| Result = isIntExtCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_FPTOUI: |
| CastKind = Ice::InstCast::Fptoui; |
| Result = isFloatToIntCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_FPTOSI: |
| CastKind = Ice::InstCast::Fptosi; |
| Result = isFloatToIntCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_UITOFP: |
| CastKind = Ice::InstCast::Uitofp; |
| Result = isIntToFloatCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_SITOFP: |
| CastKind = Ice::InstCast::Sitofp; |
| Result = isIntToFloatCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_FPTRUNC: |
| CastKind = Ice::InstCast::Fptrunc; |
| Result = isFloatTruncCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_FPEXT: |
| CastKind = Ice::InstCast::Fpext; |
| Result = isFloatExtCastValid(SourceType, TargetType); |
| break; |
| case naclbitc::CAST_BITCAST: |
| CastKind = Ice::InstCast::Bitcast; |
| Result = isBitcastValid(SourceType, TargetType); |
| break; |
| } |
| if (!Result) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Illegal cast: " << Ice::InstCast::getCastName(CastKind) << " " |
| << SourceType << " to " << TargetType; |
| Error(StrBuf.str()); |
| } |
| return Result; |
| } |
| |
| // Converts PNaCl bitcode Icmp operator to corresponding ICE op. Returns true |
| // if able to convert, false otherwise. |
| bool convertNaClBitcICmpOpToIce(uint64_t Op, |
| Ice::InstIcmp::ICond &Cond) const { |
| switch (Op) { |
| case naclbitc::ICMP_EQ: |
| Cond = Ice::InstIcmp::Eq; |
| return true; |
| case naclbitc::ICMP_NE: |
| Cond = Ice::InstIcmp::Ne; |
| return true; |
| case naclbitc::ICMP_UGT: |
| Cond = Ice::InstIcmp::Ugt; |
| return true; |
| case naclbitc::ICMP_UGE: |
| Cond = Ice::InstIcmp::Uge; |
| return true; |
| case naclbitc::ICMP_ULT: |
| Cond = Ice::InstIcmp::Ult; |
| return true; |
| case naclbitc::ICMP_ULE: |
| Cond = Ice::InstIcmp::Ule; |
| return true; |
| case naclbitc::ICMP_SGT: |
| Cond = Ice::InstIcmp::Sgt; |
| return true; |
| case naclbitc::ICMP_SGE: |
| Cond = Ice::InstIcmp::Sge; |
| return true; |
| case naclbitc::ICMP_SLT: |
| Cond = Ice::InstIcmp::Slt; |
| return true; |
| case naclbitc::ICMP_SLE: |
| Cond = Ice::InstIcmp::Sle; |
| return true; |
| default: |
| // Make sure Cond is always initialized. |
| Cond = static_cast<Ice::InstIcmp::ICond>(0); |
| return false; |
| } |
| } |
| |
| // Converts PNaCl bitcode Fcmp operator to corresponding ICE op. Returns true |
| // if able to convert, false otherwise. |
| bool convertNaClBitcFCompOpToIce(uint64_t Op, |
| Ice::InstFcmp::FCond &Cond) const { |
| switch (Op) { |
| case naclbitc::FCMP_FALSE: |
| Cond = Ice::InstFcmp::False; |
| return true; |
| case naclbitc::FCMP_OEQ: |
| Cond = Ice::InstFcmp::Oeq; |
| return true; |
| case naclbitc::FCMP_OGT: |
| Cond = Ice::InstFcmp::Ogt; |
| return true; |
| case naclbitc::FCMP_OGE: |
| Cond = Ice::InstFcmp::Oge; |
| return true; |
| case naclbitc::FCMP_OLT: |
| Cond = Ice::InstFcmp::Olt; |
| return true; |
| case naclbitc::FCMP_OLE: |
| Cond = Ice::InstFcmp::Ole; |
| return true; |
| case naclbitc::FCMP_ONE: |
| Cond = Ice::InstFcmp::One; |
| return true; |
| case naclbitc::FCMP_ORD: |
| Cond = Ice::InstFcmp::Ord; |
| return true; |
| case naclbitc::FCMP_UNO: |
| Cond = Ice::InstFcmp::Uno; |
| return true; |
| case naclbitc::FCMP_UEQ: |
| Cond = Ice::InstFcmp::Ueq; |
| return true; |
| case naclbitc::FCMP_UGT: |
| Cond = Ice::InstFcmp::Ugt; |
| return true; |
| case naclbitc::FCMP_UGE: |
| Cond = Ice::InstFcmp::Uge; |
| return true; |
| case naclbitc::FCMP_ULT: |
| Cond = Ice::InstFcmp::Ult; |
| return true; |
| case naclbitc::FCMP_ULE: |
| Cond = Ice::InstFcmp::Ule; |
| return true; |
| case naclbitc::FCMP_UNE: |
| Cond = Ice::InstFcmp::Une; |
| return true; |
| case naclbitc::FCMP_TRUE: |
| Cond = Ice::InstFcmp::True; |
| return true; |
| default: |
| // Make sure Cond is always initialized. |
| Cond = static_cast<Ice::InstFcmp::FCond>(0); |
| return false; |
| } |
| } |
| |
| // Creates an error instruction, generating a value of type Ty, and adds a |
| // placeholder so that instruction indices line up. Some instructions, such |
| // as a call, will not generate a value if the return type is void. In such |
| // cases, a placeholder value for the badly formed instruction is not needed. |
| // Hence, if Ty is void, an error instruction is not appended. |
| void appendErrorInstruction(Ice::Type Ty) { |
| // Note: we don't worry about downstream translation errors because the |
| // function will not be translated if any errors occur. |
| if (Ty == Ice::IceType_void) |
| return; |
| Ice::Variable *Var = getNextInstVar(Ty); |
| CurrentNode->appendInst(Ice::InstAssign::create(Func.get(), Var, Var)); |
| } |
| |
| Ice::Operand *reportGetOperandUndefined(NaClBcIndexSize_t Index) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Value index " << Index << " not defined!"; |
| Error(StrBuf.str()); |
| // Recover and return some value. |
| if (!LocalOperands.empty()) |
| return LocalOperands.front(); |
| return Context->getGlobalConstantByID(0); |
| } |
| |
| void verifyCallArgTypeMatches(Ice::FunctionDeclaration *Fcn, Ice::SizeT Index, |
| Ice::Type ArgType, Ice::Type ParamType) { |
| if (ArgType != ParamType) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Argument " << (Index + 1) << " of " << printName(Fcn) |
| << " expects " << ParamType << ". Found: " << ArgType; |
| Error(StrBuf.str()); |
| } |
| } |
| |
| const std::string printName(Ice::FunctionDeclaration *Fcn) { |
| if (Fcn) |
| return Fcn->getName().toString(); |
| return "function"; |
| } |
| }; |
| |
| void FunctionParser::ExitBlock() { |
| // Check if the last instruction in the function was terminating. |
| if (!InstIsTerminating) { |
| Error("Last instruction in function not terminator"); |
| // Recover by inserting an unreachable instruction. |
| CurrentNode->appendInst(Ice::InstUnreachable::create(Func.get())); |
| } |
| ++CurrentBbIndex; |
| if (CurrentBbIndex != DeclaredNumberBbs) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function declared " << DeclaredNumberBbs |
| << " basic blocks, but defined " << CurrentBbIndex << "."; |
| Error(StrBuf.str()); |
| } |
| // Before translating, check for blocks without instructions, and insert |
| // unreachable. This shouldn't happen, but be safe. |
| size_t Index = 0; |
| for (Ice::CfgNode *Node : Func->getNodes()) { |
| if (Node->getInsts().empty()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Basic block " << Index << " contains no instructions"; |
| Error(StrBuf.str()); |
| Node->appendInst(Ice::InstUnreachable::create(Func.get())); |
| } |
| ++Index; |
| } |
| Func->computeInOutEdges(); |
| } |
| |
| void FunctionParser::reportInvalidBinaryOp(Ice::InstArithmetic::OpKind Op, |
| Ice::Type OpTy) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Invalid operator type for " << Ice::InstArithmetic::getOpName(Op) |
| << ". Found " << OpTy; |
| Error(StrBuf.str()); |
| } |
| |
| void FunctionParser::ProcessRecord() { |
| // Note: To better separate parse/IR generation times, when IR generation is |
| // disabled we do the following: |
| // 1) Delay exiting until after we extract operands. |
| // 2) return before we access operands, since all operands will be a nullptr. |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| if (InstIsTerminating) { |
| InstIsTerminating = false; |
| ++CurrentBbIndex; |
| CurrentNode = getBasicBlock(CurrentBbIndex); |
| } |
| // The base index for relative indexing. |
| NaClBcIndexSize_t BaseIndex = getNextInstIndex(); |
| switch (Record.GetCode()) { |
| case naclbitc::FUNC_CODE_DECLAREBLOCKS: { |
| // DECLAREBLOCKS: [n] |
| if (!isValidRecordSize(1, "count")) |
| return; |
| if (DeclaredNumberBbs > 0) { |
| Error("Duplicate function block count record"); |
| return; |
| } |
| |
| // Check for bad large sizes, since they can make ridiculous memory |
| // requests and hang the user for large amounts of time. |
| uint64_t NumBbs = Values[0]; |
| if (NumBbs > MaxRecordsInBlock) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function defines " << NumBbs |
| << " basic blocks, which is too big for a function containing " |
| << NumBytesDefiningFunction << " bytes"; |
| Error(StrBuf.str()); |
| NumBbs = MaxRecordsInBlock; |
| } |
| |
| if (NumBbs == 0) { |
| Error("Functions must contain at least one basic block."); |
| NumBbs = 1; |
| } |
| |
| DeclaredNumberBbs = NumBbs; |
| // Install the basic blocks, skipping bb0 which was created in the |
| // constructor. |
| for (size_t i = 1; i < NumBbs; ++i) |
| installNextBasicBlock(); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_BINOP: { |
| // Note: Old bitcode files may have an additional 'flags' operand, which is |
| // ignored. |
| |
| // BINOP: [opval, opval, opcode, [flags]] |
| |
| if (!isValidRecordSizeInRange(3, 4, "binop")) |
| return; |
| Ice::Operand *Op1 = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Op2 = getRelativeOperand(Values[1], BaseIndex); |
| Ice::Type Type1 = Op1->getType(); |
| Ice::Type Type2 = Op2->getType(); |
| if (Type1 != Type2) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Binop argument types differ: " << Type1 << " and " << Type2; |
| Error(StrBuf.str()); |
| appendErrorInstruction(Type1); |
| return; |
| } |
| |
| Ice::InstArithmetic::OpKind Opcode; |
| if (!convertBinopOpcode(Values[2], Type1, Opcode)) { |
| appendErrorInstruction(Type1); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstArithmetic::create( |
| Func.get(), Opcode, getNextInstVar(Type1), Op1, Op2)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_CAST: { |
| // CAST: [opval, destty, castopc] |
| if (!isValidRecordSize(3, "cast")) |
| return; |
| Ice::Operand *Src = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Type CastType = Context->getSimpleTypeByID(Values[1]); |
| Ice::InstCast::OpKind CastKind; |
| if (!convertCastOpToIceOp(Values[2], Src->getType(), CastType, CastKind)) { |
| appendErrorInstruction(CastType); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstCast::create( |
| Func.get(), CastKind, getNextInstVar(CastType), Src)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_VSELECT: { |
| // VSELECT: [opval, opval, pred] |
| if (!isValidRecordSize(3, "select")) |
| return; |
| Ice::Operand *ThenVal = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *ElseVal = getRelativeOperand(Values[1], BaseIndex); |
| Ice::Operand *CondVal = getRelativeOperand(Values[2], BaseIndex); |
| Ice::Type ThenType = ThenVal->getType(); |
| Ice::Type ElseType = ElseVal->getType(); |
| if (ThenType != ElseType) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Select operands not same type. Found " << ThenType << " and " |
| << ElseType; |
| Error(StrBuf.str()); |
| appendErrorInstruction(ThenType); |
| return; |
| } |
| Ice::Type CondType = CondVal->getType(); |
| if (isVectorType(CondType)) { |
| if (!isVectorType(ThenType) || |
| typeElementType(CondType) != Ice::IceType_i1 || |
| typeNumElements(ThenType) != typeNumElements(CondType)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Select condition type " << CondType |
| << " not allowed for values of type " << ThenType; |
| Error(StrBuf.str()); |
| appendErrorInstruction(ThenType); |
| return; |
| } |
| } else if (CondVal->getType() != Ice::IceType_i1) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Select condition " << CondVal |
| << " not type i1. Found: " << CondVal->getType(); |
| Error(StrBuf.str()); |
| appendErrorInstruction(ThenType); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstSelect::create( |
| Func.get(), getNextInstVar(ThenType), CondVal, ThenVal, ElseVal)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_EXTRACTELT: { |
| // EXTRACTELT: [opval, opval] |
| if (!isValidRecordSize(2, "extract element")) |
| return; |
| Ice::Operand *Vec = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Index = getRelativeOperand(Values[1], BaseIndex); |
| Ice::Type VecType = Vec->getType(); |
| VectorIndexCheckValue IndexCheckValue = validateVectorIndex(Vec, Index); |
| if (IndexCheckValue != VectorIndexValid) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| dumpVectorIndexCheckValue(StrBuf, IndexCheckValue); |
| StrBuf << ": extractelement " << VecType << " " << *Vec << ", " |
| << Index->getType() << " " << *Index; |
| Error(StrBuf.str()); |
| appendErrorInstruction(VecType); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstExtractElement::create( |
| Func.get(), getNextInstVar(typeElementType(VecType)), Vec, Index)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_INSERTELT: { |
| // INSERTELT: [opval, opval, opval] |
| if (!isValidRecordSize(3, "insert element")) |
| return; |
| Ice::Operand *Vec = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Elt = getRelativeOperand(Values[1], BaseIndex); |
| Ice::Operand *Index = getRelativeOperand(Values[2], BaseIndex); |
| Ice::Type VecType = Vec->getType(); |
| VectorIndexCheckValue IndexCheckValue = validateVectorIndex(Vec, Index); |
| if (IndexCheckValue != VectorIndexValid) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| dumpVectorIndexCheckValue(StrBuf, IndexCheckValue); |
| StrBuf << ": insertelement " << VecType << " " << *Vec << ", " |
| << Elt->getType() << " " << *Elt << ", " << Index->getType() << " " |
| << *Index; |
| Error(StrBuf.str()); |
| appendErrorInstruction(Elt->getType()); |
| return; |
| } |
| if (Ice::typeElementType(VecType) != Elt->getType()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Insertelement: Element type " |
| << Ice::typeString(Elt->getType()) << " doesn't match vector type " |
| << Ice::typeString(VecType); |
| Error(StrBuf.str()); |
| appendErrorInstruction(Elt->getType()); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstInsertElement::create( |
| Func.get(), getNextInstVar(VecType), Vec, Elt, Index)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_CMP2: { |
| // CMP2: [opval, opval, pred] |
| if (!isValidRecordSize(3, "compare")) |
| return; |
| Ice::Operand *Op1 = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Op2 = getRelativeOperand(Values[1], BaseIndex); |
| Ice::Type Op1Type = Op1->getType(); |
| Ice::Type Op2Type = Op2->getType(); |
| Ice::Type DestType = getCompareResultType(Op1Type); |
| if (Op1Type != Op2Type) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Compare argument types differ: " << Op1Type << " and " |
| << Op2Type; |
| Error(StrBuf.str()); |
| appendErrorInstruction(DestType); |
| Op2 = Op1; |
| } |
| if (DestType == Ice::IceType_void) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Compare not defined for type " << Op1Type; |
| Error(StrBuf.str()); |
| return; |
| } |
| Ice::Variable *Dest = getNextInstVar(DestType); |
| if (isIntegerType(Op1Type)) { |
| Ice::InstIcmp::ICond Cond; |
| if (!convertNaClBitcICmpOpToIce(Values[2], Cond)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Compare record contains unknown integer predicate index: " |
| << Values[2]; |
| Error(StrBuf.str()); |
| appendErrorInstruction(DestType); |
| } |
| CurrentNode->appendInst( |
| Ice::InstIcmp::create(Func.get(), Cond, Dest, Op1, Op2)); |
| } else if (isFloatingType(Op1Type)) { |
| Ice::InstFcmp::FCond Cond; |
| if (!convertNaClBitcFCompOpToIce(Values[2], Cond)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Compare record contains unknown float predicate index: " |
| << Values[2]; |
| Error(StrBuf.str()); |
| appendErrorInstruction(DestType); |
| } |
| CurrentNode->appendInst( |
| Ice::InstFcmp::create(Func.get(), Cond, Dest, Op1, Op2)); |
| } else { |
| // Not sure this can happen, but be safe. |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Compare on type not understood: " << Op1Type; |
| Error(StrBuf.str()); |
| appendErrorInstruction(DestType); |
| return; |
| } |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_RET: { |
| // RET: [opval?] |
| InstIsTerminating = true; |
| if (!isValidRecordSizeInRange(0, 1, "return")) |
| return; |
| if (Values.empty()) { |
| CurrentNode->appendInst(Ice::InstRet::create(Func.get())); |
| } else { |
| Ice::Operand *RetVal = getRelativeOperand(Values[0], BaseIndex); |
| CurrentNode->appendInst(Ice::InstRet::create(Func.get(), RetVal)); |
| } |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_BR: { |
| InstIsTerminating = true; |
| if (Values.size() == 1) { |
| // BR: [bb#] |
| Ice::CfgNode *Block = getBranchBasicBlock(Values[0]); |
| if (Block == nullptr) |
| return; |
| CurrentNode->appendInst(Ice::InstBr::create(Func.get(), Block)); |
| } else { |
| // BR: [bb#, bb#, opval] |
| if (!isValidRecordSize(3, "branch")) |
| return; |
| Ice::Operand *Cond = getRelativeOperand(Values[2], BaseIndex); |
| if (Cond->getType() != Ice::IceType_i1) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Branch condition " << *Cond |
| << " not i1. Found: " << Cond->getType(); |
| Error(StrBuf.str()); |
| return; |
| } |
| Ice::CfgNode *ThenBlock = getBranchBasicBlock(Values[0]); |
| Ice::CfgNode *ElseBlock = getBranchBasicBlock(Values[1]); |
| if (ThenBlock == nullptr || ElseBlock == nullptr) |
| return; |
| CurrentNode->appendInst( |
| Ice::InstBr::create(Func.get(), Cond, ThenBlock, ElseBlock)); |
| } |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_SWITCH: { |
| // SWITCH: [Condty, Cond, BbIndex, NumCases Case ...] |
| // where Case = [1, 1, Value, BbIndex]. |
| // |
| // Note: Unlike most instructions, we don't infer the type of Cond, but |
| // provide it as a separate field. There are also unnecessary data fields |
| // (i.e. constants 1). These were not cleaned up in PNaCl bitcode because |
| // the bitcode format was already frozen when the problem was noticed. |
| InstIsTerminating = true; |
| if (!isValidRecordSizeAtLeast(4, "switch")) |
| return; |
| |
| Ice::Type CondTy = Context->getSimpleTypeByID(Values[0]); |
| if (!Ice::isScalarIntegerType(CondTy)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Case condition must be non-wide integer. Found: " << CondTy; |
| Error(StrBuf.str()); |
| return; |
| } |
| Ice::SizeT BitWidth = Ice::getScalarIntBitWidth(CondTy); |
| Ice::Operand *Cond = getRelativeOperand(Values[1], BaseIndex); |
| |
| if (CondTy != Cond->getType()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Case condition expects type " << CondTy |
| << ". Found: " << Cond->getType(); |
| Error(StrBuf.str()); |
| return; |
| } |
| Ice::CfgNode *DefaultLabel = getBranchBasicBlock(Values[2]); |
| if (DefaultLabel == nullptr) |
| return; |
| uint64_t NumCasesRaw = Values[3]; |
| if (NumCasesRaw > std::numeric_limits<uint32_t>::max()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Too many cases specified in switch: " << NumCasesRaw; |
| Error(StrBuf.str()); |
| NumCasesRaw = std::numeric_limits<uint32_t>::max(); |
| } |
| uint32_t NumCases = NumCasesRaw; |
| |
| // Now recognize each of the cases. |
| if (!isValidRecordSize(4 + NumCases * 4, "switch")) |
| return; |
| std::unique_ptr<Ice::InstSwitch> Switch( |
| Ice::InstSwitch::create(Func.get(), NumCases, Cond, DefaultLabel)); |
| unsigned ValCaseIndex = 4; // index to beginning of case entry. |
| for (uint32_t CaseIndex = 0; CaseIndex < NumCases; |
| ++CaseIndex, ValCaseIndex += 4) { |
| if (Values[ValCaseIndex] != 1 || Values[ValCaseIndex + 1] != 1) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Sequence [1, 1, value, label] expected for case entry " |
| << "in switch record. (at index" << ValCaseIndex << ")"; |
| Error(StrBuf.str()); |
| return; |
| } |
| BitcodeInt Value(BitWidth, |
| NaClDecodeSignRotatedValue(Values[ValCaseIndex + 2])); |
| Ice::CfgNode *Label = getBranchBasicBlock(Values[ValCaseIndex + 3]); |
| if (Label == nullptr) |
| return; |
| Switch->addBranch(CaseIndex, Value.getSExtValue(), Label); |
| } |
| CurrentNode->appendInst(Switch.release()); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_UNREACHABLE: { |
| // UNREACHABLE: [] |
| InstIsTerminating = true; |
| if (!isValidRecordSize(0, "unreachable")) |
| return; |
| CurrentNode->appendInst(Ice::InstUnreachable::create(Func.get())); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_PHI: { |
| // PHI: [ty, val1, bb1, ..., valN, bbN] for n >= 2. |
| if (!isValidRecordSizeAtLeast(3, "phi")) |
| return; |
| Ice::Type Ty = Context->getSimpleTypeByID(Values[0]); |
| if ((Values.size() & 0x1) == 0) { |
| // Not an odd number of values. |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "function block phi record size not valid: " << Values.size(); |
| Error(StrBuf.str()); |
| appendErrorInstruction(Ty); |
| return; |
| } |
| if (Ty == Ice::IceType_void) { |
| Error("Phi record using type void not allowed"); |
| return; |
| } |
| Ice::Variable *Dest = getNextInstVar(Ty); |
| Ice::InstPhi *Phi = |
| Ice::InstPhi::create(Func.get(), Values.size() >> 1, Dest); |
| for (size_t i = 1; i < Values.size(); i += 2) { |
| Ice::Operand *Op = |
| getRelativeOperand(NaClDecodeSignRotatedValue(Values[i]), BaseIndex); |
| if (Op->getType() != Ty) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Value " << *Op << " not type " << Ty |
| << " in phi instruction. Found: " << Op->getType(); |
| Error(StrBuf.str()); |
| appendErrorInstruction(Ty); |
| return; |
| } |
| Phi->addArgument(Op, getBasicBlock(Values[i + 1])); |
| } |
| CurrentNode->appendInst(Phi); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_ALLOCA: { |
| // ALLOCA: [Size, align] |
| if (!isValidRecordSize(2, "alloca")) |
| return; |
| Ice::Operand *ByteCount = getRelativeOperand(Values[0], BaseIndex); |
| uint32_t Alignment = Context->extractAlignment(this, "Alloca", Values[1]); |
| Ice::Type PtrTy = Ice::getPointerType(); |
| if (ByteCount->getType() != Ice::IceType_i32) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Alloca on non-i32 value. Found: " << *ByteCount; |
| Error(StrBuf.str()); |
| appendErrorInstruction(PtrTy); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstAlloca::create( |
| Func.get(), getNextInstVar(PtrTy), ByteCount, Alignment)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_LOAD: { |
| // LOAD: [address, align, ty] |
| if (!isValidRecordSize(3, "load")) |
| return; |
| Ice::Operand *Address = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Type Ty = Context->getSimpleTypeByID(Values[2]); |
| uint32_t Alignment = Context->extractAlignment(this, "Load", Values[1]); |
| if (!isValidPointerType(Address, "Load")) { |
| appendErrorInstruction(Ty); |
| return; |
| } |
| if (!isValidLoadStoreAlignment(Alignment, Ty, "Load")) { |
| appendErrorInstruction(Ty); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstLoad::create( |
| Func.get(), getNextInstVar(Ty), Address, Alignment)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_STORE: { |
| // STORE: [address, value, align] |
| if (!isValidRecordSize(3, "store")) |
| return; |
| Ice::Operand *Address = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Value = getRelativeOperand(Values[1], BaseIndex); |
| uint32_t Alignment = Context->extractAlignment(this, "Store", Values[2]); |
| if (!isValidPointerType(Address, "Store")) |
| return; |
| if (!isValidLoadStoreAlignment(Alignment, Value->getType(), "Store")) |
| return; |
| CurrentNode->appendInst( |
| Ice::InstStore::create(Func.get(), Value, Address, Alignment)); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_CALL: |
| case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: { |
| // CALL: [cc, fnid, arg0, arg1...] |
| // CALL_INDIRECT: [cc, fn, returnty, args...] |
| // |
| // Note: The difference between CALL and CALL_INDIRECT is that CALL has a |
| // reference to an explicit function declaration, while the CALL_INDIRECT |
| // is just an address. For CALL, we can infer the return type by looking up |
| // the type signature associated with the function declaration. For |
| // CALL_INDIRECT we can only infer the type signature via argument types, |
| // and the corresponding return type stored in CALL_INDIRECT record. |
| Ice::SizeT ParamsStartIndex = 2; |
| if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { |
| if (!isValidRecordSizeAtLeast(2, "call")) |
| return; |
| } else { |
| if (!isValidRecordSizeAtLeast(3, "call indirect")) |
| return; |
| ParamsStartIndex = 3; |
| } |
| |
| uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex); |
| Ice::Operand *Callee = getOperand(CalleeIndex); |
| |
| // Pull out signature/return type of call (if possible). |
| Ice::FunctionDeclaration *Fcn = nullptr; |
| const Ice::FuncSigType *Signature = nullptr; |
| Ice::Type ReturnType = Ice::IceType_void; |
| const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = nullptr; |
| if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { |
| Fcn = Context->getFunctionByID(CalleeIndex); |
| Signature = &Fcn->getSignature(); |
| ReturnType = Signature->getReturnType(); |
| Ice::SizeT NumParams = Values.size() - ParamsStartIndex; |
| if (NumParams != Signature->getNumArgs()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Call to " << printName(Fcn) << " has " << NumParams |
| << " parameters. Signature expects: " << Signature->getNumArgs(); |
| Error(StrBuf.str()); |
| if (ReturnType != Ice::IceType_void) |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| |
| // Check if this direct call is to an Intrinsic (starts with "llvm.") |
| IntrinsicInfo = Fcn->getIntrinsicInfo(getTranslator().getContext()); |
| if (IntrinsicInfo && IntrinsicInfo->getNumArgs() != NumParams) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Call to " << printName(Fcn) << " has " << NumParams |
| << " parameters. Intrinsic expects: " << Signature->getNumArgs(); |
| Error(StrBuf.str()); |
| if (ReturnType != Ice::IceType_void) |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| } else { // Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL_INDIRECT |
| // There is no signature. Assume defined by parameter types. |
| ReturnType = Context->getSimpleTypeByID(Values[2]); |
| if (Callee != nullptr) |
| isValidPointerType(Callee, "Call indirect"); |
| } |
| |
| if (Callee == nullptr) |
| return; |
| |
| // Extract out the the call parameters. |
| SmallVector<Ice::Operand *, 8> Params; |
| for (Ice::SizeT Index = ParamsStartIndex; Index < Values.size(); ++Index) { |
| Ice::Operand *Op = getRelativeOperand(Values[Index], BaseIndex); |
| if (Op == nullptr) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Parameter " << (Index - ParamsStartIndex + 1) << " of " |
| << printName(Fcn) << " is not defined"; |
| Error(StrBuf.str()); |
| if (ReturnType != Ice::IceType_void) |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| Params.push_back(Op); |
| } |
| |
| // Check return type. |
| if (IntrinsicInfo == nullptr && !isCallReturnType(ReturnType)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Return type of " << printName(Fcn) |
| << " is invalid: " << ReturnType; |
| Error(StrBuf.str()); |
| ReturnType = Ice::IceType_i32; |
| } |
| |
| // Type check call parameters. |
| for (Ice::SizeT Index = 0; Index < Params.size(); ++Index) { |
| Ice::Operand *Op = Params[Index]; |
| Ice::Type OpType = Op->getType(); |
| if (Signature) |
| verifyCallArgTypeMatches(Fcn, Index, OpType, |
| Signature->getArgType(Index)); |
| else if (!isCallParameterType(OpType)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Argument " << *Op << " of " << printName(Fcn) |
| << " has invalid type: " << Op->getType(); |
| Error(StrBuf.str()); |
| appendErrorInstruction(ReturnType); |
| return; |
| } |
| } |
| |
| // Extract call information. |
| uint64_t CCInfo = Values[0]; |
| CallingConv::ID CallingConv; |
| if (!naclbitc::DecodeCallingConv(CCInfo >> 1, CallingConv)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function call calling convention value " << (CCInfo >> 1) |
| << " not understood."; |
| Error(StrBuf.str()); |
| appendErrorInstruction(ReturnType); |
| return; |
| } |
| const bool IsTailCall = (CCInfo & 1); |
| |
| // Create the call instruction. |
| Ice::Variable *Dest = (ReturnType == Ice::IceType_void) |
| ? nullptr |
| : getNextInstVar(ReturnType); |
| std::unique_ptr<Ice::InstCall> Instr; |
| if (IntrinsicInfo) { |
| Instr.reset(Ice::InstIntrinsicCall::create( |
| Func.get(), Params.size(), Dest, Callee, IntrinsicInfo->Info)); |
| } else { |
| Instr.reset(Ice::InstCall::create(Func.get(), Params.size(), Dest, Callee, |
| IsTailCall)); |
| } |
| for (Ice::Operand *Param : Params) |
| Instr->addArg(Param); |
| CurrentNode->appendInst(Instr.release()); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: { |
| // FORWARDTYPEREF: [opval, ty] |
| if (!isValidRecordSize(2, "forward type ref")) |
| return; |
| Ice::Type OpType = Context->getSimpleTypeByID(Values[1]); |
| setOperand(Values[0], createInstVar(OpType)); |
| return; |
| } |
| default: |
| // Generate error message! |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| } |
| |
| /// Parses constants within a function block. |
| class ConstantsParser final : public BlockParserBaseClass { |
| ConstantsParser() = delete; |
| ConstantsParser(const ConstantsParser &) = delete; |
| ConstantsParser &operator=(const ConstantsParser &) = delete; |
| |
| public: |
| ConstantsParser(unsigned BlockID, FunctionParser *FuncParser) |
| : BlockParserBaseClass(BlockID, FuncParser), |
| Timer(Ice::TimerStack::TT_parseConstants, getTranslator().getContext()), |
| FuncParser(FuncParser) {} |
| |
| ~ConstantsParser() override = default; |
| |
| const char *getBlockName() const override { return "constants"; } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // The parser of the function block this constants block appears in. |
| FunctionParser *FuncParser; |
| // The type to use for succeeding constants. |
| Ice::Type NextConstantType = Ice::IceType_void; |
| |
| void ProcessRecord() override; |
| |
| Ice::GlobalContext *getContext() { return getTranslator().getContext(); } |
| |
| // Returns true if the type to use for succeeding constants is defined. If |
| // false, also generates an error message. |
| bool isValidNextConstantType() { |
| if (NextConstantType != Ice::IceType_void) |
| return true; |
| Error("Constant record not preceded by set type record"); |
| return false; |
| } |
| }; |
| |
| void ConstantsParser::ProcessRecord() { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| switch (Record.GetCode()) { |
| case naclbitc::CST_CODE_SETTYPE: { |
| // SETTYPE: [typeid] |
| if (!isValidRecordSize(1, "set type")) |
| return; |
| NextConstantType = Context->getSimpleTypeByID(Values[0]); |
| if (NextConstantType == Ice::IceType_void) |
| Error("constants block set type not allowed for void type"); |
| return; |
| } |
| case naclbitc::CST_CODE_UNDEF: { |
| // UNDEF |
| if (!isValidRecordSize(0, "undef")) |
| return; |
| if (!isValidNextConstantType()) |
| return; |
| FuncParser->setNextConstantID( |
| getContext()->getConstantUndef(NextConstantType)); |
| return; |
| } |
| case naclbitc::CST_CODE_INTEGER: { |
| // INTEGER: [intval] |
| if (!isValidRecordSize(1, "integer")) |
| return; |
| if (!isValidNextConstantType()) |
| return; |
| if (Ice::isScalarIntegerType(NextConstantType)) { |
| BitcodeInt Value(Ice::getScalarIntBitWidth(NextConstantType), |
| NaClDecodeSignRotatedValue(Values[0])); |
| if (Ice::Constant *C = getContext()->getConstantInt( |
| NextConstantType, Value.getSExtValue())) { |
| FuncParser->setNextConstantID(C); |
| return; |
| } |
| } |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "constant block integer record for non-integer type " |
| << NextConstantType; |
| Error(StrBuf.str()); |
| return; |
| } |
| case naclbitc::CST_CODE_FLOAT: { |
| // FLOAT: [fpval] |
| if (!isValidRecordSize(1, "float")) |
| return; |
| if (!isValidNextConstantType()) |
| return; |
| switch (NextConstantType) { |
| case Ice::IceType_f32: { |
| const BitcodeInt Value(32, static_cast<uint32_t>(Values[0])); |
| float FpValue = Value.convertToFp<int32_t, float>(); |
| FuncParser->setNextConstantID(getContext()->getConstantFloat(FpValue)); |
| return; |
| } |
| case Ice::IceType_f64: { |
| const BitcodeInt Value(64, Values[0]); |
| double FpValue = Value.convertToFp<uint64_t, double>(); |
| FuncParser->setNextConstantID(getContext()->getConstantDouble(FpValue)); |
| return; |
| } |
| default: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "constant block float record for non-floating type " |
| << NextConstantType; |
| Error(StrBuf.str()); |
| return; |
| } |
| } |
| } |
| default: |
| // Generate error message! |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| } |
| |
| // Parses valuesymtab blocks appearing in a function block. |
| class FunctionValuesymtabParser final : public ValuesymtabParser { |
| FunctionValuesymtabParser() = delete; |
| FunctionValuesymtabParser(const FunctionValuesymtabParser &) = delete; |
| void operator=(const FunctionValuesymtabParser &) = delete; |
| |
| public: |
| FunctionValuesymtabParser(unsigned BlockID, FunctionParser *EnclosingParser) |
| : ValuesymtabParser(BlockID, EnclosingParser), |
| Timer(Ice::TimerStack::TT_parseFunctionValuesymtabs, |
| getTranslator().getContext()) {} |
| |
| private: |
| Ice::TimerMarker Timer; |
| // Returns the enclosing function parser. |
| FunctionParser *getFunctionParser() const { |
| return reinterpret_cast<FunctionParser *>(GetEnclosingParser()); |
| } |
| |
| const char *getTableKind() const override { return "Function"; } |
| |
| void setValueName(NaClBcIndexSize_t Index, StringType &Name) override; |
| void setBbName(NaClBcIndexSize_t Index, StringType &Name) override; |
| |
| // Reports that the assignment of Name to the value associated with index is |
| // not possible, for the given Context. |
| void reportUnableToAssign(const char *Context, NaClBcIndexSize_t Index, |
| StringType &Name) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function-local " << Context << " name '" << Name |
| << "' can't be associated with index " << Index; |
| Error(StrBuf.str()); |
| } |
| }; |
| |
| void FunctionValuesymtabParser::setValueName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| // Note: We check when Index is too small, so that we can error recover |
| // (FP->getOperand will create fatal error). |
| if (Index < getFunctionParser()->getNumGlobalIDs()) { |
| reportUnableToAssign("Global value", Index, Name); |
| return; |
| } |
| Ice::Operand *Op = getFunctionParser()->getOperand(Index); |
| if (auto *V = dyn_cast<Ice::Variable>(Op)) { |
| if (Ice::BuildDefs::dump()) { |
| std::string Nm(Name.data(), Name.size()); |
| V->setName(getFunctionParser()->getFunc(), Nm); |
| } |
| } else { |
| reportUnableToAssign("Local value", Index, Name); |
| } |
| } |
| |
| void FunctionValuesymtabParser::setBbName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| if (!Ice::BuildDefs::dump()) |
| return; |
| if (Index >= getFunctionParser()->getFunc()->getNumNodes()) { |
| reportUnableToAssign("Basic block", Index, Name); |
| return; |
| } |
| std::string Nm(Name.data(), Name.size()); |
| if (Ice::BuildDefs::dump()) |
| getFunctionParser()->getFunc()->getNodes()[Index]->setName(Nm); |
| } |
| |
| bool FunctionParser::ParseBlock(unsigned BlockID) { |
| #ifndef PNACL_LLVM |
| constexpr bool PNaClAllowLocalSymbolTables = true; |
| #endif // !PNACL_LLVM |
| switch (BlockID) { |
| case naclbitc::CONSTANTS_BLOCK_ID: { |
| ConstantsParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| case naclbitc::VALUE_SYMTAB_BLOCK_ID: { |
| if (PNaClAllowLocalSymbolTables) { |
| FunctionValuesymtabParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| return BlockParserBaseClass::ParseBlock(BlockID); |
| } |
| |
| /// Parses the module block in the bitcode file. |
| class ModuleParser final : public BlockParserBaseClass { |
| ModuleParser() = delete; |
| ModuleParser(const ModuleParser &) = delete; |
| ModuleParser &operator=(const ModuleParser &) = delete; |
| |
| public: |
| ModuleParser(unsigned BlockID, TopLevelParser *Context) |
| : BlockParserBaseClass(BlockID, Context), |
| Timer(Ice::TimerStack::TT_parseModule, |
| Context->getTranslator().getContext()), |
| IsParseParallel(Ice::getFlags().isParseParallel()) {} |
| ~ModuleParser() override = default; |
| const char *getBlockName() const override { return "module"; } |
| NaClBitstreamCursor &getCursor() const { return Record.GetCursor(); } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // True if we have already installed names for unnamed global declarations, |
| // and have generated global constant initializers. |
| bool GlobalDeclarationNamesAndInitializersInstalled = false; |
| // True if we have already processed the symbol table for the module. |
| bool FoundValuesymtab = false; |
| const bool IsParseParallel; |
| |
| // Generates names for unnamed global addresses (i.e. functions and global |
| // variables). Then lowers global variable declaration initializers to the |
| // target. May be called multiple times. Only the first call will do the |
| // installation. |
| void installGlobalNamesAndGlobalVarInitializers() { |
| if (!GlobalDeclarationNamesAndInitializersInstalled) { |
| Context->installGlobalNames(); |
| Context->createValueIDs(); |
| Context->verifyFunctionTypeSignatures(); |
| std::unique_ptr<Ice::VariableDeclarationList> Globals = |
| Context->getGlobalVariables(); |
| if (Globals) |
| getTranslator().lowerGlobals(std::move(Globals)); |
| GlobalDeclarationNamesAndInitializersInstalled = true; |
| } |
| } |
| bool ParseBlock(unsigned BlockID) override; |
| |
| void ExitBlock() override { |
| installGlobalNamesAndGlobalVarInitializers(); |
| Context->getTranslator().getContext()->waitForWorkerThreads(); |
| } |
| |
| void ProcessRecord() override; |
| }; |
| |
| class ModuleValuesymtabParser : public ValuesymtabParser { |
| ModuleValuesymtabParser() = delete; |
| ModuleValuesymtabParser(const ModuleValuesymtabParser &) = delete; |
| void operator=(const ModuleValuesymtabParser &) = delete; |
| |
| public: |
| ModuleValuesymtabParser(unsigned BlockID, ModuleParser *MP) |
| : ValuesymtabParser(BlockID, MP), |
| Timer(Ice::TimerStack::TT_parseModuleValuesymtabs, |
| getTranslator().getContext()) {} |
| |
| ~ModuleValuesymtabParser() override = default; |
| |
| private: |
| Ice::TimerMarker Timer; |
| const char *getTableKind() const override { return "Module"; } |
| void setValueName(NaClBcIndexSize_t Index, StringType &Name) override; |
| void setBbName(NaClBcIndexSize_t Index, StringType &Name) override; |
| }; |
| |
| void ModuleValuesymtabParser::setValueName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| Ice::GlobalDeclaration *Decl = Context->getGlobalDeclarationByID(Index); |
| if (llvm::isa<Ice::VariableDeclaration>(Decl) && |
| Decl->isPNaClABIExternalName(Name.str())) { |
| // Force linkage of (specific) Global Variables be external for the PNaCl |
| // ABI. PNaCl bitcode has a linkage field for Functions, but not for |
| // GlobalVariables (because the latter is not needed for pexes, so it has |
| // been removed). |
| Decl->setLinkage(llvm::GlobalValue::ExternalLinkage); |
| } |
| |
| // Unconditionally capture the name if it is provided in the input file, |
| // regardless of whether dump is enabled or whether the symbol is internal vs |
| // external. This fits in well with the lit tests, and most symbols in a |
| // conforming pexe are nameless and don't take this path. |
| Decl->setName(getTranslator().getContext(), |
| StringRef(Name.data(), Name.size())); |
| } |
| |
| void ModuleValuesymtabParser::setBbName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| reportUnableToAssign("Basic block", Index, Name); |
| } |
| |
| class CfgParserWorkItem final : public Ice::OptWorkItem { |
| CfgParserWorkItem() = delete; |
| CfgParserWorkItem(const CfgParserWorkItem &) = delete; |
| CfgParserWorkItem &operator=(const CfgParserWorkItem &) = delete; |
| |
| public: |
| CfgParserWorkItem(unsigned BlockID, NaClBcIndexSize_t FcnId, |
| ModuleParser *ModParser, std::unique_ptr<uint8_t[]> Buffer, |
| uintptr_t BufferSize, uint64_t StartBit, uint32_t SeqNumber) |
| : BlockID(BlockID), FcnId(FcnId), ModParser(ModParser), |
| Buffer(std::move(Buffer)), BufferSize(BufferSize), StartBit(StartBit), |
| SeqNumber(SeqNumber) {} |
| std::unique_ptr<Ice::Cfg> getParsedCfg() override; |
| ~CfgParserWorkItem() override = default; |
| |
| private: |
| const unsigned BlockID; |
| const NaClBcIndexSize_t FcnId; |
| // Note: ModParser can't be const because the function parser needs to |
| // access non-const member functions (of ModuleParser and TopLevelParser). |
| // TODO(kschimpf): Fix this issue. |
| ModuleParser *ModParser; |
| const std::unique_ptr<uint8_t[]> Buffer; |
| const uintptr_t BufferSize; |
| const uint64_t StartBit; |
| const uint32_t SeqNumber; |
| }; |
| |
| std::unique_ptr<Ice::Cfg> CfgParserWorkItem::getParsedCfg() { |
| NaClBitstreamCursor &OldCursor(ModParser->getCursor()); |
| llvm::NaClBitstreamReader Reader(OldCursor.getStartWordByteForBit(StartBit), |
| Buffer.get(), Buffer.get() + BufferSize, |
| OldCursor.getBitStreamReader()); |
| NaClBitstreamCursor NewCursor(Reader); |
| NewCursor.JumpToBit(NewCursor.getWordBitNo(StartBit)); |
| FunctionParser Parser(BlockID, ModParser, FcnId, NewCursor); |
| return Parser.parseFunction(SeqNumber); |
| } |
| |
| bool ModuleParser::ParseBlock(unsigned BlockID) { |
| switch (BlockID) { |
| case naclbitc::BLOCKINFO_BLOCK_ID: |
| return NaClBitcodeParser::ParseBlock(BlockID); |
| case naclbitc::TYPE_BLOCK_ID_NEW: { |
| TypesParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| case naclbitc::GLOBALVAR_BLOCK_ID: { |
| GlobalsParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| case naclbitc::VALUE_SYMTAB_BLOCK_ID: { |
| if (FoundValuesymtab) |
| Fatal("Duplicate valuesymtab in module"); |
| |
| // If we have already processed a function block (i.e. we have already |
| // installed global names and variable initializers) we can no longer accept |
| // the value symbol table. Names have already been generated. |
| if (GlobalDeclarationNamesAndInitializersInstalled) |
| Fatal("Module valuesymtab not allowed after function blocks"); |
| |
| FoundValuesymtab = true; |
| ModuleValuesymtabParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| case naclbitc::FUNCTION_BLOCK_ID: { |
| installGlobalNamesAndGlobalVarInitializers(); |
| Ice::GlobalContext *Ctx = Context->getTranslator().getContext(); |
| uint32_t SeqNumber = Context->getTranslator().getNextSequenceNumber(); |
| NaClBcIndexSize_t FcnId = Context->getNextFunctionBlockValueID(); |
| if (IsParseParallel) { |
| // Skip the block and copy into a buffer. Note: We copy into a buffer |
| // using the top-level parser to make sure that the underlying |
| // buffer reading from the data streamer is not thread safe. |
| NaClBitstreamCursor &Cursor = Record.GetCursor(); |
| uint64_t StartBit = Cursor.GetCurrentBitNo(); |
| if (SkipBlock()) |
| return true; |
| const uint64_t EndBit = Cursor.GetCurrentBitNo(); |
| const uintptr_t StartByte = Cursor.getStartWordByteForBit(StartBit); |
| const uintptr_t EndByte = Cursor.getEndWordByteForBit(EndBit); |
| const uintptr_t BufferSize = EndByte - StartByte; |
| std::unique_ptr<uint8_t[]> Buffer((uint8_t *)(new uint8_t[BufferSize])); |
| for (size_t i = Cursor.fillBuffer(Buffer.get(), BufferSize, StartByte); |
| i < BufferSize; ++i) { |
| Buffer[i] = 0; |
| } |
| Ctx->optQueueBlockingPush(Ice::makeUnique<CfgParserWorkItem>( |
| BlockID, FcnId, this, std::move(Buffer), BufferSize, StartBit, |
| SeqNumber)); |
| return false; |
| } else { |
| FunctionParser Parser(BlockID, this, FcnId); |
| std::unique_ptr<Ice::Cfg> Func = Parser.parseFunction(SeqNumber); |
| bool Failed = Func->hasError(); |
| getTranslator().translateFcn(std::move(Func)); |
| return Failed && !Ice::getFlags().getAllowErrorRecovery(); |
| } |
| } |
| default: |
| return BlockParserBaseClass::ParseBlock(BlockID); |
| } |
| } |
| |
| void ModuleParser::ProcessRecord() { |
| const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| switch (Record.GetCode()) { |
| case naclbitc::MODULE_CODE_VERSION: { |
| // VERSION: [version#] |
| if (!isValidRecordSize(1, "version")) |
| return; |
| uint64_t Version = Values[0]; |
| if (Version != 1) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Unknown bitstream version: " << Version; |
| Error(StrBuf.str()); |
| } |
| return; |
| } |
| case naclbitc::MODULE_CODE_FUNCTION: { |
| // FUNCTION: [type, callingconv, isproto, linkage] |
| if (!isValidRecordSize(4, "address")) |
| return; |
| const Ice::FuncSigType &Signature = Context->getFuncSigTypeByID(Values[0]); |
| CallingConv::ID CallingConv; |
| if (!naclbitc::DecodeCallingConv(Values[1], CallingConv)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function address has unknown calling convention: " |
| << Values[1]; |
| Error(StrBuf.str()); |
| return; |
| } |
| GlobalValue::LinkageTypes Linkage; |
| if (!naclbitc::DecodeLinkage(Values[3], Linkage)) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Function address has unknown linkage. Found " << Values[3]; |
| Error(StrBuf.str()); |
| return; |
| } |
| bool IsProto = Values[2] == 1; |
| auto *Func = Ice::FunctionDeclaration::create( |
| Context->getTranslator().getContext(), Signature, CallingConv, Linkage, |
| IsProto); |
| Context->setNextFunctionID(Func); |
| return; |
| } |
| default: |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| } |
| |
| bool TopLevelParser::ParseBlock(unsigned BlockID) { |
| if (BlockID == naclbitc::MODULE_BLOCK_ID) { |
| if (ParsedModuleBlock) |
| Fatal("Input can't contain more than one module"); |
| ModuleParser Parser(BlockID, this); |
| bool ParseFailed = Parser.ParseThisBlock(); |
| ParsedModuleBlock = true; |
| return ParseFailed; |
| } |
| // Generate error message by using default block implementation. |
| BlockParserBaseClass Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| |
| } // end of anonymous namespace |
| |
| namespace Ice { |
| |
| void PNaClTranslator::translateBuffer(const std::string &IRFilename, |
| MemoryBuffer *MemBuf) { |
| std::unique_ptr<MemoryObject> MemObj(getNonStreamedMemoryObject( |
| reinterpret_cast<const unsigned char *>(MemBuf->getBufferStart()), |
| reinterpret_cast<const unsigned char *>(MemBuf->getBufferEnd()))); |
| translate(IRFilename, std::move(MemObj)); |
| } |
| |
| void PNaClTranslator::translate(const std::string &IRFilename, |
| std::unique_ptr<MemoryObject> &&MemObj) { |
| // On error, we report_fatal_error to avoid destroying the MemObj. That may |
| // still be in use by IceBrowserCompileServer. Otherwise, we need to change |
| // the MemObj to be ref-counted, or have a wrapper, or simply leak. We also |
| // need a hook to tell the IceBrowserCompileServer to unblock its |
| // QueueStreamer. |
| // https://code.google.com/p/nativeclient/issues/detail?id=4163 |
| // Read header and verify it is good. |
| NaClBitcodeHeader Header; |
| if (Header.Read(MemObj.get())) { |
| llvm::report_fatal_error("Invalid PNaCl bitcode header"); |
| } |
| if (!Header.IsSupported()) { |
| getContext()->getStrError() << Header.Unsupported(); |
| if (!Header.IsReadable()) { |
| llvm::report_fatal_error("Invalid PNaCl bitcode header"); |
| } |
| } |
| |
| // Create a bitstream reader to read the bitcode file. |
| NaClBitstreamReader InputStreamFile(MemObj.release(), Header); |
| NaClBitstreamCursor InputStream(InputStreamFile); |
| |
| TopLevelParser Parser(*this, InputStream, ErrorStatus); |
| while (!InputStream.AtEndOfStream()) { |
| if (Parser.Parse()) { |
| ErrorStatus.assign(EC_Bitcode); |
| return; |
| } |
| } |
| |
| if (!Parser.parsedModuleBlock()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << IRFilename << ": Does not contain a module!"; |
| llvm::report_fatal_error(StrBuf.str()); |
| } |
| if (InputStreamFile.getBitcodeBytes().getExtent() % 4 != 0) { |
| llvm::report_fatal_error("Bitcode stream should be a multiple of 4 bytes"); |
| } |
| } |
| |
| } // end of namespace Ice |