Move JIT
Esse commit está contido em:
@@ -44,6 +44,7 @@
|
||||
#include "hphp/runtime/vm/translator/types.h"
|
||||
#include "hphp/runtime/vm/translator/runtime-type.h"
|
||||
#include "hphp/runtime/vm/translator/translator-runtime.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/type.h"
|
||||
#include "hphp/runtime/base/types.h"
|
||||
#include "hphp/runtime/vm/func.h"
|
||||
#include "hphp/runtime/vm/class.h"
|
||||
@@ -677,490 +678,6 @@ enum OpcodeFlag : uint64_t {
|
||||
bool opcodeHasFlags(Opcode opc, uint64_t flags);
|
||||
Opcode getStackModifyingOpcode(Opcode opc);
|
||||
|
||||
#define IRT_BOXES(name, bit) \
|
||||
IRT(name, (bit)) \
|
||||
IRT(Boxed##name, (bit) << kBoxShift) \
|
||||
IRT(PtrTo##name, (bit) << kPtrShift) \
|
||||
IRT(PtrToBoxed##name, (bit) << kPtrBoxShift)
|
||||
|
||||
#define IRT_PHP(c) \
|
||||
c(Uninit, 1ULL << 0) \
|
||||
c(InitNull, 1ULL << 1) \
|
||||
c(Bool, 1ULL << 2) \
|
||||
c(Int, 1ULL << 3) \
|
||||
c(Dbl, 1ULL << 4) \
|
||||
c(StaticStr, 1ULL << 5) \
|
||||
c(CountedStr, 1ULL << 6) \
|
||||
c(StaticArr, 1ULL << 7) \
|
||||
c(CountedArr, 1ULL << 8) \
|
||||
c(Obj, 1ULL << 9)
|
||||
// Boxed*: 10-19
|
||||
// PtrTo*: 20-29
|
||||
// PtrToBoxed*: 30-39
|
||||
|
||||
// This list should be in non-decreasing order of specificity
|
||||
#define IRT_PHP_UNIONS(c) \
|
||||
c(Null, kUninit|kInitNull) \
|
||||
c(Str, kStaticStr|kCountedStr) \
|
||||
c(Arr, kStaticArr|kCountedArr) \
|
||||
c(UncountedInit, kInitNull|kBool|kInt|kDbl|kStaticStr|kStaticArr) \
|
||||
c(Uncounted, kUncountedInit|kUninit) \
|
||||
c(Cell, kUncounted|kStr|kArr|kObj)
|
||||
|
||||
#define IRT_RUNTIME \
|
||||
IRT(Cls, 1ULL << 40) \
|
||||
IRT(Func, 1ULL << 41) \
|
||||
IRT(VarEnv, 1ULL << 42) \
|
||||
IRT(NamedEntity, 1ULL << 43) \
|
||||
IRT(FuncCls, 1ULL << 44) /* {Func*, Cctx} */ \
|
||||
IRT(FuncObj, 1ULL << 45) /* {Func*, Obj} */ \
|
||||
IRT(Cctx, 1ULL << 46) /* Class* with the lowest bit set, */ \
|
||||
/* as stored in ActRec.m_cls field */ \
|
||||
IRT(RetAddr, 1ULL << 47) /* Return address */ \
|
||||
IRT(StkPtr, 1ULL << 48) /* stack pointer */ \
|
||||
IRT(FramePtr, 1ULL << 49) /* frame pointer */ \
|
||||
IRT(TCA, 1ULL << 50) \
|
||||
IRT(ActRec, 1ULL << 51) \
|
||||
IRT(None, 1ULL << 52) \
|
||||
IRT(CacheHandle, 1ULL << 53) /* TargetCache::CacheHandle */
|
||||
|
||||
// The definitions for these are in ir.cpp
|
||||
#define IRT_UNIONS \
|
||||
IRT(Ctx, kObj|kCctx) \
|
||||
IRT(FuncCtx, kFuncCls|kFuncObj)
|
||||
|
||||
// Gen, Counted, PtrToGen, and PtrToCounted are here instead of
|
||||
// IRT_PHP_UNIONS because boxing them (e.g., BoxedGen, PtrToBoxedGen)
|
||||
// would be nonsense types.
|
||||
#define IRT_SPECIAL \
|
||||
IRT(Bottom, 0) \
|
||||
IRT(Counted, kCountedStr|kCountedArr|kObj|kBoxedCell) \
|
||||
IRT(PtrToCounted, kCounted << kPtrShift) \
|
||||
IRT(Gen, kCell|kBoxedCell) \
|
||||
IRT(Init, kGen & ~kUninit) \
|
||||
IRT(PtrToGen, kGen << kPtrShift) \
|
||||
IRT(PtrToInit, kInit << kPtrShift)
|
||||
|
||||
// All types (including union types) that represent program values,
|
||||
// except Gen (which is special). Boxed*, PtrTo*, and PtrToBoxed* only
|
||||
// exist for these types.
|
||||
#define IRT_USERLAND(c) IRT_PHP(c) IRT_PHP_UNIONS(c)
|
||||
|
||||
// All types with just a single bit set
|
||||
#define IRT_PRIMITIVE IRT_PHP(IRT_BOXES) IRT_RUNTIME
|
||||
|
||||
// All types
|
||||
#define IR_TYPES IRT_USERLAND(IRT_BOXES) IRT_RUNTIME IRT_UNIONS IRT_SPECIAL
|
||||
|
||||
class Type {
|
||||
typedef uint64_t bits_t;
|
||||
|
||||
static const size_t kBoxShift = 10;
|
||||
static const size_t kPtrShift = kBoxShift * 2;
|
||||
static const size_t kPtrBoxShift = kBoxShift + kPtrShift;
|
||||
|
||||
enum TypeBits {
|
||||
#define IRT(name, bits) k##name = (bits),
|
||||
IR_TYPES
|
||||
#undef IRT
|
||||
};
|
||||
|
||||
union {
|
||||
bits_t m_bits;
|
||||
TypeBits m_typedBits;
|
||||
};
|
||||
|
||||
public:
|
||||
# define IRT(name, ...) static const Type name;
|
||||
IR_TYPES
|
||||
# undef IRT
|
||||
|
||||
explicit Type(bits_t bits = kNone)
|
||||
: m_bits(bits)
|
||||
{}
|
||||
|
||||
size_t hash() const {
|
||||
return hash_int64(m_bits);
|
||||
}
|
||||
|
||||
bool operator==(Type other) const {
|
||||
return m_bits == other.m_bits;
|
||||
}
|
||||
|
||||
bool operator!=(Type other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
Type operator|(Type other) const {
|
||||
return Type(m_bits | other.m_bits);
|
||||
}
|
||||
|
||||
Type operator&(Type other) const {
|
||||
return Type(m_bits & other.m_bits);
|
||||
}
|
||||
|
||||
Type operator-(Type other) const {
|
||||
return Type(m_bits & ~other.m_bits);
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
static std::string debugString(Type t);
|
||||
static Type fromString(const std::string& str);
|
||||
|
||||
bool isBoxed() const {
|
||||
return subtypeOf(BoxedCell);
|
||||
}
|
||||
|
||||
bool notBoxed() const {
|
||||
return subtypeOf(Cell);
|
||||
}
|
||||
|
||||
bool maybeBoxed() const {
|
||||
return (*this & BoxedCell) != Bottom;
|
||||
}
|
||||
|
||||
bool isPtr() const {
|
||||
return subtypeOf(PtrToGen);
|
||||
}
|
||||
|
||||
bool notPtr() const {
|
||||
return (*this & PtrToGen) == Bottom;
|
||||
}
|
||||
|
||||
bool isCounted() const {
|
||||
return subtypeOf(Counted);
|
||||
}
|
||||
|
||||
bool maybeCounted() const {
|
||||
return (*this & Counted) != Bottom;
|
||||
}
|
||||
|
||||
bool notCounted() const {
|
||||
return !maybeCounted();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true iff this is a union type. Note that this is the
|
||||
* plain old set definition of union, so Type::Str, Type::Arr, and
|
||||
* Type::Null will all return true.
|
||||
*/
|
||||
bool isUnion() const {
|
||||
// This will return true iff more than 1 bit is set in
|
||||
// m_bits.
|
||||
return (m_bits & (m_bits - 1)) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this value has a known constant DataType enum
|
||||
* value, which allows us to avoid several checks.
|
||||
*/
|
||||
bool isKnownDataType() const {
|
||||
// Calling this function with a type that can't be in a TypedValue isn't
|
||||
// meaningful
|
||||
assert(subtypeOf(Gen | Cls));
|
||||
// Str, Arr and Null are technically unions but can each be
|
||||
// represented by one data type. Same for a union that consists of
|
||||
// nothing but boxed types.
|
||||
if (isString() || isArray() || isNull() || isBoxed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isUnion();
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to isKnownDataType, with the added restriction that the
|
||||
* type not be Boxed.
|
||||
*/
|
||||
bool isKnownUnboxedDataType() const {
|
||||
return isKnownDataType() && notBoxed();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this requires a register to hold a DataType at
|
||||
* runtime.
|
||||
*/
|
||||
bool needsReg() const {
|
||||
return subtypeOf(Gen) && !isKnownDataType();
|
||||
}
|
||||
|
||||
bool needsStaticBitCheck() const {
|
||||
return (*this & (StaticStr | StaticArr)) != Bottom;
|
||||
}
|
||||
|
||||
// returns true if definitely not uninitialized
|
||||
bool isInit() const {
|
||||
return !Uninit.subtypeOf(*this);
|
||||
}
|
||||
|
||||
bool maybeUninit() const {
|
||||
return !isInit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a strict subtype of t2.
|
||||
*/
|
||||
bool strictSubtypeOf(Type t2) const {
|
||||
return *this != t2 && subtypeOf(t2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
*/
|
||||
bool subtypeOf(Type t2) const {
|
||||
return (m_bits & t2.m_bits) == m_bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
*/
|
||||
template<typename... Types>
|
||||
bool subtypeOfAny(Type t2, Types... ts) const {
|
||||
return subtypeOf(t2) || subtypeOfAny(ts...);
|
||||
}
|
||||
|
||||
bool subtypeOfAny() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if any subtype of this is a subtype of t2.
|
||||
*/
|
||||
bool maybe(Type t2) const {
|
||||
return (*this & t2) != Bottom;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if no subtypes of this are subtypes of t2.
|
||||
*/
|
||||
bool not(Type t2) const {
|
||||
return !maybe(t2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is exactly equal to t2. Be careful: you
|
||||
* probably mean subtypeOf.
|
||||
*/
|
||||
bool equals(Type t2) const {
|
||||
return m_bits == t2.m_bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the most refined of two types.
|
||||
*
|
||||
* Pre: the types must not be completely unrelated.
|
||||
*/
|
||||
static Type mostRefined(Type t1, Type t2) {
|
||||
assert(t1.subtypeOf(t2) || t2.subtypeOf(t1));
|
||||
return t1.subtypeOf(t2) ? t1 : t2;
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return subtypeOf(Arr);
|
||||
}
|
||||
|
||||
bool isBool() const {
|
||||
return subtypeOf(Bool);
|
||||
}
|
||||
|
||||
bool isDbl() const {
|
||||
return subtypeOf(Dbl);
|
||||
}
|
||||
|
||||
bool isInt() const {
|
||||
return subtypeOf(Int);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return subtypeOf(Null);
|
||||
}
|
||||
|
||||
bool isObj() const {
|
||||
return subtypeOf(Obj);
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return subtypeOf(Str);
|
||||
}
|
||||
|
||||
Type innerType() const {
|
||||
assert(isBoxed());
|
||||
return Type(m_bits >> kBoxShift);
|
||||
}
|
||||
|
||||
/*
|
||||
* unionOf: return the least common predefined supertype of t1 and
|
||||
* t2, i.e.. the most refined type t3 such that t1 <: t3 and t2 <:
|
||||
* t3. Note that arbitrary union types are possible, but this
|
||||
* function always returns one of the predefined types.
|
||||
*/
|
||||
static Type unionOf(Type t1, Type t2) {
|
||||
assert(t1 != None && t2 != None);
|
||||
if (t1 == t2) return t1;
|
||||
if (t1.subtypeOf(t2)) return t2;
|
||||
if (t2.subtypeOf(t1)) return t1;
|
||||
static const Type union_types[] = {
|
||||
# define IRT(name, ...) name,
|
||||
IRT_PHP_UNIONS(IRT_BOXES)
|
||||
# undef IRT
|
||||
Gen,
|
||||
PtrToGen,
|
||||
};
|
||||
Type t12 = t1 | t2;
|
||||
for (auto u : union_types) {
|
||||
if (t12.subtypeOf(u)) return u;
|
||||
}
|
||||
not_reached();
|
||||
}
|
||||
|
||||
Type box() const {
|
||||
assert(subtypeOf(Cell));
|
||||
// Boxing Uninit returns InitNull but that logic doesn't belong
|
||||
// here.
|
||||
assert(not(Uninit) || equals(Cell));
|
||||
return Type(m_bits << kBoxShift);
|
||||
}
|
||||
|
||||
// This computes the type effects of the Unbox opcode.
|
||||
Type unbox() const {
|
||||
assert(subtypeOf(Gen));
|
||||
return (*this & Cell) | (*this & BoxedCell).innerType();
|
||||
}
|
||||
|
||||
Type deref() const {
|
||||
assert(isPtr());
|
||||
return Type(m_bits >> kPtrShift);
|
||||
}
|
||||
|
||||
Type derefIfPtr() const {
|
||||
assert(subtypeOf(Gen | PtrToGen));
|
||||
return isPtr() ? deref() : *this;
|
||||
}
|
||||
|
||||
// Returns the "stripped" version of this: dereferenced and unboxed,
|
||||
// if applicable.
|
||||
Type strip() const {
|
||||
return derefIfPtr().unbox();
|
||||
}
|
||||
|
||||
Type ptr() const {
|
||||
assert(!isPtr());
|
||||
assert(subtypeOf(Gen));
|
||||
return Type(m_bits << kPtrShift);
|
||||
}
|
||||
|
||||
bool canRunDtor() const {
|
||||
return
|
||||
(*this & (Obj | CountedArr | BoxedObj | BoxedCountedArr))
|
||||
!= Type::Bottom;
|
||||
}
|
||||
|
||||
// translates a compiler Type to an HPHP::DataType
|
||||
DataType toDataType() const {
|
||||
assert(!isPtr());
|
||||
if (isBoxed()) {
|
||||
return KindOfRef;
|
||||
}
|
||||
|
||||
// Order is important here: types must progress from more specific
|
||||
// to less specific to return the most specific DataType.
|
||||
if (subtypeOf(None)) return KindOfInvalid;
|
||||
if (subtypeOf(Uninit)) return KindOfUninit;
|
||||
if (subtypeOf(Null)) return KindOfNull;
|
||||
if (subtypeOf(Bool)) return KindOfBoolean;
|
||||
if (subtypeOf(Int)) return KindOfInt64;
|
||||
if (subtypeOf(Dbl)) return KindOfDouble;
|
||||
if (subtypeOf(StaticStr)) return KindOfStaticString;
|
||||
if (subtypeOf(Str)) return KindOfString;
|
||||
if (subtypeOf(Arr)) return KindOfArray;
|
||||
if (subtypeOf(Obj)) return KindOfObject;
|
||||
if (subtypeOf(Cls)) return KindOfClass;
|
||||
if (subtypeOf(UncountedInit)) return KindOfUncountedInit;
|
||||
if (subtypeOf(Uncounted)) return KindOfUncounted;
|
||||
if (subtypeOf(Gen)) return KindOfAny;
|
||||
not_reached();
|
||||
}
|
||||
|
||||
static Type fromDataType(DataType outerType,
|
||||
DataType innerType = KindOfInvalid) {
|
||||
assert(innerType != KindOfRef);
|
||||
|
||||
switch (outerType) {
|
||||
case KindOfInvalid : return None;
|
||||
case KindOfUninit : return Uninit;
|
||||
case KindOfNull : return InitNull;
|
||||
case KindOfBoolean : return Bool;
|
||||
case KindOfInt64 : return Int;
|
||||
case KindOfDouble : return Dbl;
|
||||
case KindOfStaticString : return StaticStr;
|
||||
case KindOfString : return Str;
|
||||
case KindOfArray : return Arr;
|
||||
case KindOfObject : return Obj;
|
||||
case KindOfClass : return Cls;
|
||||
case KindOfUncountedInit : return UncountedInit;
|
||||
case KindOfUncounted : return Uncounted;
|
||||
case KindOfAny : return Gen;
|
||||
case KindOfRef: {
|
||||
if (innerType == KindOfInvalid) {
|
||||
return BoxedCell;
|
||||
} else {
|
||||
return fromDataType(innerType).box();
|
||||
}
|
||||
}
|
||||
default : not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by value in C++
|
||||
bool isSimpleType() {
|
||||
return subtypeOf(Type::Bool)
|
||||
|| subtypeOf(Type::Int)
|
||||
|| subtypeOf(Type::Dbl)
|
||||
|| subtypeOf(Type::Null);
|
||||
}
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by reference in C++
|
||||
bool isReferenceType() {
|
||||
return subtypeOf(Type::Str)
|
||||
|| subtypeOf(Type::Arr)
|
||||
|| subtypeOf(Type::Obj);
|
||||
}
|
||||
|
||||
// In tx64, KindOfUnknown is used to represent Variants (Type::Cell).
|
||||
// fromDataType() maps this to Type::None, which must be mapped
|
||||
// back to Type::Cell. This is not the best place to handle this.
|
||||
// See task #208726.
|
||||
static Type fromDataTypeWithCell(DataType type) {
|
||||
Type t = fromDataType(type);
|
||||
return t.equals(Type::None) ? Type::Cell : t;
|
||||
}
|
||||
|
||||
static Type fromDataTypeWithRef(DataType outerType, bool isRef) {
|
||||
Type t = fromDataTypeWithCell(outerType);
|
||||
return isRef ? t.box() : t;
|
||||
}
|
||||
|
||||
static Type fromRuntimeType(const Transl::RuntimeType& rtt) {
|
||||
return fromDataType(rtt.outerType(), rtt.innerType());
|
||||
}
|
||||
|
||||
static Type fromDynLocation(const Transl::DynLocation* dynLoc) {
|
||||
if (!dynLoc) {
|
||||
return JIT::Type::None;
|
||||
}
|
||||
DataType dt = dynLoc->rtt.outerType();
|
||||
if (dt == KindOfUnknown) {
|
||||
return JIT::Type::Gen;
|
||||
}
|
||||
return JIT::Type::fromDataType(dt, dynLoc->rtt.innerType());
|
||||
}
|
||||
}; // class Type
|
||||
|
||||
static_assert(sizeof(Type) <= sizeof(uint64_t),
|
||||
"JIT::Type should fit in a register");
|
||||
|
||||
/*
|
||||
* typeForConst(T)
|
||||
*
|
||||
|
||||
@@ -29,6 +29,17 @@ namespace HPHP { namespace JIT {
|
||||
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
Type Type::fromDynLocation(const Transl::DynLocation* dynLoc) {
|
||||
if (!dynLoc) {
|
||||
return Type::None;
|
||||
}
|
||||
DataType dt = dynLoc->rtt.outerType();
|
||||
if (dt == KindOfUnknown) {
|
||||
return Type::Gen;
|
||||
}
|
||||
return Type::fromDataType(dt, dynLoc->rtt.innerType());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010- 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_JIT_TYPE_H_
|
||||
#define incl_HPHP_JIT_TYPE_H_
|
||||
|
||||
#include "hphp/runtime/vm/translator/runtime-type.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace Transl {
|
||||
struct DynLocation;
|
||||
}
|
||||
namespace JIT {
|
||||
|
||||
#define IRT_BOXES(name, bit) \
|
||||
IRT(name, (bit)) \
|
||||
IRT(Boxed##name, (bit) << kBoxShift) \
|
||||
IRT(PtrTo##name, (bit) << kPtrShift) \
|
||||
IRT(PtrToBoxed##name, (bit) << kPtrBoxShift)
|
||||
|
||||
#define IRT_PHP(c) \
|
||||
c(Uninit, 1ULL << 0) \
|
||||
c(InitNull, 1ULL << 1) \
|
||||
c(Bool, 1ULL << 2) \
|
||||
c(Int, 1ULL << 3) \
|
||||
c(Dbl, 1ULL << 4) \
|
||||
c(StaticStr, 1ULL << 5) \
|
||||
c(CountedStr, 1ULL << 6) \
|
||||
c(StaticArr, 1ULL << 7) \
|
||||
c(CountedArr, 1ULL << 8) \
|
||||
c(Obj, 1ULL << 9)
|
||||
// Boxed*: 10-19
|
||||
// PtrTo*: 20-29
|
||||
// PtrToBoxed*: 30-39
|
||||
|
||||
// This list should be in non-decreasing order of specificity
|
||||
#define IRT_PHP_UNIONS(c) \
|
||||
c(Null, kUninit|kInitNull) \
|
||||
c(Str, kStaticStr|kCountedStr) \
|
||||
c(Arr, kStaticArr|kCountedArr) \
|
||||
c(UncountedInit, kInitNull|kBool|kInt|kDbl|kStaticStr|kStaticArr) \
|
||||
c(Uncounted, kUncountedInit|kUninit) \
|
||||
c(Cell, kUncounted|kStr|kArr|kObj)
|
||||
|
||||
#define IRT_RUNTIME \
|
||||
IRT(Cls, 1ULL << 40) \
|
||||
IRT(Func, 1ULL << 41) \
|
||||
IRT(VarEnv, 1ULL << 42) \
|
||||
IRT(NamedEntity, 1ULL << 43) \
|
||||
IRT(FuncCls, 1ULL << 44) /* {Func*, Cctx} */ \
|
||||
IRT(FuncObj, 1ULL << 45) /* {Func*, Obj} */ \
|
||||
IRT(Cctx, 1ULL << 46) /* Class* with the lowest bit set, */ \
|
||||
/* as stored in ActRec.m_cls field */ \
|
||||
IRT(RetAddr, 1ULL << 47) /* Return address */ \
|
||||
IRT(StkPtr, 1ULL << 48) /* stack pointer */ \
|
||||
IRT(FramePtr, 1ULL << 49) /* frame pointer */ \
|
||||
IRT(TCA, 1ULL << 50) \
|
||||
IRT(ActRec, 1ULL << 51) \
|
||||
IRT(None, 1ULL << 52) \
|
||||
IRT(CacheHandle, 1ULL << 53) /* TargetCache::CacheHandle */
|
||||
|
||||
// The definitions for these are in ir.cpp
|
||||
#define IRT_UNIONS \
|
||||
IRT(Ctx, kObj|kCctx) \
|
||||
IRT(FuncCtx, kFuncCls|kFuncObj)
|
||||
|
||||
// Gen, Counted, PtrToGen, and PtrToCounted are here instead of
|
||||
// IRT_PHP_UNIONS because boxing them (e.g., BoxedGen, PtrToBoxedGen)
|
||||
// would be nonsense types.
|
||||
#define IRT_SPECIAL \
|
||||
IRT(Bottom, 0) \
|
||||
IRT(Counted, kCountedStr|kCountedArr|kObj|kBoxedCell) \
|
||||
IRT(PtrToCounted, kCounted << kPtrShift) \
|
||||
IRT(Gen, kCell|kBoxedCell) \
|
||||
IRT(Init, kGen & ~kUninit) \
|
||||
IRT(PtrToGen, kGen << kPtrShift) \
|
||||
IRT(PtrToInit, kInit << kPtrShift)
|
||||
|
||||
// All types (including union types) that represent program values,
|
||||
// except Gen (which is special). Boxed*, PtrTo*, and PtrToBoxed* only
|
||||
// exist for these types.
|
||||
#define IRT_USERLAND(c) IRT_PHP(c) IRT_PHP_UNIONS(c)
|
||||
|
||||
// All types with just a single bit set
|
||||
#define IRT_PRIMITIVE IRT_PHP(IRT_BOXES) IRT_RUNTIME
|
||||
|
||||
// All types
|
||||
#define IR_TYPES IRT_USERLAND(IRT_BOXES) IRT_RUNTIME IRT_UNIONS IRT_SPECIAL
|
||||
|
||||
class Type {
|
||||
typedef uint64_t bits_t;
|
||||
|
||||
static const size_t kBoxShift = 10;
|
||||
static const size_t kPtrShift = kBoxShift * 2;
|
||||
static const size_t kPtrBoxShift = kBoxShift + kPtrShift;
|
||||
|
||||
enum TypeBits {
|
||||
#define IRT(name, bits) k##name = (bits),
|
||||
IR_TYPES
|
||||
#undef IRT
|
||||
};
|
||||
|
||||
union {
|
||||
bits_t m_bits;
|
||||
TypeBits m_typedBits;
|
||||
};
|
||||
|
||||
public:
|
||||
# define IRT(name, ...) static const Type name;
|
||||
IR_TYPES
|
||||
# undef IRT
|
||||
|
||||
explicit Type(bits_t bits = kNone)
|
||||
: m_bits(bits)
|
||||
{}
|
||||
|
||||
size_t hash() const {
|
||||
return hash_int64(m_bits);
|
||||
}
|
||||
|
||||
bool operator==(Type other) const {
|
||||
return m_bits == other.m_bits;
|
||||
}
|
||||
|
||||
bool operator!=(Type other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
Type operator|(Type other) const {
|
||||
return Type(m_bits | other.m_bits);
|
||||
}
|
||||
|
||||
Type operator&(Type other) const {
|
||||
return Type(m_bits & other.m_bits);
|
||||
}
|
||||
|
||||
Type operator-(Type other) const {
|
||||
return Type(m_bits & ~other.m_bits);
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
static std::string debugString(Type t);
|
||||
static Type fromString(const std::string& str);
|
||||
|
||||
bool isBoxed() const {
|
||||
return subtypeOf(BoxedCell);
|
||||
}
|
||||
|
||||
bool notBoxed() const {
|
||||
return subtypeOf(Cell);
|
||||
}
|
||||
|
||||
bool maybeBoxed() const {
|
||||
return (*this & BoxedCell) != Bottom;
|
||||
}
|
||||
|
||||
bool isPtr() const {
|
||||
return subtypeOf(PtrToGen);
|
||||
}
|
||||
|
||||
bool notPtr() const {
|
||||
return (*this & PtrToGen) == Bottom;
|
||||
}
|
||||
|
||||
bool isCounted() const {
|
||||
return subtypeOf(Counted);
|
||||
}
|
||||
|
||||
bool maybeCounted() const {
|
||||
return (*this & Counted) != Bottom;
|
||||
}
|
||||
|
||||
bool notCounted() const {
|
||||
return !maybeCounted();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true iff this is a union type. Note that this is the
|
||||
* plain old set definition of union, so Type::Str, Type::Arr, and
|
||||
* Type::Null will all return true.
|
||||
*/
|
||||
bool isUnion() const {
|
||||
// This will return true iff more than 1 bit is set in
|
||||
// m_bits.
|
||||
return (m_bits & (m_bits - 1)) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this value has a known constant DataType enum
|
||||
* value, which allows us to avoid several checks.
|
||||
*/
|
||||
bool isKnownDataType() const {
|
||||
// Calling this function with a type that can't be in a TypedValue isn't
|
||||
// meaningful
|
||||
assert(subtypeOf(Gen | Cls));
|
||||
// Str, Arr and Null are technically unions but can each be
|
||||
// represented by one data type. Same for a union that consists of
|
||||
// nothing but boxed types.
|
||||
if (isString() || isArray() || isNull() || isBoxed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isUnion();
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to isKnownDataType, with the added restriction that the
|
||||
* type not be Boxed.
|
||||
*/
|
||||
bool isKnownUnboxedDataType() const {
|
||||
return isKnownDataType() && notBoxed();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this requires a register to hold a DataType at
|
||||
* runtime.
|
||||
*/
|
||||
bool needsReg() const {
|
||||
return subtypeOf(Gen) && !isKnownDataType();
|
||||
}
|
||||
|
||||
bool needsStaticBitCheck() const {
|
||||
return (*this & (StaticStr | StaticArr)) != Bottom;
|
||||
}
|
||||
|
||||
// returns true if definitely not uninitialized
|
||||
bool isInit() const {
|
||||
return !Uninit.subtypeOf(*this);
|
||||
}
|
||||
|
||||
bool maybeUninit() const {
|
||||
return !isInit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a strict subtype of t2.
|
||||
*/
|
||||
bool strictSubtypeOf(Type t2) const {
|
||||
return *this != t2 && subtypeOf(t2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
*/
|
||||
bool subtypeOf(Type t2) const {
|
||||
return (m_bits & t2.m_bits) == m_bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
*/
|
||||
template<typename... Types>
|
||||
bool subtypeOfAny(Type t2, Types... ts) const {
|
||||
return subtypeOf(t2) || subtypeOfAny(ts...);
|
||||
}
|
||||
|
||||
bool subtypeOfAny() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if any subtype of this is a subtype of t2.
|
||||
*/
|
||||
bool maybe(Type t2) const {
|
||||
return (*this & t2) != Bottom;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if no subtypes of this are subtypes of t2.
|
||||
*/
|
||||
bool not(Type t2) const {
|
||||
return !maybe(t2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is exactly equal to t2. Be careful: you
|
||||
* probably mean subtypeOf.
|
||||
*/
|
||||
bool equals(Type t2) const {
|
||||
return m_bits == t2.m_bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the most refined of two types.
|
||||
*
|
||||
* Pre: the types must not be completely unrelated.
|
||||
*/
|
||||
static Type mostRefined(Type t1, Type t2) {
|
||||
assert(t1.subtypeOf(t2) || t2.subtypeOf(t1));
|
||||
return t1.subtypeOf(t2) ? t1 : t2;
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return subtypeOf(Arr);
|
||||
}
|
||||
|
||||
bool isBool() const {
|
||||
return subtypeOf(Bool);
|
||||
}
|
||||
|
||||
bool isDbl() const {
|
||||
return subtypeOf(Dbl);
|
||||
}
|
||||
|
||||
bool isInt() const {
|
||||
return subtypeOf(Int);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return subtypeOf(Null);
|
||||
}
|
||||
|
||||
bool isObj() const {
|
||||
return subtypeOf(Obj);
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return subtypeOf(Str);
|
||||
}
|
||||
|
||||
Type innerType() const {
|
||||
assert(isBoxed());
|
||||
return Type(m_bits >> kBoxShift);
|
||||
}
|
||||
|
||||
/*
|
||||
* unionOf: return the least common predefined supertype of t1 and
|
||||
* t2, i.e.. the most refined type t3 such that t1 <: t3 and t2 <:
|
||||
* t3. Note that arbitrary union types are possible, but this
|
||||
* function always returns one of the predefined types.
|
||||
*/
|
||||
static Type unionOf(Type t1, Type t2) {
|
||||
assert(t1 != None && t2 != None);
|
||||
if (t1 == t2) return t1;
|
||||
if (t1.subtypeOf(t2)) return t2;
|
||||
if (t2.subtypeOf(t1)) return t1;
|
||||
static const Type union_types[] = {
|
||||
# define IRT(name, ...) name,
|
||||
IRT_PHP_UNIONS(IRT_BOXES)
|
||||
# undef IRT
|
||||
Gen,
|
||||
PtrToGen,
|
||||
};
|
||||
Type t12 = t1 | t2;
|
||||
for (auto u : union_types) {
|
||||
if (t12.subtypeOf(u)) return u;
|
||||
}
|
||||
not_reached();
|
||||
}
|
||||
|
||||
Type box() const {
|
||||
assert(subtypeOf(Cell));
|
||||
// Boxing Uninit returns InitNull but that logic doesn't belong
|
||||
// here.
|
||||
assert(not(Uninit) || equals(Cell));
|
||||
return Type(m_bits << kBoxShift);
|
||||
}
|
||||
|
||||
// This computes the type effects of the Unbox opcode.
|
||||
Type unbox() const {
|
||||
assert(subtypeOf(Gen));
|
||||
return (*this & Cell) | (*this & BoxedCell).innerType();
|
||||
}
|
||||
|
||||
Type deref() const {
|
||||
assert(isPtr());
|
||||
return Type(m_bits >> kPtrShift);
|
||||
}
|
||||
|
||||
Type derefIfPtr() const {
|
||||
assert(subtypeOf(Gen | PtrToGen));
|
||||
return isPtr() ? deref() : *this;
|
||||
}
|
||||
|
||||
// Returns the "stripped" version of this: dereferenced and unboxed,
|
||||
// if applicable.
|
||||
Type strip() const {
|
||||
return derefIfPtr().unbox();
|
||||
}
|
||||
|
||||
Type ptr() const {
|
||||
assert(!isPtr());
|
||||
assert(subtypeOf(Gen));
|
||||
return Type(m_bits << kPtrShift);
|
||||
}
|
||||
|
||||
bool canRunDtor() const {
|
||||
return
|
||||
(*this & (Obj | CountedArr | BoxedObj | BoxedCountedArr))
|
||||
!= Type::Bottom;
|
||||
}
|
||||
|
||||
// translates a compiler Type to an HPHP::DataType
|
||||
DataType toDataType() const {
|
||||
assert(!isPtr());
|
||||
if (isBoxed()) {
|
||||
return KindOfRef;
|
||||
}
|
||||
|
||||
// Order is important here: types must progress from more specific
|
||||
// to less specific to return the most specific DataType.
|
||||
if (subtypeOf(None)) return KindOfInvalid;
|
||||
if (subtypeOf(Uninit)) return KindOfUninit;
|
||||
if (subtypeOf(Null)) return KindOfNull;
|
||||
if (subtypeOf(Bool)) return KindOfBoolean;
|
||||
if (subtypeOf(Int)) return KindOfInt64;
|
||||
if (subtypeOf(Dbl)) return KindOfDouble;
|
||||
if (subtypeOf(StaticStr)) return KindOfStaticString;
|
||||
if (subtypeOf(Str)) return KindOfString;
|
||||
if (subtypeOf(Arr)) return KindOfArray;
|
||||
if (subtypeOf(Obj)) return KindOfObject;
|
||||
if (subtypeOf(Cls)) return KindOfClass;
|
||||
if (subtypeOf(UncountedInit)) return KindOfUncountedInit;
|
||||
if (subtypeOf(Uncounted)) return KindOfUncounted;
|
||||
if (subtypeOf(Gen)) return KindOfAny;
|
||||
not_reached();
|
||||
}
|
||||
|
||||
static Type fromDataType(DataType outerType,
|
||||
DataType innerType = KindOfInvalid) {
|
||||
assert(innerType != KindOfRef);
|
||||
|
||||
switch (outerType) {
|
||||
case KindOfInvalid : return None;
|
||||
case KindOfUninit : return Uninit;
|
||||
case KindOfNull : return InitNull;
|
||||
case KindOfBoolean : return Bool;
|
||||
case KindOfInt64 : return Int;
|
||||
case KindOfDouble : return Dbl;
|
||||
case KindOfStaticString : return StaticStr;
|
||||
case KindOfString : return Str;
|
||||
case KindOfArray : return Arr;
|
||||
case KindOfObject : return Obj;
|
||||
case KindOfClass : return Cls;
|
||||
case KindOfUncountedInit : return UncountedInit;
|
||||
case KindOfUncounted : return Uncounted;
|
||||
case KindOfAny : return Gen;
|
||||
case KindOfRef: {
|
||||
if (innerType == KindOfInvalid) {
|
||||
return BoxedCell;
|
||||
} else {
|
||||
return fromDataType(innerType).box();
|
||||
}
|
||||
}
|
||||
default : not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by value in C++
|
||||
bool isSimpleType() {
|
||||
return subtypeOf(Type::Bool)
|
||||
|| subtypeOf(Type::Int)
|
||||
|| subtypeOf(Type::Dbl)
|
||||
|| subtypeOf(Type::Null);
|
||||
}
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by reference in C++
|
||||
bool isReferenceType() {
|
||||
return subtypeOf(Type::Str)
|
||||
|| subtypeOf(Type::Arr)
|
||||
|| subtypeOf(Type::Obj);
|
||||
}
|
||||
|
||||
// In tx64, KindOfUnknown is used to represent Variants (Type::Cell).
|
||||
// fromDataType() maps this to Type::None, which must be mapped
|
||||
// back to Type::Cell. This is not the best place to handle this.
|
||||
// See task #208726.
|
||||
static Type fromDataTypeWithCell(DataType type) {
|
||||
Type t = fromDataType(type);
|
||||
return t.equals(Type::None) ? Type::Cell : t;
|
||||
}
|
||||
|
||||
static Type fromDataTypeWithRef(DataType outerType, bool isRef) {
|
||||
Type t = fromDataTypeWithCell(outerType);
|
||||
return isRef ? t.box() : t;
|
||||
}
|
||||
|
||||
static Type fromRuntimeType(const Transl::RuntimeType& rtt) {
|
||||
return fromDataType(rtt.outerType(), rtt.innerType());
|
||||
}
|
||||
|
||||
static Type fromDynLocation(const Transl::DynLocation* dynLoc);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Type) <= sizeof(uint64_t),
|
||||
"JIT::Type should fit in a register");
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
Referência em uma Nova Issue
Bloquear um usuário