blob: f315294b21dd4a53f5096017aaefa5ce3a765716 [file] [log] [blame]
//===- subzero/src/PNaClTranslator.cpp - ICE from bitcode -----------------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the PNaCl bitcode file to Ice, to machine code
// translator.
//
//===----------------------------------------------------------------------===//
#include "PNaClTranslator.h"
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceClFlags.h"
#include "IceDefs.h"
#include "IceInst.h"
#include "IceOperand.h"
#include "IceTypeConverter.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeDecoders.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
#include "llvm/Bitcode/NaCl/NaClReaderWriter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ValueHandle.h"
#include <vector>
#include <cassert>
using namespace llvm;
namespace {
// TODO(kschimpf) Remove error recovery once implementation complete.
static cl::opt<bool> AllowErrorRecovery(
"allow-pnacl-reader-error-recovery",
cl::desc("Allow error recovery when reading PNaCl bitcode."),
cl::init(false));
// Top-level class to read PNaCl bitcode files, and translate to ICE.
class TopLevelParser : public NaClBitcodeParser {
TopLevelParser(const TopLevelParser &) LLVM_DELETED_FUNCTION;
TopLevelParser &operator=(const TopLevelParser &) LLVM_DELETED_FUNCTION;
public:
TopLevelParser(Ice::Translator &Translator, const std::string &InputName,
NaClBitcodeHeader &Header, NaClBitstreamCursor &Cursor,
bool &ErrorStatus)
: NaClBitcodeParser(Cursor), Translator(Translator),
Mod(new Module(InputName, getGlobalContext())), Header(Header),
TypeConverter(getLLVMContext()), ErrorStatus(ErrorStatus), NumErrors(0),
NumFunctionIds(0), NumFunctionBlocks(0),
GlobalVarPlaceHolderType(convertToLLVMType(Ice::IceType_i8)) {
Mod->setDataLayout(PNaClDataLayout);
}
virtual ~TopLevelParser() {}
LLVM_OVERRIDE;
Ice::Translator &getTranslator() { return Translator; }
// Generates error with given Message. Always returns true.
virtual bool Error(const std::string &Message) LLVM_OVERRIDE {
ErrorStatus = true;
++NumErrors;
NaClBitcodeParser::Error(Message);
if (!AllowErrorRecovery)
report_fatal_error("Unable to continue");
return true;
}
/// Returns the number of errors found while parsing the bitcode
/// file.
unsigned getNumErrors() const { return NumErrors; }
/// Returns the LLVM module associated with the translation.
Module *getModule() const { return Mod.get(); }
/// Returns the number of bytes in the bitcode header.
size_t getHeaderSize() const { return Header.getHeaderSize(); }
/// Returns the llvm context to use.
LLVMContext &getLLVMContext() const { return Mod->getContext(); }
/// Changes the size of the type list to the given size.
void resizeTypeIDValues(unsigned NewSize) { TypeIDValues.resize(NewSize); }
/// Returns the type associated with the given index.
Type *getTypeByID(unsigned ID) {
// Note: method resizeTypeIDValues expands TypeIDValues
// to the specified size, and fills elements with NULL.
Type *Ty = ID < TypeIDValues.size() ? TypeIDValues[ID] : NULL;
if (Ty)
return Ty;
return reportTypeIDAsUndefined(ID);
}
/// Defines type for ID.
void setTypeID(unsigned ID, Type *Ty) {
if (ID < TypeIDValues.size() && TypeIDValues[ID] == NULL) {
TypeIDValues[ID] = Ty;
return;
}
reportBadSetTypeID(ID, Ty);
}
/// Sets the next function ID to the given LLVM function.
void setNextFunctionID(Function *Fcn) {
++NumFunctionIds;
ValueIDValues.push_back(Fcn);
}
/// Defines the next function ID as one that has an implementation
/// (i.e a corresponding function block in the bitcode).
void setNextValueIDAsImplementedFunction() {
DefiningFunctionsList.push_back(ValueIDValues.size());
}
/// 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.
unsigned getNextFunctionBlockValueID() {
if (NumFunctionBlocks >= DefiningFunctionsList.size())
report_fatal_error(
"More function blocks than defined function addresses");
return DefiningFunctionsList[NumFunctionBlocks++];
}
/// Returns the LLVM IR value associatd with the global value ID.
Value *getGlobalValueByID(unsigned ID) const {
if (ID >= ValueIDValues.size())
return NULL;
return ValueIDValues[ID];
}
/// Returns the number of function addresses (i.e. ID's) defined in
/// the bitcode file.
unsigned getNumFunctionIDs() const { return NumFunctionIds; }
/// Returns the number of global values defined in the bitcode
/// file.
unsigned getNumGlobalValueIDs() const { return ValueIDValues.size(); }
/// Resizes the list of value IDs to include Count global variable
/// IDs.
void resizeValueIDsForGlobalVarCount(unsigned Count) {
ValueIDValues.resize(ValueIDValues.size() + Count);
}
/// Returns the global variable address associated with the given
/// value ID. If the ID refers to a global variable address not yet
/// defined, a placeholder is created so that we can fix it up
/// later.
Constant *getOrCreateGlobalVarRef(unsigned ID) {
if (ID >= ValueIDValues.size())
return NULL;
if (Value *C = ValueIDValues[ID])
return dyn_cast<Constant>(C);
Constant *C = new GlobalVariable(*Mod, GlobalVarPlaceHolderType, false,
GlobalValue::ExternalLinkage, 0);
ValueIDValues[ID] = C;
return C;
}
/// Assigns the given global variable (address) to the given value
/// ID. Returns true if ID is a valid global variable ID. Otherwise
/// returns false.
bool assignGlobalVariable(GlobalVariable *GV, unsigned ID) {
if (ID < NumFunctionIds || ID >= ValueIDValues.size())
return false;
WeakVH &OldV = ValueIDValues[ID];
if (OldV == NULL) {
ValueIDValues[ID] = GV;
return true;
}
// If reached, there was a forward reference to this value. Replace it.
Value *PrevVal = OldV;
GlobalVariable *Placeholder = cast<GlobalVariable>(PrevVal);
Placeholder->replaceAllUsesWith(
ConstantExpr::getBitCast(GV, Placeholder->getType()));
Placeholder->eraseFromParent();
ValueIDValues[ID] = GV;
return true;
}
/// Returns the corresponding ICE type for LLVMTy.
Ice::Type convertToIceType(Type *LLVMTy) {
Ice::Type IceTy = TypeConverter.convertToIceType(LLVMTy);
if (IceTy >= Ice::IceType_NUM) {
return convertToIceTypeError(LLVMTy);
}
return IceTy;
}
/// Returns the corresponding LLVM type for IceTy.
Type *convertToLLVMType(Ice::Type IceTy) const {
return TypeConverter.convertToLLVMType(IceTy);
}
/// Returns the LLVM integer type with the given number of Bits. If
/// Bits is not a valid PNaCl type, returns NULL.
Type *getLLVMIntegerType(unsigned Bits) const {
return TypeConverter.getLLVMIntegerType(Bits);
}
/// Returns the LLVM vector with the given Size and Ty. If not a
/// valid PNaCl vector type, returns NULL.
Type *getLLVMVectorType(unsigned Size, Ice::Type Ty) const {
return TypeConverter.getLLVMVectorType(Size, Ty);
}
/// Returns the model for pointer types in ICE.
Ice::Type getIcePointerType() const {
return TypeConverter.getIcePointerType();
}
private:
// The translator associated with the parser.
Ice::Translator &Translator;
// The parsed module.
OwningPtr<Module> Mod;
// The bitcode header.
NaClBitcodeHeader &Header;
// Converter between LLVM and ICE types.
Ice::TypeConverter TypeConverter;
// The exit status that should be set to true if an error occurs.
bool &ErrorStatus;
// The number of errors reported.
unsigned NumErrors;
// The types associated with each type ID.
std::vector<Type *> TypeIDValues;
// The (global) value IDs.
std::vector<WeakVH> ValueIDValues;
// The number of function IDs.
unsigned NumFunctionIds;
// The number of function blocks (processed so far).
unsigned NumFunctionBlocks;
// The list of value IDs (in the order found) of defining function
// addresses.
std::vector<unsigned> DefiningFunctionsList;
// Cached global variable placeholder type. Used for all forward
// references to global variable addresses.
Type *GlobalVarPlaceHolderType;
virtual bool ParseBlock(unsigned BlockID) LLVM_OVERRIDE;
/// Reports that type ID is undefined, and then returns
/// the void type.
Type *reportTypeIDAsUndefined(unsigned ID);
/// Reports error about bad call to setTypeID.
void reportBadSetTypeID(unsigned ID, Type *Ty);
// Reports that there is no corresponding ICE type for LLVMTy, and
// returns ICE::IceType_void.
Ice::Type convertToIceTypeError(Type *LLVMTy);
};
Type *TopLevelParser::reportTypeIDAsUndefined(unsigned ID) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Can't find type for type id: " << ID;
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
Type *Ty = TypeConverter.convertToLLVMType(Ice::IceType_void);
// To reduce error messages, update type list if possible.
if (ID < TypeIDValues.size())
TypeIDValues[ID] = Ty;
return Ty;
}
void TopLevelParser::reportBadSetTypeID(unsigned ID, Type *Ty) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
if (ID >= TypeIDValues.size()) {
StrBuf << "Type index " << ID << " out of range: can't install.";
} else {
// Must be case that index already defined.
StrBuf << "Type index " << ID << " defined as " << *TypeIDValues[ID]
<< " and " << *Ty << ".";
}
Error(StrBuf.str());
}
Ice::Type TopLevelParser::convertToIceTypeError(Type *LLVMTy) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid LLVM type: " << *LLVMTy;
Error(StrBuf.str());
return Ice::IceType_void;
}
// Base class for parsing blocks within the bitcode file. Note:
// Because this is the base class of block parsers, we generate error
// messages if ParseBlock or ParseRecord is not overridden in derived
// classes.
class BlockParserBaseClass : public NaClBitcodeParser {
public:
// Constructor for the top-level module block parser.
BlockParserBaseClass(unsigned BlockID, TopLevelParser *Context)
: NaClBitcodeParser(BlockID, Context), Context(Context) {}
virtual ~BlockParserBaseClass() LLVM_OVERRIDE {}
protected:
// The context parser that contains the decoded state.
TopLevelParser *Context;
// Constructor for nested block parsers.
BlockParserBaseClass(unsigned BlockID, BlockParserBaseClass *EnclosingParser)
: NaClBitcodeParser(BlockID, EnclosingParser),
Context(EnclosingParser->Context) {}
// Gets the translator associated with the bitcode parser.
Ice::Translator &getTranslator() { return Context->getTranslator(); }
// Generates an error Message with the bit address prefixed to it.
virtual bool Error(const std::string &Message) LLVM_OVERRIDE {
uint64_t Bit = Record.GetStartBit() + Context->getHeaderSize() * 8;
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "(" << format("%" PRIu64 ":%u", (Bit / 8),
static_cast<unsigned>(Bit % 8)) << ") " << Message;
return Context->Error(StrBuf.str());
}
// Default implementation. Reports that block is unknown and skips
// its contents.
virtual bool ParseBlock(unsigned BlockID) LLVM_OVERRIDE;
// Default implementation. Reports that the record is not
// understood.
virtual void ProcessRecord() LLVM_OVERRIDE;
// Checks if the size of the record is Size. Return true if valid.
// Otherwise generates an error and returns false.
bool isValidRecordSize(unsigned Size, const char *RecordName) {
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
if (Values.size() == Size)
return true;
ReportRecordSizeError(Size, RecordName, NULL);
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(unsigned 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(unsigned 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(unsigned LowerLimit, unsigned 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 NULL)
/// is appended to "record expects" to describe how ExpectedSize
/// should be interpreted.
void ReportRecordSizeError(unsigned ExpectedSize, const char *RecordName,
const char *ContextMessage);
};
void BlockParserBaseClass::ReportRecordSizeError(unsigned ExpectedSize,
const char *RecordName,
const char *ContextMessage) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << RecordName << " record expects";
if (ContextMessage)
StrBuf << " " << ContextMessage;
StrBuf << " " << ExpectedSize << " argument";
if (ExpectedSize > 1)
StrBuf << "s";
StrBuf << ". Found: " << Record.GetValues().size();
Error(StrBuf.str());
}
bool BlockParserBaseClass::ParseBlock(unsigned BlockID) {
// If called, derived class doesn't know how to handle block.
// Report error and skip.
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Don't know how to parse block id: " << BlockID;
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
SkipBlock();
return false;
}
void BlockParserBaseClass::ProcessRecord() {
// If called, derived class doesn't know how to handle.
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Don't know how to process record: " << Record;
Error(StrBuf.str());
}
// Class to parse a types block.
class TypesParser : public BlockParserBaseClass {
public:
TypesParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser)
: BlockParserBaseClass(BlockID, EnclosingParser), NextTypeId(0) {}
~TypesParser() LLVM_OVERRIDE {}
private:
// The type ID that will be associated with the next type defining
// record in the types block.
unsigned NextTypeId;
virtual void ProcessRecord() LLVM_OVERRIDE;
};
void TypesParser::ProcessRecord() {
Type *Ty = NULL;
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::TYPE_CODE_NUMENTRY:
// NUMENTRY: [numentries]
if (!isValidRecordSize(1, "Type count"))
return;
Context->resizeTypeIDValues(Values[0]);
return;
case naclbitc::TYPE_CODE_VOID:
// VOID
if (!isValidRecordSize(0, "Type void"))
return;
Ty = Context->convertToLLVMType(Ice::IceType_void);
break;
case naclbitc::TYPE_CODE_FLOAT:
// FLOAT
if (!isValidRecordSize(0, "Type float"))
return;
Ty = Context->convertToLLVMType(Ice::IceType_f32);
break;
case naclbitc::TYPE_CODE_DOUBLE:
// DOUBLE
if (!isValidRecordSize(0, "Type double"))
return;
Ty = Context->convertToLLVMType(Ice::IceType_f64);
break;
case naclbitc::TYPE_CODE_INTEGER:
// INTEGER: [width]
if (!isValidRecordSize(1, "Type integer"))
return;
Ty = Context->getLLVMIntegerType(Values[0]);
if (Ty == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Type integer record with invalid bitsize: " << Values[0];
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
// Fix type so that we can continue.
Ty = Context->convertToLLVMType(Ice::IceType_i32);
}
break;
case naclbitc::TYPE_CODE_VECTOR: {
// VECTOR: [numelts, eltty]
if (!isValidRecordSize(2, "Type vector"))
return;
Type *BaseTy = Context->getTypeByID(Values[1]);
Ty = Context->getLLVMVectorType(Values[0],
Context->convertToIceType(BaseTy));
if (Ty == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid type vector record: <" << Values[0] << " x " << *BaseTy
<< ">";
Error(StrBuf.str());
Ty = Context->convertToLLVMType(Ice::IceType_void);
}
break;
}
case naclbitc::TYPE_CODE_FUNCTION: {
// FUNCTION: [vararg, retty, paramty x N]
if (!isValidRecordSizeAtLeast(2, "Type signature"))
return;
SmallVector<Type *, 8> ArgTys;
for (unsigned i = 2, e = Values.size(); i != e; ++i) {
ArgTys.push_back(Context->getTypeByID(Values[i]));
}
Ty = FunctionType::get(Context->getTypeByID(Values[1]), ArgTys, Values[0]);
break;
}
default:
BlockParserBaseClass::ProcessRecord();
return;
}
// If Ty not defined, assume error. Use void as filler.
if (Ty == NULL)
Ty = Context->convertToLLVMType(Ice::IceType_void);
Context->setTypeID(NextTypeId++, Ty);
}
/// Parses the globals block (i.e. global variables).
class GlobalsParser : public BlockParserBaseClass {
public:
GlobalsParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser)
: BlockParserBaseClass(BlockID, EnclosingParser), InitializersNeeded(0),
Alignment(1), IsConstant(false) {
NextGlobalID = Context->getNumFunctionIDs();
}
virtual ~GlobalsParser() LLVM_OVERRIDE {}
private:
// Holds the sequence of initializers for the global.
SmallVector<Constant *, 10> Initializers;
// Keeps track of how many initializers are expected for
// the global variable being built.
unsigned InitializersNeeded;
// The alignment assumed for the global variable being built.
unsigned Alignment;
// True if the global variable being built is a constant.
bool IsConstant;
// The index of the next global variable.
unsigned NextGlobalID;
virtual void ExitBlock() LLVM_OVERRIDE {
verifyNoMissingInitializers();
unsigned NumIDs = Context->getNumGlobalValueIDs();
if (NextGlobalID < NumIDs) {
unsigned NumFcnIDs = Context->getNumFunctionIDs();
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Globals block expects " << (NumIDs - NumFcnIDs)
<< " global definitions. Found: " << (NextGlobalID - NumFcnIDs);
Error(StrBuf.str());
}
BlockParserBaseClass::ExitBlock();
}
virtual void ProcessRecord() LLVM_OVERRIDE;
// Checks if the number of initializers needed 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() {
if (InitializersNeeded != Initializers.size()) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Global variable @g"
<< (NextGlobalID - Context->getNumFunctionIDs()) << " expected "
<< InitializersNeeded << " initializer";
if (InitializersNeeded > 1)
StrBuf << "s";
StrBuf << ". Found: " << Initializers.size();
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
// Fix up state so that we can continue.
InitializersNeeded = Initializers.size();
installGlobalVar();
}
}
// Reserves a slot in the list of initializers being built. If there
// isn't room for the slot, an error message is generated.
void reserveInitializer(const char *RecordName) {
if (InitializersNeeded <= Initializers.size()) {
Error(std::string(RecordName) +
" record: Too many initializers, ignoring.");
}
}
// Takes the initializers (and other parser state values) and
// installs a global variable (with the initializers) into the list
// of ValueIDs.
void installGlobalVar() {
Constant *Init = NULL;
switch (Initializers.size()) {
case 0:
Error("No initializer for global variable in global vars block");
return;
case 1:
Init = Initializers[0];
break;
default:
Init = ConstantStruct::getAnon(Context->getLLVMContext(), Initializers,
true);
break;
}
GlobalVariable *GV =
new GlobalVariable(*Context->getModule(), Init->getType(), IsConstant,
GlobalValue::InternalLinkage, Init, "");
GV->setAlignment(Alignment);
if (!Context->assignGlobalVariable(GV, NextGlobalID)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Defining global V[" << NextGlobalID
<< "] not allowed. Out of range.";
Error(StrBuf.str());
}
++NextGlobalID;
Initializers.clear();
InitializersNeeded = 0;
Alignment = 1;
IsConstant = false;
}
};
void GlobalsParser::ProcessRecord() {
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::GLOBALVAR_COUNT:
// COUNT: [n]
if (!isValidRecordSize(1, "Globals count"))
return;
if (NextGlobalID != Context->getNumFunctionIDs()) {
Error("Globals count record not first in block.");
return;
}
verifyNoMissingInitializers();
Context->resizeValueIDsForGlobalVarCount(Values[0]);
return;
case naclbitc::GLOBALVAR_VAR: {
// VAR: [align, isconst]
if (!isValidRecordSize(2, "Globals variable"))
return;
verifyNoMissingInitializers();
InitializersNeeded = 1;
Initializers.clear();
Alignment = (1 << Values[0]) >> 1;
IsConstant = Values[1] != 0;
return;
}
case naclbitc::GLOBALVAR_COMPOUND:
// COMPOUND: [size]
if (!isValidRecordSize(1, "globals compound"))
return;
if (Initializers.size() > 0 || InitializersNeeded != 1) {
Error("Globals compound record not first initializer");
return;
}
if (Values[0] < 2) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Globals compound record size invalid. Found: " << Values[0];
Error(StrBuf.str());
return;
}
InitializersNeeded = Values[0];
return;
case naclbitc::GLOBALVAR_ZEROFILL: {
// ZEROFILL: [size]
if (!isValidRecordSize(1, "Globals zerofill"))
return;
reserveInitializer("Globals zerofill");
Type *Ty =
ArrayType::get(Context->convertToLLVMType(Ice::IceType_i8), Values[0]);
Constant *Zero = ConstantAggregateZero::get(Ty);
Initializers.push_back(Zero);
break;
}
case naclbitc::GLOBALVAR_DATA: {
// DATA: [b0, b1, ...]
if (!isValidRecordSizeAtLeast(1, "Globals data"))
return;
reserveInitializer("Globals data");
unsigned Size = Values.size();
SmallVector<uint8_t, 32> Buf;
for (unsigned i = 0; i < Size; ++i)
Buf.push_back(static_cast<uint8_t>(Values[i]));
Constant *Init = ConstantDataArray::get(
Context->getLLVMContext(), ArrayRef<uint8_t>(Buf.data(), Buf.size()));
Initializers.push_back(Init);
break;
}
case naclbitc::GLOBALVAR_RELOC: {
// RELOC: [val, [addend]]
if (!isValidRecordSizeInRange(1, 2, "Globals reloc"))
return;
Constant *BaseVal = Context->getOrCreateGlobalVarRef(Values[0]);
if (BaseVal == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Can't find global relocation value: " << Values[0];
Error(StrBuf.str());
return;
}
Type *IntPtrType = Context->convertToLLVMType(Context->getIcePointerType());
Constant *Val = ConstantExpr::getPtrToInt(BaseVal, IntPtrType);
if (Values.size() == 2) {
Val = ConstantExpr::getAdd(Val, ConstantInt::get(IntPtrType, Values[1]));
}
Initializers.push_back(Val);
break;
}
default:
BlockParserBaseClass::ProcessRecord();
return;
}
// If reached, just processed another initializer. See if time
// to install global.
if (InitializersNeeded == Initializers.size())
installGlobalVar();
}
// Parses a valuesymtab block in the bitcode file.
class ValuesymtabParser : public BlockParserBaseClass {
typedef SmallString<128> StringType;
public:
ValuesymtabParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser,
bool AllowBbEntries)
: BlockParserBaseClass(BlockID, EnclosingParser),
AllowBbEntries(AllowBbEntries) {}
virtual ~ValuesymtabParser() LLVM_OVERRIDE {}
private:
// True if entries to name basic blocks allowed.
bool AllowBbEntries;
virtual void ProcessRecord() LLVM_OVERRIDE;
void ConvertToString(StringType &ConvertedName) {
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
for (size_t i = 1, e = Values.size(); i != e; ++i) {
ConvertedName += static_cast<char>(Values[i]);
}
}
};
void ValuesymtabParser::ProcessRecord() {
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
StringType ConvertedName;
switch (Record.GetCode()) {
case naclbitc::VST_CODE_ENTRY: {
// VST_ENTRY: [ValueId, namechar x N]
if (!isValidRecordSizeAtLeast(2, "Valuesymtab value entry"))
return;
ConvertToString(ConvertedName);
Value *V = Context->getGlobalValueByID(Values[0]);
if (V == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid global address ID in valuesymtab: " << Values[0];
Error(StrBuf.str());
return;
}
V->setName(StringRef(ConvertedName.data(), ConvertedName.size()));
return;
}
case naclbitc::VST_CODE_BBENTRY: {
// VST_BBENTRY: [BbId, namechar x N]
// For now, since we aren't processing function blocks, don't handle.
if (AllowBbEntries) {
Error("Valuesymtab bb entry not implemented");
return;
}
break;
}
default:
break;
}
// If reached, don't know how to handle record.
BlockParserBaseClass::ProcessRecord();
return;
}
/// Parses function blocks in the bitcode file.
class FunctionParser : public BlockParserBaseClass {
FunctionParser(const FunctionParser &) LLVM_DELETED_FUNCTION;
FunctionParser &operator=(const FunctionParser &) LLVM_DELETED_FUNCTION;
public:
FunctionParser(unsigned BlockID, BlockParserBaseClass *EnclosingParser)
: BlockParserBaseClass(BlockID, EnclosingParser),
Func(new Ice::Cfg(getTranslator().getContext())), CurrentBbIndex(0),
FcnId(Context->getNextFunctionBlockValueID()),
LLVMFunc(cast<Function>(Context->getGlobalValueByID(FcnId))),
CachedNumGlobalValueIDs(Context->getNumGlobalValueIDs()),
InstIsTerminating(false) {
Func->setFunctionName(LLVMFunc->getName());
Func->setReturnType(Context->convertToIceType(LLVMFunc->getReturnType()));
Func->setInternal(LLVMFunc->hasInternalLinkage());
CurrentNode = InstallNextBasicBlock();
for (Function::const_arg_iterator ArgI = LLVMFunc->arg_begin(),
ArgE = LLVMFunc->arg_end();
ArgI != ArgE; ++ArgI) {
Func->addArg(NextInstVar(Context->convertToIceType(ArgI->getType())));
}
}
~FunctionParser() LLVM_OVERRIDE;
private:
// Timer for reading function bitcode and converting to ICE.
Ice::Timer TConvert;
// The corresponding ICE function defined by the function block.
Ice::Cfg *Func;
// The index to the current basic block being built.
uint32_t CurrentBbIndex;
// The basic block being built.
Ice::CfgNode *CurrentNode;
// The ID for the function.
unsigned FcnId;
// The corresponding LLVM function.
Function *LLVMFunc;
// Holds operands local to the function block, based on indices
// defined in the bitcode file.
std::vector<Ice::Operand *> LocalOperands;
// Holds the dividing point between local and global absolute value indices.
uint32_t CachedNumGlobalValueIDs;
// True if the last processed instruction was a terminating
// instruction.
bool InstIsTerminating;
virtual void ProcessRecord() LLVM_OVERRIDE;
virtual void ExitBlock() LLVM_OVERRIDE;
// Creates and appends a new basic block to the list of basic blocks.
Ice::CfgNode *InstallNextBasicBlock() { return Func->makeNode(); }
// Returns the Index-th basic block in the list of basic blocks.
Ice::CfgNode *GetBasicBlock(uint32_t Index) {
const Ice::NodeList &Nodes = Func->getNodes();
if (Index >= Nodes.size()) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Reference to basic block " << Index
<< " not found. Must be less than " << Nodes.size();
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
Index = 0;
}
return Nodes[Index];
}
// Returns the Index-th basic block in the list of basic blocks.
// Assumes Index corresponds to a branch instruction. Hence, if
// the branch references the entry block, it also generates a
// corresponding error.
Ice::CfgNode *getBranchBasicBlock(uint32_t Index) {
if (Index == 0) {
Error("Branch to entry block not allowed");
// TODO(kschimpf) Remove error recovery once implementation complete.
}
return GetBasicBlock(Index);
}
// Generates the next available local variable using the given
// type. Note: if Ty is void, this function returns NULL.
Ice::Variable *NextInstVar(Ice::Type Ty) {
if (Ty == Ice::IceType_void)
return NULL;
Ice::Variable *Var = Func->makeVariable(Ty, CurrentNode);
LocalOperands.push_back(Var);
return Var;
}
// Converts a relative index (to the next instruction to be read) to
// an absolute value index.
uint32_t convertRelativeToAbsIndex(int32_t Id) {
int32_t AbsNextId = CachedNumGlobalValueIDs + LocalOperands.size();
if (Id > 0 && AbsNextId < Id) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid relative value id: " << Id
<< " (must be <= " << AbsNextId << ")";
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
return 0;
}
return AbsNextId - Id;
}
// Returns the value referenced by the given value Index.
Ice::Operand *getOperand(uint32_t Index) {
if (Index < CachedNumGlobalValueIDs) {
// TODO(kschimpf): Define implementation.
report_fatal_error("getOperand of global addresses not implemented");
}
uint32_t LocalIndex = Index - CachedNumGlobalValueIDs;
if (LocalIndex >= LocalOperands.size()) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Value index " << Index << " out of range. Must be less than "
<< (LocalOperands.size() + CachedNumGlobalValueIDs);
Error(StrBuf.str());
report_fatal_error("Unable to continue");
}
return LocalOperands[LocalIndex];
}
// Returns the relative operand (wrt to next instruction) referenced by
// the given value index.
Ice::Operand *getRelativeOperand(uint32_t Index) {
return getOperand(convertRelativeToAbsIndex(Index));
}
// 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 false if valid. Otherwise generates an error message and
// returns true.
bool isValidFloatingArithOp(Ice::InstArithmetic::OpKind Op, Ice::Type OpTy) {
if (Ice::isFloatingType(OpTy))
return true;
ReportInvalidBinaryOp(Op, OpTy);
return false;
}
// Reports that the given binary Opcode, for the given type Ty,
// is not understood.
void ReportInvalidBinopOpcode(unsigned Opcode, Ice::Type Ty);
// 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) {
Instruction::BinaryOps LLVMOpcode;
if (!naclbitc::DecodeBinaryOpcode(Opcode, Context->convertToLLVMType(Ty),
LLVMOpcode)) {
ReportInvalidBinopOpcode(Opcode, Ty);
// TODO(kschimpf) Remove error recovery once implementation complete.
Op = Ice::InstArithmetic::Add;
return false;
}
switch (LLVMOpcode) {
default: {
ReportInvalidBinopOpcode(Opcode, Ty);
// TODO(kschimpf) Remove error recovery once implementation complete.
Op = Ice::InstArithmetic::Add;
return false;
}
case Instruction::Add:
Op = Ice::InstArithmetic::Add;
return isValidIntegerArithOp(Op, Ty);
case Instruction::FAdd:
Op = Ice::InstArithmetic::Fadd;
return isValidFloatingArithOp(Op, Ty);
case Instruction::Sub:
Op = Ice::InstArithmetic::Sub;
return isValidIntegerArithOp(Op, Ty);
case Instruction::FSub:
Op = Ice::InstArithmetic::Fsub;
return isValidFloatingArithOp(Op, Ty);
case Instruction::Mul:
Op = Ice::InstArithmetic::Mul;
return isValidIntegerArithOp(Op, Ty);
case Instruction::FMul:
Op = Ice::InstArithmetic::Fmul;
return isValidFloatingArithOp(Op, Ty);
case Instruction::UDiv:
Op = Ice::InstArithmetic::Udiv;
return isValidIntegerArithOp(Op, Ty);
case Instruction::SDiv:
Op = Ice::InstArithmetic::Sdiv;
return isValidIntegerArithOp(Op, Ty);
case Instruction::FDiv:
Op = Ice::InstArithmetic::Fdiv;
return isValidFloatingArithOp(Op, Ty);
case Instruction::URem:
Op = Ice::InstArithmetic::Urem;
return isValidIntegerArithOp(Op, Ty);
case Instruction::SRem:
Op = Ice::InstArithmetic::Srem;
return isValidIntegerArithOp(Op, Ty);
case Instruction::FRem:
Op = Ice::InstArithmetic::Frem;
return isValidFloatingArithOp(Op, Ty);
case Instruction::Shl:
Op = Ice::InstArithmetic::Shl;
return isValidIntegerArithOp(Op, Ty);
case Instruction::LShr:
Op = Ice::InstArithmetic::Lshr;
return isValidIntegerArithOp(Op, Ty);
case Instruction::AShr:
Op = Ice::InstArithmetic::Ashr;
return isValidIntegerArithOp(Op, Ty);
case Instruction::And:
Op = Ice::InstArithmetic::And;
return isValidIntegerLogicalOp(Op, Ty);
case Instruction::Or:
Op = Ice::InstArithmetic::Or;
return isValidIntegerLogicalOp(Op, Ty);
case Instruction::Xor:
Op = Ice::InstArithmetic::Xor;
return isValidIntegerLogicalOp(Op, Ty);
}
}
/// Converts an LLVM cast opcode LLVMCastOp to the corresponding Ice
/// cast opcode and assigns to CastKind. Returns true if successful,
/// false otherwise.
bool convertLLVMCastOpToIceOp(Instruction::CastOps LLVMCastOp,
Ice::InstCast::OpKind &CastKind) const {
switch (LLVMCastOp) {
case Instruction::ZExt:
CastKind = Ice::InstCast::Zext;
break;
case Instruction::SExt:
CastKind = Ice::InstCast::Sext;
break;
case Instruction::Trunc:
CastKind = Ice::InstCast::Trunc;
break;
case Instruction::FPTrunc:
CastKind = Ice::InstCast::Fptrunc;
break;
case Instruction::FPExt:
CastKind = Ice::InstCast::Fpext;
break;
case Instruction::FPToSI:
CastKind = Ice::InstCast::Fptosi;
break;
case Instruction::FPToUI:
CastKind = Ice::InstCast::Fptoui;
break;
case Instruction::SIToFP:
CastKind = Ice::InstCast::Sitofp;
break;
case Instruction::UIToFP:
CastKind = Ice::InstCast::Uitofp;
break;
case Instruction::BitCast:
CastKind = Ice::InstCast::Bitcast;
break;
default:
return false;
}
return true;
}
// 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:
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:
return false;
}
}
};
FunctionParser::~FunctionParser() {
if (getTranslator().getFlags().SubzeroTimingEnabled) {
errs() << "[Subzero timing] Convert function " << Func->getFunctionName()
<< ": " << TConvert.getElapsedSec() << " sec\n";
}
}
void FunctionParser::ReportInvalidBinopOpcode(unsigned Opcode, Ice::Type Ty) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Binary opcode " << Opcode << "not understood for type " << Ty;
Error(StrBuf.str());
}
void FunctionParser::ExitBlock() {
// Before translating, check for blocks without instructions, and
// insert unreachable. This shouldn't happen, but be safe.
unsigned Index = 0;
const Ice::NodeList &Nodes = Func->getNodes();
for (std::vector<Ice::CfgNode *>::const_iterator Iter = Nodes.begin(),
IterEnd = Nodes.end();
Iter != IterEnd; ++Iter, ++Index) {
Ice::CfgNode *Node = *Iter;
if (Node->getInsts().size() == 0) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Basic block " << Index << " contains no instructions";
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
Node->appendInst(Ice::InstUnreachable::create(Func));
}
}
// Note: Once any errors have been found, we turn off all
// translation of all remaining functions. This allows use to see
// multiple errors, without adding extra checks to the translator
// for such parsing errors.
if (Context->getNumErrors() == 0)
getTranslator().translateFcn(Func);
}
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() {
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
if (InstIsTerminating) {
InstIsTerminating = false;
CurrentNode = GetBasicBlock(++CurrentBbIndex);
}
Ice::Inst *Inst = NULL;
switch (Record.GetCode()) {
case naclbitc::FUNC_CODE_DECLAREBLOCKS: {
// DECLAREBLOCKS: [n]
if (!isValidRecordSize(1, "function block count"))
return;
if (Func->getNodes().size() != 1) {
Error("Duplicate function block count record");
return;
}
uint32_t NumBbs = Values[0];
if (NumBbs == 0) {
Error("Functions must contain at least one basic block.");
// TODO(kschimpf) Remove error recovery once implementation complete.
NumBbs = 1;
}
// Install the basic blocks, skipping bb0 which was created in the
// constructor.
for (size_t i = 1; i < NumBbs; ++i)
InstallNextBasicBlock();
break;
}
case naclbitc::FUNC_CODE_INST_BINOP: {
// BINOP: [opval, opval, opcode]
if (!isValidRecordSize(3, "function block binop"))
return;
Ice::Operand *Op1 = getRelativeOperand(Values[0]);
Ice::Operand *Op2 = getRelativeOperand(Values[1]);
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());
// TODO(kschimpf) Remove error recovery once implementation complete.
Op2 = Op1;
}
Ice::InstArithmetic::OpKind Opcode;
if (!convertBinopOpcode(Values[2], Type1, Opcode))
return;
Ice::Variable *Dest = NextInstVar(Type1);
Inst = Ice::InstArithmetic::create(Func, Opcode, Dest, Op1, Op2);
break;
}
case naclbitc::FUNC_CODE_INST_CAST: {
// CAST: [opval, destty, castopc]
if (!isValidRecordSize(3, "function block cast"))
return;
Ice::Operand *Src = getRelativeOperand(Values[0]);
Type *CastType = Context->getTypeByID(Values[1]);
Instruction::CastOps LLVMCastOp;
Ice::InstCast::OpKind CastKind;
if (!naclbitc::DecodeCastOpcode(Values[2], LLVMCastOp) ||
!convertLLVMCastOpToIceOp(LLVMCastOp, CastKind)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Cast opcode not understood: " << Values[2];
Error(StrBuf.str());
return;
}
Type *SrcType = Context->convertToLLVMType(Src->getType());
if (!CastInst::castIsValid(LLVMCastOp, SrcType, CastType)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Illegal cast: " << Instruction::getOpcodeName(LLVMCastOp)
<< " " << *SrcType << " to " << *CastType;
Error(StrBuf.str());
return;
}
Ice::Variable *Dest = NextInstVar(Context->convertToIceType(CastType));
Inst = Ice::InstCast::create(Func, CastKind, Dest, Src);
break;
}
case naclbitc::FUNC_CODE_INST_VSELECT: {
// VSELECT: [opval, opval, pred]
Ice::Operand *ThenVal = getOperand(convertRelativeToAbsIndex(Values[0]));
Ice::Type ThenType = ThenVal->getType();
Ice::Operand *ElseVal = getOperand(convertRelativeToAbsIndex(Values[1]));
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());
return;
}
Ice::Operand *CondVal = getOperand(convertRelativeToAbsIndex(Values[2]));
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 " << CondType
<< " not allowed for values of type " << ThenType;
Error(StrBuf.str());
return;
}
} else if (CondVal->getType() != Ice::IceType_i1) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Select condition not type i1. Found: " << CondVal->getType();
Error(StrBuf.str());
return;
}
Ice::Variable *DestVal = NextInstVar(ThenType);
Inst = Ice::InstSelect::create(Func, DestVal, CondVal, ThenVal, ElseVal);
break;
}
case naclbitc::FUNC_CODE_INST_EXTRACTELT: {
// EXTRACTELT: [opval, opval]
if (!isValidRecordSize(2, "function block extract element"))
return;
Ice::Operand *Vec = getRelativeOperand(Values[0]);
Ice::Type VecType = Vec->getType();
if (!Ice::isVectorType(VecType)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Extractelement not on vector. Found: " << Vec;
Error(StrBuf.str());
}
Ice::Operand *Index = getRelativeOperand(Values[1]);
if (Index->getType() != Ice::IceType_i32) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Extractelement index not i32. Found: " << Index;
Error(StrBuf.str());
}
// TODO(kschimpf): Restrict index to a legal constant index (once
// constants can be defined).
Ice::Variable *Dest = NextInstVar(typeElementType(VecType));
Inst = Ice::InstExtractElement::create(Func, Dest, Vec, Index);
break;
}
case naclbitc::FUNC_CODE_INST_INSERTELT: {
// INSERTELT: [opval, opval, opval]
if (!isValidRecordSize(3, "function block insert element"))
return;
Ice::Operand *Vec = getRelativeOperand(Values[0]);
Ice::Type VecType = Vec->getType();
if (!Ice::isVectorType(VecType)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Insertelement not on vector. Found: " << Vec;
Error(StrBuf.str());
}
Ice::Operand *Elt = getRelativeOperand(Values[1]);
Ice::Type EltType = Elt->getType();
if (EltType != typeElementType(VecType)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Insertelement element not " << typeElementType(VecType)
<< ". Found: " << Elt;
Error(StrBuf.str());
}
Ice::Operand *Index = getRelativeOperand(Values[2]);
if (Index->getType() != Ice::IceType_i32) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Insertelement index not i32. Found: " << Index;
Error(StrBuf.str());
}
// TODO(kschimpf): Restrict index to a legal constant index (once
// constants can be defined).
Ice::Variable *Dest = NextInstVar(EltType);
Inst = Ice::InstInsertElement::create(Func, Dest, Vec, Elt, Index);
break;
}
case naclbitc::FUNC_CODE_INST_CMP2: {
// CMP2: [opval, opval, pred]
if (!isValidRecordSize(3, "function block compare"))
return;
Ice::Operand *Op1 = getRelativeOperand(Values[0]);
Ice::Operand *Op2 = getRelativeOperand(Values[1]);
Ice::Type Op1Type = Op1->getType();
Ice::Type Op2Type = Op2->getType();
if (Op1Type != Op2Type) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Compare argument types differ: " << Op1Type
<< " and " << Op2Type;
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
Op2 = Op1;
}
Ice::Type DestType = getCompareResultType(Op1Type);
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 = NextInstVar(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());
// TODO(kschimpf) Remove error recovery once implementation complete.
Cond = Ice::InstIcmp::Eq;
}
Inst = Ice::InstIcmp::create(Func, 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());
// TODO(kschimpf) Remove error recovery once implementation complete.
Cond = Ice::InstFcmp::False;
}
Inst = Ice::InstFcmp::create(Func, 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());
return;
}
break;
}
case naclbitc::FUNC_CODE_INST_RET: {
// RET: [opval?]
if (!isValidRecordSizeInRange(0, 1, "function block ret"))
return;
if (Values.size() == 0) {
Inst = Ice::InstRet::create(Func);
} else {
Inst = Ice::InstRet::create(Func, getRelativeOperand(Values[0]));
}
InstIsTerminating = true;
break;
}
case naclbitc::FUNC_CODE_INST_BR: {
if (Values.size() == 1) {
// BR: [bb#]
Ice::CfgNode *Block = getBranchBasicBlock(Values[0]);
if (Block == NULL)
return;
Inst = Ice::InstBr::create(Func, Block);
} else {
// BR: [bb#, bb#, opval]
if (!isValidRecordSize(3, "function block branch"))
return;
Ice::Operand *Cond = getRelativeOperand(Values[2]);
if (Cond->getType() != Ice::IceType_i1) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Branch condition not i1";
Error(StrBuf.str());
return;
}
Ice::CfgNode *ThenBlock = getBranchBasicBlock(Values[0]);
Ice::CfgNode *ElseBlock = getBranchBasicBlock(Values[1]);
if (ThenBlock == NULL || ElseBlock == NULL)
return;
Inst = Ice::InstBr::create(Func, Cond, ThenBlock, ElseBlock);
}
InstIsTerminating = true;
break;
}
default:
// Generate error message!
BlockParserBaseClass::ProcessRecord();
break;
}
if (Inst)
CurrentNode->appendInst(Inst);
}
/// Parses the module block in the bitcode file.
class ModuleParser : public BlockParserBaseClass {
public:
ModuleParser(unsigned BlockID, TopLevelParser *Context)
: BlockParserBaseClass(BlockID, Context) {}
virtual ~ModuleParser() LLVM_OVERRIDE {}
protected:
virtual bool ParseBlock(unsigned BlockID) LLVM_OVERRIDE;
virtual void ProcessRecord() LLVM_OVERRIDE;
};
bool ModuleParser::ParseBlock(unsigned BlockID) LLVM_OVERRIDE {
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: {
ValuesymtabParser Parser(BlockID, this, false);
return Parser.ParseThisBlock();
}
case naclbitc::FUNCTION_BLOCK_ID: {
FunctionParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
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, "Module version"))
return;
unsigned 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, "Function heading"))
return;
Type *Ty = Context->getTypeByID(Values[0]);
FunctionType *FTy = dyn_cast<FunctionType>(Ty);
if (FTy == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Function heading expects function type. Found: " << Ty;
Error(StrBuf.str());
return;
}
CallingConv::ID CallingConv;
if (!naclbitc::DecodeCallingConv(Values[1], CallingConv)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Function heading 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 heading has unknown linkage. Found " << Values[3];
Error(StrBuf.str());
return;
}
Function *Func = Function::Create(FTy, Linkage, "", Context->getModule());
Func->setCallingConv(CallingConv);
if (Values[2] == 0)
Context->setNextValueIDAsImplementedFunction();
Context->setNextFunctionID(Func);
// TODO(kschimpf) verify if Func matches PNaCl ABI.
return;
}
default:
BlockParserBaseClass::ProcessRecord();
return;
}
}
bool TopLevelParser::ParseBlock(unsigned BlockID) {
if (BlockID == naclbitc::MODULE_BLOCK_ID) {
ModuleParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
// Generate error message by using default block implementation.
BlockParserBaseClass Parser(BlockID, this);
return Parser.ParseThisBlock();
}
} // end of anonymous namespace
namespace Ice {
void PNaClTranslator::translate(const std::string &IRFilename) {
OwningPtr<MemoryBuffer> MemBuf;
if (error_code ec =
MemoryBuffer::getFileOrSTDIN(IRFilename.c_str(), MemBuf)) {
errs() << "Error reading '" << IRFilename << "': " << ec.message() << "\n";
ErrorStatus = true;
return;
}
if (MemBuf->getBufferSize() % 4 != 0) {
errs() << IRFilename
<< ": Bitcode stream should be a multiple of 4 bytes in length.\n";
ErrorStatus = true;
return;
}
const unsigned char *BufPtr = (const unsigned char *)MemBuf->getBufferStart();
const unsigned char *EndBufPtr = BufPtr + MemBuf->getBufferSize();
// Read header and verify it is good.
NaClBitcodeHeader Header;
if (Header.Read(BufPtr, EndBufPtr) || !Header.IsSupported()) {
errs() << "Invalid PNaCl bitcode header.\n";
ErrorStatus = true;
return;
}
// Create a bitstream reader to read the bitcode file.
NaClBitstreamReader InputStreamFile(BufPtr, EndBufPtr);
NaClBitstreamCursor InputStream(InputStreamFile);
TopLevelParser Parser(*this, MemBuf->getBufferIdentifier(), Header,
InputStream, ErrorStatus);
int TopLevelBlocks = 0;
while (!InputStream.AtEndOfStream()) {
if (Parser.Parse()) {
ErrorStatus = true;
return;
}
++TopLevelBlocks;
}
if (TopLevelBlocks != 1) {
errs() << IRFilename
<< ": Contains more than one module. Found: " << TopLevelBlocks
<< "\n";
ErrorStatus = true;
}
return;
}
} // end of namespace Ice