Arquivos
hhvm/hphp/runtime/vm/unit.h
T
Edwin Smith 9b6bf426b9 Move runtime/base/array/* up one level.
Also rename array_inline.h to array_data-inl.h
2013-07-15 17:34:47 -07:00

1103 linhas
32 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_VM_UNIT_H_
#define incl_HPHP_VM_UNIT_H_
// Expects that runtime/vm/core_types.h is already included.
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/base/complex_types.h"
#include "hphp/runtime/vm/repo_helpers.h"
#include "hphp/runtime/vm/named_entity.h"
#include "hphp/runtime/base/hphp_array.h"
#include "hphp/util/range.h"
#include "hphp/util/parser/location.h"
#include "hphp/runtime/base/md5.h"
#include "hphp/util/tiny_vector.h"
namespace HPHP {
// Forward declarations.
namespace Compiler { class Peephole; }
struct ActRec;
class Func;
class FuncEmitter;
class Repo;
class FuncDict;
class Unit;
class PreClassEmitter;
enum class UnitOrigin {
File = 0,
Eval = 1
};
enum UnitMergeKind {
// UnitMergeKindClass is required to be 0 for correctness.
UnitMergeKindClass = 0,
UnitMergeKindUniqueDefinedClass = 1,
// Top level, scalar defines in the unit
UnitMergeKindDefine = 2,
// Top level, scalar defines that will be loaded once
// and preserved from request to request
UnitMergeKindPersistentDefine = 3,
UnitMergeKindGlobal = 4,
// 5 is available
UnitMergeKindReqDoc = 6,
UnitMergeKindDone = 7,
// We cannot add more kinds here; this has to fit in 3 bits.
};
enum UnitMergeState {
UnitMergeStateUnmerged = 0,
UnitMergeStateMerging = 1,
UnitMergeStateMerged = 2,
UnitMergeStateUniqueFuncs = 4,
UnitMergeStateNeedsCompact = 8,
UnitMergeStateEmpty = 32
};
inline bool ALWAYS_INLINE isMergeKindReq(UnitMergeKind k) {
return k == UnitMergeKindReqDoc;
}
struct UnitMergeInfo {
typedef IterRange<Func* const*> FuncRange;
typedef IterRange<Func**> MutableFuncRange;
unsigned m_firstHoistableFunc;
unsigned m_firstHoistablePreClass;
unsigned m_firstMergeablePreClass;
unsigned m_mergeablesSize;
void* m_mergeables[1];
static UnitMergeInfo* alloc(size_t num);
Func** funcBegin() const {
return (Func**)m_mergeables;
}
Func** funcEnd() const {
return funcBegin() + m_firstHoistablePreClass;
}
Func** funcHoistableBegin() const {
return funcBegin() + m_firstHoistableFunc;
}
MutableFuncRange nonMainFuncs() const {
return MutableFuncRange(funcBegin() + 1, funcEnd());
}
MutableFuncRange hoistableFuncs() const {
return MutableFuncRange(funcHoistableBegin(), funcEnd());
}
FuncRange funcs() const {
return FuncRange(funcBegin(), funcEnd());
}
MutableFuncRange mutableFuncs() {
return MutableFuncRange(funcBegin(), funcEnd());
}
void*& mergeableObj(int ix) { return ((void**)m_mergeables)[ix]; }
void* mergeableData(int ix) { return (char*)m_mergeables + ix*sizeof(void*); }
};
// Exception handler table entry.
class EHEnt {
public:
enum class Type {
Catch,
Fault
};
Type m_type;
Offset m_base;
Offset m_past;
int m_iterId;
bool m_itRef;
int m_parentIndex;
Offset m_fault;
typedef std::vector<std::pair<Id, Offset> > CatchVec;
CatchVec m_catches;
template<class SerDe> void serde(SerDe& sd) {
sd(m_type)
(m_base)
(m_past)
(m_iterId)
(m_fault)
(m_itRef)
// eh.m_parentIndex is re-computed in sortEHTab, not serialized.
;
if (m_type == Type::Catch) {
sd(m_catches);
}
}
};
// Function paramater info region table entry.
class FPIEnt {
public:
Offset m_fpushOff;
Offset m_fcallOff;
Offset m_fpOff; // evaluation stack depth to current frame pointer
int m_parentIndex;
int m_fpiDepth;
template<class SerDe> void serde(SerDe& sd) {
sd(m_fpushOff)(m_fcallOff)(m_fpOff);
// These fields are recomputed by sortFPITab:
// m_parentIndex;
// m_fpiDepth;
}
};
class FPIEntComp {
public:
bool operator() (const FPIEnt &fpi1, const FPIEnt &fpi2) {
return fpi1.m_fpushOff < fpi2.m_fpushOff;
}
};
class SourceLoc {
public:
SourceLoc() : line0(1), char0(1), line1(1), char1(1) {}
explicit SourceLoc(const Location& l) { setLoc(&l); }
int line0;
int char0;
int line1;
int char1;
// {1, 1, 1, 1} is a special "invalid" value.
void reset() {
line0 = char0 = line1 = char1 = 1;
}
bool valid() {
return line0 != 1 || char0 != 1 || line1 != 1 || char1 != 1;
}
void setLoc(const Location *l) {
line0 = l->line0;
char0 = l->char0;
line1 = l->line1;
char1 = l->char1;
}
bool same(const SourceLoc *l) const {
return (this == l) ||
(line0 == l->line0 && char0 == l->char0 &&
line1 == l->line1 && char1 == l->char1);
}
bool operator==(const SourceLoc &l) const {
return same(&l);
}
};
class OffsetRange {
public:
OffsetRange() : m_base(0), m_past(0) {}
OffsetRange(Offset base, Offset past) : m_base(base), m_past(past) {}
Offset m_base;
Offset m_past;
};
typedef std::vector<OffsetRange> OffsetRangeVec;
template<typename T>
class TableEntry {
public:
TableEntry() : m_pastOffset(0) {}
TableEntry(Offset pastOffset, T val)
: m_pastOffset(pastOffset), m_val(val) {}
Offset pastOffset() const { return m_pastOffset; }
T val() const { return m_val; }
bool operator <(const TableEntry& other) const {
return m_pastOffset < other.m_pastOffset;
}
template<class SerDe> void serde(SerDe& sd) { sd(m_pastOffset)(m_val); }
private:
Offset m_pastOffset;
T m_val;
};
typedef TableEntry<int> LineEntry;
typedef std::vector<LineEntry> LineTable;
typedef TableEntry<const Func*> FuncEntry;
typedef std::vector<FuncEntry> FuncTable;
/*
* This is the runtime representation of a typedef. Typedefs are only
* allowed when hip hop extensions are enabled.
*
* The m_kind field is KindOfObject whenever the typedef is basically
* just a name. At runtime we still might resolve this name to
* another typedef, becoming a typedef for KindOfArray or something in
* that request.
*
* For the per-request struct, see TypedefReq below.
*/
struct Typedef {
const StringData* name;
const StringData* value;
DataType kind;
bool nullable; // Null is allowed; for ?Foo typedefs
template<class SerDe> void serde(SerDe& sd) {
sd(name)
(value)
(kind)
(nullable)
;
}
};
/*
* In a given request, a defined typedef is turned into a TypedefReq
* struct. This contains the information needed to validate parameter
* type hints for a typedef at runtime.
*/
struct TypedefReq {
DataType kind; // may be KindOfAny for "mixed"
bool nullable; // for option types, like ?Foo
Class* klass; // nullptr if kind != KindOfObject
const StringData* name; // needed for error messages; nullptr if not defined
};
//==============================================================================
// (const StringData*) versus (StringData*)
//
// All (const StringData*) values are static strings that came from e.g.
// StringData::GetStaticString(). Therefore no reference counting is required.
//
//==============================================================================
/*
* Metadata about a compilation unit.
*
* Contains the list of PreClasses and global functions, along with a
* special function called the 'pseudo-main', which is logically
* invoked (modulo optimizations that avoid it) during execution when
* the unit is included/required.
*/
struct Unit {
friend class UnitEmitter;
friend class UnitRepoProxy;
friend class FuncDict;
typedef UnitMergeInfo::FuncRange FuncRange;
typedef UnitMergeInfo::MutableFuncRange MutableFuncRange;
typedef hphp_hash_map<const Class*, Func*,
pointer_hash<Class> > PseudoMainCacheMap;
class MetaInfo {
public:
enum class Kind {
None,
String,
Class,
NopOut,
/*
* Marks types that are proven to be a particular type by static
* analysis. Guards are not needed in these cases.
*/
DataTypeInferred,
/*
* Marks types that are predicted by static analysis. Guards
* will still be needed in case the prediction is wrong.
*/
DataTypePredicted,
GuardedThis,
GuardedCls,
NoSurprise,
ArrayCapacity,
/*
* Information about the known class of a property base in the
* middle of a vector instruction.
*
* In this case, m_arg is the index of the member code for the
* relevant property dim. (Unlike other cases, m_arg is not an
* index into the instruction inputs in NormalizedInstruction.)
*
* Whatever the base is when processing that member code will be
* an object of the supplied class type (or a null).
*/
MVecPropClass,
/*
* At a Ret{C,V} site, indicates which locals are known not to
* be reference counted. m_data is the id of the local variable
* that cannot be reference counted at this point.
*/
NonRefCounted,
};
/*
* This flag is used to mark that m_arg is an index into an
* MVector input list. (We need to know this so we can bump the
* indexes different amounts depending on the instruction type;
* see applyInputMetaData.)
*/
static const int VectorArg = 1 << 7;
MetaInfo(Kind k, int a, Id d) : m_kind(k), m_arg(a), m_data(d) {
assert((int)m_arg == a);
}
MetaInfo() : m_kind(Kind::None), m_arg(-1), m_data(0) {}
/*
* m_arg indicates which input the MetaInfo applies to.
*
* For instructions taking vector immediates, it is an index into
* the immediate elements, excluding any MW members (and including
* the base). (This is currently even if the instruction takes
* other stack arguments.)
*/
Kind m_kind;
uint8_t m_arg;
Id m_data;
};
class MetaHandle {
/*
The meta-data in Unit::m_bc_meta is stored as:
Offset <num entries>
Offset byte-code-offset-1
Offset byte-code-offset-2
...
Offset byte-code_offset-n
Offset INT_MAX # sentinel
Offset data-offset-1
Offset data-offset-2
...
Offset data-offset-n
Offset m_bc_meta_len # sentinel
uint8 m_kind1
uint8 m_arg1
VSI m_data1
...
uint8 m_kind-n
uint8 m_arg-n
VSI m_data-n
*/
public:
MetaHandle() : index(nullptr), cur(0) {}
bool findMeta(const Unit* unit, Offset offset);
bool nextArg(MetaInfo& info);
private:
const Offset* index;
unsigned cur;
const uint8_t *ptr;
};
Unit();
~Unit();
void* operator new(size_t sz);
void operator delete(void* p, size_t sz);
int repoId() const { return m_repoId; }
int64_t sn() const { return m_sn; }
PC entry() const { return m_bc; }
Offset bclen() const { return m_bclen; }
PC at(const Offset off) const {
assert(off >= 0 && off <= Offset(m_bclen));
return m_bc + off;
}
Offset offsetOf(const Opcode* op) const {
assert(op >= m_bc && op <= (m_bc + m_bclen));
return op - m_bc;
}
Offset offsetOf(const Op* op) const {
return offsetOf(reinterpret_cast<const Opcode*>(op));
}
const StringData* filepath() const {
assert(m_filepath);
return m_filepath;
}
CStrRef filepathRef() const {
assert(m_filepath);
return *(String*)(&m_filepath);
}
const StringData* dirpath() const {
assert(m_dirpath);
return m_dirpath;
}
MD5 md5() const { return m_md5; }
static NamedEntity* GetNamedEntity(const StringData *)
__attribute__((__flatten__));
static size_t GetNamedEntityTableSize();
static Array getUserFunctions();
static Array getClassesInfo();
static Array getInterfacesInfo();
static Array getTraitsInfo();
size_t numLitstrs() const {
return m_namedInfo.size();
}
StringData* lookupLitstrId(Id id) const {
assert(id >= 0 && id < Id(m_namedInfo.size()));
return const_cast<StringData*>(m_namedInfo[id].first);
}
const NamedEntity* lookupNamedEntityId(Id id) const {
return lookupNamedEntityPairId(id).second;
}
const NamedEntityPair& lookupNamedEntityPairId(Id id) const {
assert(id < Id(m_namedInfo.size()));
const NamedEntityPair &ne = m_namedInfo[id];
assert(ne.first);
if (UNLIKELY(!ne.second)) {
const_cast<const NamedEntity*&>(ne.second) = GetNamedEntity(ne.first);
}
return ne;
}
size_t numArrays() const {
return m_arrays.size();
}
ArrayData* lookupArrayId(Id id) const {
return const_cast<ArrayData*>(m_arrays.at(id));
}
static Func *lookupFunc(const NamedEntity *ne);
static Func *lookupFunc(const StringData *funcName);
static Func *loadFunc(const NamedEntity *ne, const StringData* name);
static Func *loadFunc(const StringData* name);
static Class* defClass(const HPHP::PreClass* preClass,
bool failIsFatal = true);
static bool aliasClass(Class* original, const StringData* alias);
void defTypedef(Id id);
static TypedValue* lookupCns(const StringData* cnsName);
static TypedValue* lookupPersistentCns(const StringData* cnsName);
static TypedValue* loadCns(const StringData* cnsName);
static bool defCns(const StringData* cnsName, const TypedValue* value,
bool persistent = false);
static uint64_t defCnsHelper(uint64_t ch,
const TypedValue* value,
const StringData* cnsName);
static void defDynamicSystemConstant(const StringData* cnsName,
const void* data);
static bool defCnsDynamic(const StringData* cnsName, TypedValue* value);
/*
* Find the Class* for a defined class corresponding to the name
* `clsName'.
*
* Returns: nullptr if the class of the given name is not yet
* defined in this request.
*/
static Class *lookupClass(const StringData *clsName) {
return lookupClass(GetNamedEntity(clsName));
}
/*
* Find the Class* for a defined class with name mapped to the
* supplied NamedEntity.
*
* Returns: nullptr if the class is not yet defined in this request.
*/
static Class *lookupClass(const NamedEntity *ne) {
Class* cls;
if (LIKELY((cls = ne->getCachedClass()) != nullptr)) {
return cls;
}
return nullptr;
}
/*
* Same as lookupClass, except if it's not defined *and* is unique,
* return the Class* anyway.
*
* The point of this is that when jitting code before a unique class
* is defined, we can often still burn the Class* into the TC, since
* it will be defined by the time the code that needs the Class*
* runs (via autoload or whatnot).
*/
static Class *lookupUniqueClass(const NamedEntity *ne) {
Class* cls = ne->clsList();
if (LIKELY(cls != nullptr)) {
if (cls->attrs() & AttrUnique && RuntimeOption::RepoAuthoritative) {
return cls;
}
return cls->getCached();
}
return nullptr;
}
static Class *lookupUniqueClass(const StringData *clsName) {
return lookupUniqueClass(GetNamedEntity(clsName));
}
static Class *loadClass(const NamedEntity *ne,
const StringData *name);
static Class *loadClass(const StringData *name) {
return loadClass(GetNamedEntity(name), name);
}
static Class *loadMissingClass(const NamedEntity *ne,
const StringData *name);
static Class* getClass(const StringData* name, bool tryAutoload) {
return getClass(GetNamedEntity(name), name, tryAutoload);
}
static Class* getClass(const NamedEntity *ne, const StringData *name,
bool tryAutoload);
static bool classExists(const StringData* name, bool autoload,
Attr typeAttrs);
bool compileTimeFatal(const StringData*& msg, int& line) const;
const TypedValue *getMainReturn() const {
return &m_mainReturn;
}
private:
template <bool debugger>
void mergeImpl(void* tcbase, UnitMergeInfo* mi);
public:
Func* firstHoistable() const {
return *m_mergeInfo->funcHoistableBegin();
}
Func* getMain(Class* cls = nullptr) const;
// Ranges for iterating over functions.
MutableFuncRange nonMainFuncs() const {
return m_mergeInfo->nonMainFuncs();
}
MutableFuncRange hoistableFuncs() const {
return m_mergeInfo->hoistableFuncs();
}
void renameFunc(const StringData* oldName, const StringData* newName);
static void loadFunc(const Func *func);
FuncRange funcs() const {
return m_mergeInfo->funcs();
}
MutableFuncRange mutableFuncs() {
return m_mergeInfo->mutableFuncs();
}
Func* lookupFuncId(Id id) const {
assert(id < Id(m_mergeInfo->m_firstHoistablePreClass));
return m_mergeInfo->funcBegin()[id];
}
size_t numPreClasses() const {
return (size_t)m_preClasses.size();
}
PreClass* lookupPreClassId(Id id) const {
assert(id < Id(m_preClasses.size()));
return m_preClasses[id].get();
}
typedef std::vector<PreClassPtr> PreClassPtrVec;
typedef Range<PreClassPtrVec> PreClassRange;
void initialMerge();
void merge();
PreClassRange preclasses() const {
return PreClassRange(m_preClasses);
}
bool mergeClasses() const;
int getLineNumber(Offset pc) const;
bool getSourceLoc(Offset pc, SourceLoc& sLoc) const;
bool getOffsetRanges(int line, OffsetRangeVec& offsets) const;
bool getOffsetRange(Offset pc, OffsetRange& range) const;
Op getOpcode(size_t instrOffset) const {
assert(instrOffset < m_bclen);
return toOp(m_bc[instrOffset]);
}
/*
* Return the Func* for the code at offset off.
*
* Returns nullptr if the offset is not in a func body (but this
* should be impossible).
*/
const Func* getFunc(Offset pc) const;
void setCacheId(unsigned id) {
m_cacheOffset = id >> 3;
m_cacheMask = 1 << (id & 7);
}
bool isMergeOnly() const { return m_mergeOnly; }
void clearMergeOnly() { m_mergeOnly = false; }
bool isEmpty() const { return m_mergeState & UnitMergeStateEmpty; }
void* replaceUnit() const;
public:
static Mutex s_classesMutex;
struct PrintOpts {
PrintOpts()
: startOffset(kInvalidOffset)
, stopOffset(kInvalidOffset)
, showLines(true)
, showFuncs(true)
, indentSize(1)
{}
PrintOpts& range(Offset start, Offset stop) {
startOffset = start;
stopOffset = stop;
return *this;
}
PrintOpts& noLineNumbers() {
showLines = false;
return *this;
}
PrintOpts& noFuncs() {
showFuncs = false;
return *this;
}
PrintOpts& indent(int i) {
indentSize = i;
return *this;
}
Offset startOffset;
Offset stopOffset;
bool showLines;
bool showFuncs;
int indentSize;
};
void prettyPrint(std::ostream&, PrintOpts = PrintOpts()) const;
std::string toString() const;
public: // Translator field access
static size_t bcOff() { return offsetof(Unit, m_bc); }
private:
// pseudoMain's return value, or KindOfUninit if its not known.
TypedValue m_mainReturn;
int64_t m_sn;
uchar const* m_bc;
size_t m_bclen;
uchar const* m_bc_meta;
size_t m_bc_meta_len;
const StringData* m_filepath;
const StringData* m_dirpath;
MD5 m_md5;
std::vector<NamedEntityPair> m_namedInfo;
std::vector<const ArrayData*> m_arrays;
PreClassPtrVec m_preClasses;
FixedVector<Typedef> m_typedefs;
UnitMergeInfo* m_mergeInfo;
unsigned m_cacheOffset;
int8_t m_repoId;
uint8_t m_mergeState;
uint8_t m_cacheMask;
bool m_mergeOnly;
LineTable m_lineTable;
FuncTable m_funcTable;
mutable PseudoMainCacheMap *m_pseudoMainCache;
};
class UnitEmitter {
friend class UnitRepoProxy;
friend class ::HPHP::Compiler::Peephole;
public:
explicit UnitEmitter(const MD5& md5);
~UnitEmitter();
void addTrivialPseudoMain();
int repoId() const { return m_repoId; }
void setRepoId(int repoId) { m_repoId = repoId; }
int64_t sn() const { return m_sn; }
void setSn(int64_t sn) { m_sn = sn; }
Offset bcPos() const { return (Offset)m_bclen; }
void setBc(const uchar* bc, size_t bclen);
void setBcMeta(const uchar* bc_meta, size_t bc_meta_len);
const StringData* getFilepath() { return m_filepath; }
void setFilepath(const StringData* filepath) { m_filepath = filepath; }
void setMainReturn(const TypedValue* v) { m_mainReturn = *v; }
void setMergeOnly(bool b) { m_mergeOnly = b; }
const MD5& md5() const { return m_md5; }
Id addTypedef(const Typedef& td);
Id mergeLitstr(const StringData* litstr);
Id mergeArray(ArrayData* a, const StringData* key=nullptr);
FuncEmitter* getMain();
void initMain(int line1, int line2);
FuncEmitter* newFuncEmitter(const StringData* n);
void appendTopEmitter(FuncEmitter* func);
FuncEmitter* newMethodEmitter(const StringData* n, PreClassEmitter* pce);
PreClassEmitter* newPreClassEmitter(const StringData* n,
PreClass::Hoistable hoistable);
PreClassEmitter* pce(Id preClassId) { return m_pceVec[preClassId]; }
/*
* Record source location information for the last chunk of bytecode
* added to this UnitEmitter. Adjacent regions associated with the
* same source line will be collapsed as this is created.
*/
void recordSourceLocation(const Location *sLoc, Offset start);
/*
* Adds a new FuncEmitter to the unit. You can only do this once
* for the FuncEmitter (after you are done setting it up). Also,
* all FuncEmitter's added to the unit must not overlap.
*
* Takes ownership of `fe'.
*/
void recordFunction(FuncEmitter *fe);
private:
template<class T>
void emitImpl(T n, int64_t pos) {
uchar *c = (uchar*)&n;
if (pos == -1) {
// Make sure m_bc is large enough.
while (m_bclen + sizeof(T) > m_bcmax) {
m_bc = (uchar*)realloc(m_bc, m_bcmax << 1);
m_bcmax <<= 1;
}
memcpy(&m_bc[m_bclen], c, sizeof(T));
m_bclen += sizeof(T);
} else {
assert(pos + sizeof(T) <= m_bclen);
for (uint i = 0; i < sizeof(T); ++i) {
m_bc[pos + i] = c[i];
}
}
}
public:
void emitOp(Op op, int64_t pos = -1) {
emitByte((uchar)op, pos);
}
void emitByte(uchar n, int64_t pos = -1) { emitImpl(n, pos); }
void emitInt32(int n, int64_t pos = -1) { emitImpl(n, pos); }
template<typename T> void emitIVA(T n) {
if (LIKELY((n & 0x7f) == n)) {
emitByte((unsigned char)n << 1);
} else {
assert((n & 0x7fffffff) == n);
emitInt32((n << 1) | 0x1);
}
}
void emitInt64(int64_t n, int64_t pos = -1) { emitImpl(n, pos); }
void emitDouble(double n, int64_t pos = -1) { emitImpl(n, pos); }
bool insert(UnitOrigin unitOrigin, RepoTxn& txn);
void commit(UnitOrigin unitOrigin);
Func* newFunc(const FuncEmitter* fe, Unit& unit, Id id, int line1, int line2,
Offset base, Offset past,
const StringData* name, Attr attrs, bool top,
const StringData* docComment, int numParams,
bool isClosureBody, bool isGenerator);
Func* newFunc(const FuncEmitter* fe, Unit& unit, PreClass* preClass,
int line1, int line2, Offset base, Offset past,
const StringData* name, Attr attrs, bool top,
const StringData* docComment, int numParams,
bool isClosureBody, bool isGenerator);
Unit* create();
void returnSeen() { m_returnSeen = true; }
void pushMergeableClass(PreClassEmitter* e);
void pushMergeableInclude(UnitMergeKind kind, const StringData* unitName);
void insertMergeableInclude(int ix, UnitMergeKind kind, Id id);
void pushMergeableDef(UnitMergeKind kind,
const StringData* name, const TypedValue& tv);
void insertMergeableDef(int ix, UnitMergeKind kind,
Id id, const TypedValue& tv);
private:
void setLines(const LineTable& lines);
private:
int m_repoId;
int64_t m_sn;
static const size_t BCMaxInit = 4096; // Initial bytecode size.
size_t m_bcmax;
uchar* m_bc;
size_t m_bclen;
uchar* m_bc_meta;
size_t m_bc_meta_len;
TypedValue m_mainReturn;
const StringData* m_filepath;
MD5 m_md5;
typedef hphp_hash_map<const StringData*, Id,
string_data_hash, string_data_same> LitstrMap;
LitstrMap m_litstr2id;
std::vector<const StringData*> m_litstrs;
typedef hphp_hash_map<const StringData*, Id,
string_data_hash, string_data_same> ArrayIdMap;
ArrayIdMap m_array2id;
typedef struct {
const StringData* serialized;
const ArrayData* array;
} ArrayVecElm;
typedef std::vector<ArrayVecElm> ArrayVec;
ArrayVec m_arrays;
int m_nextFuncSn;
bool m_mergeOnly;
typedef std::vector<FuncEmitter*> FeVec;
FeVec m_fes;
typedef hphp_hash_map<const FuncEmitter*, const Func*,
pointer_hash<FuncEmitter> > FMap;
FMap m_fMap;
typedef std::vector<PreClassEmitter*> PceVec;
typedef std::vector<Id> IdVec;
PceVec m_pceVec;
typedef hphp_hash_set<const StringData*, string_data_hash,
string_data_isame> HoistedPreClassSet;
HoistedPreClassSet m_hoistablePreClassSet;
IdVec m_hoistablePceIdVec;
typedef std::vector<std::pair<UnitMergeKind, Id> > MergeableStmtVec;
MergeableStmtVec m_mergeableStmts;
std::vector<std::pair<Id,TypedValue> > m_mergeableValues;
bool m_allClassesHoistable;
bool m_returnSeen;
/*
* m_sourceLocTab and m_feTab are interval maps. Each entry encodes
* an open-closed range of bytecode offsets.
*
* The m_sourceLocTab is keyed by the start of each half-open range.
* This is to allow appending new bytecode offsets that are part of
* the same range to coalesce.
*
* The m_feTab is keyed by the past-the-end offset. This is the
* format we'll want it in when we go to create a Unit.
*/
std::vector<std::pair<Offset,SourceLoc> > m_sourceLocTab;
std::vector<std::pair<Offset,const FuncEmitter*> > m_feTab;
std::vector<Typedef> m_typedefs;
};
class UnitRepoProxy : public RepoProxy {
friend class Unit;
friend class UnitEmitter;
public:
explicit UnitRepoProxy(Repo& repo);
~UnitRepoProxy();
void createSchema(int repoId, RepoTxn& txn);
Unit* load(const std::string& name, const MD5& md5);
#define URP_IOP(o) URP_OP(Insert##o, insert##o)
#define URP_GOP(o) URP_OP(Get##o, get##o)
#define URP_OPS \
URP_IOP(Unit) \
URP_GOP(Unit) \
URP_IOP(UnitLitstr) \
URP_GOP(UnitLitstrs) \
URP_IOP(UnitArray) \
URP_GOP(UnitArrays) \
URP_IOP(UnitMergeable) \
URP_GOP(UnitMergeables) \
URP_IOP(UnitSourceLoc) \
URP_GOP(SourceLoc) \
URP_GOP(SourceLocPastOffsets) \
URP_GOP(SourceLocBaseOffset) \
URP_GOP(BaseOffsetAtPCLoc) \
URP_GOP(BaseOffsetAfterPCLoc)
class InsertUnitStmt : public RepoProxy::Stmt {
public:
InsertUnitStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void insert(RepoTxn& txn, int64_t& unitSn, const MD5& md5, const uchar* bc,
size_t bclen, const uchar* bc_meta, size_t bc_meta_len,
const TypedValue* mainReturn, bool mergeOnly,
const LineTable& lines,
const std::vector<Typedef>&);
};
class GetUnitStmt : public RepoProxy::Stmt {
public:
GetUnitStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(UnitEmitter& ue, const MD5& md5);
};
class InsertUnitLitstrStmt : public RepoProxy::Stmt {
public:
InsertUnitLitstrStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void insert(RepoTxn& txn, int64_t unitSn, Id litstrId,
const StringData* litstr);
};
class GetUnitLitstrsStmt : public RepoProxy::Stmt {
public:
GetUnitLitstrsStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void get(UnitEmitter& ue);
};
class InsertUnitArrayStmt : public RepoProxy::Stmt {
public:
InsertUnitArrayStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void insert(RepoTxn& txn, int64_t unitSn, Id arrayId,
const StringData* array);
};
class GetUnitArraysStmt : public RepoProxy::Stmt {
public:
GetUnitArraysStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void get(UnitEmitter& ue);
};
class InsertUnitMergeableStmt : public RepoProxy::Stmt {
public:
InsertUnitMergeableStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void insert(RepoTxn& txn, int64_t unitSn,
int ix, UnitMergeKind kind,
Id id, TypedValue *value);
};
class GetUnitMergeablesStmt : public RepoProxy::Stmt {
public:
GetUnitMergeablesStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void get(UnitEmitter& ue);
};
class InsertUnitSourceLocStmt : public RepoProxy::Stmt {
public:
InsertUnitSourceLocStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
void insert(RepoTxn& txn, int64_t unitSn, Offset pastOffset, int line0,
int char0, int line1, int char1);
};
class GetSourceLocStmt : public RepoProxy::Stmt {
public:
GetSourceLocStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(int64_t unitSn, Offset pc, SourceLoc& sLoc);
};
class GetSourceLocPastOffsetsStmt : public RepoProxy::Stmt {
public:
GetSourceLocPastOffsetsStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(int64_t unitSn, int line, OffsetRangeVec& ranges);
};
class GetSourceLocBaseOffsetStmt : public RepoProxy::Stmt {
public:
GetSourceLocBaseOffsetStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(int64_t unitSn, OffsetRange& range);
};
class GetBaseOffsetAtPCLocStmt : public RepoProxy::Stmt {
public:
GetBaseOffsetAtPCLocStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(int64_t unitSn, Offset pc, Offset& offset);
};
class GetBaseOffsetAfterPCLocStmt : public RepoProxy::Stmt {
public:
GetBaseOffsetAfterPCLocStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
bool get(int64_t unitSn, Offset pc, Offset& offset);
};
#define URP_OP(c, o) \
public: \
c##Stmt& o(int repoId) { return *m_##o[repoId]; } \
private: \
c##Stmt m_##o##Local; \
c##Stmt m_##o##Central; \
c##Stmt* m_##o[RepoIdCount];
URP_OPS
#undef URP_OP
};
/**
* AllFuncs
* MutableAllFuncs
*
* Range over all Func's in a single unit.
*/
struct ConstPreClassMethodRanger {
typedef Func* const* Iter;
typedef const Func* Value;
static Iter get(PreClassPtr pc) {
return pc->methods();
}
};
struct MutablePreClassMethodRanger {
typedef Func** Iter;
typedef Func* Value;
static Func** get(PreClassPtr pc) {
return pc->mutableMethods();
}
};
template<typename FuncRange,
typename GetMethods>
class AllFuncsImpl {
public:
explicit AllFuncsImpl(const Unit* unit)
: fr(unit->funcs())
, mr(0, 0)
, cr(unit->preclasses())
{
if (fr.empty()) skip();
}
bool empty() const { return fr.empty() && mr.empty() && cr.empty(); }
typedef typename GetMethods::Value FuncPtr;
FuncPtr front() const {
assert(!empty());
if (!fr.empty()) return fr.front();
assert(!mr.empty());
return mr.front();
}
FuncPtr popFront() {
FuncPtr f = !fr.empty() ? fr.popFront() :
!mr.empty() ? mr.popFront() : 0;
assert(f);
if (fr.empty() && mr.empty()) skip();
return f;
}
private:
void skip() {
assert(fr.empty());
while (!cr.empty() && mr.empty()) {
PreClassPtr c = cr.popFront();
mr = Unit::FuncRange(GetMethods::get(c),
GetMethods::get(c) + c->numMethods());
}
}
Unit::FuncRange fr;
Unit::FuncRange mr;
Unit::PreClassRange cr;
};
typedef AllFuncsImpl<Unit::FuncRange, ConstPreClassMethodRanger> AllFuncs;
typedef AllFuncsImpl<Unit::MutableFuncRange, MutablePreClassMethodRanger> MutableAllFuncs;
/**
*
* Range over all defined classes.
*/
class AllClasses {
protected:
NamedEntityMap::iterator m_next, m_end;
Class* m_current;
void next();
void skip();
public:
AllClasses();
bool empty() const;
Class* front() const;
Class* popFront();
};
/**
* If name starts with '\\', returns a new String with the leading
* slash stripped. Otherwise returns a null string.
*/
inline const String normalizeNS(const StringData* name) {
assert(name->data()[name->size()] == 0);
if (name->data()[0] == '\\') {
return String(name->data() + 1);
}
return null_string;
}
}
#endif