diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index bf8ed4d4a..2d4607ae3 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -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 ObjectData* of the specific type Class Counted {CountedStr|CountedArr|Obj|BoxedCell} Cell {Null|Bool|Int|Dbl|Str|Arr|Obj} diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 3c466ef46..b87a05e25 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -1653,38 +1653,68 @@ void CodeGenerator::cgOpGte(IRInstruction* inst) { // Type check operators /////////////////////////////////////////////////////////////////////////////// -template -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 +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 +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 -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 +void CodeGenerator::emitTypeCheck(Type type, + Loc typeSrc, + Loc dataSrc, + Block* taken) { + emitTypeTest(type, typeSrc, dataSrc, + [&](ConditionCode cc) { + emitFwdJcc(ccNegate(cc), taken); + }); } -template -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 +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()->offset) + - TVOFF(m_type); - emitTypeGuard(inst->getTypeParam(), rSP[off]); + auto const baseOff = cellsToBytes(inst->getExtra()->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()->offset) + - TVOFF(m_type); - emitTypeCheck(inst->getTypeParam(), rbase[off], inst->getTaken()); + auto const baseOff = cellsToBytes(inst->getExtra()->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()->locId) + - TVOFF(m_type); - emitTypeGuard(inst->getTypeParam(), rFP[off]); + auto const baseOff = getLocalOffset(inst->getExtra()->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()->locId) + - TVOFF(m_type); - emitTypeCheck(inst->getTypeParam(), rbase[off], inst->getTaken()); + auto const baseOff = getLocalOffset(inst->getExtra()->locId); + emitTypeCheck(inst->getTypeParam(), rbase[baseOff + TVOFF(m_type)], + rbase[baseOff + TVOFF(m_data)], inst->getTaken()); } -template -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 +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(); 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(); 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) { diff --git a/hphp/runtime/vm/translator/hopt/codegen.h b/hphp/runtime/vm/translator/hopt/codegen.h index 3fa265a91..4857090ac 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.h +++ b/hphp/runtime/vm/translator/hopt/codegen.h @@ -193,13 +193,16 @@ private: void cgLoad(PhysReg base, int64_t off, IRInstruction* inst); - template - ConditionCode emitTypeTest(Type type, OpndType src, bool negate); + template + void emitTypeTest(Type type, + Loc1 typeSrc, + Loc2 dataSrc, + JmpFn doJcc); - template - void emitTypeCheck(Type type, MemLoc src, Block* taken); - template - void emitTypeGuard(Type type, MemLoc mem); + template + void emitTypeCheck(Type type, Loc typeSrc, Loc dataSrc, Block* taken); + template + 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 - void emitSideExitGuard(Type type, MemLoc mem, Offset taken); + template + 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 + 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); diff --git a/hphp/runtime/vm/translator/hopt/test/type.cpp b/hphp/runtime/vm/translator/hopt/test/type.cpp index 1ff15a0c8..3ce3f5663 100644 --- a/hphp/runtime/vm/translator/hopt/test/type.cpp +++ b/hphp/runtime/vm/translator/hopt/test/type.cpp @@ -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 { @@ -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) { diff --git a/hphp/runtime/vm/translator/hopt/type.h b/hphp/runtime/vm/translator/hopt/type.h index 194d99441..a0e22c9be 100644 --- a/hphp/runtime/vm/translator/hopt/type.h +++ b/hphp/runtime/vm/translator/hopt/type.h @@ -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(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 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))"); }} diff --git a/hphp/runtime/vm/translator/runtime-type.cpp b/hphp/runtime/vm/translator/runtime-type.cpp index 6a40b3619..1c984d89c 100644 --- a/hphp/runtime/vm/translator/runtime-type.cpp +++ b/hphp/runtime/vm/translator/runtime-type.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/runtime-type.h b/hphp/runtime/vm/translator/runtime-type.h index 7724805f3..c0a7e65b6 100644 --- a/hphp/runtime/vm/translator/runtime-type.h +++ b/hphp/runtime/vm/translator/runtime-type.h @@ -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; };