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