diff --git a/hphp/runtime/base/datatype.h b/hphp/runtime/base/datatype.h index a21c5aaeb..967286848 100644 --- a/hphp/runtime/base/datatype.h +++ b/hphp/runtime/base/datatype.h @@ -81,7 +81,8 @@ enum DataTypeCategory { DataTypeGeneric, DataTypeCountness, DataTypeCountnessInit, - DataTypeSpecific + DataTypeSpecific, + DataTypeSpecialized }; /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/vm/jit/runtime-type.cpp b/hphp/runtime/vm/jit/runtime-type.cpp index 7b5b7d8c6..357a7d7a9 100644 --- a/hphp/runtime/vm/jit/runtime-type.cpp +++ b/hphp/runtime/vm/jit/runtime-type.cpp @@ -371,15 +371,18 @@ string RuntimeType::pretty() const { sprintf(buf, "(Value %s)", tname(m_value.outerType).c_str()); } string retval = buf; - if (valueType() == KindOfObject && valueClass() != nullptr) { - char buf2[1024]; - sprintf(buf2, "(OfClass %s)", valueClass()->preClass()->name()->data()); - retval += string(buf2); + if (valueType() == KindOfObject) { + if (valueClass() != nullptr) { + retval += folly::format("(OfClass {})", + knownClass()->name()->data()).str(); + } else if (hasKnownType()) { + retval += folly::format("(Known Class {})", + knownClass()->name()->data()).str(); + } } if (valueType() == KindOfClass && valueClass() != nullptr) { - char buf2[1024]; - sprintf(buf2, "(Class %s)", valueClass()->preClass()->name()->data()); - retval += string(buf2); + retval += folly::format("(Class {})", + knownClass()->name()->data()).str(); } return retval; } diff --git a/hphp/runtime/vm/jit/translator.cpp b/hphp/runtime/vm/jit/translator.cpp index 477ca33b7..de6d6c7e3 100644 --- a/hphp/runtime/vm/jit/translator.cpp +++ b/hphp/runtime/vm/jit/translator.cpp @@ -2327,7 +2327,7 @@ DynLocation* TraceletContext::recordRead(const InputInfo& ii, m_resolvedDeps[l] = dl; } } else { - RuntimeType rtt = tx64->liveType(l, *curUnit()); + RuntimeType rtt = tx64->liveType(l, *curUnit(), true); assert(rtt.isIter() || !rtt.isVagueValue()); // Allocate a new DynLocation to represent this and store it in the // current map. @@ -2502,13 +2502,14 @@ bool NormalizedInstruction::isAnyOutputUsed() const } GuardType::GuardType(DataType outer, DataType inner) - : outerType(outer), innerType(inner) { + : outerType(outer), innerType(inner), klass(nullptr) { } GuardType::GuardType(const RuntimeType& rtt) { assert(rtt.isValue()); outerType = rtt.outerType(); innerType = rtt.innerType(); + klass = rtt.hasKnownType() ? rtt.knownClass() : nullptr; } GuardType::GuardType(const GuardType& other) { @@ -2524,10 +2525,18 @@ const DataType GuardType::getInnerType() const { return innerType; } +const Class* GuardType::getSpecializedClass() const { + return klass; +} + bool GuardType::isSpecific() const { return outerType > KindOfInvalid; } +bool GuardType::isSpecialized() const { + return outerType == KindOfObject && klass != nullptr; +} + bool GuardType::isRelaxed() const { switch (outerType) { case KindOfAny: @@ -2566,7 +2575,9 @@ DataTypeCategory GuardType::getCategory() const { case KindOfAny: return DataTypeGeneric; case KindOfUncounted: return DataTypeCountness; case KindOfUncountedInit: return DataTypeCountnessInit; - default: return DataTypeSpecific; + default: return klass != nullptr ? + DataTypeSpecialized : + DataTypeSpecific; } } @@ -2592,10 +2603,27 @@ GuardType GuardType::getCountness() const { case KindOfBoolean: case KindOfInt64: case KindOfDouble: return GuardType(KindOfUncounted); - default: return *this; + default: return GuardType(outerType, innerType); } } +GuardType GuardType::dropSpecialization() const { + return GuardType(outerType, innerType); +} + +RuntimeType GuardType::getRuntimeType() const { + if (klass != nullptr) { + return RuntimeType(outerType, innerType).setKnownClass(klass); + } + return RuntimeType(outerType, innerType); +} + +bool GuardType::isEqual(GuardType other) const { + return outerType == other.outerType && + innerType == other.innerType && + klass == other.klass; +} + GuardType GuardType::getCountnessInit() const { assert(isSpecific()); switch (outerType) { @@ -2603,7 +2631,7 @@ GuardType GuardType::getCountnessInit() const { case KindOfBoolean: case KindOfInt64: case KindOfDouble: return GuardType(KindOfUncountedInit); - default: return *this; + default: return GuardType(outerType, innerType); } } @@ -2625,7 +2653,8 @@ bool isPopped(DynLocation* loc, NormalizedInstruction* instr) { DataTypeCategory Translator::getOperandConstraintCategory(NormalizedInstruction* instr, - size_t opndIdx) { + size_t opndIdx, + const GuardType& specType) { auto opc = instr->op(); switch (opc) { @@ -2691,6 +2720,28 @@ Translator::getOperandConstraintCategory(NormalizedInstruction* instr, // which takes care of dec-refing it if needed return opndIdx == 0 ? DataTypeGeneric : DataTypeSpecific; + // + // Collections and Iterator related specializations + // + case OpCGetM: + case OpIssetM: + case OpFPassM: + if (instr->inputs.size() == 2 && opndIdx == 0) { + const Class* klass = specType.getSpecializedClass(); + if (klass != nullptr && isOptimizableCollectionClass(klass)) { + return DataTypeSpecialized; + } + } + return DataTypeSpecific; + case OpSetM: + if (instr->inputs.size() == 3 && opndIdx == 1) { + const Class* klass = specType.getSpecializedClass(); + if (klass != nullptr && isOptimizableCollectionClass(klass)) { + return DataTypeSpecialized; + } + } + return DataTypeSpecific; + default: return DataTypeSpecific; } @@ -2700,21 +2751,25 @@ GuardType Translator::getOperandConstraintType(NormalizedInstruction* instr, size_t opndIdx, const GuardType& specType) { - DataTypeCategory dtCategory = getOperandConstraintCategory(instr, opndIdx); + DataTypeCategory dtCategory = getOperandConstraintCategory(instr, + opndIdx, + specType); switch (dtCategory) { case DataTypeGeneric: return GuardType(KindOfAny); case DataTypeCountness: return specType.getCountness(); case DataTypeCountnessInit: return specType.getCountnessInit(); - case DataTypeSpecific: - default: return specType; + case DataTypeSpecific: return specType.dropSpecialization(); + case DataTypeSpecialized: + return specType; } + return specType; } void Translator::constrainOperandType(GuardType& relxType, NormalizedInstruction* instr, size_t opndIdx, const GuardType& specType) { - if (relxType.isSpecific()) return; // Can't constrain any further + if (relxType.isEqual(specType)) return; // Can't constrain any further GuardType consType = getOperandConstraintType(instr, opndIdx, specType); if (consType.isMoreRefinedThan(relxType)) { @@ -2733,7 +2788,7 @@ void Translator::constrainDep(const DynLocation* loc, NormalizedInstruction* firstInstr, GuardType specType, GuardType& relxType) { - if (relxType.isSpecific()) return; // can't contrain it any further + if (relxType.isEqual(specType)) return; // can't contrain it any further for (NormalizedInstruction* instr = firstInstr; instr; instr = instr->next) { if (instr->noOp) continue; @@ -2765,13 +2820,17 @@ void Translator::constrainDep(const DynLocation* loc, if (specType.getOuterType() == KindOfRef && instr->isAnyOutputUsed()) { // Value gets unboxed along the way. Pessimize it for now. - relxType = specType; + if (!relxType.isSpecialized()) { + relxType = specType.dropSpecialization(); + } return; } break; default: - relxType = specType; + if (!relxType.isSpecialized()) { + relxType = specType.dropSpecialization(); + } return; } @@ -2797,67 +2856,6 @@ void Translator::constrainDep(const DynLocation* loc, } } -/** - * Propagates the relaxed type relxType for loc to all its consumers, - * starting at firstInstr. - */ -void Translator::propagateRelaxedType(Tracelet& tclet, - NormalizedInstruction* firstInstr, - DynLocation* loc, - const GuardType& relxType) { - assert(relxType.isRelaxed()); - - for (NormalizedInstruction* instr = firstInstr; instr; instr = instr->next) { - if (instr->noOp) continue; - auto opc = instr->op(); - size_t nInputs = instr->inputs.size(); - for (size_t i = 0; i < nInputs; i++) { - DynLocation* usedLoc = instr->inputs[i]; - if (usedLoc == loc) { - auto outKind = instrInfo[opc].type; - - // stack input propagates to outputs - bool outputIsStackInput = (i == 0 && // stack input is inputs[0] - (outKind == OutSameAsInput || outKind == OutCInput)); - bool outputIsLocalInput = (i == nInputs - 1 && // local input is last - outKind == OutCInputL); - bool outputIsInput = outputIsStackInput || outputIsLocalInput; - - if (outputIsInput) { - // if instr has outStack, update its type and propagate to consumers - if (instr->outStack) { - - TRACE(6, - "propagateRelaxedType: Loc: %s oldType: %s => newType: %s\n", - instr->outStack->location.pretty().c_str(), - instr->outStack->rtt.pretty().c_str(), - RuntimeType(relxType.getOuterType(), - relxType.getInnerType()).pretty().c_str()); - - instr->outStack->rtt = RuntimeType(relxType.getOuterType(), - relxType.getInnerType()); - propagateRelaxedType(tclet, instr->next, instr->outStack, relxType); - } - // if instr has outLocal, update its type and propagate to consumers - if (instr->outLocal) { - - TRACE(6, - "propagateRelaxedType: Loc: %s oldType: %s => newType: %s\n", - instr->outLocal->location.pretty().c_str(), - instr->outLocal->rtt.pretty().c_str(), - RuntimeType(relxType.getOuterType(), - relxType.getInnerType()).pretty().c_str()); - - instr->outLocal->rtt = RuntimeType(relxType.getOuterType(), - relxType.getInnerType()); - propagateRelaxedType(tclet, instr->next, instr->outLocal, relxType); - } - } - } - } - } -} - /* * This method looks at all the uses of the tracelet dependencies in the * instruction stream and tries to relax the type associated with each location. @@ -2876,7 +2874,9 @@ void Translator::relaxDeps(Tracelet& tclet, TraceletContext& tctxt) { GuardType relxType = GuardType(KindOfAny); GuardType specType = GuardType(rtt); constrainDep(loc, tclet.m_instrStream.first, specType, relxType); - locRelxTypeMap[loc] = relxType; + if (!specType.isEqual(relxType)) { + locRelxTypeMap[loc] = relxType; + } } } @@ -2885,56 +2885,15 @@ void Translator::relaxDeps(Tracelet& tclet, TraceletContext& tctxt) { for (auto& kv : locRelxTypeMap) { DynLocation* loc = kv.first; const GuardType& relxType = kv.second; - if (relxType.isRelaxed()) { - TRACE(1, "relaxDeps: Loc: %s oldType: %s => newType: %s\n", - loc->location.pretty().c_str(), - deps[loc->location]->rtt.pretty().c_str(), - RuntimeType(relxType.getOuterType(), - relxType.getInnerType()).pretty().c_str()); - assert(deps[loc->location] == loc); - assert(relxType.getOuterType() != KindOfInvalid); - deps[loc->location]->rtt = RuntimeType(relxType.getOuterType(), - relxType.getInnerType()); - propagateRelaxedType(tclet, tclet.m_instrStream.first, loc, relxType); - } - } -} - -/* - * This method looks at all the uses of the tracelet dependencies in the - * instruction stream and tries to specialize the type associated - * with each location. - */ -void Translator::specializeDeps(Tracelet& tclet, TraceletContext& tctxt) { - // Process the instruction stream, look for CGetM/SetM/IssetM and if the - // instructions are for Vector types and consistent with Vector usage - // specialize the guard - for (NormalizedInstruction* instr = tclet.m_instrStream.first; instr; - instr = instr->next) { - auto op = instr->op(); - if ((op == OpCGetM || op == OpIssetM || op == OpFPassM) && - instr->inputs.size() == 2) { - specializeCollections(instr, 0, tctxt); - } else if (op == OpSetM && instr->inputs.size() == 3) { - specializeCollections(instr, 1, tctxt); - } - } -} - -void Translator::specializeCollections(NormalizedInstruction* instr, - int index, - TraceletContext& tctxt) { - if (instr->inputs[index]->isObject() - || instr->inputs[index]->isRefToObject()) { - Location l = instr->inputs[index]->location; - auto dep = tctxt.m_dependencies.find(l); - if (dep != tctxt.m_dependencies.end()) { - RuntimeType specialized = liveType(l, *curUnit(), true); - const Class* klass = specialized.knownClass(); - if (klass != nullptr && isOptimizableCollectionClass(klass)) { - tctxt.m_dependencies[l]->rtt = specialized; - } - } + TRACE(1, "relaxDeps: Loc: %s oldType: %s => newType: %s\n", + loc->location.pretty().c_str(), + deps[loc->location]->rtt.pretty().c_str(), + RuntimeType(relxType.getOuterType(), + relxType.getInnerType(), + relxType.getSpecializedClass()).pretty().c_str()); + assert(deps[loc->location] == loc); + assert(relxType.getOuterType() != KindOfInvalid); + deps[loc->location]->rtt = relxType.getRuntimeType(); } } @@ -3510,7 +3469,6 @@ breakBB: } relaxDeps(t, tas); - specializeDeps(t, tas); // Mark the last instruction appropriately assert(t.m_instrStream.last); diff --git a/hphp/runtime/vm/jit/translator.h b/hphp/runtime/vm/jit/translator.h index 02168d517..279bc3d11 100644 --- a/hphp/runtime/vm/jit/translator.h +++ b/hphp/runtime/vm/jit/translator.h @@ -371,7 +371,9 @@ class GuardType { GuardType(const GuardType& other); const DataType getOuterType() const; const DataType getInnerType() const; + const Class* getSpecializedClass() const; bool isSpecific() const; + bool isSpecialized() const; bool isRelaxed() const; bool isGeneric() const; bool isCounted() const; @@ -380,10 +382,14 @@ class GuardType { GuardType getCountness() const; GuardType getCountnessInit() const; DataTypeCategory getCategory() const; + GuardType dropSpecialization() const; + RuntimeType getRuntimeType() const; + bool isEqual(GuardType other) const; private: DataType outerType; DataType innerType; + const Class* klass; }; /* @@ -705,20 +711,13 @@ private: int& currentStackOffset, bool& varEnvTaint); void relaxDeps(Tracelet& tclet, TraceletContext& tctxt); - void propagateRelaxedType(Tracelet& tclet, - NormalizedInstruction* firstInstr, - DynLocation* loc, - const GuardType& relxType); void constrainDep(const DynLocation* loc, NormalizedInstruction* firstInstr, GuardType specType, GuardType& relxType); - void specializeDeps(Tracelet& tclet, TraceletContext& tctxt); - void specializeCollections(NormalizedInstruction* instr, - int index, - TraceletContext& tctxt); DataTypeCategory getOperandConstraintCategory(NormalizedInstruction* instr, - size_t opndIdx); + size_t opndIdx, + const GuardType& specType); GuardType getOperandConstraintType(NormalizedInstruction* instr, size_t opndIdx, const GuardType& specType);