| //===- 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. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the PNaCl bitcode file to Ice, to machine code |
| // translator. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PNaClTranslator.h" |
| |
| #include "IceAPInt.h" |
| #include "IceAPFloat.h" |
| #include "IceCfg.h" |
| #include "IceCfgNode.h" |
| #include "IceClFlags.h" |
| #include "IceDefs.h" |
| #include "IceGlobalInits.h" |
| #include "IceInst.h" |
| #include "IceOperand.h" |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wunused-parameter" |
| #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" |
| #pragma clang diagnostic pop |
| |
| 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; |
| } |
| } |
| |
| class BlockParserBaseClass; |
| |
| // Top-level class to read PNaCl bitcode files, and translate to ICE. |
| class TopLevelParser : public NaClBitcodeParser { |
| TopLevelParser() = delete; |
| TopLevelParser(const TopLevelParser &) = delete; |
| TopLevelParser &operator=(const TopLevelParser &) = delete; |
| |
| public: |
| typedef std::vector<Ice::FunctionDeclaration *> FunctionDeclarationListType; |
| |
| 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; } |
| |
| void setBlockParser(BlockParserBaseClass *NewBlockParser) { |
| BlockParser = NewBlockParser; |
| } |
| |
| /// 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) final; |
| |
| /// Generates error message with respect to the current block parser. |
| bool blockError(const std::string &Message); |
| |
| /// Returns the number of errors found while parsing the bitcode |
| /// file. |
| unsigned getNumErrors() const { return NumErrors; } |
| |
| /// 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 true if generation of Subzero IR is disabled. |
| bool isIRGenerationDisabled() const { |
| return Translator.getFlags().getDisableIRGeneration(); |
| } |
| |
| /// 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]; |
| } |
| TypeIDValues.resize(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) { |
| FunctionDeclarationList.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 = FunctionDeclarationList.size(); |
| while (NextDefiningFunctionID < NumDeclaredFunctions && |
| FunctionDeclarationList[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 < FunctionDeclarationList.size()) |
| return FunctionDeclarationList[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 createValueIDs() { |
| assert(VariableDeclarations); |
| ValueIDConstants.reserve(VariableDeclarations->size() + |
| FunctionDeclarationList.size()); |
| createValueIDsForFunctions(); |
| createValueIDsForGlobalVars(); |
| } |
| |
| /// Returns the number of function declarations in the bitcode file. |
| size_t getNumFunctionIDs() const { return FunctionDeclarationList.size(); } |
| |
| /// Returns the number of global declarations (i.e. IDs) defined in |
| /// the bitcode file. |
| size_t getNumGlobalIDs() const { |
| if (VariableDeclarations) { |
| return FunctionDeclarationList.size() + VariableDeclarations->size(); |
| } else { |
| return ValueIDConstants.size(); |
| } |
| } |
| |
| /// Creates Count global variable declarations. |
| void createGlobalVariables(NaClBcIndexSize_t Count) { |
| assert(VariableDeclarations); |
| assert(VariableDeclarations->empty()); |
| for (size_t i = 0; i < Count; ++i) { |
| VariableDeclarations->push_back( |
| Ice::VariableDeclaration::create(getTranslator().getContext())); |
| } |
| } |
| |
| /// Returns the number of global variable declarations in the |
| /// bitcode file. |
| size_t getNumGlobalVariables() const { |
| if (VariableDeclarations) { |
| return VariableDeclarations->size(); |
| } else { |
| return ValueIDConstants.size() - FunctionDeclarationList.size(); |
| } |
| } |
| |
| /// 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 = FunctionDeclarationList.size(); |
| if (Index < NumFunctionIds) |
| return getFunctionByID(Index); |
| else |
| return getGlobalVariableByID(Index - NumFunctionIds); |
| } |
| |
| /// 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); |
| } |
| |
| private: |
| // The translator associated with the parser. |
| Ice::Translator &Translator; |
| // The exit status that should be set to true if an error occurs. |
| Ice::ErrorCode &ErrorStatus; |
| // The number of errors reported. |
| unsigned NumErrors = 0; |
| // The types associated with each type ID. |
| std::vector<ExtendedType> TypeIDValues; |
| // The set of functions (prototype and defined). |
| FunctionDeclarationListType FunctionDeclarationList; |
| // The ID of the next possible defined function ID in |
| // FunctionDeclarationList. FunctionDeclarationList 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. |
| std::vector<Ice::Constant *> ValueIDConstants; |
| // Error recovery value to use when getFuncSigTypeByID fails. |
| Ice::FuncSigType UndefinedFuncSigType; |
| // The block parser currently being applied. Used for error |
| // reporting. |
| BlockParserBaseClass *BlockParser = nullptr; |
| |
| 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 warning if created names may conflict with named |
| // declarations. |
| void installDeclarationName(Ice::GlobalDeclaration *Decl, |
| const Ice::IceString &Prefix, |
| const char *DeclType, |
| NaClBcIndexSize_t &NameIndex) { |
| if (Decl->hasName()) { |
| Translator.checkIfUnnamedNameSafe(Decl->getName(), DeclType, Prefix); |
| } else { |
| Decl->setName(Translator.createUnnamedName(Prefix, NameIndex)); |
| ++NameIndex; |
| } |
| } |
| |
| // Installs names for global variables without names. |
| void installGlobalVarNames() { |
| assert(VariableDeclarations); |
| const Ice::IceString &GlobalPrefix = |
| getTranslator().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 Ice::IceString &FunctionPrefix = |
| getTranslator().getFlags().getDefaultFunctionPrefix(); |
| if (!FunctionPrefix.empty()) { |
| NaClBcIndexSize_t NameIndex = 0; |
| for (Ice::FunctionDeclaration *Func : FunctionDeclarationList) { |
| installDeclarationName(Func, FunctionPrefix, "function", NameIndex); |
| } |
| } |
| } |
| |
| // Builds a constant symbol named Name, suppressing name mangling if |
| // SuppressMangling. IsExternal is true iff the symbol is external. |
| Ice::Constant *getConstantSym(const Ice::IceString &Name, |
| bool SuppressMangling, bool IsExternal) const { |
| if (IsExternal) { |
| return getTranslator().getContext()->getConstantExternSym(Name); |
| } else { |
| const Ice::RelocOffsetT Offset = 0; |
| return getTranslator().getContext()->getConstantSym(Offset, Name, |
| SuppressMangling); |
| } |
| } |
| |
| // Converts function declarations into constant value IDs. |
| void createValueIDsForFunctions() { |
| for (const Ice::FunctionDeclaration *Func : FunctionDeclarationList) { |
| Ice::Constant *C = nullptr; |
| if (!isIRGenerationDisabled()) { |
| C = getConstantSym(Func->getName(), Func->getSuppressMangling(), |
| Func->isProto()); |
| } |
| ValueIDConstants.push_back(C); |
| } |
| } |
| |
| // Converts global variable declarations into constant value IDs. |
| void createValueIDsForGlobalVars() { |
| for (const Ice::VariableDeclaration *Decl : *VariableDeclarations) { |
| Ice::Constant *C = nullptr; |
| if (!isIRGenerationDisabled()) { |
| C = getConstantSym(Decl->getName(), Decl->getSuppressMangling(), |
| !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) { |
| ErrorStatus.assign(Ice::EC_Bitcode); |
| ++NumErrors; |
| Ice::GlobalContext *Context = Translator.getContext(); |
| { // 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 && |
| !Translator.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 " |
| << FunctionDeclarationList.size(); |
| blockError(StrBuf.str()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| if (!FunctionDeclarationList.empty()) |
| return FunctionDeclarationList[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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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; |
| } |
| |
| // 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) { |
| Context->setBlockParser(this); |
| } |
| |
| ~BlockParserBaseClass() override { Context->setBlockParser(nullptr); } |
| |
| // 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) final; |
| |
| protected: |
| // The context parser that contains the decoded state. |
| TopLevelParser *Context; |
| |
| // 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(); } |
| |
| const Ice::ClFlags &getFlags() const { return getTranslator().getFlags(); } |
| |
| bool isIRGenerationDisabled() const { |
| return getTranslator().getFlags().getDisableIRGeneration(); |
| } |
| |
| // 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) { |
| if (BlockParser) |
| return BlockParser->Error(Message); |
| else |
| 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) { |
| 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 (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, 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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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 : 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(); |
| FuncSigExtendedType *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; |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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 : 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()), |
| DummyGlobalVar( |
| Ice::VariableDeclaration::create(getTranslator().getContext())), |
| CurGlobalVar(DummyGlobalVar) {} |
| |
| ~GlobalsParser() final = default; |
| |
| const char *getBlockName() const override { return "globals"; } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // 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; |
| |
| void ExitBlock() override { |
| verifyNoMissingInitializers(); |
| unsigned NumIDs = Context->getNumGlobalVariables(); |
| if (NextGlobalID < NumIDs) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << getBlockName() << " block expects " << NumIDs |
| << " global variable declarations. Found: " << NextGlobalID; |
| Error(StrBuf.str()); |
| } |
| 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 (NextGlobalID != Context->getNumGlobalVariables()) { |
| Error("Globals count record not first in block."); |
| return; |
| } |
| Context->createGlobalVariables(Values[0]); |
| return; |
| case naclbitc::GLOBALVAR_VAR: { |
| // VAR: [align, isconst] |
| if (!isValidRecordSize(2, "variable")) |
| return; |
| verifyNoMissingInitializers(); |
| if (!isIRGenerationDisabled()) { |
| InitializersNeeded = 1; |
| CurGlobalVar = Context->getGlobalVariableByID(NextGlobalID); |
| CurGlobalVar->setAlignment((1 << Values[0]) >> 1); |
| 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; |
| } |
| if (isIRGenerationDisabled()) |
| return; |
| InitializersNeeded = Values[0]; |
| return; |
| case naclbitc::GLOBALVAR_ZEROFILL: { |
| // ZEROFILL: [size] |
| if (!isValidRecordSize(1, "zerofill")) |
| return; |
| if (isIRGenerationDisabled()) |
| return; |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::ZeroInitializer::create(Values[0])); |
| return; |
| } |
| case naclbitc::GLOBALVAR_DATA: { |
| // DATA: [b0, b1, ...] |
| if (!isValidRecordSizeAtLeast(1, "data")) |
| return; |
| if (isIRGenerationDisabled()) |
| return; |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::DataInitializer::create(Values)); |
| return; |
| } |
| case naclbitc::GLOBALVAR_RELOC: { |
| // RELOC: [val, [addend]] |
| if (!isValidRecordSizeInRange(1, 2, "reloc")) |
| return; |
| if (isIRGenerationDisabled()) |
| return; |
| unsigned Index = Values[0]; |
| 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()); |
| } |
| } |
| CurGlobalVar->addInitializer( |
| Ice::VariableDeclaration::RelocInitializer::create( |
| Context->getGlobalDeclarationByID(Index), 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: |
| typedef SmallString<128> StringType; |
| |
| // 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; |
| |
| private: |
| void ProcessRecord() override; |
| |
| void convertToString(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]); |
| } |
| } |
| }; |
| |
| 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; |
| convertToString(ConvertedName); |
| setValueName(Values[0], ConvertedName); |
| return; |
| } |
| case naclbitc::VST_CODE_BBENTRY: { |
| // VST_BBENTRY: [BbId, namechar x N] |
| if (!isValidRecordSizeAtLeast(2, "basic block entry")) |
| return; |
| convertToString(ConvertedName); |
| setBbName(Values[0], 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 : public BlockParserBaseClass { |
| FunctionParser() = delete; |
| FunctionParser(const FunctionParser &) = delete; |
| FunctionParser &operator=(const FunctionParser &) = delete; |
| |
| public: |
| FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser) |
| : BlockParserBaseClass(BlockID, EnclosingParser), |
| Timer(Ice::TimerStack::TT_parseFunctions, getTranslator().getContext()), |
| Func(nullptr), FcnId(Context->getNextFunctionBlockValueID()), |
| FuncDecl(Context->getFunctionByID(FcnId)), |
| CachedNumGlobalValueIDs(Context->getNumGlobalIDs()), |
| NextLocalInstIndex(Context->getNumGlobalIDs()) {} |
| |
| bool convertFunction() { |
| const Ice::TimerStackIdT StackID = Ice::GlobalContext::TSK_Funcs; |
| Ice::TimerIdT TimerID = 0; |
| const bool TimeThisFunction = getFlags().getTimeEachFunction(); |
| if (TimeThisFunction) { |
| TimerID = getTranslator().getContext()->getTimerID(StackID, |
| FuncDecl->getName()); |
| getTranslator().getContext()->pushTimer(TimerID, StackID); |
| } |
| |
| if (!isIRGenerationDisabled()) |
| Func = Ice::Cfg::create(getTranslator().getContext(), |
| getTranslator().getNextSequenceNumber()); |
| Ice::Cfg::setCurrentCfg(Func.get()); |
| |
| // TODO(kschimpf) Clean up API to add a function signature to |
| // a CFG. |
| const Ice::FuncSigType &Signature = FuncDecl->getSignature(); |
| if (isIRGenerationDisabled()) { |
| CurrentNode = nullptr; |
| for (Ice::Type ArgType : Signature.getArgList()) { |
| (void)ArgType; |
| setNextLocalInstIndex(nullptr); |
| } |
| } else { |
| 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)); |
| } |
| } |
| bool ParserResult = ParseThisBlock(); |
| |
| // Temporarily end per-function timing, which will be resumed by |
| // the translator function. This is because translation may be |
| // done asynchronously in a separate thread. |
| if (TimeThisFunction) |
| getTranslator().getContext()->popTimer(TimerID, StackID); |
| |
| Ice::Cfg::setCurrentCfg(nullptr); |
| // Note: Once any errors have been found, we turn off all |
| // translation of all remaining functions. This allows successive |
| // parsing errors to be reported, without adding extra checks to |
| // the translator for such parsing errors. |
| if (Context->getNumErrors() == 0 && Func) { |
| getTranslator().translateFcn(std::move(Func)); |
| // The translator now has ownership of Func. |
| } else { |
| Func.reset(); |
| } |
| |
| return ParserResult; |
| } |
| |
| ~FunctionParser() final = 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()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Value index " << Index << " not defined!"; |
| Fatal(StrBuf.str()); |
| } |
| Ice::Operand *Op = LocalOperands[LocalIndex]; |
| if (Op == nullptr) { |
| if (isIRGenerationDisabled()) |
| return nullptr; |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Value index " << Index << " not defined!"; |
| Fatal(StrBuf.str()); |
| } |
| return Op; |
| } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // 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 basic block being built. |
| Ice::CfgNode *CurrentNode = nullptr; |
| // The ID for the function. |
| NaClBcIndexSize_t FcnId; |
| // 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. |
| std::vector<Ice::Operand *> 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; |
| // Upper limit of alignment power allowed by LLVM |
| static const uint32_t AlignPowerLimit = 29; |
| |
| // Extracts the corresponding Alignment to use, given the AlignPower |
| // (i.e. 2**(AlignPower-1), or 0 if AlignPower == 0). InstName is the |
| // name of the instruction the alignment appears in. |
| void extractAlignment(const char *InstName, uint32_t AlignPower, |
| uint32_t &Alignment) { |
| if (AlignPower <= AlignPowerLimit + 1) { |
| Alignment = (1 << AlignPower) >> 1; |
| return; |
| } |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << InstName << " alignment greater than 2**" << AlignPowerLimit |
| << ". Found: 2**" << (AlignPower - 1); |
| Error(StrBuf.str()); |
| // Error recover with value that is always acceptable. |
| Alignment = 1; |
| } |
| |
| bool ParseBlock(unsigned BlockID) override; |
| |
| void ProcessRecord() override; |
| |
| void ExitBlock() override; |
| |
| // Creates and appends a new basic block to the list of basic blocks. |
| Ice::CfgNode *installNextBasicBlock() { |
| assert(!isIRGenerationDisabled()); |
| return Func->makeNode(); |
| } |
| |
| // Returns the Index-th basic block in the list of basic blocks. |
| Ice::CfgNode *getBasicBlock(NaClBcIndexSize_t Index) { |
| assert(!isIRGenerationDisabled()); |
| const Ice::NodeList &Nodes = Func->getNodes(); |
| if (Index >= Nodes.size()) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Reference to basic block " << Index |
| << " not found. Must be less than " << Nodes.size(); |
| Error(StrBuf.str()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| Index = 0; |
| } |
| return Nodes[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) { |
| assert(!isIRGenerationDisabled()); |
| if (Index == 0) { |
| Error("Branch to entry block not allowed"); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| } |
| return getBasicBlock(Index); |
| } |
| |
| // Generate an instruction variable with type Ty. |
| Ice::Variable *createInstVar(Ice::Type Ty) { |
| assert(!isIRGenerationDisabled()); |
| 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(!isIRGenerationDisabled()); |
| 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 (Ice::Variable *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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| ++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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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 || isIRGenerationDisabled()); |
| // 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()) |
| LocalOperands.resize(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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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; |
| 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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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. |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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)); |
| } |
| }; |
| |
| void FunctionParser::ExitBlock() { |
| // Check if the last instruction in the function was terminating. |
| if (!InstIsTerminating) { |
| Error("Last instruction in function not terminator"); |
| if (isIRGenerationDisabled()) |
| return; |
| // Recover by inserting an unreachable instruction. |
| CurrentNode->appendInst(Ice::InstUnreachable::create(Func.get())); |
| } |
| if (isIRGenerationDisabled()) |
| return; |
| // 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()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| 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; |
| if (!isIRGenerationDisabled()) |
| 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; |
| uint64_t NumBbsRaw = Values[0]; |
| if (NumBbsRaw == 0) { |
| Error("Functions must contain at least one basic block."); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| NumBbsRaw = 1; |
| } else if (NumBbsRaw > NaClBcIndexSize_t_Max) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "To many basic blocks specified: " << NumBbsRaw; |
| Error(StrBuf.str()); |
| NumBbsRaw = NaClBcIndexSize_t_Max; |
| } |
| if (isIRGenerationDisabled()) |
| return; |
| if (Func->getNodes().size() != 1) { |
| Error("Duplicate function block count record"); |
| return; |
| } |
| // Install the basic blocks, skipping bb0 which was created in the |
| // constructor. |
| for (size_t i = 1, NumBbs = NumBbsRaw; i < NumBbs; ++i) |
| installNextBasicBlock(); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_BINOP: { |
| // BINOP: [opval, opval, opcode] |
| if (!isValidRecordSize(3, "binop")) |
| return; |
| Ice::Operand *Op1 = getRelativeOperand(Values[0], BaseIndex); |
| Ice::Operand *Op2 = getRelativeOperand(Values[1], BaseIndex); |
| if (isIRGenerationDisabled()) { |
| assert(Op1 == nullptr && Op2 == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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 (isIRGenerationDisabled()) { |
| assert(Src == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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); |
| if (isIRGenerationDisabled()) { |
| assert(ThenVal == nullptr && ElseVal == nullptr && CondVal == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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); |
| if (isIRGenerationDisabled()) { |
| assert(Vec == nullptr && Index == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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); |
| if (isIRGenerationDisabled()) { |
| assert(Vec == nullptr && Elt == nullptr && Index == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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; |
| } |
| 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); |
| if (isIRGenerationDisabled()) { |
| assert(Op1 == nullptr && Op2 == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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()) { |
| if (isIRGenerationDisabled()) |
| return; |
| CurrentNode->appendInst(Ice::InstRet::create(Func.get())); |
| } else { |
| Ice::Operand *RetVal = getRelativeOperand(Values[0], BaseIndex); |
| if (isIRGenerationDisabled()) { |
| assert(RetVal == nullptr); |
| return; |
| } |
| CurrentNode->appendInst(Ice::InstRet::create(Func.get(), RetVal)); |
| } |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_BR: { |
| InstIsTerminating = true; |
| if (Values.size() == 1) { |
| // BR: [bb#] |
| if (isIRGenerationDisabled()) |
| return; |
| 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 (isIRGenerationDisabled()) { |
| assert(Cond == nullptr); |
| return; |
| } |
| 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 |
| // unnecesary 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); |
| |
| const bool isIRGenDisabled = isIRGenerationDisabled(); |
| if (isIRGenDisabled) { |
| assert(Cond == nullptr); |
| } else 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 = |
| isIRGenDisabled ? nullptr : getBranchBasicBlock(Values[2]); |
| 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; |
| Ice::InstSwitch *Switch = |
| isIRGenDisabled |
| ? nullptr |
| : 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; |
| } |
| Ice::APInt Value(BitWidth, |
| NaClDecodeSignRotatedValue(Values[ValCaseIndex + 2])); |
| if (isIRGenDisabled) |
| continue; |
| Ice::CfgNode *Label = getBranchBasicBlock(Values[ValCaseIndex + 3]); |
| Switch->addBranch(CaseIndex, Value.getSExtValue(), Label); |
| } |
| if (isIRGenDisabled) |
| return; |
| CurrentNode->appendInst(Switch); |
| return; |
| } |
| case naclbitc::FUNC_CODE_INST_UNREACHABLE: { |
| // UNREACHABLE: [] |
| InstIsTerminating = true; |
| if (!isValidRecordSize(0, "unreachable")) |
| return; |
| if (isIRGenerationDisabled()) |
| 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; |
| } |
| if (isIRGenerationDisabled()) { |
| // Verify arguments are defined before quitting. |
| for (unsigned i = 1; i < Values.size(); i += 2) { |
| assert(getRelativeOperand(NaClDecodeSignRotatedValue(Values[i]), |
| BaseIndex) == nullptr); |
| } |
| setNextLocalInstIndex(nullptr); |
| 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; |
| extractAlignment("Alloca", Values[1], Alignment); |
| if (isIRGenerationDisabled()) { |
| assert(ByteCount == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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(), ByteCount, Alignment, getNextInstVar(PtrTy))); |
| 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; |
| extractAlignment("Load", Values[1], Alignment); |
| if (isIRGenerationDisabled()) { |
| assert(Address == nullptr); |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| 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; |
| extractAlignment("Store", Values[2], Alignment); |
| if (isIRGenerationDisabled()) { |
| assert(Address == nullptr && Value == nullptr); |
| return; |
| } |
| 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; |
| } |
| |
| // Extract out the called function and its return type. |
| uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex); |
| Ice::Operand *Callee = getOperand(CalleeIndex); |
| Ice::Type ReturnType = Ice::IceType_void; |
| const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = nullptr; |
| if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { |
| Ice::FunctionDeclaration *Fcn = Context->getFunctionByID(CalleeIndex); |
| const Ice::FuncSigType &Signature = Fcn->getSignature(); |
| ReturnType = Signature.getReturnType(); |
| |
| // Check if this direct call is to an Intrinsic (starts with "llvm.") |
| bool BadIntrinsic; |
| const Ice::IceString &Name = Fcn->getName(); |
| IntrinsicInfo = getTranslator().getContext()->getIntrinsicsInfo().find( |
| Name, BadIntrinsic); |
| if (BadIntrinsic) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Invalid PNaCl intrinsic call to " << Name; |
| Error(StrBuf.str()); |
| appendErrorInstruction(ReturnType); |
| return; |
| } |
| } else { |
| ReturnType = Context->getSimpleTypeByID(Values[2]); |
| } |
| |
| // 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; |
| } |
| bool IsTailCall = static_cast<bool>(CCInfo & 1); |
| Ice::SizeT NumParams = Values.size() - ParamsStartIndex; |
| |
| if (isIRGenerationDisabled()) { |
| assert(Callee == nullptr); |
| // Check that parameters are defined. |
| for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) { |
| assert(getRelativeOperand(Values[ParamsStartIndex + ParamIndex], |
| BaseIndex) == nullptr); |
| } |
| // Define value slot only if value returned. |
| if (ReturnType != Ice::IceType_void) |
| setNextLocalInstIndex(nullptr); |
| return; |
| } |
| |
| // Create the call instruction. |
| Ice::Variable *Dest = (ReturnType == Ice::IceType_void) |
| ? nullptr |
| : getNextInstVar(ReturnType); |
| Ice::InstCall *Inst = nullptr; |
| if (IntrinsicInfo) { |
| Inst = Ice::InstIntrinsicCall::create(Func.get(), NumParams, Dest, Callee, |
| IntrinsicInfo->Info); |
| } else { |
| Inst = Ice::InstCall::create(Func.get(), NumParams, Dest, Callee, |
| IsTailCall); |
| } |
| |
| // Add parameters. |
| for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) { |
| Inst->addArg( |
| getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex)); |
| } |
| |
| // If intrinsic call, validate call signature. |
| if (IntrinsicInfo) { |
| Ice::SizeT ArgIndex = 0; |
| switch (IntrinsicInfo->validateCall(Inst, ArgIndex)) { |
| case Ice::Intrinsics::IsValidCall: |
| break; |
| case Ice::Intrinsics::BadReturnType: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Intrinsic call expects return type " |
| << IntrinsicInfo->getReturnType() |
| << ". Found: " << Inst->getReturnType(); |
| Error(StrBuf.str()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| break; |
| } |
| case Ice::Intrinsics::WrongNumOfArgs: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Intrinsic call expects " << IntrinsicInfo->getNumArgs() |
| << ". Found: " << Inst->getNumArgs(); |
| Error(StrBuf.str()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| break; |
| } |
| case Ice::Intrinsics::WrongCallArgType: { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Intrinsic call argument " << ArgIndex << " expects type " |
| << IntrinsicInfo->getArgType(ArgIndex) |
| << ". Found: " << Inst->getArg(ArgIndex)->getType(); |
| Error(StrBuf.str()); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| break; |
| } |
| } |
| } |
| |
| CurrentNode->appendInst(Inst); |
| 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], |
| isIRGenerationDisabled() ? nullptr : createInstVar(OpType)); |
| return; |
| } |
| default: |
| // Generate error message! |
| BlockParserBaseClass::ProcessRecord(); |
| return; |
| } |
| } |
| |
| /// Parses constants within a function block. |
| class ConstantsParser : 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; |
| if (isIRGenerationDisabled()) { |
| FuncParser->setNextConstantID(nullptr); |
| return; |
| } |
| FuncParser->setNextConstantID( |
| getContext()->getConstantUndef(NextConstantType)); |
| return; |
| } |
| case naclbitc::CST_CODE_INTEGER: { |
| // INTEGER: [intval] |
| if (!isValidRecordSize(1, "integer")) |
| return; |
| if (!isValidNextConstantType()) |
| return; |
| if (isIRGenerationDisabled()) { |
| FuncParser->setNextConstantID(nullptr); |
| return; |
| } |
| if (Ice::isScalarIntegerType(NextConstantType)) { |
| Ice::APInt 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; |
| if (isIRGenerationDisabled()) { |
| FuncParser->setNextConstantID(nullptr); |
| return; |
| } |
| switch (NextConstantType) { |
| case Ice::IceType_f32: { |
| const Ice::APInt IntValue(32, static_cast<uint32_t>(Values[0])); |
| float FpValue = Ice::convertAPIntToFp<int32_t, float>(IntValue); |
| FuncParser->setNextConstantID(getContext()->getConstantFloat(FpValue)); |
| return; |
| } |
| case Ice::IceType_f64: { |
| const Ice::APInt IntValue(64, Values[0]); |
| double FpValue = Ice::convertAPIntToFp<uint64_t, double>(IntValue); |
| 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 : 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()); |
| } |
| |
| 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("instruction", Index, Name); |
| // TODO(kschimpf) Remove error recovery once implementation complete. |
| return; |
| } |
| if (isIRGenerationDisabled()) |
| return; |
| Ice::Operand *Op = getFunctionParser()->getOperand(Index); |
| if (Ice::Variable *V = dyn_cast<Ice::Variable>(Op)) { |
| if (Ice::BuildDefs::dump()) { |
| std::string Nm(Name.data(), Name.size()); |
| V->setName(getFunctionParser()->getFunc(), Nm); |
| } |
| } else { |
| reportUnableToAssign("variable", Index, Name); |
| } |
| } |
| |
| void FunctionValuesymtabParser::setBbName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| if (isIRGenerationDisabled()) |
| return; |
| if (Index >= getFunctionParser()->getFunc()->getNumNodes()) { |
| reportUnableToAssign("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) { |
| 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 : 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()) {} |
| |
| ~ModuleParser() override = default; |
| |
| const char *getBlockName() const override { return "module"; } |
| |
| private: |
| Ice::TimerMarker Timer; |
| // True if we have already installed names for unnamed global declarations, |
| // and have generated global constant initializers. |
| bool GlobalDeclarationNamesAndInitializersInstalled = false; |
| |
| // 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(); |
| getTranslator().lowerGlobals(Context->getGlobalVariables()); |
| GlobalDeclarationNamesAndInitializersInstalled = true; |
| } |
| } |
| bool ParseBlock(unsigned BlockID) override; |
| |
| void ExitBlock() override { installGlobalNamesAndGlobalVarInitializers(); } |
| |
| 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; |
| void setValueName(NaClBcIndexSize_t Index, StringType &Name) override; |
| void setBbName(NaClBcIndexSize_t Index, StringType &Name) override; |
| }; |
| |
| void ModuleValuesymtabParser::setValueName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| Context->getGlobalDeclarationByID(Index) |
| ->setName(StringRef(Name.data(), Name.size())); |
| } |
| |
| void ModuleValuesymtabParser::setBbName(NaClBcIndexSize_t Index, |
| StringType &Name) { |
| std::string Buffer; |
| raw_string_ostream StrBuf(Buffer); |
| StrBuf << "Can't define basic block name at global level: '" << Name |
| << "' -> " << Index; |
| Error(StrBuf.str()); |
| } |
| |
| 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: { |
| ModuleValuesymtabParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| case naclbitc::FUNCTION_BLOCK_ID: { |
| installGlobalNamesAndGlobalVarInitializers(); |
| FunctionParser Parser(BlockID, this); |
| return Parser.convertFunction(); |
| } |
| 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; |
| Ice::FunctionDeclaration *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) { |
| ModuleParser Parser(BlockID, this); |
| return Parser.ParseThisBlock(); |
| } |
| // 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 |
| Ostream &ErrStream = getContext()->getStrError(); |
| // 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()) { |
| ErrStream << 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); |
| int TopLevelBlocks = 0; |
| while (!InputStream.AtEndOfStream()) { |
| if (Parser.Parse()) { |
| ErrorStatus.assign(EC_Bitcode); |
| return; |
| } |
| ++TopLevelBlocks; |
| } |
| |
| if (TopLevelBlocks != 1) { |
| ErrStream << IRFilename |
| << ": Contains more than one module. Found: " << TopLevelBlocks |
| << "\n"; |
| llvm::report_fatal_error("Bitcode has more than one module"); |
| } |
| if (InputStreamFile.getBitcodeBytes().getExtent() % 4 != 0) { |
| ErrStream |
| << IRFilename |
| << ": Bitcode stream should be a multiple of 4 bytes in length.\n"; |
| llvm::report_fatal_error("Bitcode stream should be a multiple of 4 bytes"); |
| } |
| } |
| |
| } // end of namespace Ice |