Extend the type system of the JIT (Type class) to allow for specialized Class instead of just Type::Obj
Allow RuntimeType to specify a more specific Type (by adding a Class*) and transfer that Class* to Type. Extended API to allow discovering when a Type is a subclass of Type::Obj
Esse commit está contido em:
@@ -125,6 +125,7 @@ StaticStr and CountedStr both appear as type String at the PHP level).
|
||||
CountedArr ArrayData* where isStatic() == false
|
||||
Arr ArrayData* {CountedArr|StaticArr}
|
||||
Obj ObjectData*
|
||||
Obj<Class> ObjectData* of the specific type Class
|
||||
Counted {CountedStr|CountedArr|Obj|BoxedCell}
|
||||
Cell {Null|Bool|Int|Dbl|Str|Arr|Obj}
|
||||
|
||||
|
||||
@@ -1653,38 +1653,68 @@ void CodeGenerator::cgOpGte(IRInstruction* inst) {
|
||||
// Type check operators
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class OpndType>
|
||||
ConditionCode CodeGenerator::emitTypeTest(Type type, OpndType src,
|
||||
bool negate) {
|
||||
// Overloads to put the ObjectData* into a register so emitTypeTest
|
||||
// can cmp to the Class* expected by the specialized Type
|
||||
|
||||
// Nothing to do, return the register that contain the ObjectData already
|
||||
Reg64 getObjectDataEnregistered(Asm& as, PhysReg dataSrc, Reg64 scratch) {
|
||||
return dataSrc;
|
||||
}
|
||||
|
||||
// Enregister the meoryRef so it can be used with an offset by the
|
||||
// cmp instruction
|
||||
Reg64 getObjectDataEnregistered(Asm& as,
|
||||
MemoryRef dataSrc,
|
||||
Reg64 scratch) {
|
||||
as.loadq(dataSrc, scratch);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
template<class Loc1, class Loc2, class JmpFn>
|
||||
void CodeGenerator::emitTypeTest(Type type, Loc1 typeSrc, Loc2 dataSrc,
|
||||
JmpFn doJcc) {
|
||||
assert(!type.subtypeOf(Type::Cls));
|
||||
ConditionCode cc;
|
||||
if (type.isString()) {
|
||||
emitTestTVType(m_as, KindOfStringBit, src);
|
||||
emitTestTVType(m_as, KindOfStringBit, typeSrc);
|
||||
cc = CC_NZ;
|
||||
} else if (type.equals(Type::UncountedInit)) {
|
||||
emitTestTVType(m_as, KindOfUncountedInitBit, src);
|
||||
emitTestTVType(m_as, KindOfUncountedInitBit, typeSrc);
|
||||
cc = CC_NZ;
|
||||
} else if (type.equals(Type::Uncounted)) {
|
||||
emitCmpTVType(m_as, KindOfRefCountThreshold, src);
|
||||
emitCmpTVType(m_as, KindOfRefCountThreshold, typeSrc);
|
||||
cc = CC_LE;
|
||||
} else if (type.equals(Type::Cell)) {
|
||||
emitCmpTVType(m_as, KindOfRef, src);
|
||||
emitCmpTVType(m_as, KindOfRef, typeSrc);
|
||||
cc = CC_L;
|
||||
} else if (type.equals(Type::Gen)) {
|
||||
return CC_None; // nothing to check
|
||||
// nothing to check
|
||||
return;
|
||||
} else {
|
||||
DataType dataType = type.toDataType();
|
||||
assert(dataType == KindOfRef ||
|
||||
(dataType >= KindOfUninit && dataType <= KindOfObject));
|
||||
emitCmpTVType(m_as, dataType, src);
|
||||
emitCmpTVType(m_as, dataType, typeSrc);
|
||||
cc = CC_E;
|
||||
}
|
||||
return negate ? ccNegate(cc) : cc;
|
||||
doJcc(cc);
|
||||
if (type.strictSubtypeOf(Type::Obj)) {
|
||||
// emit the specific class test
|
||||
assert(type.getClass()->attrs() & AttrFinal);
|
||||
auto reg = getObjectDataEnregistered(m_as, dataSrc, m_rScratch);
|
||||
m_as.cmpq(type.getClass(), reg[ObjectData::getVMClassOffset()]);
|
||||
doJcc(cc);
|
||||
}
|
||||
}
|
||||
|
||||
ConditionCode CodeGenerator::emitIsTypeTest(IRInstruction* inst, bool negate) {
|
||||
template<class JmpFn>
|
||||
void CodeGenerator::emitIsTypeTest(IRInstruction* inst, JmpFn doJcc) {
|
||||
auto const src = inst->getSrc(0);
|
||||
|
||||
// punt if specialized object for now
|
||||
if (inst->getTypeParam().strictSubtypeOf(Type::Obj)) {
|
||||
CG_PUNT(IsType-SpecializedUnsupported);
|
||||
}
|
||||
if (inst->getTypeParam().equals(Type::Obj)) {
|
||||
auto const srcReg = m_regs[src].getReg();
|
||||
if (src->isA(Type::PtrToGen)) {
|
||||
@@ -1707,57 +1737,79 @@ ConditionCode CodeGenerator::emitIsTypeTest(IRInstruction* inst, bool negate) {
|
||||
srcReg[ObjectData::getVMClassOffset()]);
|
||||
}
|
||||
// At this point, the flags say "equal" if is_object is false.
|
||||
return negate ? CC_E : CC_NE;
|
||||
doJcc(CC_NE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src->isA(Type::PtrToGen)) {
|
||||
PhysReg base = m_regs[src].getReg();
|
||||
return emitTypeTest(inst->getTypeParam(), base[TVOFF(m_type)], negate);
|
||||
emitTypeTest(inst->getTypeParam(), base[TVOFF(m_type)],
|
||||
base[TVOFF(m_data)],
|
||||
[&](ConditionCode cc) { doJcc(cc); });
|
||||
return;
|
||||
}
|
||||
assert(src->isA(Type::Gen));
|
||||
assert(!src->isConst());
|
||||
|
||||
PhysReg srcReg = m_regs[src].getReg(1); // type register
|
||||
if (srcReg == InvalidReg) {
|
||||
PhysReg typeSrcReg = m_regs[src].getReg(1); // type register
|
||||
if (typeSrcReg == InvalidReg) {
|
||||
CG_PUNT(IsType-KnownType);
|
||||
}
|
||||
return emitTypeTest(inst->getTypeParam(), srcReg, negate);
|
||||
PhysReg dataSrcReg = m_regs[src].getReg(); // data register
|
||||
emitTypeTest(inst->getTypeParam(), typeSrcReg, dataSrcReg,
|
||||
[&](ConditionCode cc) { doJcc(cc); });
|
||||
}
|
||||
|
||||
template<class MemLoc>
|
||||
void CodeGenerator::emitTypeCheck(Type type, MemLoc mem, Block* taken) {
|
||||
auto const negate = true;
|
||||
auto const cc = emitTypeTest(type, mem, negate);
|
||||
if (cc == CC_None) return;
|
||||
emitFwdJcc(cc, taken);
|
||||
template<class Loc>
|
||||
void CodeGenerator::emitTypeCheck(Type type,
|
||||
Loc typeSrc,
|
||||
Loc dataSrc,
|
||||
Block* taken) {
|
||||
emitTypeTest(type, typeSrc, dataSrc,
|
||||
[&](ConditionCode cc) {
|
||||
emitFwdJcc(ccNegate(cc), taken);
|
||||
});
|
||||
}
|
||||
|
||||
template<class MemLoc>
|
||||
void CodeGenerator::emitTypeGuard(Type type, MemLoc mem) {
|
||||
auto const negate = true;
|
||||
auto const cc = emitTypeTest(type, mem, negate);
|
||||
if (cc == CC_None) return;
|
||||
|
||||
auto const destSK = SrcKey(getCurFunc(), m_curTrace->getBcOff());
|
||||
auto const destSR = m_tx64->getSrcRec(destSK);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, cc);
|
||||
template<class Loc>
|
||||
void CodeGenerator::emitTypeGuard(Type type, Loc typeSrc, Loc dataSrc) {
|
||||
emitTypeTest(type, typeSrc, dataSrc,
|
||||
[&](ConditionCode cc) {
|
||||
auto const destSK = SrcKey(getCurFunc(), m_curTrace->getBcOff());
|
||||
auto const destSR = m_tx64->getSrcRec(destSK);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, ccNegate(cc));
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::emitSetCc(IRInstruction* inst, ConditionCode cc) {
|
||||
if (cc == CC_None) return;
|
||||
m_as.setcc(cc, rbyte(m_regs[inst->getDst()].getReg()));
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIsTypeMemCommon(IRInstruction* inst, bool negate) {
|
||||
emitSetCc(inst, emitIsTypeTest(inst, negate));
|
||||
bool called = false; // check emitSetCc is called only once
|
||||
emitIsTypeTest(inst,
|
||||
[&](ConditionCode cc) {
|
||||
assert(!called);
|
||||
emitSetCc(inst, negate ? ccNegate(cc) : cc);
|
||||
called = true;
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIsTypeCommon(IRInstruction* inst, bool negate) {
|
||||
emitSetCc(inst, emitIsTypeTest(inst, negate));
|
||||
bool called = false; // check emitSetCc is called only once
|
||||
emitIsTypeTest(inst,
|
||||
[&](ConditionCode cc) {
|
||||
assert(!called);
|
||||
emitSetCc(inst, negate ? ccNegate(cc) : cc);
|
||||
called = true;
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgJmpIsTypeCommon(IRInstruction* inst, bool negate) {
|
||||
emitFwdJcc(emitIsTypeTest(inst, negate), inst->getTaken());
|
||||
emitIsTypeTest(inst,
|
||||
[&](ConditionCode cc) {
|
||||
emitFwdJcc(negate ? ccNegate(cc) : cc, inst->getTaken());
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIsType(IRInstruction* inst) {
|
||||
@@ -3813,11 +3865,13 @@ void CodeGenerator::cgLoadTypedValue(PhysReg base,
|
||||
if (typeDstReg != InvalidReg) {
|
||||
emitLoadTVType(m_as, base[off + TVOFF(m_type)], typeDstReg);
|
||||
if (label) {
|
||||
emitTypeCheck(inst->getTypeParam(), typeDstReg, inst->getTaken());
|
||||
emitTypeCheck(inst->getTypeParam(), typeDstReg,
|
||||
valueDstReg, inst->getTaken());
|
||||
}
|
||||
} else if (label) {
|
||||
emitTypeCheck(inst->getTypeParam(),
|
||||
base[off + TVOFF(m_type)],
|
||||
base[off + TVOFF(m_data)],
|
||||
inst->getTaken());
|
||||
}
|
||||
|
||||
@@ -3891,6 +3945,7 @@ void CodeGenerator::cgLoad(PhysReg base,
|
||||
if (label != NULL) {
|
||||
emitTypeCheck(inst->getTypeParam(),
|
||||
base[off + TVOFF(m_type)],
|
||||
base[off + TVOFF(m_data)],
|
||||
inst->getTaken());
|
||||
}
|
||||
if (type.isNull()) return; // these are constants
|
||||
@@ -3974,38 +4029,44 @@ void CodeGenerator::cgLdStack(IRInstruction* inst) {
|
||||
|
||||
void CodeGenerator::cgGuardStk(IRInstruction* inst) {
|
||||
auto const rSP = m_regs[inst->getSrc(0)].getReg();
|
||||
auto const off = cellsToBytes(inst->getExtra<GuardStk>()->offset) +
|
||||
TVOFF(m_type);
|
||||
emitTypeGuard(inst->getTypeParam(), rSP[off]);
|
||||
auto const baseOff = cellsToBytes(inst->getExtra<GuardStk>()->offset);
|
||||
emitTypeGuard(inst->getTypeParam(),
|
||||
rSP[baseOff + TVOFF(m_type)],
|
||||
rSP[baseOff + TVOFF(m_data)]);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgCheckStk(IRInstruction* inst) {
|
||||
auto const rbase = m_regs[inst->getSrc(0)].getReg();
|
||||
auto const off = cellsToBytes(inst->getExtra<CheckStk>()->offset) +
|
||||
TVOFF(m_type);
|
||||
emitTypeCheck(inst->getTypeParam(), rbase[off], inst->getTaken());
|
||||
auto const baseOff = cellsToBytes(inst->getExtra<CheckStk>()->offset);
|
||||
emitTypeCheck(inst->getTypeParam(), rbase[baseOff + TVOFF(m_type)],
|
||||
rbase[baseOff + TVOFF(m_data)], inst->getTaken());
|
||||
}
|
||||
|
||||
void CodeGenerator::cgGuardLoc(IRInstruction* inst) {
|
||||
auto const rFP = m_regs[inst->getSrc(0)].getReg();
|
||||
auto const off = getLocalOffset(inst->getExtra<GuardLoc>()->locId) +
|
||||
TVOFF(m_type);
|
||||
emitTypeGuard(inst->getTypeParam(), rFP[off]);
|
||||
auto const baseOff = getLocalOffset(inst->getExtra<GuardLoc>()->locId);
|
||||
emitTypeGuard(inst->getTypeParam(),
|
||||
rFP[baseOff + TVOFF(m_type)],
|
||||
rFP[baseOff + TVOFF(m_data)]);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgCheckLoc(IRInstruction* inst) {
|
||||
auto const rbase = m_regs[inst->getSrc(0)].getReg();
|
||||
auto const off = getLocalOffset(inst->getExtra<CheckLoc>()->locId) +
|
||||
TVOFF(m_type);
|
||||
emitTypeCheck(inst->getTypeParam(), rbase[off], inst->getTaken());
|
||||
auto const baseOff = getLocalOffset(inst->getExtra<CheckLoc>()->locId);
|
||||
emitTypeCheck(inst->getTypeParam(), rbase[baseOff + TVOFF(m_type)],
|
||||
rbase[baseOff + TVOFF(m_data)], inst->getTaken());
|
||||
}
|
||||
|
||||
template<class MemLoc>
|
||||
void CodeGenerator::emitSideExitGuard(Type type, MemLoc mem, Offset taken) {
|
||||
auto const cc = emitTypeTest(type, mem, true /* negate */);
|
||||
auto const sk = SrcKey(getCurFunc(), taken);
|
||||
if (cc == CC_None) return;
|
||||
m_tx64->emitBindJcc(m_as, cc, sk, REQ_BIND_SIDE_EXIT);
|
||||
template<class Loc>
|
||||
void CodeGenerator::emitSideExitGuard(Type type,
|
||||
Loc typeSrc,
|
||||
Loc dataSrc,
|
||||
Offset taken) {
|
||||
emitTypeTest(type, typeSrc, dataSrc,
|
||||
[&](ConditionCode cc) {
|
||||
auto const sk = SrcKey(getCurFunc(), taken);
|
||||
m_tx64->emitBindJcc(m_as, ccNegate(cc), sk, REQ_BIND_SIDE_EXIT);
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgSideExitGuardLoc(IRInstruction* inst) {
|
||||
@@ -4013,6 +4074,7 @@ void CodeGenerator::cgSideExitGuardLoc(IRInstruction* inst) {
|
||||
auto const extra = inst->getExtra<SideExitGuardLoc>();
|
||||
emitSideExitGuard(inst->getTypeParam(),
|
||||
fp[getLocalOffset(extra->checkedSlot) + TVOFF(m_type)],
|
||||
fp[getLocalOffset(extra->checkedSlot) + TVOFF(m_data)],
|
||||
extra->taken);
|
||||
}
|
||||
|
||||
@@ -4021,6 +4083,7 @@ void CodeGenerator::cgSideExitGuardStk(IRInstruction* inst) {
|
||||
auto const extra = inst->getExtra<SideExitGuardStk>();
|
||||
emitSideExitGuard(inst->getTypeParam(),
|
||||
sp[cellsToBytes(extra->checkedSlot) + TVOFF(m_type)],
|
||||
sp[cellsToBytes(extra->checkedSlot) + TVOFF(m_data)],
|
||||
extra->taken);
|
||||
}
|
||||
|
||||
@@ -4031,22 +4094,24 @@ void CodeGenerator::cgDefMIStateBase(IRInstruction* inst) {
|
||||
|
||||
void CodeGenerator::cgCheckType(IRInstruction* inst) {
|
||||
auto const src = inst->getSrc(0);
|
||||
auto const rData = m_regs[src].getReg(0);
|
||||
auto const rType = m_regs[src].getReg(1);
|
||||
|
||||
auto const cc = emitTypeTest(inst->getTypeParam(), rType, true);
|
||||
if (cc == CC_None) return;
|
||||
emitTypeTest(inst->getTypeParam(), rType, rData,
|
||||
[&](ConditionCode cc) {
|
||||
emitFwdJcc(ccNegate(cc), inst->getTaken());
|
||||
|
||||
emitFwdJcc(cc, inst->getTaken());
|
||||
|
||||
auto const dstReg = m_regs[inst->getDst()].getReg();
|
||||
if (dstReg != InvalidReg) {
|
||||
emitMovRegReg(m_as, m_regs[src].getReg(0), dstReg);
|
||||
}
|
||||
auto const dstReg = m_regs[inst->getDst()].getReg();
|
||||
if (dstReg != InvalidReg) {
|
||||
emitMovRegReg(m_as, m_regs[src].getReg(0), dstReg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgCheckTypeMem(IRInstruction* inst) {
|
||||
auto const reg = m_regs[inst->getSrc(0)].getReg();
|
||||
emitTypeCheck(inst->getTypeParam(), reg[TVOFF(m_type)], inst->getTaken());
|
||||
emitTypeCheck(inst->getTypeParam(), reg[TVOFF(m_type)],
|
||||
reg[TVOFF(m_data)], inst->getTaken());
|
||||
}
|
||||
|
||||
void CodeGenerator::cgGuardRefs(IRInstruction* inst) {
|
||||
@@ -4810,11 +4875,14 @@ void CodeGenerator::cgBoxPtr(IRInstruction* inst) {
|
||||
auto base = m_regs[addr].getReg();
|
||||
auto dstReg = m_regs[dst].getReg();
|
||||
emitMovRegReg(m_as, base, dstReg);
|
||||
auto const cc = emitTypeTest(Type::BoxedCell, base[TVOFF(m_type)], true);
|
||||
ifThen(m_as, cc, [&] {
|
||||
cgCallHelper(m_as, (TCA)tvBox, dstReg, kNoSyncPoint,
|
||||
ArgGroup(m_regs).ssa(addr));
|
||||
});
|
||||
emitTypeTest(Type::BoxedCell, base[TVOFF(m_type)],
|
||||
base[TVOFF(m_data)],
|
||||
[&](ConditionCode cc) {
|
||||
ifThen(m_as, ccNegate(cc), [&] {
|
||||
cgCallHelper(m_as, (TCA)tvBox, dstReg, kNoSyncPoint,
|
||||
ArgGroup(m_regs).ssa(addr));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgDefCns(IRInstruction* inst) {
|
||||
@@ -5088,9 +5156,12 @@ void traceCallback(ActRec* fp, Cell* sp, int64_t pcOff, void* rip) {
|
||||
}
|
||||
|
||||
void CodeGenerator::cgDbgAssertType(IRInstruction* inst) {
|
||||
ConditionCode cc = emitTypeTest(inst->getTypeParam(),
|
||||
m_regs[inst->getSrc(0)].getReg(1), true);
|
||||
ifThen(m_as, cc, [&] { m_as.ud2(); });
|
||||
emitTypeTest(inst->getTypeParam(),
|
||||
m_regs[inst->getSrc(0)].getReg(1),
|
||||
m_regs[inst->getSrc(0)].getReg(0),
|
||||
[&](ConditionCode cc) {
|
||||
ifThen(m_as, ccNegate(cc), [&] { m_as.ud2(); });
|
||||
});
|
||||
}
|
||||
|
||||
void CodeGenerator::cgVerifyParamCls(IRInstruction* inst) {
|
||||
|
||||
@@ -193,13 +193,16 @@ private:
|
||||
|
||||
void cgLoad(PhysReg base, int64_t off, IRInstruction* inst);
|
||||
|
||||
template<class OpndType>
|
||||
ConditionCode emitTypeTest(Type type, OpndType src, bool negate);
|
||||
template<class Loc1, class Loc2, class JmpFn>
|
||||
void emitTypeTest(Type type,
|
||||
Loc1 typeSrc,
|
||||
Loc2 dataSrc,
|
||||
JmpFn doJcc);
|
||||
|
||||
template<class MemLoc>
|
||||
void emitTypeCheck(Type type, MemLoc src, Block* taken);
|
||||
template<class MemLoc>
|
||||
void emitTypeGuard(Type type, MemLoc mem);
|
||||
template<class Loc>
|
||||
void emitTypeCheck(Type type, Loc typeSrc, Loc dataSrc, Block* taken);
|
||||
template<class Loc>
|
||||
void emitTypeGuard(Type type, Loc typeLoc, Loc dataLoc);
|
||||
|
||||
void cgStMemWork(IRInstruction* inst, bool genStoreType);
|
||||
void cgStRefWork(IRInstruction* inst, bool genStoreType);
|
||||
@@ -254,8 +257,9 @@ private:
|
||||
int64_t (*obj_cmp_int)(ObjectData*, int64_t),
|
||||
int64_t (*arr_cmp_arr)(ArrayData*, ArrayData*));
|
||||
|
||||
template<class MemLoc>
|
||||
void emitSideExitGuard(Type type, MemLoc mem, Offset taken);
|
||||
template<class Loc>
|
||||
void emitSideExitGuard(Type type, Loc typeLoc,
|
||||
Loc dataLoc, Offset taken);
|
||||
void emitReqBindJcc(ConditionCode cc, const ReqBindJccData*);
|
||||
|
||||
void emitCompare(SSATmp*, SSATmp*);
|
||||
@@ -273,7 +277,8 @@ private:
|
||||
const RegAllocInfo& allocInfo,
|
||||
RegXMM rXMMScratch);
|
||||
void emitSetCc(IRInstruction*, ConditionCode);
|
||||
ConditionCode emitIsTypeTest(IRInstruction* inst, bool negate);
|
||||
template<class JmpFn>
|
||||
void emitIsTypeTest(IRInstruction* inst, JmpFn doJcc);
|
||||
void doubleCmp(X64Assembler& a, RegXMM xmmReg0, RegXMM xmmReg1);
|
||||
void cgIsTypeCommon(IRInstruction* inst, bool negate);
|
||||
void cgJmpIsTypeCommon(IRInstruction* inst, bool negate);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "hphp/util/base.h"
|
||||
#include "hphp/runtime/vm/translator/hopt/ir.h"
|
||||
// for specialized object tests to get some real VM::Class
|
||||
#include "hphp/system/lib/systemlib.h"
|
||||
|
||||
namespace std { namespace tr1 {
|
||||
template<> struct hash<HPHP::JIT::Type> {
|
||||
@@ -127,6 +129,47 @@ TEST(Type, Subtypes) {
|
||||
EXPECT_TRUE(Type::PtrToCell.strictSubtypeOf(Type::PtrToGen));
|
||||
}
|
||||
|
||||
TEST(Type, RuntimeType) {
|
||||
HPHP::Transl::RuntimeType rt(new StringData());
|
||||
Type t = Type::fromRuntimeType(rt);
|
||||
EXPECT_TRUE(t.subtypeOf(Type::Str));
|
||||
EXPECT_FALSE(t.subtypeOf(Type::Int));
|
||||
|
||||
rt = HPHP::Transl::RuntimeType(HphpArray::GetStaticEmptyArray());
|
||||
t = Type::fromRuntimeType(rt);
|
||||
EXPECT_TRUE(t.subtypeOf(Type::Arr));
|
||||
EXPECT_FALSE(t.subtypeOf(Type::Str));
|
||||
|
||||
rt = HPHP::Transl::RuntimeType(true);
|
||||
t = Type::fromRuntimeType(rt);
|
||||
EXPECT_TRUE(t.subtypeOf(Type::Bool));
|
||||
EXPECT_FALSE(t.subtypeOf(Type::Obj));
|
||||
|
||||
rt = HPHP::Transl::RuntimeType((int64_t) 1);
|
||||
t = Type::fromRuntimeType(rt);
|
||||
EXPECT_TRUE(t.subtypeOf(Type::Int));
|
||||
EXPECT_FALSE(t.subtypeOf(Type::Dbl));
|
||||
|
||||
rt = HPHP::Transl::RuntimeType(DataType::KindOfObject,
|
||||
DataType::KindOfInvalid);
|
||||
rt = rt.setKnownClass(SystemLib::s_TraversableClass);
|
||||
t = Type::fromRuntimeType(rt);
|
||||
EXPECT_TRUE(t.subtypeOf(Type::Obj));
|
||||
EXPECT_FALSE(Type::Obj.subtypeOf(t));
|
||||
EXPECT_FALSE(Type::Int.subtypeOf(t));
|
||||
HPHP::Transl::RuntimeType rt1 =
|
||||
HPHP::Transl::RuntimeType(DataType::KindOfObject,
|
||||
DataType::KindOfInvalid);
|
||||
rt1 = rt1.setKnownClass(SystemLib::s_IteratorClass);
|
||||
Type t1 = Type::fromRuntimeType(rt1);
|
||||
EXPECT_TRUE(t1.subtypeOf(Type::Obj));
|
||||
EXPECT_TRUE(t1.subtypeOf(t));
|
||||
EXPECT_FALSE(Type::Obj.subtypeOf(t1));
|
||||
EXPECT_FALSE(t.subtypeOf(t1));
|
||||
EXPECT_FALSE(t.subtypeOf(Type::Str));
|
||||
EXPECT_FALSE(Type::Int.subtypeOf(t));
|
||||
}
|
||||
|
||||
TEST(Type, CanRunDtor) {
|
||||
TypeSet types = allTypes();
|
||||
auto expectTrue = [&](Type t) {
|
||||
|
||||
@@ -117,6 +117,12 @@ class Type {
|
||||
bits_t m_bits;
|
||||
TypeBits m_typedBits;
|
||||
};
|
||||
const Class* m_class;
|
||||
|
||||
// private ctor to build a specialized type
|
||||
explicit Type(bits_t bits, const Class* klass)
|
||||
: m_bits(bits), m_class(klass)
|
||||
{}
|
||||
|
||||
public:
|
||||
# define IRT(name, ...) static const Type name;
|
||||
@@ -124,15 +130,15 @@ public:
|
||||
# undef IRT
|
||||
|
||||
explicit Type(bits_t bits = kNone)
|
||||
: m_bits(bits)
|
||||
: m_bits(bits), m_class(nullptr)
|
||||
{}
|
||||
|
||||
size_t hash() const {
|
||||
return hash_int64(m_bits);
|
||||
return hash_int64_pair(m_bits, reinterpret_cast<uintptr_t>(m_class));
|
||||
}
|
||||
|
||||
bool operator==(Type other) const {
|
||||
return m_bits == other.m_bits;
|
||||
return equals(other);
|
||||
}
|
||||
|
||||
bool operator!=(Type other) const {
|
||||
@@ -140,14 +146,23 @@ public:
|
||||
}
|
||||
|
||||
Type operator|(Type other) const {
|
||||
assert(m_class == nullptr && other.m_class == nullptr);
|
||||
return Type(m_bits | other.m_bits);
|
||||
}
|
||||
|
||||
Type operator&(Type other) const {
|
||||
if (m_class != nullptr && other.m_class != nullptr) {
|
||||
if (m_class->classof(other.m_class)) {
|
||||
return Type(m_bits & other.m_bits).specialize(other.m_class);
|
||||
} else if (other.m_class->classof(m_class)) {
|
||||
return Type(m_bits & other.m_bits).specialize(m_class);
|
||||
}
|
||||
}
|
||||
return Type(m_bits & other.m_bits);
|
||||
}
|
||||
|
||||
Type operator-(Type other) const {
|
||||
assert(m_class == nullptr && other.m_class == nullptr);
|
||||
return Type(m_bits & ~other.m_bits);
|
||||
}
|
||||
|
||||
@@ -253,14 +268,17 @@ public:
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
* Returns true if this is same type or a subtype of any of the arguments.
|
||||
*/
|
||||
bool subtypeOf(Type t2) const {
|
||||
return (m_bits & t2.m_bits) == m_bits;
|
||||
return (m_bits & t2.m_bits) == m_bits
|
||||
&& (t2.m_class == nullptr
|
||||
|| (m_class != nullptr
|
||||
&& m_class->classof(t2.m_class)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a non-strict subtype of any of the arguments.
|
||||
* Returns true if and only if this is the same as or a subtype of t2.
|
||||
*/
|
||||
template<typename... Types>
|
||||
bool subtypeOfAny(Type t2, Types... ts) const {
|
||||
@@ -290,7 +308,7 @@ public:
|
||||
* probably mean subtypeOf.
|
||||
*/
|
||||
bool equals(Type t2) const {
|
||||
return m_bits == t2.m_bits;
|
||||
return m_bits == t2.m_bits && m_class == t2.m_class;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -331,6 +349,11 @@ public:
|
||||
return subtypeOf(Str);
|
||||
}
|
||||
|
||||
const Class* getClass() const {
|
||||
assert(isObj());
|
||||
return m_class;
|
||||
}
|
||||
|
||||
Type innerType() const {
|
||||
assert(isBoxed());
|
||||
return Type(m_bits >> kBoxShift);
|
||||
@@ -397,6 +420,11 @@ public:
|
||||
return Type(m_bits << kPtrShift);
|
||||
}
|
||||
|
||||
Type specialize(const Class* klass) const {
|
||||
assert(isObj() && m_class == nullptr);
|
||||
return Type(m_bits, klass);
|
||||
}
|
||||
|
||||
bool canRunDtor() const {
|
||||
return
|
||||
(*this & (Obj | CountedArr | BoxedObj | BoxedCountedArr))
|
||||
@@ -430,7 +458,8 @@ public:
|
||||
}
|
||||
|
||||
static Type fromDataType(DataType outerType,
|
||||
DataType innerType = KindOfInvalid) {
|
||||
DataType innerType = KindOfInvalid,
|
||||
const Class* klass = nullptr) {
|
||||
assert(innerType != KindOfRef);
|
||||
|
||||
switch (outerType) {
|
||||
@@ -443,7 +472,13 @@ public:
|
||||
case KindOfStaticString : return StaticStr;
|
||||
case KindOfString : return Str;
|
||||
case KindOfArray : return Arr;
|
||||
case KindOfObject : return Obj;
|
||||
case KindOfObject : {
|
||||
if (klass != nullptr) {
|
||||
return Obj.specialize(klass);
|
||||
} else {
|
||||
return Obj;
|
||||
}
|
||||
}
|
||||
case KindOfClass : return Cls;
|
||||
case KindOfUncountedInit : return UncountedInit;
|
||||
case KindOfUncounted : return Uncounted;
|
||||
@@ -461,7 +496,7 @@ public:
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by value in C++
|
||||
bool isSimpleType() {
|
||||
bool isSimpleType() const {
|
||||
return subtypeOf(Type::Bool)
|
||||
|| subtypeOf(Type::Int)
|
||||
|| subtypeOf(Type::Dbl)
|
||||
@@ -470,7 +505,7 @@ public:
|
||||
|
||||
// return true if this corresponds to a type that
|
||||
// is passed by reference in C++
|
||||
bool isReferenceType() {
|
||||
bool isReferenceType() const {
|
||||
return subtypeOf(Type::Str)
|
||||
|| subtypeOf(Type::Arr)
|
||||
|| subtypeOf(Type::Obj);
|
||||
@@ -491,14 +526,14 @@ public:
|
||||
}
|
||||
|
||||
static Type fromRuntimeType(const Transl::RuntimeType& rtt) {
|
||||
return fromDataType(rtt.outerType(), rtt.innerType());
|
||||
return fromDataType(rtt.outerType(), rtt.innerType(), rtt.knownClass());
|
||||
}
|
||||
|
||||
static Type fromDynLocation(const Transl::DynLocation* dynLoc);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Type) <= sizeof(uint64_t),
|
||||
"JIT::Type should fit in a register");
|
||||
static_assert(sizeof(Type) <= 2 * sizeof(uint64_t),
|
||||
"JIT::Type should fit in (2 * sizeof(uint64_t))");
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ RuntimeType::RuntimeType(DataType outer, DataType inner /* = KindOfInvalid */,
|
||||
m_value.outerType = normalizeDataType(outer);
|
||||
m_value.innerType = normalizeDataType(inner);
|
||||
m_value.klass = klass;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
@@ -50,6 +51,7 @@ RuntimeType::RuntimeType(const StringData* sd)
|
||||
m_value.outerType = KindOfString;
|
||||
m_value.innerType = KindOfInvalid;
|
||||
m_value.string = sd;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
@@ -58,6 +60,7 @@ RuntimeType::RuntimeType(const ArrayData* ad)
|
||||
m_value.outerType = KindOfArray;
|
||||
m_value.innerType = KindOfInvalid;
|
||||
m_value.array = ad;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
@@ -68,6 +71,7 @@ RuntimeType::RuntimeType(bool value)
|
||||
m_value.klass = nullptr;
|
||||
m_value.boolean = value;
|
||||
m_value.boolValid = true;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
@@ -76,6 +80,7 @@ RuntimeType::RuntimeType(int64_t value)
|
||||
m_value.outerType = KindOfInt64;
|
||||
m_value.innerType = KindOfInvalid;
|
||||
m_value.intval = value;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
@@ -84,18 +89,16 @@ RuntimeType::RuntimeType(const Class* klass)
|
||||
m_value.outerType = KindOfClass;
|
||||
m_value.innerType = KindOfInvalid;
|
||||
m_value.klass = klass;
|
||||
m_value.knownClass = nullptr;
|
||||
consistencyCheck();
|
||||
}
|
||||
|
||||
RuntimeType::RuntimeType(const RuntimeType& source) {
|
||||
*this = source;
|
||||
}
|
||||
|
||||
RuntimeType::RuntimeType() :
|
||||
m_kind(VALUE) {
|
||||
m_value.outerType = KindOfInvalid;
|
||||
m_value.innerType = KindOfInvalid;
|
||||
m_value.klass = nullptr;
|
||||
m_value.knownClass = nullptr;
|
||||
}
|
||||
|
||||
RuntimeType::RuntimeType(const Iter* it) :
|
||||
@@ -195,6 +198,12 @@ RuntimeType::valueGeneric() const {
|
||||
return m_value.intval;
|
||||
}
|
||||
|
||||
const Class*
|
||||
RuntimeType::knownClass() const {
|
||||
consistencyCheck();
|
||||
return m_value.knownClass;
|
||||
}
|
||||
|
||||
RuntimeType
|
||||
RuntimeType::setValueType(DataType newInner) const {
|
||||
assert(m_kind == VALUE);
|
||||
@@ -212,6 +221,18 @@ RuntimeType::setValueType(DataType newInner) const {
|
||||
return rtt;
|
||||
}
|
||||
|
||||
RuntimeType
|
||||
RuntimeType::setKnownClass(const Class* klass) const {
|
||||
assert(isObject());
|
||||
RuntimeType rtt;
|
||||
rtt.m_kind = VALUE;
|
||||
rtt.m_value.outerType = outerType();
|
||||
rtt.m_value.klass = m_value.klass;
|
||||
rtt.m_value.knownClass = klass;
|
||||
rtt.consistencyCheck();
|
||||
return rtt;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
DataType RuntimeType::outerType() const {
|
||||
consistencyCheck();
|
||||
@@ -285,6 +306,10 @@ bool RuntimeType::isClass() const {
|
||||
return isValue() && outerType() == KindOfClass;
|
||||
}
|
||||
|
||||
bool RuntimeType::hasKnownType() const {
|
||||
return isObject() && m_value.knownClass != nullptr;
|
||||
}
|
||||
|
||||
bool RuntimeType::isArray() const {
|
||||
return isValue() && outerType() == KindOfArray;
|
||||
}
|
||||
@@ -307,16 +332,6 @@ bool RuntimeType::operator==(const RuntimeType& r) const {
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeType& RuntimeType::operator=(const RuntimeType& r) {
|
||||
m_kind = r.m_kind;
|
||||
m_value.innerType = r.m_value.innerType;
|
||||
m_value.outerType = r.m_value.outerType;
|
||||
m_value.klass = r.m_value.klass;
|
||||
consistencyCheck();
|
||||
assert(*this == r);
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t
|
||||
RuntimeType::operator()(const RuntimeType& r) const {
|
||||
uint64_t p1 = HPHP::hash_int64(m_kind);
|
||||
|
||||
@@ -188,6 +188,9 @@ class RuntimeType {
|
||||
struct {
|
||||
DataType outerType;
|
||||
DataType innerType;
|
||||
// Set when we want to transfer the type information to the
|
||||
// IR type system (Type object)
|
||||
const Class* knownClass;
|
||||
union {
|
||||
// We may have even more precise data about this set of values.
|
||||
const StringData* string; // KindOfString: The exact value.
|
||||
@@ -225,6 +228,8 @@ class RuntimeType {
|
||||
m_value.klass == nullptr);
|
||||
assert(m_value.innerType != KindOfStaticString &&
|
||||
m_value.outerType != KindOfStaticString);
|
||||
assert(m_value.knownClass == nullptr ||
|
||||
m_value.outerType == KindOfObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +241,7 @@ class RuntimeType {
|
||||
explicit RuntimeType(const Class*);
|
||||
explicit RuntimeType(bool value);
|
||||
explicit RuntimeType(int64_t value);
|
||||
RuntimeType(const RuntimeType& copy);
|
||||
RuntimeType(const RuntimeType& copy) = default;
|
||||
RuntimeType();
|
||||
explicit RuntimeType(const Iter* iter);
|
||||
explicit RuntimeType(ArrayIter::Type type);
|
||||
@@ -247,6 +252,7 @@ class RuntimeType {
|
||||
RuntimeType box() const;
|
||||
RuntimeType unbox() const;
|
||||
RuntimeType setValueType(DataType vt) const;
|
||||
RuntimeType setKnownClass(const Class* klass) const;
|
||||
|
||||
// Accessors
|
||||
DataType outerType() const;
|
||||
@@ -259,6 +265,7 @@ class RuntimeType {
|
||||
int valueBoolean() const;
|
||||
int64_t valueInt() const;
|
||||
int64_t valueGeneric() const;
|
||||
const Class* knownClass() const;
|
||||
|
||||
// Helpers for typechecking
|
||||
DataType typeCheckValue() const;
|
||||
@@ -279,8 +286,9 @@ class RuntimeType {
|
||||
bool isString() const;
|
||||
bool isObject() const;
|
||||
bool isClass() const;
|
||||
bool hasKnownType() const;
|
||||
bool operator==(const RuntimeType& r) const;
|
||||
RuntimeType &operator=(const RuntimeType& r);
|
||||
RuntimeType &operator=(const RuntimeType& r) = default;
|
||||
size_t operator()(const RuntimeType& r) const; // hash function
|
||||
std::string pretty() const;
|
||||
};
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário