blob: f1606596baf46e9c93577f21d2ccc5d45718425b [file] [log] [blame] [edit]
//===-- NaClObjDump.cpp - Dump PNaCl bitcode contents ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
#include "llvm/Analysis/NaCl/PNaClABIProps.h"
#include "llvm/Analysis/NaCl/PNaClABITypeChecker.h"
#include "llvm/Analysis/NaCl/PNaClAllowedIntrinsics.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeDecoders.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
#include "llvm/Bitcode/NaCl/NaClBitCodes.h"
#include "llvm/Bitcode/NaCl/NaClObjDumpStream.h"
#include "llvm/Bitcode/NaCl/NaClReaderWriter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
namespace {
using namespace llvm;
static cl::opt<bool>
ReportWarningsAsErrors(
"Werror",
cl::desc("Report warnings as errors."),
cl::init(false));
static cl::opt<bool>
IgnorePNaClABIChecks(
"ignore-pnaclabi-checks",
cl::desc("Ignore checking bitcode for PNaCl ABI violations"),
cl::init(false));
/// Class to handle sign rotations in a human readable form. That is,
/// the sign is in the low bit. The two special cases are:
/// 1) -1 is true for i1.
/// 2) The representation allows -0 (which is different than 0).
class SignRotatedInt {
public:
SignRotatedInt(uint64_t Value, Type* ValueType)
: SignedValue(Value >> 1),
IsNegated((Value & 0x1)
&& !(ValueType->isIntegerTy()
&& ValueType->getIntegerBitWidth() == 1)) {
}
SignRotatedInt()
: SignedValue(0), IsNegated(false) {}
explicit SignRotatedInt(const SignRotatedInt &V)
: SignedValue(V.SignedValue), IsNegated(V.IsNegated) {}
void operator=(const SignRotatedInt &V) {
SignedValue = V.SignedValue;
IsNegated = V.IsNegated;
}
void Print(raw_ostream &Strm) const {
if (IsNegated) Strm << "-";
Strm << SignedValue;
}
private:
int64_t SignedValue;
bool IsNegated;
};
inline raw_ostream &operator<<(raw_ostream &Strm, const SignRotatedInt &V) {
V.Print(Strm);
return Strm;
}
/// Convenience class to be able to print value ids to raw_ostream's.
/// Kinds of bitcode Ids:
/// a : For abbreviations.
/// b : For basic blocks.
/// c : For local constants.
/// f : For function addresses.
/// g : For global variable addresses.
/// p : For parameter arguments.
/// t : For type values.
/// v : For values generated by instructions.
class BitcodeId {
public:
BitcodeId(char Kind, uint32_t Index)
: Kind(Kind), Index(Index), IsGlobal(IsGlobalKind(Kind)) {
}
BitcodeId(const BitcodeId &Id)
: Kind(Id.Kind), Index(Id.Index), IsGlobal(Id.IsGlobal) {}
BitcodeId(char Kind, uint32_t Index, bool IsGlobal)
: Kind(Kind), Index(Index), IsGlobal(IsGlobal) {}
void operator=(const BitcodeId &Id) {
Kind = Id.Kind;
Index = Id.Index;
IsGlobal = Id.IsGlobal;
}
raw_ostream &Print(raw_ostream &Stream) const {
return Stream << Prefix() << Kind << Index;
}
char GetKind() const {
return Kind;
}
std::string GetName() const {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
Print(StrBuf);
return StrBuf.str();
}
private:
char Kind;
uint32_t Index;
bool IsGlobal;
// Returns true if (default) assumption for kind implies global.
static bool IsGlobalKind(char Kind);
// Returns the bitcode prefix character that communicates if the
// bitcode Id is lcoal or global.
char Prefix() const { return IsGlobal ? '@' : '%'; }
};
bool BitcodeId::IsGlobalKind(char Kind) {
switch (Kind) {
case 'f':
case 'g':
case 't':
return true;
case 'b':
case 'c':
case 'p':
case 'v':
return false;
default:
errs() << "Bad bitcode id, can't determine (statically) if global: "
<< Kind << "\n";
report_fatal_error("Unable to continue");
}
}
raw_ostream &operator<<(raw_ostream &Stream, const BitcodeId &Id) {
return Id.Print(Stream);
}
class NaClDisBlockParser;
/// The text formatter for PNaClAsm instructions.
class AssemblyTextFormatter : public naclbitc::TextFormatter {
public:
// Special directive to tokenize type expressions. Used to convert
// type signatures into a sequence of tokens.
class TypeDirective : public naclbitc::TextFormatter::Directive {
public:
TypeDirective(TextFormatter *Formatter)
: naclbitc::TextFormatter::Directive(Formatter),
Typ(0),
FcnId(0),
AddParams(false) {}
~TypeDirective() override {}
private:
/// Calls the corresponding method in AssemblyTextFormatter, with
/// the locally stored arguments.
void MyApply(bool Replay) const override;
void MaybeSaveForReplay() const override {}
// The type to tokenize.
Type *Typ;
// Pointer to function id, if not NULL.
BitcodeId *FcnId;
// true if parameter id's should be added to function signature.
bool AddParams;
friend class AssemblyTextFormatter;
// Internal routine to allow AssemblyTextFormatter::AllocateTypeDirective
// to initialize a type directive.
void Init(Type *NewTyp, BitcodeId *NewFcnId, bool NewAddParams) {
Typ = NewTyp;
FcnId = NewFcnId;
AddParams = NewAddParams;
}
};
// Special directive to tokenize an abbreviation. Used to convert
// an abbreviation (pointer) into a sequence of tokens.
class AbbreviationDirective : public naclbitc::TextFormatter::Directive {
public:
AbbreviationDirective(TextFormatter *Formatter)
: naclbitc::TextFormatter::Directive(Formatter),
Abbrev(0) {}
~AbbreviationDirective() override {}
private:
void MyApply(bool Replay) const override;
void MaybeSaveForReplay() const override {}
// The abbreviation to tokenize.
NaClBitCodeAbbrev *Abbrev;
friend class AssemblyTextFormatter;
};
// Special directive to tokenize an abbreviation index, if the corresponding
// record in the block parser used a user-defined abbreviation.
class AbbrevIndexDirective : public naclbitc::TextFormatter::Directive {
public:
AbbrevIndexDirective(TextFormatter *Formatter)
: naclbitc::TextFormatter::Directive(Formatter),
Record(0), NumGlobalAbbreviations(0) {}
~AbbrevIndexDirective() override {}
private:
void MyApply(bool Replay) const override;
void MaybeSaveForReplay() const override {}
// The record containing the associated abbreviation.
NaClBitcodeRecord *Record;
// The number of global abbreviations defined for the block the
// record appears in.
unsigned NumGlobalAbbreviations;
friend class AssemblyTextFormatter;
};
public:
/// Creates an assembly text formatter for the given dump stream.
AssemblyTextFormatter(naclbitc::ObjDumpStream &ObjDump)
: TextFormatter(ObjDump.Assembly(),
std::max(20U, GetAssemblyWidth(ObjDump)),
" "),
Comma(this, ","),
Semicolon(this, ";"),
Colon(this, ":"),
Space(this),
OpenParen(this, "("),
CloseParen(this, ")"),
OpenAngle(this, "<"),
CloseAngle(this, ">"),
OpenCurly(this, "{"),
CloseCurly(this, "}"),
OpenSquare(this, "["),
CloseSquare(this, "]"),
Endline(this),
StartCluster(this),
FinishCluster(this)
{
ContinuationIndent = GetIndent(2);
}
~AssemblyTextFormatter() override {}
naclbitc::TokenTextDirective Comma;
naclbitc::TokenTextDirective Semicolon;
naclbitc::TokenTextDirective Colon;
naclbitc::SpaceTextDirective Space;
naclbitc::OpenTextDirective OpenParen;
naclbitc::CloseTextDirective CloseParen;
naclbitc::OpenTextDirective OpenAngle;
naclbitc::CloseTextDirective CloseAngle;
naclbitc::OpenTextDirective OpenCurly;
naclbitc::CloseTextDirective CloseCurly;
naclbitc::OpenTextDirective OpenSquare;
naclbitc::CloseTextDirective CloseSquare;
naclbitc::EndlineTextDirective Endline;
naclbitc::StartClusteringDirective StartCluster;
naclbitc::FinishClusteringDirective FinishCluster;
/// Prints the given type as a sequence of tokens.
TypeDirective &TokenizeType(Type *Typ) {
return AllocateTypeDirective(Typ, 0, false);
}
/// Prints the named function type as a sequence of tokens.
/// Typ is the type signature of the function, and FunctionName
/// points to the name of the function.
TypeDirective &TokenizeFunctionType(FunctionType *Typ,
BitcodeId *FunctionName) {
return AllocateTypeDirective(Typ, FunctionName, false);
}
/// Prints the function signature of the function type. Typ is
/// the type signature of the function. FunctionName points to the
/// name of the function. Note: Unlike TokenizeFunctionType, this
/// method also adds the parameter names to paramter argumements.
TypeDirective &TokenizeFunctionSignature(FunctionType *Typ,
BitcodeId *FunctionName) {
return AllocateTypeDirective(Typ, FunctionName, true);
}
/// Prints out the abbreviation defined by Abbrev.
AbbreviationDirective &TokenizeAbbreviation(NaClBitCodeAbbrev *Abbrev) {
AbbreviationDirective *Dir = AbbrevDirMemoryPool.Allocate(this);
Dir->Abbrev = Abbrev;
return *Dir;
}
/// If the record was read using a user-defined abbreviation,
/// generates assembly tokens describing the abbreviation index
/// used. Otherwise, no tokens are generated. NumGlobalAbbreviations
/// is used to determine if the user-defined abbreviation is local
/// or global.
AbbrevIndexDirective &TokenizeAbbrevIndex(NaClBitcodeRecord &Record,
unsigned NumGlobalAbbreviations) {
AbbrevIndexDirective *Dir = AbbrevIndexDirMemoryPool.Allocate(this);
Dir->Record = &Record;
Dir->NumGlobalAbbreviations = NumGlobalAbbreviations;
return *Dir;
}
private:
// Converts the given type to tokens, based on the values passed in
// by TokenizeType, TokenizeFunctionType, or TokenizeFunctionSignature.
void TokenizeTypeInternal(Type *Typ, BitcodeId* FcnName, bool AddParams);
// The free list of type directives.
naclbitc::DirectiveMemoryPool<TypeDirective> TypeDirMemoryPool;
// Allocates an instance of TypeDirective with the following fields.
TypeDirective &AllocateTypeDirective(Type *Typ, BitcodeId *FcnId,
bool AddParams);
// Converts the given abbreviation to tokens.
void TokenizeAbbreviationInternal(const NaClBitCodeAbbrev *Abbrev);
// Helper function that prints out the abbreviation expression
// located at Index in the given abbreviation. Updates Index to
// next expression in the abbreviation.
void TokenizeAbbrevExpression(const NaClBitCodeAbbrev *Abbrev,
unsigned &Index);
// Prints out the given abbreviation operator.
void TokenizeAbbrevOp(const NaClBitCodeAbbrevOp &Op);
// The free list for abbreviation directives.
naclbitc::DirectiveMemoryPool<AbbreviationDirective> AbbrevDirMemoryPool;
// The free list for abbreviation index directives.
naclbitc::DirectiveMemoryPool<AbbrevIndexDirective> AbbrevIndexDirMemoryPool;
// Computes how wide the assembly portion of the dump file should be.
static unsigned GetAssemblyWidth(naclbitc::ObjDumpStream &ObjDump) {
int Diff = 80 - (ObjDump.GetRecordWidth()+1);
// Make sure there is enough room to print out assembly.
return Diff < 20 ? 20 : Diff;
}
};
void AssemblyTextFormatter::TypeDirective::MyApply(bool Replay) const {
assert(!Replay && "Shouldn't have been saved for replay");
AssemblyTextFormatter *AssemblyFormatter =
reinterpret_cast<AssemblyTextFormatter*>(Formatter);
AssemblyFormatter->TokenizeTypeInternal(Typ, FcnId, AddParams);
AssemblyFormatter->TypeDirMemoryPool.Free(const_cast<TypeDirective*>(this));
}
void AssemblyTextFormatter::AbbreviationDirective::MyApply(bool Replay) const {
assert(!Replay && "Shouldn't have been saved for replay");
AssemblyTextFormatter *AssemblyFormatter =
reinterpret_cast<AssemblyTextFormatter*>(Formatter);
AssemblyFormatter->TokenizeAbbreviationInternal(Abbrev);
AssemblyFormatter->AbbrevDirMemoryPool.Free(
const_cast<AbbreviationDirective*>(this));
}
AssemblyTextFormatter::TypeDirective &AssemblyTextFormatter::
AllocateTypeDirective(Type *Typ, BitcodeId *FcnId, bool AddParams) {
TypeDirective *Element = TypeDirMemoryPool.Allocate(this);
Element->Init(Typ, FcnId, AddParams);
return *Element;
}
void AssemblyTextFormatter::TokenizeTypeInternal(
Type *Typ, BitcodeId* FcnName, bool AddParams) {
switch (Typ->getTypeID()) {
case Type::VoidTyID:
TextStream << "void";
break;
case Type::FloatTyID:
TextStream << "float";
break;
case Type::DoubleTyID:
TextStream << "double";
break;
case Type::IntegerTyID:
TextStream << "i" << Typ->getIntegerBitWidth();
break;
case Type::VectorTyID: {
VectorType *VecType = cast<VectorType>(Typ);
TextStream << StartCluster << OpenAngle << VecType->getNumElements()
<< Space << "x" << Space
<< TokenizeType(VecType->getElementType())
<< CloseAngle << FinishCluster;
break;
}
case Type::FunctionTyID: {
FunctionType *FcnType = cast<FunctionType>(Typ);
unsigned NumParams = FcnType->getFunctionNumParams();
TextStream << StartCluster
<< TokenizeType(FcnType->getReturnType())
<< Space
<< StartCluster;
if (NumParams > 0) TextStream << StartCluster;
if (FcnName) TextStream << *FcnName;
TextStream << OpenParen << FinishCluster;
bool HasParamStartCluster = false;
for (unsigned i = 0, NumParams = FcnType->getFunctionNumParams();
i < NumParams; ++i) {
if (i > 0) {
TextStream << Comma << FinishCluster << Space;
HasParamStartCluster = false;
}
TextStream << StartCluster
<< TokenizeType(FcnType->getFunctionParamType(i));
if (AddParams) TextStream << Space << BitcodeId('p', i);
HasParamStartCluster = true;
}
if (FcnType->isFunctionVarArg()) {
if (HasParamStartCluster) {
TextStream << Comma << FinishCluster << Space;
}
TextStream << StartCluster << "...";
HasParamStartCluster = true;
}
if (HasParamStartCluster) TextStream << FinishCluster;
TextStream << CloseParen << FinishCluster;
if (NumParams > 0) TextStream << FinishCluster;
break;
}
default:
report_fatal_error("Unsupported PNaCl type found");
break;
}
}
void AssemblyTextFormatter::TokenizeAbbrevOp(const NaClBitCodeAbbrevOp &Op) {
// Note: Mimics NaClBitCodeAbbrevOp::Print in NaClBitCodes.cpp
switch (Op.getEncoding()) {
case NaClBitCodeAbbrevOp::Literal:
Tokens() << Op.getValue();
return;
case NaClBitCodeAbbrevOp::Fixed:
Tokens() << StartCluster << "fixed" << OpenParen << Op.getValue()
<< CloseParen << FinishCluster;
return;
case NaClBitCodeAbbrevOp::VBR:
Tokens() << StartCluster << "vbr" << OpenParen << Op.getValue()
<< CloseParen << FinishCluster;
return;
case NaClBitCodeAbbrevOp::Array:
Tokens() << "array";
return;
case NaClBitCodeAbbrevOp::Char6:
Tokens() << "char6";
return;
}
}
void AssemblyTextFormatter::
TokenizeAbbrevExpression(const NaClBitCodeAbbrev *Abbrev, unsigned &Index) {
// Note: Mimics PrintExpression in NaClBitCodes.cpp
const NaClBitCodeAbbrevOp &Op = Abbrev->getOperandInfo(Index);
TokenizeAbbrevOp(Op);
if (unsigned NumArgs = Op.NumArguments()) {
Tokens() << StartCluster << OpenParen;
for (unsigned i = 0; i < NumArgs; ++i) {
++Index;
if (i > 0) {
Tokens() << Comma << FinishCluster << Space << StartCluster;
}
TokenizeAbbrevExpression(Abbrev, Index);
}
Tokens() << CloseParen << FinishCluster;
}
}
void AssemblyTextFormatter::
TokenizeAbbreviationInternal(const NaClBitCodeAbbrev *Abbrev) {
Tokens() << StartCluster << OpenAngle;
for (unsigned i = 0; i < Abbrev->getNumOperandInfos(); ++i) {
if (i > 0) {
Tokens() << Comma << FinishCluster << Space << StartCluster;
}
TokenizeAbbrevExpression(Abbrev, i);
}
Tokens() << CloseAngle << FinishCluster;
}
void AssemblyTextFormatter::AbbrevIndexDirective::MyApply(bool Replay) const {
assert(!Replay && "Shouldn't have been saved for replay");
if (!Record->UsedAnAbbreviation()) return;
unsigned Index = Record->GetAbbreviationIndex();
if (Index < naclbitc::FIRST_APPLICATION_ABBREV) return;
Index -= naclbitc::FIRST_APPLICATION_ABBREV;
bool IsGlobal = Index < NumGlobalAbbreviations;
if (!IsGlobal) Index -= NumGlobalAbbreviations;
BitcodeId AbbrevIndex('a', Index, IsGlobal);
AssemblyTextFormatter *Fmtr =
reinterpret_cast<AssemblyTextFormatter*>(Formatter);
Tokens() << Fmtr->Space << Fmtr->StartCluster << Fmtr->OpenAngle
<< AbbrevIndex << Fmtr->CloseAngle << Fmtr->FinishCluster;
Fmtr->AbbrevIndexDirMemoryPool.Free(const_cast<AbbrevIndexDirective*>(this));
}
// Holds possible alignements for loads/stores etc. Used to extract
// expected values.
static unsigned NaClPossibleLoadStoreAlignments[] = {
1, 2, 4, 8
};
// Finds the expected alignments for the load/store, for type Ty.
static void NaClGetExpectedLoadStoreAlignment(
const DataLayout &DL, Type *Ty, std::vector<unsigned> &ValidAlignments) {
for (size_t i = 0; i < array_lengthof(NaClPossibleLoadStoreAlignments); ++i) {
unsigned Alignment = NaClPossibleLoadStoreAlignments[i];
if (PNaClABIProps::isAllowedAlignment(&DL, Alignment, Ty)) {
ValidAlignments.push_back(Alignment);
}
}
}
/// Top-level class to parse bitcode file and transform to
/// corresponding disassembled code.
class NaClDisTopLevelParser : public NaClBitcodeParser {
NaClDisTopLevelParser(const NaClDisTopLevelParser&) LLVM_DELETED_FUNCTION;
void operator=(const NaClDisTopLevelParser&) LLVM_DELETED_FUNCTION;
public:
NaClDisTopLevelParser(NaClBitcodeHeader &Header,
NaClBitstreamCursor &Cursor,
naclbitc::ObjDumpStream &ObjDump)
: NaClBitcodeParser(Cursor),
Mod("ObjDump", getGlobalContext()),
ObjDump(ObjDump),
AbbrevListener(this),
DL(&Mod),
AllowedIntrinsics(&Mod.getContext()),
AssemblyFormatter(ObjDump),
Header(Header),
NumFunctions(0),
NumGlobals(0),
ExpectedNumGlobals(0),
NumParams(0),
NumConstants(0),
NumValuedInsts(0),
UnknownType(Type::getVoidTy(Mod.getContext())),
PointerType(Type::getInt32Ty(Mod.getContext())),
ComparisonType(Type::getInt1Ty(Mod.getContext())),
NumDefinedFunctions(0) {
SetListener(&AbbrevListener);
}
~NaClDisTopLevelParser() override {
// Be sure to flush any remaining errors.
ObjDump.Flush();
}
// Returns the number of errors that were sent to the ObjDump.
unsigned GetNumErrors() {
return ObjDump.GetNumErrors();
}
/// Generates an error with the given message.
bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit,
const std::string &Message) final {
ObjDump.ErrorAt(Level, Bit) << Message << "\n";
if (Level == naclbitc::Fatal)
ObjDump.FlushThenQuit();
return true;
}
/// Parses the top-level module block.
bool ParseBlock(unsigned BlockID) override;
/// Installs the given type to the next available type index.
void InstallType(Type *Ty) {
TypeIdType.push_back(Ty);
}
/// Returns the type associated with the given type index.
Type *GetType(uint32_t Index) {
if (Index >= TypeIdType.size()) {
BitcodeId Id('t', Index);
Errors() << "Can't find definition for " << Id << "\n";
// Recover so that additional errors can be found.
return UnknownType;
}
return TypeIdType[Index];
}
/// Returns the number of types (currently) defined in the bitcode
/// file.
uint32_t GetNumTypes() const {
return TypeIdType.size();
}
/// Returns true if Type represents a (non-zero sized) value.
bool isValidValueType(Type *Ty) const {
return Ty->isIntegerTy() || Ty->isFloatTy() || Ty->isDoubleTy()
|| Ty->isVectorTy();
}
/// Installs the given type to the next available function index.
void InstallFunctionType(FunctionType *Ty, uint64_t BitAddress) {
assert(FunctionIdType.size() == NumFunctions);
++NumFunctions;
FunctionIdType.push_back(Ty);
FunctionIdAddress.push_back(BitAddress);
// Let Valuesymtab change this to true if appropriate.
FunctionIdIsIntrinsic.push_back(false);
}
/// Returns the bit address where the function record appeared in
/// the bitcode file. Returns 0 if unknown.
uint64_t GetFunctionIdAddress(uint32_t Index) {
if (Index >= FunctionIdAddress.size()) return 0;
return FunctionIdAddress[Index];
}
/// Sets the record bit address to the function ID address (if known).
void SetRecordAddressToFunctionIdAddress(uint32_t Index) {
uint64_t Address = GetFunctionIdAddress(Index);
if (Address)
ObjDump.SetRecordBitAddress(Address);
}
//// Returns the type associated with the given function index.
FunctionType *GetFunctionType(uint32_t Index) {
if (Index >= FunctionIdType.size()) {
BitcodeId Id('f', Index);
Errors() << "Can't find definition for " << Id << "\n";
Fatal();
}
return FunctionIdType[Index];
}
/// Marks the function associated with the given Index as intrinsic.
void MarkFunctionAsIntrinsic(uint32_t Index) {
if (Index >= FunctionIdIsIntrinsic.size()) {
BitcodeId Id('f', Index);
Errors() << "Can't define " << Id << " as intrinsic, no definition";
}
FunctionIdIsIntrinsic[Index] = true;
}
/// Returns true if the given Index is for a function intrinsic.
bool IsFunctionIntrinsic(uint32_t Index) {
if (Index >= FunctionIdIsIntrinsic.size()) return false;
return FunctionIdIsIntrinsic[Index];
}
/// Returns true if the function is an intrinsic function.
bool IsIntrinsicAllowed(const std::string &FuncName,
const FunctionType *FuncType) {
return AllowedIntrinsics.isAllowed(FuncName, FuncType);
}
/// Returns the type of the Name'd intrinsic, if defined as an
/// allowed intrinsic name. Otherwise returns 0.
FunctionType *GetIntrinsicType(const std::string &Name) {
return AllowedIntrinsics.getIntrinsicType(Name);
}
/// Returns the number of functions (currently) defined/declared in
/// the bitcode file.
uint32_t GetNumFunctions() const {
return NumFunctions;
}
/// Installs that the given function index as having a function body
/// (i.e. the function address uses the "define" keyword instead of
/// the "declare" keyword).
void InstallDefinedFunction(uint32_t Index) {
DefinedFunctions.push_back(Index);
}
/// Returns true if there is an Index defined function block.
bool HasDefinedFunctionIndex(uint32_t Index) const {
return Index < DefinedFunctions.size();
}
/// Returns the function index associated with the Index defined
/// function block.
uint32_t GetDefinedFunctionIndex(uint32_t Index) const {
assert(Index < DefinedFunctions.size());
return DefinedFunctions[NumDefinedFunctions];
}
/// Returns true if there is a next defined function index.
bool HasNextDefinedFunctionIndex() const {
return HasDefinedFunctionIndex(NumDefinedFunctions);
}
/// Returns the function index associated with the next defined function.
uint32_t GetNextDefinedFunctionIndex() const {
return GetDefinedFunctionIndex(NumDefinedFunctions);
}
/// Returns true if the given value ID corresponds to a declared (rather
/// than defined) function.
bool IsDeclaredFunction(uint32_t Index) {
if (Index >= NumFunctions) return false;
for (size_t i = 0, e = DefinedFunctions.size(); i < e; ++i) {
if (DefinedFunctions[i] == Index) return false;
}
return true;
}
/// Increments the number of (currently) defined functions in the
/// bitcode file by one.
void IncNumDefinedFunctions() {
++NumDefinedFunctions;
}
void IncNumGlobals() {
NumGlobals++;
}
/// Returns the number of globals (currently) defined in the bitcode
/// file.
uint32_t GetNumGlobals() const {
return NumGlobals;
}
/// Returns the expected number of globals (as defined by the count
/// record of the globals block).
uint32_t GetExpectedNumGlobals() const {
return ExpectedNumGlobals;
}
/// Sets field ExpectedNumGlobals to the given value.
void SetExpectedNumGlobals(uint32_t NewValue) {
ExpectedNumGlobals = NewValue;
}
/// Installs the given type to the next available parameter index.
void InstallParamType(Type *Ty) {
while (ParamIdType.size() <= NumParams) {
ParamIdType.push_back(UnknownType);
}
ParamIdType[NumParams++] = Ty;
}
/// Returns the type associated with the given parameter index.
Type *GetParamType(uint32_t Index) {
if (Index >= ParamIdType.size()) {
BitcodeId Id('p', Index);
Errors() << "Can't find defintion for " << Id << "\n";
Fatal();
}
return ParamIdType[Index];
}
/// Returns the number of parameters (currently) defined in the
/// enclosing defined function.
uint32_t GetNumParams() const {
return NumParams;
}
/// Installs the given type to the next available constant index.
void InstallConstantType(Type *Ty) {
while (ConstantIdType.size() <= NumConstants) {
ConstantIdType.push_back(UnknownType);
}
ConstantIdType[NumConstants++] = Ty;
}
/// Returns the type associated with the given constant index.
Type *GetConstantType(uint32_t Index) {
if (Index >= ConstantIdType.size()) {
BitcodeId Id('c', Index);
Errors() << "Can't find definition for " << Id << "\n";
Fatal();
}
return ConstantIdType[Index];
}
/// Returns the number of constants (currently) defined in the
/// enclosing defined function.
uint32_t GetNumConstants() const {
return NumConstants;
}
/// Installs the given type to the given instruction index. Note:
/// Instruction indices are only associated with instructions that
/// generate values.
void InstallInstType(Type *Ty, uint32_t Index) {
while (InstIdType.size() <= Index) {
InstIdType.push_back(UnknownType);
}
if (InstIdType[Index] != UnknownType && Ty != InstIdType[Index]) {
Errors() << BitcodeId('v', Index) << " defined with multiple types: "
<< *Ty << " and " << *InstIdType[Index] << "\n";
}
InstIdType[Index] = Ty;
}
/// Installs the given type to the next available instruction index.
void InstallInstType(Type *Ty) {
InstallInstType(Ty, NumValuedInsts++);
}
/// Returns the type associated with the given instruction index.
/// Note: Instruction indices are only associated with instructions
/// that generate values.
Type *GetInstType(uint32_t Index) {
if (Index >= InstIdType.size()) {
Errors() << "Can't find type for " << BitcodeId('v', Index) << "\n";
return UnknownType;
}
return InstIdType[Index];
}
/// Returns the number of instructions (in the defined function)
/// that (currently) generate values.
uint32_t GetNumValuedInstructions() const {
return NumValuedInsts;
}
/// Resets index counters local to a defined function.
void ResetLocalCounters() {
ParamIdType.clear();
NumParams = 0;
NumConstants = 0;
InstIdType.clear();
NumValuedInsts = 0;
}
/// Returns the bitcode id associated with the absolute value index
BitcodeId GetBitcodeId(uint32_t Index);
/// Returns the type associated with the given absolute value index.
/// If UnderlyingType is false, function indices always return a
/// pointer type. Otherwise, function indices returns the declared
/// type of the function index.
Type *GetValueType(uint32_t Index, bool UnderlyingType = false);
/// Returns a type to use when the type is unknown.
Type *GetUnknownType() const {
return UnknownType;
}
/// Returns the type used to represent a pointer.
Type *GetPointerType() const {
return PointerType;
}
/// Returns the type used to represent comparison results.
Type *GetComparisonType() const {
return ComparisonType;
}
/// Returns the void type.
Type *GetVoidType() const {
return Type::getVoidTy(getGlobalContext());
}
/// Returns the float (32-bit) type.
Type *GetFloatType() const {
return Type::getFloatTy(getGlobalContext());
}
/// Returns the double (64-bit) type.
Type *GetDoubleType() const {
return Type::getDoubleTy(getGlobalContext());
}
/// Returns an integer type of N bits.
Type *GetIntegerType(unsigned N) const {
return Type::getIntNTy(getGlobalContext(), N);
}
/// Returns the number of defined global abbreviations (currently)
/// for the given block id.
unsigned GetNumGlobalAbbreviations(unsigned BlockID) {
return GlobalAbbrevsCountMap[BlockID];
}
/// Increments the current number of defined global abbreviations.
void IncNumGlobalAbbreviations(unsigned BlockID) {
++GlobalAbbrevsCountMap[BlockID];
}
/// Verifies the given integer operator has the right type. Returns
/// the operator.
const char *VerifyIntArithmeticOp(const char *Op, Type *OpTy) {
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidIntArithmeticType(OpTy)) {
Errors() << Op << ": Invalid integer arithmetic type: " << *OpTy;
}
return Op;
}
// Checks the Alignment for loading/storing a value of type Ty. If
// invalid, generates an appropriate error message.
void VerifyMemoryAccessAlignment(const char *Op, Type *Ty,
unsigned Alignment) {
if (IgnorePNaClABIChecks) return;
if (!PNaClABIProps::isAllowedAlignment(&DL, Alignment, Ty)) {
raw_ostream &Errs = Errors();
std::vector<unsigned> Alignments;
NaClGetExpectedLoadStoreAlignment(DL, Ty, Alignments);
if (!Alignments.empty()) {
Errs << Op << ": Illegal alignment for " << *Ty << ". Expects: ";
bool isFirst = true;
for (auto Align : Alignments) {
if (isFirst)
isFirst = false;
else
Errs << " or ";
Errs << Align;
}
Errs << "\n";
} else {
Errs << Op << ": Not allowed for type: " << *Ty << "\n";
}
}
}
// ******************************************************
// The following return the corresponding methods/fields
// from the the assembly formatter/objdumper.
// ******************************************************
naclbitc::TokenTextDirective &Semicolon() {
return AssemblyFormatter.Semicolon;
}
naclbitc::TokenTextDirective &Colon() {
return AssemblyFormatter.Colon;
}
naclbitc::SpaceTextDirective &Space() {
return AssemblyFormatter.Space;
}
naclbitc::TokenTextDirective &Comma() {
return AssemblyFormatter.Comma;
}
naclbitc::OpenTextDirective &OpenParen() {
return AssemblyFormatter.OpenParen;
}
naclbitc::CloseTextDirective &CloseParen() {
return AssemblyFormatter.CloseParen;
}
naclbitc::OpenTextDirective &OpenAngle() {
return AssemblyFormatter.OpenAngle;
}
naclbitc::CloseTextDirective &CloseAngle() {
return AssemblyFormatter.CloseAngle;
}
naclbitc::OpenTextDirective &OpenCurly() {
return AssemblyFormatter.OpenCurly;
}
naclbitc::CloseTextDirective &CloseCurly() {
return AssemblyFormatter.CloseCurly;
}
naclbitc::OpenTextDirective &OpenSquare() {
return AssemblyFormatter.OpenSquare;
}
naclbitc::CloseTextDirective &CloseSquare() {
return AssemblyFormatter.CloseSquare;
}
naclbitc::EndlineTextDirective &Endline() {
return AssemblyFormatter.Endline;
}
naclbitc::StartClusteringDirective &StartCluster() {
return AssemblyFormatter.StartCluster;
}
naclbitc::FinishClusteringDirective &FinishCluster() {
return AssemblyFormatter.FinishCluster;
}
raw_ostream &Tokens() {
return AssemblyFormatter.Tokens();
}
raw_ostream &Errors() {
return ObjDump.Error();
}
raw_ostream &Warnings() {
if (ReportWarningsAsErrors) return Errors();
return ObjDump.Warning();
}
const std::string &GetAssemblyIndent() const {
return AssemblyFormatter.GetIndent();
}
unsigned GetAssemblyNumTabs() const {
return AssemblyFormatter.GetNumTabs();
}
void IncAssemblyIndent() {
AssemblyFormatter.Inc();
}
void DecAssemblyIndent() {
AssemblyFormatter.Dec();
}
void IncRecordIndent() {
ObjDump.IncRecordIndent();
}
void DecRecordIndent() {
ObjDump.DecRecordIndent();
}
void ObjDumpFlush() {
ObjDump.Flush();
}
void ObjDumpSetRecordBitAddress(uint64_t Bit) {
ObjDump.SetRecordBitAddress(Bit);
}
void ObjDumpWrite(uint64_t Bit,
const NaClBitcodeRecordData &Record,
int32_t AbbrevIndex =
naclbitc::ABBREV_INDEX_NOT_SPECIFIED) {
ObjDump.Write(Bit, Record, AbbrevIndex);
}
void ObjDumpWrite(uint64_t Bit,
const NaClBitcodeRecord &Record) {
ObjDump.Write(Bit, Record.GetRecordData(), Record.GetAbbreviationIndex());
}
AssemblyTextFormatter::TypeDirective &
TokenizeType(Type *Typ) {
return AssemblyFormatter.TokenizeType(Typ);
}
AssemblyTextFormatter::TypeDirective &
TokenizeFunctionType(FunctionType *Type, BitcodeId *FcnId) {
return AssemblyFormatter.TokenizeFunctionType(Type, FcnId);
}
AssemblyTextFormatter::TypeDirective &
TokenizeFunctionSignature(FunctionType *Typ, BitcodeId *FcnId) {
return AssemblyFormatter.TokenizeFunctionSignature(Typ, FcnId);
}
AssemblyTextFormatter::AbbreviationDirective &
TokenizeAbbreviation(NaClBitCodeAbbrev *Abbrev) {
return AssemblyFormatter.TokenizeAbbreviation(Abbrev);
}
AssemblyTextFormatter::AbbrevIndexDirective &
TokenizeAbbrevIndex(NaClBitcodeRecord &Record,
unsigned NumGlobalAbbreviations) {
return AssemblyFormatter.TokenizeAbbrevIndex(Record,
NumGlobalAbbreviations);
}
private:
// A placeholder module for associating context and data layout.
Module Mod;
// The output stream to generate disassembly into.
naclbitc::ObjDumpStream &ObjDump;
// Listener used to get abbreviations as they are read.
NaClBitcodeParserListener AbbrevListener;
// DataLayout to use.
const DataLayout DL;
// The set of allowed intrinsics.
PNaClAllowedIntrinsics AllowedIntrinsics;
// The formatter to use to format assembly code.
AssemblyTextFormatter AssemblyFormatter;
// The header appearing before the beginning of the input stream.
NaClBitcodeHeader &Header;
// The list of known types (index i defines the type associated with
// type index i).
std::vector<Type*> TypeIdType;
// The list of known function signatures (index i defines the type
// signature associated with function index i).
std::vector<FunctionType*> FunctionIdType;
// boolean flag defining if function id is an intrinsic.
std::vector<bool> FunctionIdIsIntrinsic;
// The list of record bit addresses associated with corresponding
// declaration of function IDs.
std::vector<uint64_t> FunctionIdAddress;
// The number of function indices currently defined.
uint32_t NumFunctions;
// The number of global indices currently defined.
uint32_t NumGlobals;
// The expected number of global indices (i.e. value of count record
// in the globals block).
uint32_t ExpectedNumGlobals;
// The list of known parameter types (index i defines the type
// associated with parameter index i).
std::vector<Type*>ParamIdType;
// The number of parameter indices currently defined.
uint32_t NumParams;
std::vector<Type*>ConstantIdType;
// The number of constant indices currently defined.
uint32_t NumConstants;
// The list of known instruction types (index i defines the type
// associated with valued instruction index i).
std::vector<Type*> InstIdType;
// The number of valued instructions currently defined.
uint32_t NumValuedInsts;
// Models an unknown type.
Type *UnknownType;
// Models the pointer type.
Type *PointerType;
// Models a result of a comparison.
Type *ComparisonType;
// Keeps list of function indicies (in the order found in the bitcode)
// that correspond to defined functions.
std::vector<uint32_t> DefinedFunctions;
// The number of function indices currently known to be defined.
uint32_t NumDefinedFunctions;
// Holds the number of global abbreviations defined for each block.
std::map<unsigned, unsigned> GlobalAbbrevsCountMap;
};
BitcodeId NaClDisTopLevelParser::GetBitcodeId(uint32_t Index) {
if (Index < NumFunctions) {
return BitcodeId('f', Index);
}
Index -= NumFunctions;
if (Index < ExpectedNumGlobals) {
return BitcodeId('g', Index);
}
Index -= ExpectedNumGlobals;
if (Index < NumParams) {
return BitcodeId('p', Index);
}
Index -= NumParams;
if (Index < NumConstants) {
return BitcodeId('c', Index);
}
Index -= NumConstants;
return BitcodeId('v', Index);
}
Type *NaClDisTopLevelParser::GetValueType(uint32_t Index, bool UnderlyingType) {
uint32_t Idx = Index;
if (Idx < NumFunctions)
return UnderlyingType ? GetFunctionType(Idx) : PointerType;
Idx -= NumFunctions;
if (Idx < ExpectedNumGlobals)
return PointerType;
Idx -= ExpectedNumGlobals;
if (Idx < NumParams)
return GetParamType(Idx);
Idx -= NumParams;
if (Idx < NumConstants)
return GetConstantType(Idx);
Idx -= NumConstants;
return GetInstType(Idx);
}
// Base class of all block parsers for the bitcode file. Handles
// common actions needed by derived blocks.
//
// Note: This class also handles blocks with unknown block ID's.
class NaClDisBlockParser : public NaClBitcodeParser {
protected:
/// Constructor for the top-level block parser.
NaClDisBlockParser(unsigned BlockID, NaClDisTopLevelParser *Context)
: NaClBitcodeParser(BlockID, Context),
Context(Context){
InitAbbreviations();
}
/// Constructor for nested block parsers.
NaClDisBlockParser(unsigned BlockID, NaClDisBlockParser *EnclosingParser)
: NaClBitcodeParser(BlockID, EnclosingParser),
Context(EnclosingParser->Context) {
InitAbbreviations();
}
public:
~NaClDisBlockParser() override {
// Be sure to flush any remaining errors reported at end of block.
ObjDumpFlush();
}
bool ParseBlock(unsigned BlockID) override;
protected:
void EnterBlock(unsigned NumWords) override;
void ExitBlock() override;
void ProcessRecord() override;
void ProcessAbbreviation(unsigned BlockID, NaClBitCodeAbbrev *Abbrev,
bool IsLocal) override;
void InitAbbreviations() {
NumGlobalAbbreviations = Context->GetNumGlobalAbbreviations(GetBlockID());
NumLocalAbbreviations = 0;
}
// Prints the block header instruction for the block. Called by EnterBlock.
virtual void PrintBlockHeader();
// Dumps the corresponding record for a block enter.
void DumpEnterBlockRecord();
// Returns the identifier for the next local abbreviation appearing in
// the block.
BitcodeId NextLocalAbbreviationId() {
return BitcodeId('a', NumLocalAbbreviations, false);
}
AssemblyTextFormatter::AbbrevIndexDirective &TokenizeAbbrevIndex() {
return Context->TokenizeAbbrevIndex(Record, NumGlobalAbbreviations);
}
// *****************************************************************
// The following are dispatching methods that call the corresponding
// method on the Context (i.e. NaClDisTopLevelParser).
// *****************************************************************
/// Returns a directive to tokenize the given type.
AssemblyTextFormatter::TypeDirective &TokenizeType(Type *Type) {
return Context->TokenizeType(Type);
}
/// Returns a directive to tokenize the given function type,
/// using the given function id.
AssemblyTextFormatter::TypeDirective
&TokenizeFunctionType(FunctionType *Type, BitcodeId *FcnId) {
return Context->TokenizeFunctionType(Type, FcnId);
}
/// Returns a directive to tokenize the function signature, given the
/// type of the function signature, and the given function id.
AssemblyTextFormatter::TypeDirective
&TokenizeFunctionSignature(FunctionType *Typ, BitcodeId *FcnId) {
return Context->TokenizeFunctionSignature(Typ, FcnId);
}
AssemblyTextFormatter::AbbreviationDirective &TokenizeAbbreviation(
NaClBitCodeAbbrev *Abbrev) {
return Context->TokenizeAbbreviation(Abbrev);
}
naclbitc::TokenTextDirective &Semicolon() {
return Context->Semicolon();
}
naclbitc::TokenTextDirective &Colon() {
return Context->Colon();
}
naclbitc::SpaceTextDirective &Space() {
return Context->Space();
}
naclbitc::TokenTextDirective &Comma() {
return Context->Comma();
}
naclbitc::OpenTextDirective &OpenParen() {
return Context->OpenParen();
}
naclbitc::CloseTextDirective &CloseParen() {
return Context->CloseParen();
}
naclbitc::OpenTextDirective &OpenAngle() {
return Context->OpenAngle();
}
naclbitc::CloseTextDirective &CloseAngle() {
return Context->CloseAngle();
}
naclbitc::OpenTextDirective &OpenCurly() {
return Context->OpenCurly();
}
naclbitc::CloseTextDirective &CloseCurly() {
return Context->CloseCurly();
}
naclbitc::OpenTextDirective &OpenSquare() {
return Context->OpenSquare();
}
naclbitc::CloseTextDirective &CloseSquare() {
return Context->CloseSquare();
}
naclbitc::EndlineTextDirective &Endline() {
return Context->Endline();
}
naclbitc::StartClusteringDirective &StartCluster() {
return Context->StartCluster();
}
naclbitc::FinishClusteringDirective &FinishCluster() {
return Context->FinishCluster();
}
raw_ostream &Tokens() {
return Context->Tokens();
}
raw_ostream &Errors() {
return Context->Errors();
}
raw_ostream &Warnings() {
return Context->Warnings();
}
bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit,
const std::string &Message) final {
return Context->ErrorAt(Level, Bit, Message);
}
const std::string &GetAssemblyIndent() const {
return Context->GetAssemblyIndent();
}
unsigned GetAssemblyNumTabs() const {
return Context->GetAssemblyNumTabs();
}
void IncAssemblyIndent() {
Context->IncAssemblyIndent();
}
void DecAssemblyIndent() {
Context->DecAssemblyIndent();
}
void IncRecordIndent() {
Context->IncRecordIndent();
}
void DecRecordIndent() {
Context->DecRecordIndent();
}
void ObjDumpFlush() {
Context->ObjDumpFlush();
}
void ObjDumpWrite(uint64_t Bit,
const NaClBitcodeRecordData &Record,
int32_t AbbrevIndex =
naclbitc::ABBREV_INDEX_NOT_SPECIFIED) {
Context->ObjDumpWrite(Bit, Record, AbbrevIndex);
}
void ObjDumpWrite(uint64_t Bit,
const NaClBitcodeRecord &Record) {
Context->ObjDumpWrite(Bit, Record);
}
void ObjDumpSetRecordBitAddress(uint64_t Bit) {
Context->ObjDumpSetRecordBitAddress(Bit);
}
void InstallType(Type *Ty) {
Context->InstallType(Ty);
}
Type *GetType(uint32_t Index) {
return Context->GetType(Index);
}
uint32_t GetNumTypes() const {
return Context->GetNumTypes();
}
bool isValidValueType(Type *Ty) const {
return Context->isValidValueType(Ty);
}
FunctionType *GetFunctionType(uint32_t Index) {
return Context->GetFunctionType(Index);
}
uint32_t GetNumFunctions() const {
return Context->GetNumFunctions();
}
void IncNumGlobals() {
Context->IncNumGlobals();
}
uint32_t GetNumGlobals() const {
return Context->GetNumGlobals();
}
void InstallFunctionType(FunctionType *Ty) {
return Context->InstallFunctionType(Ty, Record.GetStartBit());
}
void InstallDefinedFunction(uint32_t Index) {
Context->InstallDefinedFunction(Index);
}
BitcodeId GetBitcodeId(uint32_t Id) {
return Context->GetBitcodeId(Id);
}
void InstallParamType(Type *Ty) {
Context->InstallParamType(Ty);
}
uint32_t GetNumParams() const {
return Context->GetNumParams();
}
void InstallConstantType(Type *ConstantType) {
Context->InstallConstantType(ConstantType);
}
Type *GetConstantType(uint32_t Index) {
return Context->GetConstantType(Index);
}
uint32_t GetNumConstants() const {
return Context->GetNumConstants();
}
void InstallInstType(Type *Ty, uint32_t Index) {
Context->InstallInstType(Ty, Index);
}
void InstallInstType(Type *Ty) {
Context->InstallInstType(Ty);
}
uint32_t GetNumValuedInstructions() const {
return Context->GetNumValuedInstructions();
}
Type *GetValueType(uint32_t Id, bool UnderlyingType = false) {
return Context->GetValueType(Id, UnderlyingType);
}
Type *GetFunctionValueType(uint32_t Id) {
return GetValueType(Id, /* UnderlyingType = */ true);
}
Type *GetUnknownType() const {
return Context->GetUnknownType();
}
Type *GetPointerType() const {
return Context->GetPointerType();
}
Type *GetComparisonType() const {
return Context->GetComparisonType();
}
Type *GetVoidType() const {
return Context->GetVoidType();
}
Type *GetFloatType() const {
return Context->GetFloatType();
}
Type *GetDoubleType() const {
return Context->GetDoubleType();
}
Type *GetIntegerType(unsigned Size) const {
return Context->GetIntegerType(Size);
}
const char *VerifyIntArithmeticOp(const char *Op, Type *OpTy) {
return Context->VerifyIntArithmeticOp(Op, OpTy);
}
protected:
// The context parser that contains decoding state.
NaClDisTopLevelParser *Context;
// The number of global abbreviations defined for this block.
unsigned NumGlobalAbbreviations;
// The current number of local abbreviations defined for this block.
unsigned NumLocalAbbreviations;
};
bool NaClDisBlockParser::ParseBlock(unsigned BlockId) {
// Only called if we don't know the details about the block.
ObjDumpSetRecordBitAddress(GetBlock().GetStartBit());
Errors() << "Don't know how to parse block " << BlockId
<< ", when in block " << GetBlockID() << "\n";
NaClDisBlockParser Parser(BlockId, this);
return Parser.ParseThisBlock();
}
void NaClDisBlockParser::PrintBlockHeader() {
Errors() << "Unknown block id found: " << GetBlockID() << "\n";
Tokens() << "unknown" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = "
<< GetBlockID() << Endline();
}
void NaClDisBlockParser::EnterBlock(unsigned NumWords) {
PrintBlockHeader();
DumpEnterBlockRecord();
IncRecordIndent();
IncAssemblyIndent();
}
void NaClDisBlockParser::ExitBlock() {
DecAssemblyIndent();
DecRecordIndent();
Tokens() << CloseCurly() << Endline();
NaClBitcodeRecordData Exit;
Exit.Code = naclbitc::BLK_CODE_EXIT;
ObjDumpWrite(Record.GetStartBit(), Exit, naclbitc::END_BLOCK);
}
void NaClDisBlockParser::DumpEnterBlockRecord() {
// TODO(kschimpf): Better integrate this with the bitstream reader
// (which currently doesn't build any records).
NaClBitcodeRecordData Enter;
Enter.Code = naclbitc::BLK_CODE_ENTER;
Enter.Values.push_back(GetBlockID());
Enter.Values.push_back(Record.GetCursor().getAbbrevIDWidth());
ObjDumpWrite(GetBlock().GetStartBit(), Enter, naclbitc::ENTER_SUBBLOCK);
}
void NaClDisBlockParser::ProcessAbbreviation(unsigned BlockID,
NaClBitCodeAbbrev *Abbrev,
bool IsLocal) {
Tokens() << NextLocalAbbreviationId() << Space() << "=" << Space()
<< "abbrev" << Space() << TokenizeAbbreviation(Abbrev)
<< Semicolon() << Endline();
++NumLocalAbbreviations;
ObjDumpWrite(Record.GetStartBit(), Record);
}
void NaClDisBlockParser::ProcessRecord() {
// Note: Only called if block is not understood. Hence, we
// only report the records.
ObjDumpWrite(Record.GetStartBit(), Record);
}
/// Parses and disassembles the blockinfo block.
class NaClDisBlockInfoParser : public NaClDisBlockParser {
public:
NaClDisBlockInfoParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser) {
}
~NaClDisBlockInfoParser() override {}
private:
void PrintBlockHeader() override;
void SetBID() override;
void ProcessAbbreviation(unsigned BlockID, NaClBitCodeAbbrev *Abbrev,
bool IsLocal) override;
/// Returns the abbreviation id for the next global abbreviation
/// to be defined for the given block id.
BitcodeId NextGlobalAbbreviationId(unsigned BlockID) {
return BitcodeId('a', Context->GetNumGlobalAbbreviations(BlockID), true);
}
};
void NaClDisBlockInfoParser::PrintBlockHeader() {
Tokens() << "abbreviations" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = "
<< GetBlockID() << Endline();
}
void NaClDisBlockInfoParser::SetBID() {
std::string BlockName;
uint64_t BlockID = Record.GetValues()[0];
switch (BlockID) {
case naclbitc::MODULE_BLOCK_ID:
Tokens() << "module";
break;
case naclbitc::CONSTANTS_BLOCK_ID:
Tokens() << "constants";
break;
case naclbitc::FUNCTION_BLOCK_ID:
Tokens() << "function";
break;
case naclbitc::VALUE_SYMTAB_BLOCK_ID:
Tokens() << "valuesymtab";
break;
case naclbitc::TYPE_BLOCK_ID_NEW:
Tokens() << "types";
break;
case naclbitc::GLOBALVAR_BLOCK_ID:
Tokens() << "globals";
break;
default:
Tokens() << "block" << OpenParen() << BlockID << CloseParen();
Errors() << "Block id " << BlockID << " not understood.\n";
break;
}
Tokens() << Colon() << Endline();
ObjDumpWrite(Record.GetStartBit(), Record);
}
void NaClDisBlockInfoParser::ProcessAbbreviation(unsigned BlockID,
NaClBitCodeAbbrev *Abbrev,
bool IsLocal) {
IncAssemblyIndent();
Tokens() << NextGlobalAbbreviationId(BlockID) << Space() << "=" << Space()
<< "abbrev" << Space() << TokenizeAbbreviation(Abbrev)
<< Semicolon() << Endline();
Context->IncNumGlobalAbbreviations(BlockID);
DecAssemblyIndent();
ObjDumpWrite(Record.GetStartBit(), Record);
}
/// Parses and disassembles the types block.
class NaClDisTypesParser : public NaClDisBlockParser {
public:
NaClDisTypesParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser),
ExpectedNumTypes(0),
IsFirstRecord(true)
{
}
~NaClDisTypesParser() override;
private:
void PrintBlockHeader() override;
void ProcessRecord() override;
/// Returns the value id for the next type to be defined.
BitcodeId NextTypeId() {
return BitcodeId('t', GetNumTypes());
}
// Installs unknown type as definition of next type.
void InstallUnknownTypeForNextId();
uint32_t ExpectedNumTypes;
bool IsFirstRecord;
};
NaClDisTypesParser::~NaClDisTypesParser() {
if (GetNumTypes() != ExpectedNumTypes) {
Errors() << "Expected " << ExpectedNumTypes << " types but found: "
<< GetNumTypes() << "\n";
}
}
void NaClDisTypesParser::PrintBlockHeader() {
Tokens() << "types" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
void NaClDisTypesParser::InstallUnknownTypeForNextId() {
Type *UnknownType = GetUnknownType();
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(UnknownType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(UnknownType);
}
void NaClDisTypesParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::TYPE_CODE_NUMENTRY: {
// NUMENTRY: [numentries]
uint64_t Size = 0;
if (Values.size() == 1) {
Size = Values[0];
} else {
Errors() << "Count record should have 1 argument. Found: "
<< Values.size() << "\n";
}
if (!IsFirstRecord) {
Errors() << "Count record not first record of types block\n";
}
Tokens() << "count" << Space() << Size << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
ExpectedNumTypes = Size;
break;
}
case naclbitc::TYPE_CODE_VOID: {
// VOID
if (!Values.empty())
Errors() << "Void record shouldn't have arguments. Found: "
<< Values.size() << "\n";
Type *VoidType = GetVoidType();
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(VoidType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(VoidType);
break;
}
case naclbitc::TYPE_CODE_FLOAT: {
// FLOAT
if (!Values.empty())
Errors() << "Float record shoudn't have arguments. Found: "
<< Values.size() << "\n";
Type *FloatType = GetFloatType();
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(FloatType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(FloatType);
break;
}
case naclbitc::TYPE_CODE_DOUBLE: {
// DOUBLE
if (!Values.empty())
Errors() << "Double record shound't have arguments. Found: "
<< Values.size() << "\n";
Type *DoubleType = GetDoubleType();
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(DoubleType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(DoubleType);
break;
}
case naclbitc::TYPE_CODE_INTEGER: {
// INTEGER: [width]
uint64_t Size;
if (Values.size() == 1) {
Size = Values[0];
} else {
Errors() << "Integer record should have one argument. Found: "
<< Values.size() << "\n";
Size = 32;
}
switch (Size) {
case 1:
case 8:
case 16:
case 32:
case 64: {
break;
}
default:
if (!IgnorePNaClABIChecks) {
Errors() << "Integer record contains bad integer size: "
<< Size << "\n";
Size = 32;
}
break;
}
Type *IntType = GetIntegerType(Size);
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(IntType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(IntType);
break;
}
case naclbitc::TYPE_CODE_VECTOR: {
// VECTOR: [numelts, eltty]
if (Values.size() != 2) {
Errors() << "Vector record should contain two arguments. Found: "
<< Values.size() << "\n";
InstallUnknownTypeForNextId();
break;
}
Type *BaseType = GetType(Values[1]);
if (!(BaseType->isIntegerTy()
|| BaseType->isFloatTy()
|| BaseType->isDoubleTy())) {
Type *ErrorRecoveryTy = GetIntegerType(32);
Errors() << "Vectors can only be defined on primitive types. Found "
<< *BaseType << ". Assuming " << *ErrorRecoveryTy
<< " instead.\n";
BaseType = ErrorRecoveryTy;
}
uint64_t NumElements = Values[0];
Type *VecType = VectorType::get(BaseType, NumElements);
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidVectorType(VecType)) {
Errors() << "Vector type " << *VecType << " not allowed.\n";
}
Tokens() << NextTypeId() << Space() << "=" << Space()
<< TokenizeType(VecType) << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
InstallType(VecType);
break;
}
case naclbitc::TYPE_CODE_FUNCTION: {
// FUNCTION: [vararg, retty, paramty x N]
if (Values.size() < 2) {
Errors()
<< "Function record should contain at least 2 arguments. Found: "
<< Values.size() << "\n";
InstallUnknownTypeForNextId();
break;
}
if (Values[0]) {
Errors() << "Functions with variable length arguments is not supported\n";
}
Type *ReturnType = GetType(Values[1]);
if (!(isValidValueType(ReturnType) || ReturnType->isVoidTy())) {
Type *ReplaceType = GetIntegerType(32);
Errors() << "Invalid return type. Found: " << *ReturnType
<< ". Assuming: " << *ReplaceType << "\n";
ReturnType = ReplaceType;
}
SmallVector<Type*, 8> Signature;
for (size_t i = 2; i < Values.size(); ++i) {
Type *Ty = GetType(Values[i]);
if (!isValidValueType(Ty)) {
Type *ReplaceTy = GetIntegerType(32);
Errors() << "Invalid type for parameter " << (i - 1) << ". Found: "
<< *Ty << ". Assuming: " << *ReplaceTy << "\n";
Ty = ReplaceTy;
}
Signature.push_back(Ty);
}
Type *FcnType = FunctionType::get(ReturnType, Signature, Values[0]);
Tokens() << NextTypeId() << Space() << "=" << Space()
<< StartCluster() << TokenizeType(FcnType)
<< Semicolon() << FinishCluster() << TokenizeAbbrevIndex()
<< Endline();
InstallType(FcnType);
break;
}
default:
Errors() << "Unknown record code in types block. Found: "
<< Record.GetCode() << "\n";
break;
}
ObjDumpWrite(Record.GetStartBit(), Record);
IsFirstRecord = false;
}
/// Parses and disassembles the globalvars block.
class NaClDisGlobalsParser : public NaClDisBlockParser {
public:
NaClDisGlobalsParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser),
NumInitializers(0),
InsideCompound(false),
BaseTabs(GetAssemblyNumTabs()+1) {}
~NaClDisGlobalsParser() override {}
private:
void PrintBlockHeader() override;
void ProcessRecord() override;
void ExitBlock() override;
// Expected number of initializers associated with last globalvars
// record
uint32_t NumInitializers;
// True if last globalvars record was defined by a compound record.
bool InsideCompound;
// Number of tabs used to indent elements in the globals block.
unsigned BaseTabs;
// Returns the ID for the next defined global.
BitcodeId NextGlobalId() {
return BitcodeId('g', GetNumGlobals());
}
// Prints out the close initializer "}" if necessary, and fixes
// the indentation to match previous indentation.
void InsertCloseInitializer();
uint32_t GetExpectedNumGlobals() const {
return Context->GetExpectedNumGlobals();
}
};
void NaClDisGlobalsParser::PrintBlockHeader() {
Tokens() << "globals" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
void NaClDisGlobalsParser::InsertCloseInitializer() {
if (InsideCompound) {
while (BaseTabs + 1 < GetAssemblyNumTabs()) DecAssemblyIndent();
Tokens() << CloseCurly() << Endline();
}
while (BaseTabs < GetAssemblyNumTabs()) DecAssemblyIndent();
ObjDumpFlush();
NumInitializers = 0;
InsideCompound = false;
}
void NaClDisGlobalsParser::ExitBlock() {
if (NumInitializers > 0) {
BitcodeId LastGlobal('g', (GetNumGlobals()-1));
Errors() << "More initializers for " << LastGlobal << " expected: "
<< NumInitializers << "\n";
}
if (GetNumGlobals() != GetExpectedNumGlobals()) {
Errors() << "Expected " << GetExpectedNumGlobals()
<< " globals but found: " << GetNumGlobals() << "\n";
}
NaClDisBlockParser::ExitBlock();
}
void NaClDisGlobalsParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::GLOBALVAR_VAR: {
// VAR: [align, isconst]
if (Values.size() != 2) {
Errors() << "Globalvar record expects two arguments. Found: "
<< Values.size() << "\n";
break;
}
if (GetNumGlobals() == GetExpectedNumGlobals()) {
Errors() << "Exceeded expected number of globals: "
<< GetExpectedNumGlobals() << "\n";
}
if (NumInitializers > 0) {
Errors() << "Previous initializer list too short. Expects "
<< NumInitializers << " more initializers\n";
InsertCloseInitializer();
}
uint32_t Alignment = (1 << Values[0]) >> 1;
Tokens() << StartCluster() << (Values[1] ? "const" : "var")
<< Space() << NextGlobalId()
<< Comma() << FinishCluster() << Space()
<< StartCluster() << "align" << Space() << Alignment
<< Comma() << FinishCluster() << TokenizeAbbrevIndex()
<< Endline();
IncAssemblyIndent();
IncNumGlobals();
NumInitializers = 1;
break;
}
case naclbitc::GLOBALVAR_COMPOUND:
// COMPOUND: [size]
if (Values.size() != 1) {
Errors() << "Compound record expects one argument. Found: "
<< Values.size() << "\n";
break;
}
if (NumInitializers == 1) {
if (InsideCompound) {
Errors() << "Nested initialization records not allowed\n";
InsertCloseInitializer();
}
} else {
Errors() << "Compound record must follow globalvars record\n";
InsertCloseInitializer();
}
Tokens() << StartCluster() << "initializers" << Space()
<< Values[0] << FinishCluster() << Space()
<< OpenCurly() << TokenizeAbbrevIndex() << Endline();
IncAssemblyIndent();
NumInitializers = Values[0];
InsideCompound = true;
break;
case naclbitc::GLOBALVAR_ZEROFILL:
// ZEROFILL: [size]
if (Values.size() != 1) {
Errors() << "Zerofill record expects one argument. Found: "
<< Values.size() << "\n";
break;
}
if (NumInitializers == 0) {
Errors() << "Zerofill initializer not associated with globalvar record\n";
}
Tokens() << "zerofill" << Space() << Values[0] << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
--NumInitializers;
break;
case naclbitc::GLOBALVAR_DATA: {
// DATA: [b0, b1, ...]
if (Values.empty()) {
Errors() << "Globals data record must have arguments.\n";
}
if (NumInitializers == 0) {
Errors() << "Data initializer not associated with globalvar record\n";
}
Tokens() << StartCluster() << OpenCurly();
for (size_t i = 0; i < Values.size(); ++i) {
if (i > 0) Tokens() << Comma() << FinishCluster() << Space();
uint64_t Byte = Values[i];
if (Byte >= 256) {
Errors() << "Invalid byte value in data record: " << Byte << "\n";
Byte &= 0xFF;
}
if (i > 0) Tokens() << StartCluster();
Tokens() << format("%3u", static_cast<unsigned>(Byte));
}
Tokens() << CloseCurly() << FinishCluster() << TokenizeAbbrevIndex()
<< Endline();
--NumInitializers;
break;
}
case naclbitc::GLOBALVAR_RELOC:
// RELOC: [val, [addend]]
if (Values.empty() || Values.size() > 2) {
Errors() << "Invalid reloc record size: " << Values.size() << "\n";
break;
}
if (NumInitializers == 0) {
Errors() <<
"Relocation initializer not associated with globalvar record\n";
}
Tokens() << "reloc" << Space() << StartCluster() << GetBitcodeId(Values[0]);
if (Values.size() == 2) {
int32_t Addend = static_cast<int32_t>(Values[1]);
char Operator = '+';
if (Addend < 0) {
Operator = '-';
Addend = -Addend;
}
Tokens() << Space()<< Operator << Space() << Addend;
}
Tokens() << Semicolon() << FinishCluster() << TokenizeAbbrevIndex()
<< Endline();
--NumInitializers;
break;
case naclbitc::GLOBALVAR_COUNT: {
// COUNT: [n]
uint32_t Count = 0;
if (Values.size() == 1) {
Count = Values[0];
} else {
Errors() << "Globals count record expects one argument. Found: "
<< Values.size() << "\n";
}
if (GetNumGlobals() != 0)
Errors() << "Count record not first record in block.\n";
Tokens() << "count" << Space() << Count << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
Context->SetExpectedNumGlobals(Count);
break;
}
default:
Errors() << "Unknown record found in globals block.\n";
}
ObjDumpWrite(Record.GetStartBit(), Record);
if (GetNumGlobals() > 0 && NumInitializers == 0)
InsertCloseInitializer();
}
/// Parsers and disassembles a valuesymtab block.
class NaClDisValueSymtabParser : public NaClDisBlockParser {
public:
NaClDisValueSymtabParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser) {
}
~NaClDisValueSymtabParser() override {}
private:
void PrintBlockHeader() override;
void ProcessRecord() override;
// Displays the context of the name (in Values) for the given Id.
void DisplayEntry(BitcodeId &Id,
const NaClBitcodeRecord::RecordVector &Values);
};
void NaClDisValueSymtabParser::PrintBlockHeader() {
Tokens() << "valuesymtab" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
void NaClDisValueSymtabParser::
DisplayEntry(BitcodeId &Id,
const NaClBitcodeRecord::RecordVector &Values) {
if (Values.size() <= 1) {
Errors() << "Valuesymtab entry record expects 2 arguments. Found: "
<< Values.size() << "\n";
return;
}
Tokens() << Id << Space() << Colon() << Space();
// Check if the name of the symbol is alphanumeric. If so, print
// as a string. Otherwise, print a sequence of bytes.
// Note: The check isChar6 is a test for aphanumeric + {'.', '_'}.
bool IsChar6 = true; // Until proven otherwise.
for (size_t i = 1; i < Values.size(); ++i) {
uint64_t Byte = Values[i];
if (Byte >= 256) {
Errors() << "Argument " << i << " of symbol entry not byte: "
<< Byte << "\n";
IsChar6 = false;
break;
}
if (!NaClBitCodeAbbrevOp::isChar6(Byte))
IsChar6 = false;
}
if (IsChar6) {
Tokens() << StartCluster() << "\"";
for (size_t i = 1; i < Values.size(); ++i) {
Tokens() << static_cast<char>(Values[i]);
}
Tokens() << "\"" << Semicolon();
} else {
Tokens() << StartCluster() << OpenCurly();
for (size_t i = 1; i < Values.size(); ++i) {
if (i > 1) {
Tokens() << Comma() << FinishCluster() << Space()
<< StartCluster();
}
char ch = Values[i];
if (NaClBitCodeAbbrevOp::isChar6(ch)) {
Tokens() << "'" << ch << "'";
} else {
Tokens() << format("%3u", static_cast<unsigned>(ch));
}
}
Tokens() << CloseCurly();
}
Tokens() << FinishCluster() << TokenizeAbbrevIndex() << Endline();
}
void NaClDisValueSymtabParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::VST_CODE_ENTRY: {
// VST_ENTRY: [valueid, namechar x N]
uint32_t ValID = Values[0];
BitcodeId ID(GetBitcodeId(ValID));
DisplayEntry(ID, Values);
if (ValID < GetNumFunctions()) {
// Check if value is intrinsic name. If so, verify function signature.
std::string Name;
for (unsigned i = 1; i < Values.size(); ++i) {
Name.push_back(static_cast<char>(Values[i]));
}
FunctionType *IntrinTy = Context->GetIntrinsicType(Name);
if (IntrinTy == 0)
// TODO(kschimpf): Check for _start? Complain about other names?
break;
Context->MarkFunctionAsIntrinsic(ValID);
// Verify that expected intrinsic function type matches declared
// type signature.
FunctionType *FcnTy = GetFunctionType(ValID);
if (FcnTy->getNumParams() != IntrinTy->getNumParams()) {
Errors() << "Intrinsic " << Name
<< " expects " << IntrinTy->getNumParams()
<< " arguments. Found: " << FcnTy->getNumParams()
<< "\n";
break;
}
if (!IgnorePNaClABIChecks && !PNaClABITypeChecker::IsPointerEquivType(
IntrinTy->getReturnType(), FcnTy->getReturnType())) {
Errors() << "Intrinsic " << Name
<< " expects return type " << *IntrinTy->getReturnType()
<< ". Found: " << *FcnTy->getReturnType() << "\n";
break;
}
for (size_t i = 0; i < FcnTy->getNumParams(); ++i) {
if (!IgnorePNaClABIChecks && !PNaClABITypeChecker::IsPointerEquivType(
IntrinTy->getParamType(i),
FcnTy->getParamType(i))) {
Errors() << "Intrinsic " << Name
<< " expects " << *IntrinTy->getParamType(i)
<< " for argument " << (i+1) << ". Found: "
<< *FcnTy->getParamType(i) << "\n";
break;
}
}
}
break;
}
case naclbitc::VST_CODE_BBENTRY: {
// [bbid, namechar x N]
BitcodeId ID('b', Values[0]);
DisplayEntry(ID, Values);
break;
}
default:
Errors() << "Unknown record in valuesymtab block.\n";
break;
}
ObjDumpWrite(Record.GetStartBit(), Record);
}
/// Parses and disassembles a constants block.
class NaClDisConstantsParser : public NaClDisBlockParser {
public:
NaClDisConstantsParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser),
ConstantType(0),
BlockTabs(GetAssemblyNumTabs()) {}
virtual ~NaClDisConstantsParser() {
while (BlockTabs < GetAssemblyNumTabs()) DecAssemblyIndent();
}
private:
void PrintBlockHeader() override;
void ProcessRecord() override;
/// Generates the value id for the next generated constant.
BitcodeId NextConstId() {
return BitcodeId('c', GetNumConstants());
}
// The type associated with the constant.
Type *ConstantType;
// The number of tabs used to indent the globals block.
unsigned BlockTabs;
};
void NaClDisConstantsParser::PrintBlockHeader() {
Tokens() << "constants" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
void NaClDisConstantsParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::CST_CODE_SETTYPE:
// SETTYPE: [typeid]
while (BlockTabs + 1 < GetAssemblyNumTabs()) DecAssemblyIndent();
if (Values.size() == 1) {
ConstantType = GetType(Values[0]);
Tokens() << TokenizeType(ConstantType) << ":"
<< TokenizeAbbrevIndex() << Endline();
} else {
Errors() << "Settype record should have 1 argument. Found: "
<< Values.size() << "\n";
// Make up a type, so we can continue.
ConstantType = GetIntegerType(32);
}
IncAssemblyIndent();
break;
case naclbitc::CST_CODE_UNDEF:
// CST_CODE_UNDEF: []
if (!Values.empty()) {
Errors() << "Undefined record should not have arguments: Found: "
<< Values.size() << "\n";
}
Tokens() << NextConstId() << Space() << "=" << Space()
<< StartCluster() << TokenizeType(ConstantType)
<< Space() << "undef"
<< Semicolon() << FinishCluster()
<< TokenizeAbbrevIndex() << Endline();
InstallConstantType(ConstantType);
break;
case naclbitc::CST_CODE_INTEGER: {
// INTEGER: [intval]
SignRotatedInt Value;
if (Values.size() == 1) {
const SignRotatedInt MyValue(Values[0], ConstantType);
Value = MyValue;
} else {
Errors() << "Integer record should have 1 argument. Found: "
<< Values.size() << "\n";
}
Tokens() << NextConstId() << Space() << "=" << Space() << StartCluster()
<< StartCluster() << TokenizeType(ConstantType) << Space()
<< Value << Semicolon() << FinishCluster() << FinishCluster()
<< TokenizeAbbrevIndex() << Endline();
InstallConstantType(ConstantType);
break;
}
case naclbitc::CST_CODE_FLOAT: {
// FLOAT: [fpval]
// Define initially with default value zero, in case of errors.
const fltSemantics &FloatRep =
ConstantType->isFloatTy() ? APFloat::IEEEsingle : APFloat::IEEEdouble;
APFloat Value(APFloat::getZero(FloatRep));
if (Values.size() == 1) {
if (ConstantType->isFloatTy()) {
Value = APFloat(FloatRep, APInt(32, static_cast<uint32_t>(Values[0])));
}
else if (ConstantType->isDoubleTy()) {
Value = APFloat(FloatRep, APInt(64, Values[0]));
} else {
Errors() << "Bad floating point constant argument: "
<< Values[0] << "\n";
}
} else {
Errors() << "Float record should have 1 argument. Found: "
<< Values.size() << "\n";
}
Tokens() << NextConstId() << Space() << "=" << Space() << StartCluster()
<< TokenizeType(ConstantType) << Space();
if (ConstantType->isFloatTy()) {
Tokens() << format("%g", Value.convertToFloat());
} else {
Tokens() << format("%g", Value.convertToDouble());
}
Tokens() << Semicolon() << FinishCluster()
<< TokenizeAbbrevIndex() << Endline();
InstallConstantType(ConstantType);
break;
}
default:
Errors() << "Unknown record in valuesymtab block.\n";
break;
}
ObjDumpWrite(Record.GetStartBit(), Record);
}
/// Parses and disassembles function blocks.
class NaClDisFunctionParser : public NaClDisBlockParser {
public:
NaClDisFunctionParser(unsigned BlockID,
NaClDisBlockParser *EnclosingParser);
~NaClDisFunctionParser() override {}
void PrintBlockHeader() override;
bool ParseBlock(unsigned BlockID) override;
void ProcessRecord() override;
/// Returns the absolute value index of the first instruction that
/// generates a value in the defined function.
uint32_t FirstValuedInstructionId() const {
return GetNumFunctions() + GetNumGlobals() + GetNumParams()
+ GetNumConstants();
}
/// Converts an instruction index to the corresponding absolute value
/// index.
uint32_t ValuedInstToAbsId(uint32_t Id) const {
return FirstValuedInstructionId() + Id;
}
/// Converts the (function) relative index to the corresponding
/// absolute value index.
uint32_t RelativeToAbsId(int32_t Id) {
uint32_t AbsNextId = ValuedInstToAbsId(GetNumValuedInstructions());
if (Id > 0 && AbsNextId < static_cast<uint32_t>(Id)) {
Errors() << "Invalid relative value id: " << Id
<< " (Must be <= " << AbsNextId << ")\n";
AbsNextId = Id;
}
return AbsNextId - Id;
}
/// Converts the given absolute value index to a corresponding
/// valued instruction index.
uint32_t AbsToValuedInstId(uint32_t Id) const {
return Id - FirstValuedInstructionId();
}
/// Returns the text representation of the encoded binary operator,
/// assuming the binary operator is operating on the given type.
/// Generates appropriate error messages if the given type is not
// allowed for the given Opcode.
const char *GetBinop(uint32_t Opcode, Type *Type);
/// Returns the text representation of the encoded cast operator.
/// Generates appropriate error messages if the given Opcode is
/// not allowed for the given types.
const char *GetCastOp(uint32_t Opcode, Type *FromType, Type *ToType);
/// Returns the text representation of the encoded integer compare
/// predicate. Generates appropriate error message if the given
/// Opcode is not defined.
const char *GetIcmpPredicate(uint32_t Opcode);
/// Returns the text representation of the encoded floating point
/// compare predicate. Generates appropriate error message if the
/// given Opcode is not defined.
const char *GetFcmpPredicate(uint32_t Opcode);
/// Returns the value id for the next instruction to be defined.
BitcodeId NextInstId() const {
return BitcodeId('v', GetNumValuedInstructions());
}
private:
// The function index of the function being defined.
uint32_t FcnId;
// The function type for the function being defined.
FunctionType *FcnTy;
// The current basic block being printed.
int32_t CurrentBbIndex;
// The expected number of basic blocks.
uint64_t ExpectedNumBbs;
// True if the previously printed instruction was a terminating
// instruction. Used to figure out where to insert block labels.
bool InstIsTerminating;
/// Returns scalar type of vector type Ty, if vector type. Otherwise
/// returns Ty.
Type *UnderlyingType(Type *Ty) {
return Ty->isVectorTy() ? Ty->getVectorElementType() : Ty;
}
bool isFloatingType(Type *Ty) {
return Ty->isFloatTy() || Ty->isDoubleTy();
}
/// Verifies that OpTy is an integer, or a vector of integers, for
/// operator Op. Generates error messages if appropriate. Returns Op.
const char *VerifyIntegerOrVectorOp(const char *Op, Type *OpTy) {
Type *BaseTy = OpTy;
if (OpTy->isVectorTy()) {
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidVectorType(OpTy)) {
Errors() << Op << ": invalid vector type: " << *OpTy << "\n";
return Op;
}
BaseTy = OpTy->getVectorElementType();
}
if (BaseTy->isIntegerTy()) {
if (IgnorePNaClABIChecks ||
PNaClABITypeChecker::isValidScalarType(BaseTy)) {
return Op;
}
Errors() << Op << ": Invalid integer type: " << *OpTy << "\n";
} else {
Errors() << Op << ": Expects integer type. Found: " << *OpTy << "\n";
}
return Op;
}
/// Verifies that Opty is a floating point type, or a vector of a
/// floating points, for operator Op. Generates error messages if
/// appropriate. Returns Op.
const char *VerifyFloatingOrVectorOp(const char *Op, Type *OpTy) {
Type *BaseTy = OpTy;
if (OpTy->isVectorTy()) {
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidVectorType(OpTy)) {
Errors() << Op << ": invalid vector type: " << *OpTy << "\n";
return Op;
}
BaseTy = OpTy->getVectorElementType();
}
if (!isFloatingType(BaseTy)) {
Errors() << Op << ": Expects floating point. Found "
<< OpTy << "\n";
return Op;
}
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidScalarType(BaseTy)) {
Errors() << Op << ": type not allowed: " << OpTy << "\n";
}
return Op;
}
/// Op is the name of the operation that called this. Checks if
/// OpTy is either a (non-void) scalar, or a vector of scalars. If
/// not, generates an appropriate error message. Always returns Op.
const char *VerifyScalarOrVectorOp(const char *Op, Type *OpTy) {
if (IgnorePNaClABIChecks) return Op;
if (PNaClABITypeChecker::isValidScalarType(OpTy)) {
if (OpTy->isVoidTy())
Errors() << Op << ": Type void not allowed\n";
} else if (!PNaClABITypeChecker::isValidVectorType(OpTy)) {
Errors() << Op << ": Expects scalar/vector type. Found: "
<< OpTy << "\n";
}
return Op;
}
void VerifyIndexedVector(const char *Op, uint32_t VecValue,
uint32_t IdxValue) {
Type *VecType = GetValueType(VecValue);
Type *IdxType = GetValueType(IdxValue);
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidVectorType(VecType)){
if (VecType->isVectorTy())
Errors() << Op << ": Vector type " << *VecType << " not allowed\n";
else
Errors() << Op << ": Vector type expected. Found: " << *VecType << "\n";
}
// Note: This restriction appears to be LLVM specific.
if (!IdxType->isIntegerTy(32)) {
Errors() << Op << ": Index not i32. Found: " << *IdxType << "\n";
}
BitcodeId IdxId(GetBitcodeId(IdxValue));
if (IdxId.GetKind() != 'c') {
Errors() << Op << ": Vector index not constant: " << IdxId << "\n";
// TODO(kschimpf): We should check that Idx is a constant with
// valid access. However, we currently don't store constant
// values, so it can't be tested.
}
}
/// Checks if block Value is a valid branch target for the current
/// function block. If not, generates an appropriate error message.
void VerifyBranchRange(uint64_t Value) {
if (0 == Value || Value >= ExpectedNumBbs) {
Errors() << "Branch " << BitcodeId('b', Value)
<< " out of range. Not in [1," << ExpectedNumBbs << "]\n";
}
}
/// Convert alignment exponent (i.e. power of two (or zero)) to the
/// corresponding alignment to use. If alignment is too large, it generates
/// an error message and returns 0.
unsigned getAlignmentValue(uint64_t Exponent);
};
NaClDisFunctionParser::NaClDisFunctionParser(
unsigned BlockID,
NaClDisBlockParser *EnclosingParser)
: NaClDisBlockParser(BlockID, EnclosingParser),
CurrentBbIndex(-1),
ExpectedNumBbs(0),
InstIsTerminating(false) {
Context->ResetLocalCounters();
if (Context->HasNextDefinedFunctionIndex()) {
FcnId = Context->GetNextDefinedFunctionIndex();
FcnTy = GetFunctionType(FcnId);
} else {
FcnId = 0;
SmallVector<Type*, 8> Signature;
FcnTy = FunctionType::get(GetVoidType(), Signature, 0);
Errors() <<
"No corresponding defining function address for function block.\n";
return;
}
Context->IncNumDefinedFunctions();
// Now install parameters.
for (size_t Index = 0; Index < FcnTy->getFunctionNumParams(); ++Index) {
InstallParamType(FcnTy->getFunctionParamType(Index));
}
}
void NaClDisFunctionParser::PrintBlockHeader() {
Tokens() << "function" << Space();
BitcodeId FunctionId('f', FcnId);
bool InvalidSignature = true; // until proven otherwise.
if (Context->IsFunctionIntrinsic(FcnId))
InvalidSignature = false;
else if (IgnorePNaClABIChecks ||
PNaClABITypeChecker::isValidFunctionType(FcnTy)) {
InvalidSignature = false;
}
if (InvalidSignature) {
Errors() << "Invalid type signature for "
<< BitcodeId('f', FcnId) << ": " << *FcnTy << "\n";
}
Tokens() << TokenizeFunctionSignature(FcnTy, &FunctionId)
<< Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
const char *NaClDisFunctionParser::GetBinop(uint32_t Opcode, Type *Ty) {
Instruction::BinaryOps LLVMOpcode;
if (!naclbitc::DecodeBinaryOpcode(Opcode, Ty, LLVMOpcode)) {
Errors() << "Binary opcode not understood: " << Opcode << "\n";
return "???";
}
switch (LLVMOpcode) {
default:
Errors() << "Binary opcode not understood: " << Opcode << "\n";
return "???";
case Instruction::Add:
return VerifyIntArithmeticOp("add", Ty);
case Instruction::FAdd:
return VerifyFloatingOrVectorOp("fadd", Ty);
case Instruction::Sub:
return VerifyIntArithmeticOp("sub", Ty);
case Instruction::FSub:
return VerifyFloatingOrVectorOp("fsub", Ty);
case Instruction::Mul:
return VerifyIntArithmeticOp("mul", Ty);
case Instruction::FMul:
return VerifyFloatingOrVectorOp("fmul", Ty);
case Instruction::UDiv:
return VerifyIntArithmeticOp("udiv", Ty);
case Instruction::SDiv:
return VerifyIntArithmeticOp("sdiv", Ty);
case Instruction::FDiv:
return VerifyFloatingOrVectorOp("fdiv", Ty);
case Instruction::URem:
return VerifyIntArithmeticOp("urem", Ty);
case Instruction::SRem:
return VerifyIntArithmeticOp("srem", Ty);
case Instruction::FRem:
return VerifyFloatingOrVectorOp("frem", Ty);
case Instruction::Shl:
return VerifyIntArithmeticOp("shl", Ty);
case Instruction::LShr:
return VerifyIntArithmeticOp("lshr", Ty);
case Instruction::AShr:
return VerifyIntArithmeticOp("ashr", Ty);
case Instruction::And:
return VerifyIntegerOrVectorOp("and", Ty);
case Instruction::Or:
return VerifyIntegerOrVectorOp("or", Ty);
case Instruction::Xor:
return VerifyIntegerOrVectorOp("xor", Ty);
}
}
const char *NaClDisFunctionParser::GetCastOp(uint32_t Opcode,
Type *FromType, Type *ToType) {
Instruction::CastOps Cast;
const char *CastName = "???";
if (!naclbitc::DecodeCastOpcode(Opcode, Cast)) {
Errors() << "Cast opcode not understood: " << Opcode << "\n";
return CastName;
}
switch (Cast) {
default:
Errors() << "Cast opcode not understood: " << Opcode << "\n";
return CastName;
case Instruction::BitCast:
CastName = "bitcast";
break;
case Instruction::Trunc:
CastName = "trunc";
break;
case Instruction::ZExt:
CastName = "zext";
break;
case Instruction::SExt:
CastName = "sext";
break;
case Instruction::FPToUI:
CastName = "fptoui";
break;
case Instruction::FPToSI:
CastName = "fptosi";
break;
case Instruction::UIToFP:
CastName = "uitofp";
break;
case Instruction::SIToFP:
CastName = "sitofp";
break;
case Instruction::FPTrunc:
CastName = "fptrunc";
break;
case Instruction::FPExt:
CastName = "fpext";
break;
}
if (!CastInst::castIsValid(Cast, FromType, ToType)) {
Errors() << "Invalid cast '" << CastName << "'. Not defined on "
<< FromType << " to " << ToType << "\n";
}
return CastName;
}
const char *NaClDisFunctionParser::GetIcmpPredicate(uint32_t Opcode) {
CmpInst::Predicate Predicate;
if (!naclbitc::DecodeIcmpPredicate(Opcode, Predicate)) {
Errors() << "Icmp predicate not understood: " << Opcode << "\n";
return "???";
}
switch (Predicate) {
default:
Errors() << "Icmp predicate not understood: " << Opcode << "\n";
return "???";
case CmpInst::ICMP_EQ:
return "eq";
case CmpInst::ICMP_NE:
return "ne";
case CmpInst::ICMP_UGT:
return "ugt";
case CmpInst::ICMP_UGE:
return "uge";
case CmpInst::ICMP_ULT:
return "ult";
case CmpInst::ICMP_ULE:
return "ule";
case CmpInst::ICMP_SGT:
return "sgt";
case CmpInst::ICMP_SGE:
return "sge";
case CmpInst::ICMP_SLT:
return "slt";
case CmpInst::ICMP_SLE:
return "sle";
}
}
const char *NaClDisFunctionParser::GetFcmpPredicate(uint32_t Opcode) {
CmpInst::Predicate Predicate;
if (!naclbitc::DecodeFcmpPredicate(Opcode, Predicate)) {
Errors() << "Fcmp predicate not understood: " << Opcode << "\n";
return "???";
}
switch (Predicate) {
default:
Errors() << "Fcmp predicate not understood: " << Opcode << "\n";
return "???";
case CmpInst::FCMP_FALSE:
return "false";
case CmpInst::FCMP_OEQ:
return "oeq";
case CmpInst::FCMP_OGT:
return "ogt";
case CmpInst::FCMP_OGE:
return "oge";
case CmpInst::FCMP_OLT:
return "olt";
case CmpInst::FCMP_OLE:
return "ole";
case CmpInst::FCMP_ONE:
return "one";
case CmpInst::FCMP_ORD:
return "ord";
case CmpInst::FCMP_UNO:
return "uno";
case CmpInst::FCMP_UEQ:
return "ueq";
case CmpInst::FCMP_UGT:
return "ugt";
case CmpInst::FCMP_UGE:
return "uge";
case CmpInst::FCMP_ULT:
return "ult";
case CmpInst::FCMP_ULE:
return "ule";
case CmpInst::FCMP_UNE:
return "une";
case CmpInst::FCMP_TRUE:
return "true";
}
}
namespace {
static const unsigned MaxAlignmentExponent = 29;
static_assert(
(1u << MaxAlignmentExponent) == Value::MaximumAlignment,
"Inconsistency between Value.MaxAlignment and PNaCl alignment limit");
}
unsigned NaClDisFunctionParser::getAlignmentValue(uint64_t Exponent) {
if (Exponent > MaxAlignmentExponent + 1) {
Errors() << "Alignment can't be greater than 2**" << MaxAlignmentExponent
<< ". Found: 2**" << (Exponent - 1) << "\n";
return 0;
}
return (1 << static_cast<unsigned>(Exponent)) >> 1;
}
bool NaClDisFunctionParser::ParseBlock(unsigned BlockID) {
ObjDumpSetRecordBitAddress(GetBlock().GetStartBit());
switch (BlockID) {
case naclbitc::CONSTANTS_BLOCK_ID: {
NaClDisConstantsParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
case naclbitc::VALUE_SYMTAB_BLOCK_ID: {
if (!PNaClAllowLocalSymbolTables) break;
NaClDisValueSymtabParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
default:
break;
}
return NaClDisBlockParser::ParseBlock(BlockID);
}
void NaClDisFunctionParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector Values = Record.GetValues();
// Start by adding block label if previous instruction is terminating.
if (InstIsTerminating) {
InstIsTerminating = false;
++CurrentBbIndex;
DecAssemblyIndent();
Tokens() << BitcodeId('b', CurrentBbIndex) << ":" << Endline();
ObjDumpFlush();
IncAssemblyIndent();
}
switch (Record.GetCode()) {
case naclbitc::FUNC_CODE_DECLAREBLOCKS:
// DECLAREBLOCKS: [n]
InstIsTerminating = true; // Force block label on first instruction.
if (Values.size() != 1) {
Errors() << "Function blocks record expects a size argument.\n";
break;
}
ExpectedNumBbs = Values[0];
if (ExpectedNumBbs == 0) {
Errors() << "Functions must contain at least one block.\n";
}
Tokens() << "blocks" << Space() << ExpectedNumBbs << Semicolon();
break;
case naclbitc::FUNC_CODE_INST_BINOP: {
// BINOP: [opval, opval, opcode]
if (Values.size() != 3) {
Errors() << "Binop record expects 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t Op1 = RelativeToAbsId(Values[0]);
uint32_t Op2 = RelativeToAbsId(Values[1]);
Type *Type1 = GetValueType(Op1);
Type *Type2 = GetValueType(Op2);
if (Type1 != Type2) {
Errors() << "Binop argument types differ: " << *Type1 << " and "
<< *Type2 << "\n";
}
const char *Binop = GetBinop(Values[2], Type1);
Tokens() << NextInstId() << Space() << "=" << Space() << Binop << Space()
<< TokenizeType(Type1) << Space() << GetBitcodeId(Op1) << Comma()
<< Space() << GetBitcodeId(Op2) << Semicolon();
InstallInstType(Type1);
break;
}
case naclbitc::FUNC_CODE_INST_CAST: {
// CAST: [opval, destty, castopc]
if (Values.size() != 3) {
Errors() << "Cast record expects 3 argments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t Op = RelativeToAbsId(Values[0]);
Type *FromType = GetValueType(Op);
Type *ToType = GetType(Values[1]);
const char *CastOp = GetCastOp(Values[2], FromType, ToType);
Tokens() << NextInstId() << Space() << "=" << Space() << CastOp << Space()
<< TokenizeType(FromType) << Space() << GetBitcodeId(Op)
<< Space() << "to" << Space() << TokenizeType(ToType)
<< Semicolon();
InstallInstType(ToType);
break;
}
case naclbitc::FUNC_CODE_INST_RET: {
// RET: [opval?]
InstIsTerminating = true;
Tokens() << "ret" << Space();
switch (Values.size()) {
default:
Errors() << "Function return record expects an optional return argument. "
<< "Found: " << Values.size() << " arguments\n";
break;
case 0:
Tokens() << "void";
break;
case 1: {
uint32_t Op = RelativeToAbsId(Values[0]);
Tokens() << TokenizeType(GetValueType(Op)) << Space()<< GetBitcodeId(Op);
break;
}
}
Tokens() << Semicolon();
break;
}
case naclbitc::FUNC_CODE_INST_BR: {
// BR: [bb#, bb#, opval] or [bb#]
InstIsTerminating = true;
if (Values.size() != 1 && Values.size() != 3) {
Errors() << "Function branch record expects 1 or 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
Tokens() << "br" << Space();
if (Values.size() == 3) {
uint32_t OpIndex = RelativeToAbsId(Values[2]);
if (GetValueType(OpIndex) != GetComparisonType())
Errors() << "Branch condition not i1\n";
Tokens() << StartCluster() << "i1" << Space() << GetBitcodeId(OpIndex)
<< Comma() << FinishCluster() << Space();
}
VerifyBranchRange(Values[0]);
Tokens() << StartCluster() << "label" << Space()
<< BitcodeId('b', Values[0]);
if (Values.size() == 3) {
VerifyBranchRange(Values[1]);
Tokens() << Comma() << FinishCluster() << Space()
<< StartCluster() << "label" << Space()
<< BitcodeId('b', Values[1]);
}
Tokens() << Semicolon() << FinishCluster();
break;
}
case naclbitc::FUNC_CODE_INST_SWITCH: {
// SWITCH: [opty, op, bb#, n, (1, 1, int, bb#)*]
InstIsTerminating = true;
if (Values.size() < 4) {
Errors()
<< "Function switch record expects at least 4 arguments. Found: "
<< Values.size() << "\n";
break;
}
Type *OpType = GetType(Values[0]);
uint32_t CondId = RelativeToAbsId(Values[1]);
Type *CondType = GetValueType(CondId);
if (OpType != CondType)
Errors() << "Specified select type " << *OpType << " but found: "
<< *CondType << "\n";
if (!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidSwitchConditionType(CondType)) {
Errors() << PNaClABITypeChecker::ExpectedSwitchConditionType(CondType)
<< "\n";
}
uint32_t DefaultBb = Values[2];
unsigned NumCases = Values[3];
VerifyBranchRange(DefaultBb);
Tokens() << "switch" << Space() << StartCluster() << StartCluster()
<< TokenizeType(OpType) << Space() << GetBitcodeId(CondId)
<< FinishCluster() << Space() << "{" << FinishCluster()
<< Endline();
IncAssemblyIndent();
Tokens() << StartCluster() << "default" << ":" << Space()
<< FinishCluster() << StartCluster() << "br" << Space() << "label"
<< Space() << BitcodeId('b', DefaultBb) << Semicolon()
<< FinishCluster() << Endline();
unsigned CurIdx = 4;
for (unsigned i = 0; i < NumCases; ++i) {
unsigned NumItems = Values[CurIdx++];
bool IsSingleNumber = Values[CurIdx++];
if (NumItems != 1 || !IsSingleNumber) {
Errors() << "Case ranges are not supported in PNaCl\n";
break;
}
SignRotatedInt CaseValue(Values[CurIdx++], OpType);
// TODO(kschimpf) Check if CaseValue possible based on OpType.
uint64_t Label = Values[CurIdx++];
VerifyBranchRange(Label);
Tokens() << StartCluster() << TokenizeType(OpType) << Space()
<< CaseValue << ":" << Space() << FinishCluster()
<< StartCluster() << "br" << Space() << "label" << Space()
<< BitcodeId('b', Label) << Semicolon() << FinishCluster()
<< Endline();
}
DecAssemblyIndent();
Tokens() << "}";
break;
}
case naclbitc::FUNC_CODE_INST_UNREACHABLE:
// UNREACHABLE
InstIsTerminating = true;
if (!Values.empty()) {
Errors() << "Function unreachable record expects no arguments. Found: "
<< Values.size() << "\n";
}
Tokens() << "unreachable" << Semicolon();
break;
case naclbitc::FUNC_CODE_INST_PHI: {
// PHI: [ty, (val0, bb0)*]
if (Values.size() < 3) {
Errors() << "Function phi record expects at least 3 arguments. Found: "
<< Values.size() << "\n";
break;
} else if (Values.size() % 2 == 0) {
Errors()
<< "Function phi records should have an odd number of arguments. "
<< "Found: " << Values.size() << "\n";
}
Type* OpType = GetType(Values[0]);
Tokens() << NextInstId() << Space() << "=" << StartCluster() << Space()
<< "phi" << Space() << TokenizeType(OpType);
for (size_t i = 1; i < Values.size(); i += 2) {
if (i > 1) Tokens() << Comma();
uint32_t Index = RelativeToAbsId(NaClDecodeSignRotatedValue(Values[i]));
Tokens() << FinishCluster() << Space() << StartCluster() << OpenSquare()
<< GetBitcodeId(Index) << Comma() << Space()
<< BitcodeId('b', Values[i+1]) << CloseSquare();
}
Tokens() << Semicolon() << FinishCluster();
InstallInstType(OpType);
break;
};
case naclbitc::FUNC_CODE_INST_ALLOCA: {
// ALLOCA: [size, align]
if (Values.size() != 2) {
Errors() << "Function alloca record expects 2 arguments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t SizeOp = RelativeToAbsId(Values[0]);
Type* SizeType = GetValueType(SizeOp);
BitcodeId SizeId(GetBitcodeId(SizeOp));
unsigned Alignment = getAlignmentValue(Values[1]);
if (!IgnorePNaClABIChecks && !PNaClABIProps::isAllocaSizeType(SizeType))
Errors() << PNaClABIProps::ExpectedAllocaSizeType() << "\n";
// TODO(kschimpf) Are there any constraints on alignment?
Tokens() << NextInstId() << Space() << "=" << Space() << StartCluster()
<< "alloca" << Space() << "i8" << Comma() << FinishCluster()
<< Space() << StartCluster() << TokenizeType(SizeType) << Space()
<< SizeId << Comma() << FinishCluster() << Space()
<< StartCluster() <<"align" << Space() << Alignment << Semicolon()
<< FinishCluster();
InstallInstType(GetPointerType());
break;
}
case naclbitc::FUNC_CODE_INST_LOAD: {
// LOAD: [op, align, ty]
if (Values.size() != 3) {
Errors() << "Function load record expects 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
unsigned Alignment = getAlignmentValue(Values[1]);
Type *LoadType = GetType(Values[2]);
VerifyScalarOrVectorOp("load", LoadType);
Context->VerifyMemoryAccessAlignment("load", LoadType, Alignment);
Tokens() << NextInstId() << Space() << "=" << Space() << StartCluster()
<< "load" << Space() << TokenizeType(LoadType) << "*" << Space()
<< GetBitcodeId(RelativeToAbsId(Values[0])) << Comma()
<< FinishCluster() << Space() << StartCluster()
<< "align" << Space() << Alignment << Semicolon()
<< FinishCluster();
InstallInstType(LoadType);
break;
}
case naclbitc::FUNC_CODE_INST_STORE: {
// STORE: [ptr, val, align]
if (Values.size() != 3) {
Errors() << "Function store record expects 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
unsigned Alignment = getAlignmentValue(Values[2]);
uint32_t Val = RelativeToAbsId(Values[1]);
Type *ValType = GetValueType(Val);
VerifyScalarOrVectorOp("store", ValType);
Context->VerifyMemoryAccessAlignment("store", ValType, Alignment);
Tokens() << StartCluster() << "store" << Space() << TokenizeType(ValType)
<< Space() << GetBitcodeId(Val) << Comma() << FinishCluster()
<< Space() << StartCluster() << TokenizeType(ValType) << "*"
<< Space() << GetBitcodeId(RelativeToAbsId(Values[0])) << Comma()
<< FinishCluster() << Space() << StartCluster() << "align"
<< Space() << Alignment << Semicolon() << FinishCluster();
break;
}
case naclbitc::FUNC_CODE_INST_CMP2: {
// CMP2: [opval, opval, pred]
if (Values.size() != 3) {
Errors() << "Function compare record expects 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t Arg1 = RelativeToAbsId(Values[0]);
uint32_t Arg2 = RelativeToAbsId(Values[1]);
Type *Arg1Type = GetValueType(Arg1);
Type *Arg2Type = GetValueType(Arg2);
if (Arg1Type != Arg2Type) {
Errors() << "Arguments not of same type: " << *Arg1Type << " and "
<< *Arg2Type << "\n";
}
const char *Pred = "???";
Tokens() << NextInstId() << Space() << "=" << Space() << StartCluster();
Type* BaseType = UnderlyingType(Arg1Type);
if (BaseType->isIntegerTy()) {
Pred = GetIcmpPredicate(Values[2]);
Tokens() << "icmp" << Space() << Pred;
} else if (isFloatingType(BaseType)) {
Pred = GetFcmpPredicate(Values[2]);
Tokens() << "fcmp" << Space() << Pred;
} else {
Errors() << "Compare not on integer/float type. Found: "
<< *Arg1Type << "\n";
Tokens() << "cmp" << Space() << "???" "(" << Values[2] << ")";
}
Tokens() << FinishCluster() << Space() << StartCluster()
<< TokenizeType(Arg1Type) << Space () << GetBitcodeId(Arg1)
<< Comma() << FinishCluster() << Space() << StartCluster()
<< GetBitcodeId(Arg2) << Semicolon() << FinishCluster();
Type *ResultType = GetComparisonType();
if (Arg1Type->isVectorTy()) {
ResultType = VectorType::get(ResultType,
Arg1Type->getVectorNumElements());
}
InstallInstType(ResultType);
break;
}
case naclbitc::FUNC_CODE_INST_VSELECT: {
// VSELECT: [opval, opval, pred]
if (Values.size() != 3) {
Errors() << "Select record expects 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t CondValue = RelativeToAbsId(Values[2]);
uint32_t ThenValue = RelativeToAbsId(Values[0]);
uint32_t ElseValue = RelativeToAbsId(Values[1]);
Type *CondType = GetValueType(CondValue);
Type *ThenType = GetValueType(ThenValue);
Type *ElseType = GetValueType(ElseValue);
if (ThenType != ElseType) {
Errors() << "Selected arguments not of same type: "
<< *ThenType << " and " << *ElseType << "\n";
}
Type *BaseType = UnderlyingType(ThenType);
if (!(BaseType->isIntegerTy() || isFloatingType(BaseType))) {
Errors() << "Select arguments not integer/float. Found: " << *ThenType;
}
Tokens() << NextInstId() << Space() << "=" << Space() << StartCluster()
<< "select" << Space() << TokenizeType(CondType) << Space()
<< GetBitcodeId(CondValue) << Comma() << FinishCluster() << Space()
<< StartCluster() << TokenizeType(ThenType) << Space()
<< GetBitcodeId(ThenValue) << Comma() << FinishCluster() << Space()
<< StartCluster() << TokenizeType(ElseType) << Space()
<< GetBitcodeId(ElseValue) << Semicolon()
<< FinishCluster();
InstallInstType(ThenType);
break;
}
case naclbitc::FUNC_CODE_INST_EXTRACTELT: {
// EXTRACTELT: [opval, opval]
if (Values.size() != 2) {
Errors() << "Extract element record expects 2 arguments. Found: "
<< Values.size() << "\n";
break;
}
uint32_t VecValue = RelativeToAbsId(Values[0]);
uint32_t IdxValue = RelativeToAbsId(Values[1]);
VerifyIndexedVector("extractelement", VecValue, IdxValue);
Type *VecType = GetValueType(VecValue);
Type *IdxType = GetValueType(IdxValue);
Tokens() << NextInstId() << Space() << " = " << Space() << StartCluster()
<< "extractelement" << Space() << TokenizeType(VecType) << Space()
<< GetBitcodeId(VecValue) << Comma() << FinishCluster() << Space()
<< StartCluster() << TokenizeType(IdxType) << Space()
<< GetBitcodeId(IdxValue) << Semicolon() << FinishCluster();
InstallInstType(UnderlyingType(VecType));
break;
}
case naclbitc::FUNC_CODE_INST_INSERTELT: {
// INSERTELT: [opval, opval, opval]
uint32_t VecValue = RelativeToAbsId(Values[0]);
uint32_t EltValue = RelativeToAbsId(Values[1]);
uint32_t IdxValue = RelativeToAbsId(Values[2]);
VerifyIndexedVector("insertelement", VecValue, IdxValue);
Type *VecType = GetValueType(VecValue);
Type *EltType = GetValueType(EltValue);
Type *IdxType = GetValueType(IdxValue);
if (EltType != UnderlyingType(VecType)) {
Errors() << "insertelement: Illegal element type " << *EltType
<< ". Expected: " << *UnderlyingType(VecType) << "\n";
}
Tokens() << NextInstId() << Space() << " = " << Space() << StartCluster()
<< "insertelement" << Space() << TokenizeType(VecType) << Space()
<< GetBitcodeId(VecValue) << Comma() << FinishCluster() << Space()
<< StartCluster() << TokenizeType(EltType) << Space()
<< GetBitcodeId(EltValue) << Comma() << FinishCluster() << Space()
<< StartCluster() << TokenizeType(IdxType) << Space()
<< GetBitcodeId(IdxValue) << Semicolon() << FinishCluster();
InstallInstType(VecType);
break;
}
case naclbitc::FUNC_CODE_INST_CALL:
case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: {
// CALL: [cc, fnid, arg0, arg1...]
// CALL_INDIRECT: [cc, fnid, returnty, args...]
if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
if (Values.size() < 2) {
Errors() << "Call record expects at least 2 arguments. Found: "
<< Values.size() << "\n";
break;
}
} else if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL_INDIRECT) {
if (Values.size() < 3) {
Errors() << "Call indirect record expects at least 3 arguments. Found: "
<< Values.size() << "\n";
break;
}
}
unsigned IsTailCall = (Values[0] & 0x1);
CallingConv::ID CallingConv;
uint32_t FcnId = RelativeToAbsId(Values[1]);
if (!naclbitc::DecodeCallingConv(Values[0]>>1, CallingConv))
Errors() << "Call unknown calling convention:" << (Values[0]>>1) << "\n";
if (!IgnorePNaClABIChecks &&
!PNaClABIProps::isValidCallingConv(CallingConv)) {
Errors() << "Call uses disallowed calling convention: "
<< PNaClABIProps::CallingConvName(CallingConv) << "("
<< CallingConv << ")\n";
}
FunctionType *FcnType = 0;
Type *ReturnType = 0;
size_t ArgIndex = 2;
// Flag defining if type checking should be done on argument/return
// types.
bool CheckArgRetTypes = true;
if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
Type *FcnTy = GetFunctionValueType(FcnId);
if (FunctionType *FcnTyp = dyn_cast<FunctionType>(FcnTy)) {
// TODO(kschimpf) Add back signature checking once we know how
// to handle intrinsics (which can violate general signature
// rules).
CheckArgRetTypes = false;
FcnType = FcnTyp;
ReturnType = FcnType->getReturnType();
} else {
Errors() << "Invalid function signature: " << *FcnTy << "\n";
ReturnType = GetVoidType();
}
} else {
ReturnType = GetType(Values[2]);
ArgIndex = 3;
}
if (!ReturnType->isVoidTy()) {
Tokens() << NextInstId() << Space() << "=" << Space();
}
if (IsTailCall) {
Tokens() << "tail" << Space();
}
if (CheckArgRetTypes &&
!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidParamType(ReturnType)) {
Errors() << "Invalid return type: " << *ReturnType << "\n";
}
Tokens() << "call" << Space();
if (CallingConv != CallingConv::C) {
Tokens() << PNaClABIProps::CallingConvName(CallingConv) << Space();
}
Tokens() << TokenizeType(ReturnType) << Space()
<< StartCluster() << GetBitcodeId(FcnId) << OpenParen()
<< StartCluster();
unsigned ParamIndex = 0;
unsigned NumParams = Values.size() + 1 - ArgIndex;
for (size_t i = ArgIndex; i < Values.size(); ++i, ++ParamIndex) {
uint32_t ParamId = RelativeToAbsId(Values[i]);
Type *ParamType = GetValueType(ParamId);
if (CheckArgRetTypes &&
!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidParamType(ParamType)) {
Errors() << "invalid type for parameter " << i << ": "
<< *ParamType << "\n";
}
if (FcnType) {
if (ParamIndex < FcnType->getNumParams()) {
Type *ExpectedType = FcnType->getParamType(ParamIndex);
if (ParamType != ExpectedType) {
Warnings() << "Parameter " << (ParamIndex + 1) << " mismatch: "
<< *ParamType << " and " << *ExpectedType << "\n";
}
}
else if (ParamIndex == FcnType->getNumParams()) {
Warnings() << "Call expects " << FcnType->getNumParams()
<< " arguments. Got: " << NumParams << "\n";
}
}
if (i > ArgIndex) {
Tokens() << Comma() << FinishCluster() << Space() << StartCluster();
}
Tokens() << TokenizeType(ParamType) << Space() << GetBitcodeId(ParamId);
}
Tokens() << CloseParen() << Semicolon() << FinishCluster()
<< FinishCluster();
if (!ReturnType->isVoidTy()) InstallInstType(ReturnType);
break;
}
case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: {
// TYPE: [opval, ty]
if (Values.size() != 2) {
Errors() << "Forward declare record expects 2 arguments. Found: "
<< Values.size() << "\n";
break;
}
Type *OpType = GetType(Values[1]);
Tokens() << "declare" << Space() << StartCluster()
<< TokenizeType(OpType) << Space()
<< GetBitcodeId(Values[0]) << Semicolon() << FinishCluster();
InstallInstType(OpType, AbsToValuedInstId(Values[0]));
break;
}
default:
Errors() << "Unknown record found in module block.\n";
break;
}
Tokens() << TokenizeAbbrevIndex() << Endline();
ObjDumpWrite(Record.GetStartBit(), Record);
}
/// Parses and disassembles the module block.
class NaClDisModuleParser : public NaClDisBlockParser {
public:
NaClDisModuleParser(unsigned BlockID, NaClDisTopLevelParser *Context)
: NaClDisBlockParser(BlockID, Context) {
}
~NaClDisModuleParser() override;
bool ParseBlock(unsigned BlockID) override;
void PrintBlockHeader() override;
void ProcessRecord() override;
};
NaClDisModuleParser::~NaClDisModuleParser() {
// Note: Since we can't check type signatures of most functions till
// we know intrinsic names, and that isn't known until the (optional)
// valuesymtab block is parsed, the only reasonable spot to check
// function signatures is once the module block has been processed.
unsigned NextFcnDefinedId = 0;
for (unsigned i = 0, e = GetNumFunctions(); i < e; ++i) {
// Note: If the type of a function isn't a function type, that
// was checked in method ProcessRecord.
// Note: If the function was defined, the type was checked in
// NaClDisFunctionParser::PrintBlockHeader.
if (Context->HasDefinedFunctionIndex(NextFcnDefinedId)
&& i == Context->GetDefinedFunctionIndex(NextFcnDefinedId)) {
++NextFcnDefinedId;
continue;
}
if (FunctionType *FcnTy = dyn_cast<FunctionType>(GetFunctionValueType(i))) {
if (!Context->IsFunctionIntrinsic(i) &&
!IgnorePNaClABIChecks &&
!PNaClABITypeChecker::isValidFunctionType(FcnTy)) {
Context->SetRecordAddressToFunctionIdAddress(i);
Errors() << "Invalid type signature for "
<< BitcodeId('f', i) << ": " << *FcnTy << "\n";
}
}
}
}
bool NaClDisModuleParser::ParseBlock(unsigned BlockID) {
ObjDumpSetRecordBitAddress(GetBlock().GetStartBit());
switch (BlockID) {
case naclbitc::BLOCKINFO_BLOCK_ID: {
NaClDisBlockInfoParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
case naclbitc::TYPE_BLOCK_ID_NEW: {
NaClDisTypesParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
case naclbitc::GLOBALVAR_BLOCK_ID: {
NaClDisGlobalsParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
case naclbitc::VALUE_SYMTAB_BLOCK_ID: {
NaClDisValueSymtabParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
case naclbitc::FUNCTION_BLOCK_ID: {
NaClDisFunctionParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
default:
return NaClDisBlockParser::ParseBlock(BlockID);
}
}
void NaClDisModuleParser::PrintBlockHeader() {
Tokens() << "module" << Space() << OpenCurly()
<< Space() << Space() << "// BlockID = " << GetBlockID()
<< Endline();
}
void NaClDisModuleParser::ProcessRecord() {
ObjDumpSetRecordBitAddress(Record.GetStartBit());
const NaClBitcodeRecord::RecordVector &Values = Record.GetValues();
switch (Record.GetCode()) {
case naclbitc::MODULE_CODE_VERSION:
// [version#]
if (Values.size() != 1) {
Errors() << "Version record should have one argument. Found: "
<< Values.size() << "\n";
break;
}
Tokens() << "version" << Space() << Values[0] << Semicolon()
<< TokenizeAbbrevIndex() << Endline();
break;
case naclbitc::MODULE_CODE_FUNCTION: {
// [type, callingconv, isproto, linkage]
if (Values.size() != 4) {
Errors() << "Function record should have 4 arguments. Found: "
<< Values.size() << "\n";
break;
}
bool IsProto = (Values[2] != 0);
Tokens() << StartCluster() << (IsProto ? "declare" : "define");
uint32_t FcnId = GetNumFunctions();
BitcodeId FcnName('f', FcnId);
std::string FcnStrName(FcnName.GetName());
GlobalValue::LinkageTypes Linkage;
if (!naclbitc::DecodeLinkage(Values[3], Linkage)) {
Errors() << "Unknown linkage value: " << Values[3] << "\n";
} else {
if (!IgnorePNaClABIChecks &&
!PNaClABIProps::isValidGlobalLinkage(Linkage)) {
Errors() << "Disallowed linkage type: "
<< PNaClABIProps::LinkageName(Linkage) << "\n";
}
Tokens() << Space() << PNaClABIProps::LinkageName(Linkage);
}
CallingConv::ID CallingConv;
if (!naclbitc::DecodeCallingConv(Values[1], CallingConv)) {
Errors() << "Unknown calling convention value: " << Values[1] << "\n";
} else if (PNaClABIProps::isValidCallingConv(CallingConv)) {
if (CallingConv != CallingConv::C) {
Tokens() << Space() << PNaClABIProps::CallingConvName(CallingConv);
}
} else {
Errors() << "Function " << FcnStrName
<< " has disallowed calling convention: "
<< PNaClABIProps::CallingConvName(CallingConv)
<< " (" << CallingConv << ")\n";
}
Tokens() << FinishCluster() << Space() << StartCluster();
Type *FcnType = GetType(Values[0]);
FunctionType *FunctionTy = dyn_cast<FunctionType>(FcnType);
if (FunctionTy) {
Tokens() << TokenizeFunctionType(FunctionTy, &FcnName);
} else {
BitcodeId FcnTypeId('t', Values[0]);
Errors() << "Not function type: " << FcnTypeId << " = "
<< *FcnType << "\n";
Tokens() << "???";
SmallVector<Type*, 1> Signature;
FunctionTy = FunctionType::get(GetVoidType(), Signature, 0);
}
Tokens() << Semicolon() << FinishCluster() << TokenizeAbbrevIndex()
<< Endline();
InstallFunctionType(FunctionTy);
if (!IsProto) InstallDefinedFunction(FcnId);
break;
}
default:
Errors() << "Unknown record found in module block\n";
break;
}
ObjDumpWrite(Record.GetStartBit(), Record);
}
bool NaClDisTopLevelParser::ParseBlock(unsigned BlockID) {
// Before parsing top-level module block. Describe header by
// reconstructing the corresponding header record.
NaClBitcodeRecordData HeaderRecord;
size_t HeaderSize = Header.getHeaderSize();
HeaderRecord.Code = naclbitc::BLK_CODE_HEADER;
NaClBitstreamCursor &Cursor = Record.GetCursor();
uint64_t CurPos = Cursor.GetCurrentBitNo();
Cursor.JumpToBit(0);
for (size_t i = 0; i < HeaderSize; ++i) {
HeaderRecord.Values.push_back(Cursor.Read(CHAR_BIT));
}
Cursor.JumpToBit(CurPos);
if (ObjDump.GetDumpRecords() && ObjDump.GetDumpAssembly()) {
if (HeaderSize >= 4) {
const NaClRecordVector &Values = HeaderRecord.Values;
Tokens() << "Magic" << Space() << "Number" << Colon()
<< Space() << StartCluster() << StartCluster() << "'"
<< (char) Values[0] << (char) Values[1]
<< (char) Values[2] << (char) Values[3]
<< "'" << FinishCluster() << Space()
<< StartCluster() << OpenParen()
<< Values[0] << Comma() << Space()
<< Values[1] << Comma() << Space()
<< Values[2] << Comma() << Space()
<< Values[3] << CloseParen() << FinishCluster()
<< FinishCluster() << Endline();
}
// Show interpretation of header as assembly.
for (size_t i = 0; i < Header.NumberFields(); ++i) {
Tokens() << Header.GetField(i)->Contents() << Endline();
}
}
ObjDump.Write(0, HeaderRecord);
if (BlockID != naclbitc::MODULE_BLOCK_ID)
return Error("Module block expected at top-level, but not found");
// Now parse a module block.
NaClDisModuleParser Parser(BlockID, this);
return Parser.ParseThisBlock();
}
}
namespace llvm {
bool NaClObjDump(MemoryBufferRef MemBuf, raw_ostream &Output,
bool NoRecords, bool NoAssembly) {
// Create objects needed to run parser.
naclbitc::ObjDumpStream ObjDump(Output, !NoRecords, !NoAssembly);
if (MemBuf.getBufferSize() % 4 != 0) {
ObjDump.Error()
<< "Bitcode stream should be a multiple of 4 bytes in length.\n";
return true;
}
const unsigned char *BufPtr = (const unsigned char *)MemBuf.getBufferStart();
const unsigned char *EndBufPtr = BufPtr+MemBuf.getBufferSize();
const unsigned char *HeaderPtr = BufPtr;
// Read header and verify it is good.
NaClBitcodeHeader Header;
if (Header.Read(HeaderPtr, EndBufPtr) || !Header.IsSupported()) {
ObjDump.Error() << "Invalid PNaCl bitcode header.\n";
return true;
}
// Create a bitstream reader to read the bitcode file.
NaClBitstreamReader InputStreamFile(BufPtr, EndBufPtr,
Header.getHeaderSize());
NaClBitstreamCursor InputStream(InputStreamFile);
// Parse the the bitcode file.
::NaClDisTopLevelParser Parser(Header, InputStream, ObjDump);
int NumBlocksRead = 0;
bool ErrorsFound = false;
while (!InputStream.AtEndOfStream()) {
++NumBlocksRead;
if (Parser.Parse()) ErrorsFound = true;
}
if (NumBlocksRead != 1) {
ObjDump.Error() << "Expected 1 top level block in bitcode: Found:"
<< NumBlocksRead << "\n";
ErrorsFound = true;
}
ObjDump.Flush();
return ErrorsFound || Parser.GetNumErrors() > 0;
}
}