| //===- 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::InstIntrinsic::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 |