From 6e865771673914ff0b0ff4acdbd9007823a20ef6 Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Fri, 27 Jun 2014 02:36:10 -0700 Subject: [PATCH] Remove JIT::RuntimeType Summary: The few remaining uses of it have been replaced with JIT::Type. I also moved around some related struct and function definitions so they'd make more sense. JIT::Location and JIT::DynLocation are next on the hitlist; they have more modern replacements in RegionDesc. Reviewed By: @bertmaher Differential Revision: D1407922 --- hphp/runtime/vm/jit/extra-data.h | 6 +- hphp/runtime/vm/jit/guard-relaxation.cpp | 2 +- hphp/runtime/vm/jit/hhbc-translator.cpp | 73 +++--- hphp/runtime/vm/jit/hhbc-translator.h | 6 +- hphp/runtime/vm/jit/ir-translator.h | 1 - hphp/runtime/vm/jit/ir.h | 1 - hphp/runtime/vm/jit/location.h | 185 ++++++++++++++ hphp/runtime/vm/jit/mc-generator.h | 27 -- hphp/runtime/vm/jit/minstr-translator.cpp | 54 +++- hphp/runtime/vm/jit/prof-data.h | 1 - hphp/runtime/vm/jit/ref-deps.cpp | 27 ++ hphp/runtime/vm/jit/ref-deps.h | 41 +-- hphp/runtime/vm/jit/region-tracelet.cpp | 23 +- hphp/runtime/vm/jit/runtime-type.cpp | 410 ------------------------------ hphp/runtime/vm/jit/runtime-type.h | 336 ------------------------ hphp/runtime/vm/jit/test/type.cpp | 44 ---- hphp/runtime/vm/jit/translator.cpp | 78 ++---- hphp/runtime/vm/jit/translator.h | 158 ++++++------ hphp/runtime/vm/jit/type.cpp | 39 --- hphp/runtime/vm/jit/type.h | 14 +- hphp/runtime/vm/jit/unwind-x64.cpp | 1 - hphp/runtime/vm/jit/unwind-x64.h | 5 +- hphp/test/.gitignore | 2 + hphp/test/tools/compare-ir.sh | 2 +- hphp/util/trace.h | 3 +- 25 files changed, 423 insertions(+), 1116 deletions(-) create mode 100644 hphp/runtime/vm/jit/location.h delete mode 100644 hphp/runtime/vm/jit/runtime-type.cpp delete mode 100644 hphp/runtime/vm/jit/runtime-type.h diff --git a/hphp/runtime/vm/jit/extra-data.h b/hphp/runtime/vm/jit/extra-data.h index 0f5f440811e..83208c2adbf 100644 --- a/hphp/runtime/vm/jit/extra-data.h +++ b/hphp/runtime/vm/jit/extra-data.h @@ -17,11 +17,13 @@ #ifndef incl_HPHP_VM_EXTRADATA_H_ #define incl_HPHP_VM_EXTRADATA_H_ -#include "hphp/util/ringbuffer.h" #include + #include "hphp/runtime/vm/jit/ir.h" -#include "hphp/runtime/vm/jit/types.h" #include "hphp/runtime/vm/jit/phys-loc.h" +#include "hphp/runtime/vm/jit/types.h" +#include "hphp/runtime/vm/srckey.h" +#include "hphp/util/ringbuffer.h" namespace HPHP { namespace JIT { diff --git a/hphp/runtime/vm/jit/guard-relaxation.cpp b/hphp/runtime/vm/jit/guard-relaxation.cpp index c1f3ac448e2..2bf55fad8ad 100644 --- a/hphp/runtime/vm/jit/guard-relaxation.cpp +++ b/hphp/runtime/vm/jit/guard-relaxation.cpp @@ -443,7 +443,7 @@ TypeConstraint relaxConstraint(const TypeConstraint origTc, newTc.weak = origTc.weak; while (true) { - if (newTc.category == DataTypeSpecialized) { + if (newTc.isSpecialized()) { // We need to ask for the right kind of specialization, so grab it from // origTc. if (origTc.wantArrayKind()) newTc.setWantArrayKind(); diff --git a/hphp/runtime/vm/jit/hhbc-translator.cpp b/hphp/runtime/vm/jit/hhbc-translator.cpp index cda09a5a43b..65d322a3227 100644 --- a/hphp/runtime/vm/jit/hhbc-translator.cpp +++ b/hphp/runtime/vm/jit/hhbc-translator.cpp @@ -323,13 +323,12 @@ void HhbcTranslator::beginInlining(unsigned numParams, target}); updateMarker(); - always_assert_log( + always_assert_flog( findSpillFrame(calleeSP), - [&] { - return folly::format("Couldn't find SpillFrame for inlined call on sp {}." - " Was the FPush instruction interpreted?\n{}", - *calleeSP->inst(), m_irb->unit().toString()).str(); - }); + "Couldn't find SpillFrame for inlined call on sp {}." + " Was the FPush instruction interpreted?\n{}", + *calleeSP->inst(), m_irb->unit() + ); auto const calleeFP = gen(DefInlineFP, data, calleeSP, prevSP, m_irb->fp()); gen( @@ -2729,12 +2728,17 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) { } } - auto const ssaCls = - persistentCls ? cns(cls) - : gen(LdClsCached, makeCatch(), cns(className)); - auto const obj = - fastAlloc ? emitAllocObjFast(cls) - : gen(AllocObj, makeCatch(), ssaCls); + auto ssaCls = persistentCls ? cns(cls) + : gen(LdClsCached, makeCatch(), cns(className)); + if (!ssaCls->isConst() && uniqueCls) { + // If the Class is unique but not persistent, it's safe to use it as a + // const after the LdClsCached, which will throw if the class can't be + // defined. + ssaCls = cns(cls); + } + + auto const obj = fastAlloc ? emitAllocObjFast(cls) + : gen(AllocObj, makeCatch(), ssaCls); gen(IncRef, obj); emitFPushCtorCommon(ssaCls, obj, func, numParams); } @@ -4263,57 +4267,38 @@ void HhbcTranslator::assertTypeStack(uint32_t idx, Type type) { } /* - * Creates a RuntimeType struct from a program location. This needs access to - * more than just the location's type because RuntimeType includes known - * constant values. All accesses to the stack and locals use DataTypeGeneric so - * this function should only be used for inspecting state; when the values are - * actually used they must be constrained further. + * Returns the Type of the given location. All accesses to the stack and locals + * use DataTypeGeneric so this function should only be used for inspecting + * state; when the values are actually used they must be constrained further. */ -RuntimeType HhbcTranslator::rttFromLocation(const Location& loc) { - Type t; - SSATmp* val = nullptr; +Type HhbcTranslator::typeFromLocation(const Location& loc) { switch (loc.space) { case Location::Stack: { auto i = loc.offset; assert(i >= 0); if (i < m_irb->evalStack().size()) { - val = top(DataTypeGeneric, i); - t = val->type(); + return top(DataTypeGeneric, i)->type(); } else { auto stackVal = getStackValue(m_irb->sp(), i - m_irb->evalStack().size() + m_irb->stackDeficit()); - val = stackVal.value; - t = stackVal.knownType; - if (!val && t == Type::StackElem) return RuntimeType(KindOfAny); + return stackVal.knownType; } } break; case Location::Local: { auto l = loc.offset; - val = m_irb->localValue(l, DataTypeGeneric); - t = val ? val->type() : m_irb->localType(l, DataTypeGeneric); + return m_irb->localType(l, DataTypeGeneric); } break; case Location::Litstr: - return RuntimeType(curUnit()->lookupLitstrId(loc.offset)); + return Type::cns(curUnit()->lookupLitstrId(loc.offset)); case Location::Litint: - return RuntimeType(loc.offset); + return Type::cns(loc.offset); case Location::This: - return RuntimeType(KindOfObject, KindOfNone, curFunc()->cls()); - case Location::Invalid: - case Location::Iter: - not_reached(); - } + return Type::Obj.specialize(curFunc()->cls()); - assert(IMPLIES(val, val->type().equals(t))); - if (val && val->isConst()) { - // RuntimeType holds constant Bool, Int, Str, and Cls. - if (val->isA(Type::Bool)) return RuntimeType(val->boolVal()); - if (val->isA(Type::Int)) return RuntimeType(val->intVal()); - if (val->isA(Type::Str)) return RuntimeType(val->strVal()); - if (val->isA(Type::Cls)) return RuntimeType(val->clsVal()); + default: + always_assert(false && "Bad location in typeFromLocation"); } - - return t.toRuntimeType(); } static uint64_t packBitVec(const std::vector& bits, unsigned i) { @@ -5228,7 +5213,7 @@ folly::Optional HhbcTranslator::ratToAssertType(RepoAuthType rat) const { { auto ty = [&] { auto const cls = Unit::lookupUniqueClass(rat.clsName()); - return classIsPersistentOrCtxParent(cls) + return classIsUniqueOrCtxParent(cls) ? Type::Obj.specialize(cls) : Type::Obj; }(); diff --git a/hphp/runtime/vm/jit/hhbc-translator.h b/hphp/runtime/vm/jit/hhbc-translator.h index a64cb3bce9e..15940853fb1 100644 --- a/hphp/runtime/vm/jit/hhbc-translator.h +++ b/hphp/runtime/vm/jit/hhbc-translator.h @@ -30,7 +30,6 @@ #include "hphp/runtime/vm/member-operations.h" #include "hphp/runtime/vm/jit/guard-relaxation.h" #include "hphp/runtime/vm/jit/ir-builder.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/translator.h" #include "hphp/runtime/vm/srckey.h" @@ -132,7 +131,7 @@ struct HhbcTranslator { void checkTypeTopOfStack(Type type, Offset nextByteCode); void assertType(const RegionDesc::Location& loc, Type type); void checkType(const RegionDesc::Location& loc, Type type, Offset dest); - RuntimeType rttFromLocation(const Location& loc); + Type typeFromLocation(const Location& loc); // Inlining-related functions. void beginInlining(unsigned numArgs, @@ -528,7 +527,7 @@ public: private: /* - * MInstrTranslator is responsible for translating one of the vector + * MInstrTranslator is responsible for translating one of the m-instr * instructions (CGetM, SetM, IssetM, etc..) into hhir. */ struct MInstrTranslator { @@ -625,6 +624,7 @@ private: bool warn, bool define); Class* contextClass() const; + PropInfo getCurrentPropertyOffset(const Class*& cls); /* * genStk is a wrapper around IRBuilder::gen() to deal with instructions diff --git a/hphp/runtime/vm/jit/ir-translator.h b/hphp/runtime/vm/jit/ir-translator.h index 8264d7c63c7..1532e12b7e9 100644 --- a/hphp/runtime/vm/jit/ir-translator.h +++ b/hphp/runtime/vm/jit/ir-translator.h @@ -24,7 +24,6 @@ namespace HPHP { namespace JIT { struct NormalizedInstruction; struct Location; -struct RuntimeType; } namespace JIT { diff --git a/hphp/runtime/vm/jit/ir.h b/hphp/runtime/vm/jit/ir.h index 132260f78b4..8c54f4af35e 100644 --- a/hphp/runtime/vm/jit/ir.h +++ b/hphp/runtime/vm/jit/ir.h @@ -37,7 +37,6 @@ #include "hphp/runtime/vm/jit/phys-reg.h" #include "hphp/runtime/vm/jit/abi-x64.h" #include "hphp/runtime/vm/jit/types.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/translator-runtime.h" #include "hphp/runtime/vm/jit/type.h" #include "hphp/runtime/base/types.h" diff --git a/hphp/runtime/vm/jit/location.h b/hphp/runtime/vm/jit/location.h new file mode 100644 index 00000000000..86e47393a83 --- /dev/null +++ b/hphp/runtime/vm/jit/location.h @@ -0,0 +1,185 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2014 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_LOCATION_H_ +#define incl_HPHP_JIT_LOCATION_H_ + +#include + +#include "hphp/runtime/vm/bytecode.h" +#include "hphp/runtime/vm/jit/region-selection.h" +#include "hphp/util/safe-cast.h" + +namespace HPHP { +namespace JIT { + +// Location -- +// A user-program-visible, and bytecode ISA addressable place for a PHP +// value. +struct Location { + enum Space { + Invalid, // Unknown location + Stack, // Stack; offset == delta from top + Local, // Stack frame's registers; offset == local register + Iter, // Stack frame's iterators + Litstr, // Literal string pseudo-location + Litint, // Literal int pseudo-location + This, // $this in the current frame + }; + + explicit Location(Space spc) + : space(spc) + , offset(0) + { + assert(spc == This); + } + + Location(Space spc, int64_t off) + : space(spc) + , offset(off) + {} + + Location() : space(Invalid), offset(-1) {} + + int cmp(const Location &r) const { +#define CMP(field) do { \ + if (field > r.field) { return 1; } \ + else if (field < r.field) { return -1; } } while(0) + CMP(space); + CMP(offset); +#undef CMP + return 0; + } + + bool operator==(const Location& r) const { + return cmp(r) == 0; + } + + bool operator!=(const Location& r) const { + return cmp(r) != 0; + } + + bool operator<(const Location& r) const { + return cmp(r) < 0; + } + + // Hash function. + size_t operator()(const Location& l) const { + return HPHP::hash_int64_pair(l.space, l.offset); + } + + const char *spaceName() const { + switch(space) { + case Stack: return "Stk"; + case Local: return "Local"; + case Iter: return "Iter"; + case Litstr: return "Litstr"; + case Litint: return "Litint"; + case This: return "This"; + case Invalid:return "*invalid*"; + default: not_reached(); + } + } + + std::string pretty() const { + return folly::format("(Location {} {})", spaceName(), offset).str(); + } + + bool isStack() const { + return space == Stack; + } + + bool isLocal() const { + return space == Local; + } + + bool isInvalid() const { + return space == Invalid; + } + + bool isValid() const { + return !isInvalid(); + } + + bool isLiteral() const { + return space == Litstr || space == Litint; + } + + bool isThis() const { + return space == This; + } + + bool isIter() const { + return space == Iter; + } + + JIT::RegionDesc::Location toLocation(Offset spOffsetFromFp) const { + typedef JIT::RegionDesc::Location L; + switch (space) { + case Stack: { + auto offsetFromSp = safe_cast(offset); + return L::Stack{offsetFromSp, spOffsetFromFp - offsetFromSp}; + } + case Local: return L::Local{safe_cast(offset)}; + default: not_reached(); + } + } + +public: + Space space; + int64_t offset; +}; + +// A DynLocation is a Location-in-execution: a location, along with +// whatever is known about its runtime type. +struct DynLocation { + Location location; + Type rtt; + + DynLocation(Location l, DataType t) : location(l), rtt(t) {} + + DynLocation(Location l, Type t) : location(l), rtt(t) {} + + DynLocation() : location(), rtt() {} + + bool operator==(const DynLocation& r) const = delete; + + // Hash function + size_t operator()(const DynLocation &dl) const { + uint64_t rtthash = rtt.hash(); + uint64_t locHash = location(location); + return rtthash ^ locHash; + } + + std::string pretty() const { + return folly::to( + "DynLocation(", location.pretty(), ',', rtt.toString(), ')'); + } + + bool isStack() const { + return location.isStack(); + } + bool isLocal() const { + return location.isLocal(); + } + bool isLiteral() const { + return location.isLiteral(); + } +}; + +} } + +#endif // incl_HPHP_JIT_LOCATION_H_ diff --git a/hphp/runtime/vm/jit/mc-generator.h b/hphp/runtime/vm/jit/mc-generator.h index 2a512d49f78..1348edd5a85 100644 --- a/hphp/runtime/vm/jit/mc-generator.h +++ b/hphp/runtime/vm/jit/mc-generator.h @@ -387,33 +387,6 @@ TCA fcallHelper(ActRec* ar, void* sp); TCA funcBodyHelper(ActRec* ar, void* sp); int64_t decodeCufIterHelper(Iter* it, TypedValue func); -bool isNormalPropertyAccess(const NormalizedInstruction& i, - int propInput, - int objInput); - -struct PropInfo { - PropInfo() - : offset(-1) - , repoAuthType{} - {} - explicit PropInfo(int offset, RepoAuthType repoAuthType) - : offset(offset) - , repoAuthType{repoAuthType} - {} - - int offset; - RepoAuthType repoAuthType; -}; - -PropInfo getPropertyOffset(const NormalizedInstruction& ni, - Class* contextClass, - const Class*& baseClass, - const MInstrInfo& mii, - unsigned mInd, unsigned iInd); -PropInfo getFinalPropertyOffset(const NormalizedInstruction&, - Class* contextClass, - const MInstrInfo&); - // Both emitIncStat()s push/pop flags but don't clobber any registers. extern void emitIncStat(CodeBlock& cb, uint64_t* tl_table, uint32_t index, int n = 1, bool force = false); diff --git a/hphp/runtime/vm/jit/minstr-translator.cpp b/hphp/runtime/vm/jit/minstr-translator.cpp index 2a90689fbfa..650b97a368a 100644 --- a/hphp/runtime/vm/jit/minstr-translator.cpp +++ b/hphp/runtime/vm/jit/minstr-translator.cpp @@ -867,11 +867,42 @@ void HhbcTranslator::MInstrTranslator::emitIntermediateOp() { } } +PropInfo HhbcTranslator::MInstrTranslator::getCurrentPropertyOffset( + const Class*& knownCls +) { + auto info = getPropertyOffset(m_ni, contextClass(), knownCls, + m_mii, m_mInd, m_iInd); + if (info.offset == -1) return info; + + auto baseType = m_base->type().derefIfPtr(); + always_assert_flog( + baseType < Type::Obj, + "Got property offset for base {} which isn't an object", + *m_base->inst() + ); + + auto baseCls = baseType.getClass(); + + // baseCls and knownCls may differ due to a number of factors but they must + // always be related to each other somehow. + always_assert_flog( + baseCls->classof(knownCls) || knownCls->classof(baseCls), + "Class mismatch between baseType({}) and knownCls({})", + baseCls->name()->data(), knownCls->name()->data() + ); + + if (m_irb.constrainValue(m_base, TypeConstraint(baseCls).setWeak())) { + // We can't use this specialized class without making a guard more + // expensive, so don't do it. + knownCls = nullptr; + return PropInfo{}; + } + return info; +} + void HhbcTranslator::MInstrTranslator::emitProp() { const Class* knownCls = nullptr; - const auto propInfo = getPropertyOffset(m_ni, contextClass(), - knownCls, m_mii, - m_mInd, m_iInd); + const auto propInfo = getCurrentPropertyOffset(knownCls); auto mia = m_mii.getAttr(m_ni.immVecM[m_mInd]); if (propInfo.offset == -1 || (mia & Unset) || mightCallMagicPropMethod(mia, knownCls, propInfo)) { @@ -1341,8 +1372,7 @@ HELPER_TABLE(PROP) void HhbcTranslator::MInstrTranslator::emitCGetProp() { const Class* knownCls = nullptr; - const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls, - m_mii, m_mInd, m_iInd); + const auto propInfo = getCurrentPropertyOffset(knownCls); if (propInfo.offset != -1 && !mightCallMagicPropMethod(None, knownCls, propInfo)) { @@ -1476,8 +1506,8 @@ void HhbcTranslator::MInstrTranslator::emitSetProp() { /* If we know the class for the current base, emit a direct property set. */ const Class* knownCls = nullptr; - const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls, - m_mii, m_mInd, m_iInd); + const auto propInfo = getCurrentPropertyOffset(knownCls); + if (propInfo.offset != -1 && !mightCallMagicPropMethod(Define, knownCls, propInfo)) { emitPropSpecialized(MIA_define, propInfo); @@ -2230,7 +2260,7 @@ void HhbcTranslator::MInstrTranslator::emitArraySet(SSATmp* key, bool checkForInt; m_ht.checkStrictlyInteger(key, keyType, checkForInt); const DynLocation& base = *m_ni.inputs[m_mii.valCount()]; - bool setRef = base.outerType() == KindOfRef; + bool setRef = base.rtt.isBoxed(); typedef ArrayData* (*OpFunc)(ArrayData*, TypedValue*, TypedValue, RefData*); BUILD_OPTAB(keyType, checkForInt, setRef); @@ -2729,11 +2759,11 @@ void HhbcTranslator::MInstrTranslator::prependToTraces(IRInstruction* inst) { } bool HhbcTranslator::MInstrTranslator::needFirstRatchet() const { - if (m_ni.inputs[m_mii.valCount()]->valueType() == KindOfArray) { + if (m_ni.inputs[m_mii.valCount()]->rtt.unbox() <= Type::Arr) { switch (m_ni.immVecM[0]) { - case MEC: case MEL: case MET: case MEI: return false; - case MPC: case MPL: case MPT: case MW: return true; - default: not_reached(); + case MEC: case MEL: case MET: case MEI: return false; + case MPC: case MPL: case MPT: case MW: return true; + default: not_reached(); } } return true; diff --git a/hphp/runtime/vm/jit/prof-data.h b/hphp/runtime/vm/jit/prof-data.h index 02b5e5320db..2f70fe752d7 100644 --- a/hphp/runtime/vm/jit/prof-data.h +++ b/hphp/runtime/vm/jit/prof-data.h @@ -25,7 +25,6 @@ #include "hphp/runtime/vm/func.h" #include "hphp/runtime/vm/srckey.h" #include "hphp/runtime/vm/jit/types.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/region-selection.h" namespace HPHP { namespace JIT { diff --git a/hphp/runtime/vm/jit/ref-deps.cpp b/hphp/runtime/vm/jit/ref-deps.cpp index 649e04c246b..3103881ac1a 100644 --- a/hphp/runtime/vm/jit/ref-deps.cpp +++ b/hphp/runtime/vm/jit/ref-deps.cpp @@ -25,6 +25,33 @@ namespace JIT { TRACE_SET_MOD(trans); +std::string RefDeps::Record::pretty() const { + std::ostringstream out; + out << "mask="; + for (size_t i = 0; i < m_mask.size(); ++i) { + out << (m_mask[i] ? "1" : "0"); + } + out << " vals="; + for (size_t i = 0; i < m_vals.size(); ++i) { + out << (m_vals[i] ? "1" : "0"); + } + return out.str(); +} + +void RefDeps::addDep(int entryArDelta, unsigned argNum, bool isRef) { + if (m_arMap.find(entryArDelta) == m_arMap.end()) { + m_arMap[entryArDelta] = Record(); + } + Record& r = m_arMap[entryArDelta]; + if (argNum >= r.m_mask.size()) { + assert(argNum >= r.m_vals.size()); + r.m_mask.resize(argNum + 1); + r.m_vals.resize(argNum + 1); + } + r.m_mask[argNum] = true; + r.m_vals[argNum] = isRef; +} + void ActRecState::pushFunc(const NormalizedInstruction& inst) { assert(isFPush(inst.op())); diff --git a/hphp/runtime/vm/jit/ref-deps.h b/hphp/runtime/vm/jit/ref-deps.h index fc45f9242b1..e69e731bc22 100644 --- a/hphp/runtime/vm/jit/ref-deps.h +++ b/hphp/runtime/vm/jit/ref-deps.h @@ -16,12 +16,16 @@ #ifndef incl_HPHP_TRACELET_H_ #define incl_HPHP_TRACELET_H_ -#include +#include #include -#include "hphp/runtime/vm/jit/runtime-type.h" +#include + +#include "hphp/util/hash-map-typedefs.h" namespace HPHP { +struct Func; + namespace JIT { struct NormalizedInstruction; @@ -31,41 +35,16 @@ struct RefDeps { std::vector m_mask; std::vector m_vals; - std::string pretty() const { - std::ostringstream out; - out << "mask="; - for (size_t i = 0; i < m_mask.size(); ++i) { - out << (m_mask[i] ? "1" : "0"); - } - out << " vals="; - for (size_t i = 0; i < m_vals.size(); ++i) { - out << (m_vals[i] ? "1" : "0"); - } - return out.str(); - } + std::string pretty() const; }; typedef hphp_hash_map ArMap; ArMap m_arMap; RefDeps() {} - void addDep(int entryArDelta, unsigned argNum, bool isRef) { - if (m_arMap.find(entryArDelta) == m_arMap.end()) { - m_arMap[entryArDelta] = Record(); - } - Record& r = m_arMap[entryArDelta]; - if (argNum >= r.m_mask.size()) { - assert(argNum >= r.m_vals.size()); - r.m_mask.resize(argNum + 1); - r.m_vals.resize(argNum + 1); - } - r.m_mask[argNum] = true; - r.m_vals[argNum] = isRef; - } - - size_t size() const { - return m_arMap.size(); - } + void addDep(int entryArDelta, unsigned argNum, bool isRef); + + size_t size() const { return m_arMap.size(); } }; struct ActRecState { diff --git a/hphp/runtime/vm/jit/region-tracelet.cpp b/hphp/runtime/vm/jit/region-tracelet.cpp index c2a7f757739..8a3400852a7 100644 --- a/hphp/runtime/vm/jit/region-tracelet.cpp +++ b/hphp/runtime/vm/jit/region-tracelet.cpp @@ -229,13 +229,16 @@ RegionDescPtr RegionFormer::go() { // Since the current instruction is over, advance HhbcTranslator's sk // before emitting the prediction (if any). - if (doPrediction && m_inst.outPred < m_ht.topType(0, DataTypeGeneric)) { + if (doPrediction && + m_ht.topType(0, DataTypeGeneric).maybe(m_inst.outPred)) { m_ht.setBcOff(m_sk.offset(), false); m_ht.checkTypeStack(0, m_inst.outPred, m_sk.offset()); } } - printUnit(2, m_ht.unit(), " after tracelet formation ", + printUnit(2, m_ht.unit(), + inliningDepth() ? " after inlining tracelet formation " + : " after tracelet formation ", nullptr, nullptr, m_ht.irBuilder().guards()); if (m_region && !m_region->blocks.empty()) { @@ -277,9 +280,9 @@ bool RegionFormer::prepareInstruction() { // Read types for all the inputs and apply MetaData. auto newDynLoc = [&](const InputInfo& ii) { - auto dl = m_inst.newDynLoc(ii.loc, m_ht.rttFromLocation(ii.loc)); - FTRACE(2, "rttFromLocation: {} -> {}\n", - ii.loc.pretty(), dl->rtt.pretty()); + auto dl = m_inst.newDynLoc(ii.loc, m_ht.typeFromLocation(ii.loc)); + FTRACE(2, "typeFromLocation: {} -> {}\n", + ii.loc.pretty(), dl->rtt); return dl; }; @@ -579,27 +582,27 @@ void RegionFormer::truncateLiterals() { */ bool RegionFormer::consumeInput(int i, const InputInfo& ii) { auto& rtt = m_inst.inputs[i]->rtt; - if (ii.dontGuard || !rtt.isValue()) return true; + if (ii.dontGuard) return true; - if (m_profiling && rtt.isRef() && + if (m_profiling && rtt.isBoxed() && (m_region->blocks.size() > 1 || !m_region->blocks[0]->empty())) { // We don't want side exits when profiling, so only allow instructions that // consume refs at the beginning of the region. return false; } - if (!ii.dontBreak && !Type(rtt).isKnownDataType()) { + if (!ii.dontBreak && !rtt.isKnownDataType()) { // Trying to consume a value without a precise enough type. FTRACE(1, "selectTracelet: {} tried to consume {}\n", m_inst.toString(), m_inst.inputs[i]->pretty()); return false; } - if (!rtt.isRef() || m_inst.ignoreInnerType || ii.dontGuardInner) { + if (!rtt.isBoxed() || m_inst.ignoreInnerType || ii.dontGuardInner) { return true; } - if (!Type(rtt.innerType()).isKnownDataType()) { + if (!rtt.innerType().isKnownDataType()) { // Trying to consume a boxed value without a guess for the inner type. FTRACE(1, "selectTracelet: {} tried to consume ref {}\n", m_inst.toString(), m_inst.inputs[i]->pretty()); diff --git a/hphp/runtime/vm/jit/runtime-type.cpp b/hphp/runtime/vm/jit/runtime-type.cpp deleted file mode 100644 index 12f2a090ed8..00000000000 --- a/hphp/runtime/vm/jit/runtime-type.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | HipHop for PHP | - +----------------------------------------------------------------------+ - | Copyright (c) 2010-2014 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. | - +----------------------------------------------------------------------+ -*/ -#include "hphp/runtime/vm/jit/runtime-type.h" - -#include -#include -#include -#include "hphp/runtime/vm/bytecode.h" -#include "hphp/runtime/base/types.h" -#include "hphp/runtime/vm/jit/translator.h" - -#define KindOfUnknown DontUseKindOfUnknownInThisFile -#define KindOfInvalid DontUseKindOfInvalidInThisFile - -namespace HPHP { -namespace JIT { - -static inline DataType -normalizeDataType(DataType dt) { - // The translator treats both KindOfStaticString and KindOfString - // identically, and uses translation-time IS_REFCOUNTED_TYPE checks - // to determine how to handle refcounting. This means that an old - // KindOfStaticstring translation can get reused with KindOfString - // values. Since we emit static checks regardless, just prevent - // KindOfStaticString from entering into the translator's awareness. - return dt == KindOfStaticString ? KindOfString : dt; -} - -void RuntimeType::init(DataType outer, - DataType inner /* = KindOfNone */, - const Class* klass /*= nullptr*/) { - m_value.outerType = outer; - m_value.innerType = inner; - m_value.klass = klass; - m_value.knownClass = nullptr; - consistencyCheck(); -} - -RuntimeType::RuntimeType(DataType outer, DataType inner /* = KindOfNone */, - const Class* klass /* = NULL */) - : m_kind(VALUE) { - init(normalizeDataType(outer), normalizeDataType(inner), klass); -} - -RuntimeType::RuntimeType(const StringData* sd) - : m_kind(VALUE) { - init(KindOfString); - m_value.string = sd; -} - -RuntimeType::RuntimeType(const ArrayData* ad) - : m_kind(VALUE) { - init(KindOfArray); - m_value.array = ad; -} - -RuntimeType::RuntimeType(bool value) - : m_kind(VALUE) { - init(KindOfBoolean); - m_value.boolean = value; - m_value.boolValid = true; -} - -RuntimeType::RuntimeType(int64_t value) - : m_kind(VALUE) { - init(KindOfInt64); - m_value.intval = value; -} - -RuntimeType::RuntimeType(const Class* klass) - : m_kind(VALUE) { - init(KindOfClass, KindOfNone, klass); -} - -RuntimeType::RuntimeType() : - m_kind(VALUE) { - init(KindOfNone); -} - -RuntimeType::RuntimeType(const Iter* it) : - m_kind(ITER) { -} - -RuntimeType::RuntimeType(ArrayIter::Type type) : - m_kind(ITER) { -} - -RuntimeType RuntimeType::box() const { - assert(m_kind == VALUE); - if (m_value.outerType == KindOfRef) { - consistencyCheck(); - return *this; - } - RuntimeType rtt(KindOfRef, m_value.outerType); - return rtt; -} - -RuntimeType RuntimeType::unbox() const { - assert(m_kind == VALUE); - if (m_value.outerType != KindOfRef) { - consistencyCheck(); - return *this; - } - RuntimeType rtt(m_value.innerType); - return rtt; -} - -DataType RuntimeType::valueType() const { - assert(m_kind != ITER); - if (outerType() == KindOfRef) { - return m_value.innerType; - } - return m_value.outerType; -} - -const Class* -RuntimeType::valueClass() const { - consistencyCheck(); - assert(m_kind != ITER); - assert(valueType() == KindOfObject || valueType() == KindOfClass); - return m_value.klass; -} - -const StringData* -RuntimeType::valueString() const { - consistencyCheck(); - assert(m_kind != ITER); - assert(isString()); - return m_value.string; -} - -const StringData* -RuntimeType::valueStringOrNull() const { - if (!isString()) return nullptr; - return valueString(); -} - -const ArrayData* -RuntimeType::valueArray() const { - consistencyCheck(); - assert(m_kind != ITER); - assert(isArray()); - return m_value.array; -} - -// -1 for unknown, 0 for false, 1 for true -int -RuntimeType::valueBoolean() const { - consistencyCheck(); - assert(m_kind != ITER); - assert(isBoolean()); - return m_value.boolValid ? m_value.boolean : -1; -} - -int64_t -RuntimeType::valueInt() const { - consistencyCheck(); - assert(m_kind == VALUE); - assert(isInt()); - return m_value.intval; -} - -// Get the value as a blob. Use with care. -int64_t -RuntimeType::valueGeneric() const { - consistencyCheck(); - assert(m_kind == VALUE); - return m_value.intval; -} - -const Class* -RuntimeType::knownClass() const { - consistencyCheck(); - assert(hasKnownClass()); - return m_value.knownClass; -} - -bool -RuntimeType::hasArrayKind() const { - consistencyCheck(); - return m_value.arrayKindValid; -} - -ArrayData::ArrayKind -RuntimeType::arrayKind() const { - consistencyCheck(); - assert(hasArrayKind()); - return m_value.arrayKind; -} - -RuntimeType -RuntimeType::setValueType(DataType newInner) const { - assert(m_kind == VALUE); - if (outerType() == KindOfRef) { - RuntimeType rtt(KindOfRef, newInner); - assert(rtt.valueType() == newInner); - return rtt; - } - RuntimeType rtt(newInner); - assert(rtt.valueType() == newInner); - return rtt; -} - -RuntimeType -RuntimeType::setKnownClass(const Class* klass) const { - assert(isObject() || (isRef() && innerType() == KindOfObject)); - RuntimeType rtt(outerType(), innerType(), m_value.klass); - rtt.m_kind = this->m_kind; - rtt.m_value.knownClass = klass; - rtt.consistencyCheck(); - return rtt; -} - -RuntimeType -RuntimeType::setArrayKind(ArrayData::ArrayKind arrayKind) const { - assert(isArray() || (isRef() && innerType() == KindOfArray)); - RuntimeType rtt; - rtt.m_kind = this->m_kind; - rtt.m_value.outerType = outerType(); - rtt.m_value.innerType = innerType(); - rtt.m_value.arrayKindValid = true; - rtt.m_value.arrayKind = arrayKind; - rtt.consistencyCheck(); - return rtt; -} - -// Accessors -DataType RuntimeType::outerType() const { - consistencyCheck(); - assert(m_kind == VALUE); - return m_value.outerType; -} - -DataType RuntimeType::innerType() const { - consistencyCheck(); - assert(m_kind == VALUE); - return m_value.innerType; -} - -bool RuntimeType::isValue() const { - consistencyCheck(); - return m_kind == VALUE; -} - -DataType RuntimeType::typeCheckValue() const { - if (isIter()) return (DataType)0; - return outerType(); -} - -bool RuntimeType::isIter() const { - consistencyCheck(); - return m_kind == ITER; -} - -bool RuntimeType::isRef() const { - assert(m_kind == VALUE); - return outerType() == KindOfRef; -} - -bool RuntimeType::isVagueValue() const { - assert(IMPLIES(m_kind == VALUE, outerType() != KindOfNone)); - return m_kind == VALUE && outerType() == KindOfAny; -} - -bool RuntimeType::isRefCounted() const { - return isValue() && IS_REFCOUNTED_TYPE(outerType()); -} - -bool RuntimeType::isUninit() const { - return isValue() && outerType() == KindOfUninit; -} - -bool RuntimeType::isNull() const { - return isValue() && IS_NULL_TYPE(outerType()); -} - -bool RuntimeType::isInt() const { - return isValue() && IS_INT_TYPE(outerType()); -} - -bool RuntimeType::isDouble() const { - return isValue() && IS_DOUBLE_TYPE(outerType()); -} - -bool RuntimeType::isBoolean() const { - return isValue() && outerType() == KindOfBoolean; -} - -bool RuntimeType::isString() const { - return isValue() && IS_STRING_TYPE(outerType()); -} - -bool RuntimeType::isObject() const { - return isValue() && outerType() == KindOfObject; -} - -bool RuntimeType::isClass() const { - return isValue() && outerType() == KindOfClass; -} - -bool RuntimeType::hasKnownClass() const { - return isObject() && m_value.knownClass != nullptr; -} - -bool RuntimeType::isArray() const { - return isValue() && outerType() == KindOfArray; -} - -bool RuntimeType::operator==(const RuntimeType& r) const { - consistencyCheck(); - if (m_kind != r.m_kind) { - return false; - } - switch (m_kind) { - case ITER: - return true; - case VALUE: - return r.m_value.innerType == m_value.innerType && - r.m_value.outerType == m_value.outerType && - // punning through unions to compare a few things here: - r.m_value.klass == m_value.klass && - r.m_value.knownClass == m_value.knownClass; - } - not_reached(); -} - -size_t -RuntimeType::operator()(const RuntimeType& r) const { - uint64_t p1 = HPHP::hash_int64(m_kind); - uint64_t p2 = 0; - // We can't just hash the whole blob of memory, because - // C++ will leave padding uninitialized. The shifts are to - // make the final hash order-dependent, so that - // { field1: 0, field2: 1 } - // has a different hash than - // { field1: 1, field2: 0 } - switch(m_kind) { - case ITER: - p2 = 0; - break; - case VALUE: - p2 = HPHP::hash_int64_pair(uintptr_t(m_value.klass), - HPHP::hash_int64_pair(m_value.outerType, - m_value.innerType)); - break; - } - return p1 ^ (p2 << 1); -} - -using std::string; - -string RuntimeType::pretty() const { - char buf[1024]; - if (isIter()) { - sprintf(buf, "(Iter)"); - return std::string(buf); - } - if (m_value.outerType == KindOfRef) { - sprintf(buf, "(Value (Var %s))", tname(m_value.innerType).c_str()); - } else { - sprintf(buf, "(Value %s)", tname(m_value.outerType).c_str()); - } - string retval = buf; - if (valueType() == KindOfObject) { - if (valueClass() != nullptr) { - folly::format(&retval, - "(OfClass {})", - valueClass()->name()->data()); - } else if (hasKnownClass()) { - folly::format(&retval, - "(Known Class {})", - knownClass()->name()->data()); - } - } - if (isClass() && valueClass() != nullptr) { - folly::format(&retval, - "(Class {})", - valueClass()->name()->data()); - } - if (isArray() && hasArrayKind()) { - folly::format(&retval, - "(Kind {})", - ArrayData::kindToString(arrayKind())); - } - if (isBoolean() && valueBoolean() != -1) { - folly::format(&retval, "(Val {})", valueBoolean()); - } - return retval; -} - -std::string Location::pretty() const { - char buf[1024]; - sprintf(buf, "(Location %s %" PRId64 ")", spaceName(), offset); - return std::string(buf); -} - -} } diff --git a/hphp/runtime/vm/jit/runtime-type.h b/hphp/runtime/vm/jit/runtime-type.h deleted file mode 100644 index 2e2d0d94929..00000000000 --- a/hphp/runtime/vm/jit/runtime-type.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | HipHop for PHP | - +----------------------------------------------------------------------+ - | Copyright (c) 2010-2014 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_RUNTIME_TYPE_H_ -#define incl_HPHP_RUNTIME_TYPE_H_ - -#include "hphp/util/safe-cast.h" -#include - -#include "hphp/runtime/vm/bytecode.h" -#include "hphp/runtime/vm/jit/region-selection.h" - -namespace HPHP { -namespace JIT { - -// Location -- -// A user-program-visible, and bytecode ISA addressable place for a PHP -// value. -struct Location { - enum Space { - Invalid, // Unknown location - Stack, // Stack; offset == delta from top - Local, // Stack frame's registers; offset == local register - Iter, // Stack frame's iterators - Litstr, // Literal string pseudo-location - Litint, // Literal int pseudo-location - This, // $this in the current frame - }; - - explicit Location(Space spc) - : space(spc) - , offset(0) - { - assert(spc == This); - } - - Location(Space spc, int64_t off) - : space(spc) - , offset(off) - {} - - Location() : space(Invalid), offset(-1) {} - - int cmp(const Location &r) const { -#define CMP(field) do { \ - if (field > r.field) { return 1; } \ - else if (field < r.field) { return -1; } } while(0) - CMP(space); - CMP(offset); -#undef CMP - return 0; - } - - bool operator==(const Location& r) const { - return cmp(r) == 0; - } - - bool operator!=(const Location& r) const { - return cmp(r) != 0; - } - - bool operator<(const Location& r) const { - return cmp(r) < 0; - } - - // Hash function. - size_t operator()(const Location& l) const { - return HPHP::hash_int64_pair(l.space, l.offset); - } - - const char *spaceName() const { - switch(space) { - case Stack: return "Stk"; - case Local: return "Local"; - case Iter: return "Iter"; - case Litstr: return "Litstr"; - case Litint: return "Litint"; - case This: return "This"; - case Invalid:return "*invalid*"; - default: not_reached(); - } - } - - std::string pretty() const; - - bool isStack() const { - return space == Stack; - } - - bool isLocal() const { - return space == Local; - } - - bool isInvalid() const { - return space == Invalid; - } - - bool isValid() const { - return !isInvalid(); - } - - bool isLiteral() const { - return space == Litstr || space == Litint; - } - - bool isThis() const { - return space == This; - } - - bool isIter() const { - return space == Iter; - } - - JIT::RegionDesc::Location toLocation(Offset spOffsetFromFp) const { - typedef JIT::RegionDesc::Location L; - switch (space) { - case Stack: { - auto offsetFromSp = safe_cast(offset); - return L::Stack{offsetFromSp, spOffsetFromFp - offsetFromSp}; - } - case Local: return L::Local{safe_cast(offset)}; - default: not_reached(); - } - } - -public: - Space space; - int64_t offset; -}; - -struct InputInfo { - explicit InputInfo(const Location &l) - : loc(l) - , dontBreak(false) - , dontGuard(l.isLiteral()) - , dontGuardInner(false) - {} - - std::string pretty() const { - std::string p = loc.pretty(); - if (dontBreak) p += ":dc"; - if (dontGuard) p += ":dg"; - if (dontGuardInner) p += ":dgi"; - return p; - } - Location loc; - /* - * if an input is unknowable, dont break the tracelet - * just to find its type. But still generate a guard - * if that will tell us its type. - */ - bool dontBreak; - /* - * never break the tracelet, or generate a guard on - * account of this input. - */ - bool dontGuard; - /* - * never guard the inner type if this input is KindOfRef - */ - bool dontGuardInner; -}; - -class InputInfos : public std::vector { - public: - InputInfos() : needsRefCheck(false) {} - - std::string pretty() const { - std::string retval; - for (size_t i = 0; i < size(); i++) { - retval += (*this)[i].pretty(); - if (i != size() - 1) { - retval += std::string(" "); - } - } - return retval; - } - bool needsRefCheck; -}; - -// RuntimeType -- -// -// Symbolic description of a root location in the runtime: e.g., a stack, -// local, global, etc. -class RuntimeType { - enum Kind { - VALUE, - ITER - } m_kind; - union { - struct { - DataType outerType; - DataType innerType; - // Set when we want to transfer the type information to the - // IR type system (Type object) - union { - const Class* knownClass; - struct { - bool arrayKindValid; - ArrayData::ArrayKind arrayKind; - uint32_t emptyPadding; - }; - }; - union { - // We may have even more precise data about this set of values. - const StringData* string; // KindOfString: The exact value. - const ArrayData* array; // KindOfArray: The exact value. - const Class* klass; // KindOfObject: A known super-class. - // KindOfClass: An instance of the current - // instantiation of the preClass. The - // exact class may differ across executions - int64_t intval; // KindOfInt64: A literal int - struct { - bool boolean; // KindOfBoolean: A literal bool - // from True or False. - bool boolValid; - }; - }; - } m_value; - }; - - inline void consistencyCheck() const { - assert(m_kind == VALUE || m_kind == ITER); - if (m_kind == VALUE) { - assert(m_value.innerType != KindOfRef); - assert((m_value.outerType == KindOfRef) == - (m_value.innerType != KindOfNone)); - assert(m_value.outerType == KindOfString || - m_value.innerType == KindOfString || - m_value.outerType == KindOfClass || - m_value.innerType == KindOfClass || - m_value.outerType == KindOfObject || - m_value.innerType == KindOfObject || - m_value.outerType == KindOfResource || - m_value.innerType == KindOfResource || - m_value.outerType == KindOfArray || - m_value.innerType == KindOfArray || - m_value.outerType == KindOfBoolean || - m_value.outerType == KindOfInt64 || - m_value.klass == nullptr); - assert(m_value.innerType != KindOfStaticString && - m_value.outerType != KindOfStaticString); - assert((m_value.knownClass == nullptr || - m_value.outerType == KindOfObject || - (m_value.outerType == KindOfRef && - m_value.innerType == KindOfObject)) || - (!m_value.arrayKindValid || - m_value.outerType == KindOfArray || - (m_value.outerType == KindOfRef && - m_value.innerType == KindOfArray))); - } - } - - void init(DataType outer, - DataType inner = KindOfNone, - const Class* klass = nullptr); - - public: - explicit RuntimeType(DataType outer, DataType inner = KindOfNone, - const Class* = nullptr); - explicit RuntimeType(const StringData*); - explicit RuntimeType(const ArrayData*); - explicit RuntimeType(const Class*); - explicit RuntimeType(bool value); - explicit RuntimeType(int64_t value); - RuntimeType(const RuntimeType& copy) = default; - RuntimeType(); - explicit RuntimeType(const Iter* iter); - explicit RuntimeType(ArrayIter::Type type); - - static const int UnknownBool = -1; - - // Specializers - RuntimeType box() const; - RuntimeType unbox() const; - RuntimeType setValueType(DataType vt) const; - RuntimeType setKnownClass(const Class* klass) const; - RuntimeType setArrayKind(ArrayData::ArrayKind arrayKind) const; - - // Accessors - DataType outerType() const; - DataType innerType() const; - DataType valueType() const; - const Class* valueClass() const; - const StringData* valueString() const; - const StringData* valueStringOrNull() const; - const ArrayData* valueArray() const; - int valueBoolean() const; - int64_t valueInt() const; - int64_t valueGeneric() const; - const Class* knownClass() const; - bool hasArrayKind() const; - ArrayData::ArrayKind arrayKind() const; - - // Helpers for typechecking - DataType typeCheckValue() const; - - bool isValue() const; - bool isIter() const; - - bool isVagueValue() const; - bool isRef() const; - - bool isRefCounted() const; - bool isUninit() const; - bool isNull() const; - bool isInt() const; - bool isDouble() const; - bool isArray() const; - bool isBoolean() const; - bool isString() const; - bool isObject() const; - bool isClass() const; - bool hasKnownClass() const; - bool operator==(const RuntimeType& r) const; - RuntimeType &operator=(const RuntimeType& r) = default; - size_t operator()(const RuntimeType& r) const; // hash function - std::string pretty() const; -}; - -} } - -#endif // incl_HPHP_RUNTIME_TYPE_H_ diff --git a/hphp/runtime/vm/jit/test/type.cpp b/hphp/runtime/vm/jit/test/type.cpp index a9f002a1585..5bad9a9c75a 100644 --- a/hphp/runtime/vm/jit/test/type.cpp +++ b/hphp/runtime/vm/jit/test/type.cpp @@ -139,50 +139,6 @@ TEST(Type, Subtypes) { EXPECT_TRUE(Type::PtrToCell.strictSubtypeOf(Type::PtrToGen)); } -TEST(Type, RuntimeType) { - auto sd = StringData::MakeMalloced("", 0); - SCOPE_EXIT { sd->destruct(); }; - - HPHP::JIT::RuntimeType rt(sd); - Type t = Type(rt); - EXPECT_TRUE(t.subtypeOf(Type::Str)); - EXPECT_FALSE(t.subtypeOf(Type::Int)); - - rt = HPHP::JIT::RuntimeType(staticEmptyArray()); - t = Type(rt); - EXPECT_TRUE(t.subtypeOf(Type::Arr)); - EXPECT_FALSE(t.subtypeOf(Type::Str)); - - rt = HPHP::JIT::RuntimeType(true); - t = Type(rt); - EXPECT_TRUE(t.subtypeOf(Type::Bool)); - EXPECT_FALSE(t.subtypeOf(Type::Obj)); - - rt = HPHP::JIT::RuntimeType((int64_t) 1); - t = Type(rt); - EXPECT_TRUE(t.subtypeOf(Type::Int)); - EXPECT_FALSE(t.subtypeOf(Type::Dbl)); - - rt = HPHP::JIT::RuntimeType(DataType::KindOfObject, - DataType::KindOfInvalid); - rt = rt.setKnownClass(SystemLib::s_TraversableClass); - t = Type(rt); - EXPECT_TRUE(t.subtypeOf(Type::Obj)); - EXPECT_FALSE(Type::Obj.subtypeOf(t)); - EXPECT_FALSE(Type::Int.subtypeOf(t)); - HPHP::JIT::RuntimeType rt1 = - HPHP::JIT::RuntimeType(DataType::KindOfObject, - DataType::KindOfInvalid); - rt1 = rt1.setKnownClass(SystemLib::s_IteratorClass); - Type t1 = Type(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/jit/translator.cpp b/hphp/runtime/vm/jit/translator.cpp index c84ec755c69..e946d6d40af 100644 --- a/hphp/runtime/vm/jit/translator.cpp +++ b/hphp/runtime/vm/jit/translator.cpp @@ -111,19 +111,6 @@ static uint32_t get_random() static const int kTooPolyRet = 6; -bool -isNormalPropertyAccess(const NormalizedInstruction& i, - int propInput, - int objInput) { - const LocationCode lcode = i.immVec.locationCode(); - return - i.immVecM.size() == 1 && - (lcode == LC || lcode == LL || lcode == LR || lcode == LH) && - mcodeIsProp(i.immVecM[0]) && - i.inputs[propInput]->isString() && - i.inputs[objInput]->valueType() == KindOfObject; -} - PropInfo getPropertyOffset(const NormalizedInstruction& ni, Class* ctx, const Class*& baseClass, @@ -131,17 +118,15 @@ PropInfo getPropertyOffset(const NormalizedInstruction& ni, unsigned mInd, unsigned iInd) { if (mInd == 0) { auto const baseIndex = mii.valCount(); - baseClass = ni.inputs[baseIndex]->rtt.isObject() - ? ni.inputs[baseIndex]->rtt.valueClass() + baseClass = ni.inputs[baseIndex]->rtt < Type::Obj + ? ni.inputs[baseIndex]->rtt.getClass() : nullptr; } if (!baseClass) return PropInfo(); - if (!ni.inputs[iInd]->rtt.isString()) { - return PropInfo(); - } - auto* const name = ni.inputs[iInd]->rtt.valueString(); - if (!name) return PropInfo(); + auto keyType = ni.inputs[iInd]->rtt; + if (!keyType.isConst(Type::Str)) return PropInfo(); + auto const name = keyType.strVal(); bool accessible; // If we are not in repo-authoriative mode, we need to check that @@ -327,15 +312,15 @@ predictOutputs(const NormalizedInstruction* ni) { // anything ** double => double // double ** anything => double // anything ** anything => int - auto lhs = ni->inputs[0]; - auto rhs = ni->inputs[1]; + auto lhs = ni->inputs[0]->rtt; + auto rhs = ni->inputs[1]->rtt; - if (lhs->valueType() == KindOfInt64 && rhs->valueType() == KindOfInt64) { + if (lhs <= Type::Int && rhs <= Type::Int) { // Best guess, since overflowing isn't common return KindOfInt64; } - if (lhs->valueType() == KindOfDouble || rhs->valueType() == KindOfDouble) { + if (lhs <= Type::Dbl || rhs <= Type::Dbl) { return KindOfDouble; } @@ -351,33 +336,16 @@ predictOutputs(const NormalizedInstruction* ni) { // Integers can produce integers if there's no residue, but $i / $j in // general produces a double. $i / 0 produces boolean false, so we have // actually check the result. - auto lhs = ni->inputs[0]; - auto rhs = ni->inputs[1]; - - if (lhs->valueType() == KindOfDouble || rhs->valueType() == KindOfDouble) { - return KindOfDouble; - } - - if (rhs->isLiteral()) { - if (ni->imm[1].u_I64A == 0) return KindOfBoolean; - if (ni->imm[1].u_I64A == 1) return lhs->valueType(); - - if (rhs->isLiteral()) { - return ni->imm[0].u_I64A % ni->imm[1].u_I64A ? KindOfDouble - : KindOfInt64; - } - } - return KindOfDouble; } if (ni->op() == OpAbs) { - if (ni->inputs[0]->valueType() == KindOfDouble) { + if (ni->inputs[0]->rtt <= Type::Dbl) { return KindOfDouble; } // some types can't be converted to integers and will return false here - if (ni->inputs[0]->valueType() == KindOfArray) { + if (ni->inputs[0]->rtt <= Type::Arr) { return KindOfBoolean; } @@ -411,7 +379,9 @@ predictOutputs(const NormalizedInstruction* ni) { * since MInstrTranslator side exits in all uncommon cases. */ - auto const inDt = ni->inputs[0]->rtt.valueType(); + auto inType = ni->inputs[0]->rtt; + auto const inDt = inType.isKnownDataType() ? inType.toDataType() + : KindOfAny; // If the base is a string, the output is probably a string. Unless the // member code is MW, then we're either going to fatal or promote the // string to an array. @@ -424,7 +394,7 @@ predictOutputs(const NormalizedInstruction* ni) { break; default: - baseType = Type(ni->inputs[1]->rtt); + baseType = ni->inputs[1]->rtt; } if (baseType <= Type::Str && ni->immVecM.size() == 1) { return ni->immVecM[0] == MW ? inDt : KindOfString; @@ -438,8 +408,9 @@ predictOutputs(const NormalizedInstruction* ni) { static const double kAccept = 1.0; std::pair pred = std::make_pair(KindOfAny, 0.0); if (op == OpCGetS) { - const StringData* propName = ni->inputs[1]->rtt.valueStringOrNull(); - if (propName) { + auto nameType = ni->inputs[1]->rtt; + if (nameType.isConst(Type::Str)) { + auto propName = nameType.strVal(); pred = predictType(TypeProfileKey(TypeProfileKey::StaticPropName, propName)); TRACE(1, "prediction for static fields named %s: %d, %f\n", @@ -1317,11 +1288,6 @@ bool outputDependsOnInput(const Op instr) { not_reached(); } -bool DynLocation::canBeAliased() const { - return isValue() && - ((Translator::liveFrameIsPseudoMain() && isLocal()) || isRef()); -} - const StaticString s_http_response_header("http_response_header"); const StaticString s_php_errormsg("php_errormsg"); const StaticString s_extract("extract"); @@ -1801,9 +1767,9 @@ Translator::translateRegion(const RegionDesc& region, std::vector dynLocs; dynLocs.reserve(inputInfos.size()); auto newDynLoc = [&](const InputInfo& ii) { - dynLocs.emplace_back(ii.loc, ht.rttFromLocation(ii.loc)); - FTRACE(2, "rttFromLocation: {} -> {}\n", - ii.loc.pretty(), dynLocs.back().rtt.pretty()); + dynLocs.emplace_back(ii.loc, ht.typeFromLocation(ii.loc)); + FTRACE(2, "typeFromLocation: {} -> {}\n", + ii.loc.pretty(), dynLocs.back().rtt); return &dynLocs.back(); }; FTRACE(2, "populating inputs for {}\n", inst.toString()); @@ -1933,7 +1899,7 @@ Translator::translateRegion(const RegionDesc& region, // Check the prediction. If the predicted type is less specific than what // is currently on the eval stack, checkType won't emit any code. - if (doPrediction && inst.outPred < ht.topType(0, DataTypeGeneric)) { + if (doPrediction && ht.topType(0, DataTypeGeneric).maybe(inst.outPred)) { ht.checkTypeStack(0, inst.outPred, sk.advanced(block->unit()).offset()); } diff --git a/hphp/runtime/vm/jit/translator.h b/hphp/runtime/vm/jit/translator.h index b5456edec84..fe8811145dc 100644 --- a/hphp/runtime/vm/jit/translator.h +++ b/hphp/runtime/vm/jit/translator.h @@ -34,12 +34,13 @@ #include "hphp/runtime/vm/bytecode.h" #include "hphp/runtime/vm/jit/block.h" #include "hphp/runtime/vm/jit/fixup.h" +#include "hphp/runtime/vm/jit/location.h" #include "hphp/runtime/vm/jit/prof-data.h" #include "hphp/runtime/vm/jit/prof-src-key.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/srcdb.h" #include "hphp/runtime/vm/jit/trans-rec.h" #include "hphp/runtime/vm/jit/translator-instrs.h" +#include "hphp/runtime/vm/jit/type.h" #include "hphp/runtime/vm/jit/unique-stubs.h" #include "hphp/runtime/vm/jit/write-lease.h" #include "hphp/runtime/vm/debugger-hook.h" @@ -61,89 +62,6 @@ static const uint32_t transCountersPerChunk = 1024 * 1024 / 8; struct NormalizedInstruction; -// A DynLocation is a Location-in-execution: a location, along with -// whatever is known about its runtime type. -struct DynLocation { - Location location; - RuntimeType rtt; - - DynLocation(Location l, DataType t) : location(l), rtt(t) {} - - DynLocation(Location l, RuntimeType t) : location(l), rtt(t) {} - - DynLocation() : location(), rtt() {} - - bool operator==(const DynLocation& r) const = delete; - - // Hash function - size_t operator()(const DynLocation &dl) const { - uint64_t rtthash = rtt(rtt); - uint64_t locHash = location(location); - return rtthash ^ locHash; - } - - std::string pretty() const { - return Trace::prettyNode("DynLocation", location, rtt); - } - - // Punch through a bunch of frequently called rtt and location methods. - // While this is unlovely here, we use DynLocation in bazillions of - // places in the translator, and constantly saying ".rtt" is worse. - bool isString() const { - return rtt.isString(); - } - bool isInt() const { - return rtt.isInt(); - } - bool isDouble() const { - return rtt.isDouble(); - } - bool isBoolean() const { - return rtt.isBoolean(); - } - bool isRef() const { - return rtt.isRef(); - } - bool isRefToObject() const { - return rtt.isRef() && innerType() == KindOfObject; - } - bool isValue() const { - return rtt.isValue(); - } - bool isNull() const { - return rtt.isNull(); - } - bool isObject() const { - return rtt.isObject(); - } - bool isArray() const { - return rtt.isArray(); - } - DataType valueType() const { - return rtt.valueType(); - } - DataType innerType() const { - return rtt.innerType(); - } - DataType outerType() const { - return rtt.outerType(); - } - - bool isStack() const { - return location.isStack(); - } - bool isLocal() const { - return location.isLocal(); - } - bool isLiteral() const { - return location.isLiteral(); - } - - // Uses the runtime state. True if this dynLocation can be overwritten by - // SetG's and SetM's. - bool canBeAliased() const; -}; - struct TranslationFailedExc : std::runtime_error { TranslationFailedExc(const char* file, int line) : std::runtime_error(folly::format("TranslationFailedExc @ {}:{}", @@ -569,6 +487,56 @@ void populateImmediates(NormalizedInstruction&); bool instrMustInterp(const NormalizedInstruction&); bool isAlwaysNop(Op op); +struct InputInfo { + explicit InputInfo(const Location &l) + : loc(l) + , dontBreak(false) + , dontGuard(l.isLiteral()) + , dontGuardInner(false) + {} + + std::string pretty() const { + std::string p = loc.pretty(); + if (dontBreak) p += ":dc"; + if (dontGuard) p += ":dg"; + if (dontGuardInner) p += ":dgi"; + return p; + } + Location loc; + /* + * if an input is unknowable, dont break the tracelet + * just to find its type. But still generate a guard + * if that will tell us its type. + */ + bool dontBreak; + /* + * never break the tracelet, or generate a guard on + * account of this input. + */ + bool dontGuard; + /* + * never guard the inner type if this input is KindOfRef + */ + bool dontGuardInner; +}; + +class InputInfos : public std::vector { + public: + InputInfos() : needsRefCheck(false) {} + + std::string pretty() const { + std::string retval; + for (size_t i = 0; i < size(); i++) { + retval += (*this)[i].pretty(); + if (i != size() - 1) { + retval += std::string(" "); + } + } + return retval; + } + bool needsRefCheck; +}; + typedef std::function LocalTypeFn; void getInputs(SrcKey startSk, NormalizedInstruction& inst, InputInfos& infos, const Func* func, const LocalTypeFn& localType); @@ -580,6 +548,28 @@ bool callDestroysLocals(const NormalizedInstruction& inst, const Func* caller); int locPhysicalOffset(Location l, const Func* f = nullptr); +struct PropInfo { + PropInfo() + : offset(-1) + , repoAuthType{} + {} + explicit PropInfo(int offset, RepoAuthType repoAuthType) + : offset(offset) + , repoAuthType{repoAuthType} + {} + + int offset; + RepoAuthType repoAuthType; +}; +PropInfo getPropertyOffset(const NormalizedInstruction& ni, + Class* contextClass, + const Class*& baseClass, + const MInstrInfo& mii, + unsigned mInd, unsigned iInd); +PropInfo getFinalPropertyOffset(const NormalizedInstruction&, + Class* contextClass, + const MInstrInfo&); + namespace InstrFlags { enum OutTypeConstraints { OutNull, diff --git a/hphp/runtime/vm/jit/type.cpp b/hphp/runtime/vm/jit/type.cpp index f48a0879c2b..9e98f171d3f 100644 --- a/hphp/runtime/vm/jit/type.cpp +++ b/hphp/runtime/vm/jit/type.cpp @@ -29,7 +29,6 @@ #include "hphp/runtime/base/repo-auth-type-array.h" #include "hphp/runtime/vm/jit/ir.h" #include "hphp/runtime/vm/jit/ir-instruction.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/ssa-tmp.h" #include "hphp/runtime/vm/jit/translator.h" @@ -205,44 +204,6 @@ DataType Type::toDataType() const { "Bad Type {} in Type::toDataType()", *this); } -RuntimeType Type::toRuntimeType() const { - assert(!isPtr()); - auto fuzzyDataType = [](Type t) { - if (t.isKnownDataType()) return t.toDataType(); - if (t <= UncountedInit) return KindOfUncountedInit; - if (t <= Uncounted) return KindOfUncounted; - if (t <= Gen) return KindOfAny; - always_assert(false); - }; - - auto const outer = isBoxed() ? KindOfRef : fuzzyDataType(*this); - auto const inner = isBoxed() ? fuzzyDataType(innerType()) : KindOfNone; - auto rtt = RuntimeType{outer, inner}; - - if (isSpecialized()) { - if (subtypeOf(Type::Arr)) { - return hasArrayKind() ? rtt.setArrayKind(getArrayKind()) : rtt; - } - if (subtypeOf(Type::Obj)) { - return rtt.setKnownClass(getClass()); - } - } - - return rtt; -} - -Type::Type(const RuntimeType& rtt) - : m_bits(bitsFromDataType(rtt.outerType(), rtt.innerType())) - , m_hasConstVal(false) - , m_class(nullptr) -{ - if (rtt.outerType() == KindOfObject && rtt.hasKnownClass()) { - m_class = rtt.knownClass(); - } else if (rtt.outerType() == KindOfArray && rtt.hasArrayKind()) { - m_arrayInfo = makeArrayInfo(rtt.arrayKind(), nullptr); - } -} - Type::Type(const DynLocation* dl) : Type(dl->rtt) {} diff --git a/hphp/runtime/vm/jit/type.h b/hphp/runtime/vm/jit/type.h index 15877b08fdf..c76abeda522 100644 --- a/hphp/runtime/vm/jit/type.h +++ b/hphp/runtime/vm/jit/type.h @@ -37,9 +37,6 @@ struct Func; namespace JIT { struct DynLocation; -struct RuntimeType; -} -namespace JIT { namespace constToBits_detail { template @@ -287,7 +284,6 @@ public: , m_extra(0) {} - explicit Type(const RuntimeType& rtt); explicit Type(const DynLocation* dl); size_t hash() const { @@ -785,8 +781,6 @@ public: * pre: isKnownDataType() */ DataType toDataType() const; - - RuntimeType toRuntimeType() const; }; typedef folly::Optional OptType; @@ -864,9 +858,13 @@ struct TypeConstraint { static constexpr uint8_t kWantArrayKind = 0x1; + bool isSpecialized() const { + return category == DataTypeSpecialized || innerCat == DataTypeSpecialized; + } + TypeConstraint& setWantArrayKind() { assert(!wantClass()); - assert(category == DataTypeSpecialized); + assert(isSpecialized()); m_specialized |= kWantArrayKind; return *this; } @@ -876,7 +874,7 @@ struct TypeConstraint { TypeConstraint& setDesiredClass(const Class* cls) { assert(m_specialized == 0 || desiredClass()->classof(cls) || cls->classof(desiredClass())); - assert(category == DataTypeSpecialized || innerCat == DataTypeSpecialized); + assert(isSpecialized()); m_specialized = reinterpret_cast(cls); assert(wantClass()); return *this; diff --git a/hphp/runtime/vm/jit/unwind-x64.cpp b/hphp/runtime/vm/jit/unwind-x64.cpp index 407b2c8886f..00ffb90e2f1 100644 --- a/hphp/runtime/vm/jit/unwind-x64.cpp +++ b/hphp/runtime/vm/jit/unwind-x64.cpp @@ -23,7 +23,6 @@ #include "hphp/util/abi-cxx.h" #include "hphp/runtime/vm/jit/abi-x64.h" -#include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/base/rds.h" #include "hphp/runtime/vm/jit/mc-generator.h" #include "hphp/runtime/base/stats.h" diff --git a/hphp/runtime/vm/jit/unwind-x64.h b/hphp/runtime/vm/jit/unwind-x64.h index f48fe148381..50741aedb7e 100644 --- a/hphp/runtime/vm/jit/unwind-x64.h +++ b/hphp/runtime/vm/jit/unwind-x64.h @@ -25,12 +25,13 @@ #include #include -#include "hphp/util/assertions.h" +#include "hphp/runtime/base/rds.h" +#include "hphp/runtime/base/typed-value.h" #include "hphp/runtime/base/types.h" #include "hphp/runtime/vm/jit/types.h" #include "hphp/runtime/vm/tread-hash-map.h" #include "hphp/util/asm-x64.h" -#include "hphp/runtime/vm/jit/runtime-type.h" +#include "hphp/util/assertions.h" namespace HPHP { namespace JIT { diff --git a/hphp/test/.gitignore b/hphp/test/.gitignore index bda236b1789..0dcd4bdbbd2 100644 --- a/hphp/test/.gitignore +++ b/hphp/test/.gitignore @@ -8,6 +8,8 @@ *.testfiles *.tests *.tmp +*.log +*.log.old /frameworks/composer.json.md5 /frameworks/composer.lock /frameworks/composer.phar diff --git a/hphp/test/tools/compare-ir.sh b/hphp/test/tools/compare-ir.sh index 84557e1ea65..f2e9389e9b0 100755 --- a/hphp/test/tools/compare-ir.sh +++ b/hphp/test/tools/compare-ir.sh @@ -31,7 +31,7 @@ find $TEST_DIRS -name \*.log* -exec sed -i \ -e 's/VerifyParamCls.*/VerifyParamCls/g' \ -e 's/code-gen.cpp [0-9]* /code-gen.cpp 0000 /g' \ -e 's/0x[0-9a-f]*/0xdeadbeef/g' \ - -e 's/Arr<[0-9]>/Arr/g' {} \; + -e 's/Arr<[0-9]>/Arr/g' {} + # # Diff against the old results diff --git a/hphp/util/trace.h b/hphp/util/trace.h index 8844ca60dd8..044b4d9414b 100644 --- a/hphp/util/trace.h +++ b/hphp/util/trace.h @@ -177,7 +177,6 @@ enum Module { * * E.g.: * (Location Stack 1) - * (RuntimeType (Location Stack 1) (Home (Location Local 1))) * * The repetitve prettyNode() templates are intended to aid * implementing pretty(). @@ -246,7 +245,7 @@ struct BumpRelease { if (m_live) tl_levels[m_mod] -= m_adjust; } - BumpRelease(BumpRelease&& o) + BumpRelease(BumpRelease&& o) noexcept : m_live(o.m_live) , m_mod(o.m_mod) , m_adjust(o.m_adjust) -- 2.11.4.GIT