From 7adb4ba3ff6289e55776df570c64e4768ecbe681 Mon Sep 17 00:00:00 2001 From: jdelong Date: Fri, 25 Jan 2013 11:57:26 -0800 Subject: [PATCH] Support for InstanceOfD translations Implement InstanceOfD for HHIR, with most of the tx64 optimizations to it. Currently leaves branch fusion disabled since it appears to be too aggressive in some grepping of DumpIR on a repo sandbox. --- src/doc/ir.specification | 75 ++++- src/runtime/vm/class.cpp | 19 +- src/runtime/vm/class.h | 14 +- src/runtime/vm/translator/hopt/codegen.cpp | 373 +++++++++++++++------- src/runtime/vm/translator/hopt/codegen.h | 9 +- src/runtime/vm/translator/hopt/hhbctranslator.cpp | 70 +++- src/runtime/vm/translator/hopt/ir.cpp | 43 +-- src/runtime/vm/translator/hopt/ir.h | 55 ++-- src/runtime/vm/translator/hopt/jumpsopts.cpp | 5 +- src/runtime/vm/translator/hopt/linearscan.cpp | 6 + src/runtime/vm/translator/hopt/simplifier.cpp | 46 +-- src/runtime/vm/translator/hopt/simplifier.h | 1 - src/runtime/vm/translator/hopt/tracebuilder.cpp | 16 +- src/runtime/vm/translator/hopt/tracebuilder.h | 3 - src/runtime/vm/translator/hopt/type.cpp | 13 +- src/runtime/vm/translator/physreg.h | 7 +- src/runtime/vm/unit.cpp | 7 +- src/runtime/vm/unit.h | 4 +- src/util/asm-x64.h | 4 +- 19 files changed, 537 insertions(+), 233 deletions(-) diff --git a/src/doc/ir.specification b/src/doc/ir.specification index 5a4079e4e61..b3c5c12a85c 100644 --- a/src/doc/ir.specification +++ b/src/doc/ir.specification @@ -325,8 +325,30 @@ OpEq OpNeq OpSame OpNSame -InstanceOfD -NInstanceOfD + +D:Bool = InstanceOf S0:ClassPtr S1:ClassPtr S2:ConstBool +D:Bool = NInstanceOf S0:ClassPtr S1:ClassPtr S2:ConstBool + + Sets D based on whether S0 is a descendant of the class, interface, + or trait in S1. (Note that this is always false for a trait). S1 + may be null at runtime if the class is not defined. The flag S2 is + just a hint that is true if we should assume S1 might be an + interface---the instruction must still handle it correctly if this + hint is wrong at runtime. + +D:Bool = ExtendsClass S0:ClassPtr S1:ClassPtr + + A fast-path for instanceof checks. Sets D based on whether S0 is a + descendant of the class in S1, where S1 must be a unique class that + is not an interface or a trait. + +D:Bool = InstanceOfBitmask S0:ClassPtr S1:ConstSstr +D:Bool = NInstanceOfBitmask S0:ClassPtr S1:ConstSstr + + A fast-path for instanceof checks. Sets D based on whether S0 is a + descendant of the class named by S1, where S1 must have a bit + allocated for it in the fast instance check bitvector (see class.h). + IsSet IsType IsNSet @@ -344,17 +366,29 @@ JmpLt JmpLte JmpEq JmpNeq -JmpZero -JmpNZero JmpSame JmpNSame -JmpInstanceOfD -JmpNInstanceOfD +JmpInstanceOf +JmpNInstanceOf +JmpInstanceOfBitmask +JmpNInstanceOfBitmask JmpIsSet JmpIsType JmpIsNSet JmpIsNType -Jmp_ + + Fused jump instructions. These all operate exactly as their + corresponding query op, but also take a label to jump to when the + condition is true. + +D:None = JmpZero S0:{Bool|Int} L:Label +D:None = JmpNZero S0:{Bool|Int} L:Label + + Conditionally jump to L based on S0. + +D:None = Jmp_ L:Label + + Unconditionally jump to the label L. ExitWhenSurprised L:Label @@ -442,7 +476,10 @@ D:T = LdConst D:ConstT = DefConst -LdCls +D:ClassPtr = LdCls S0:ConstSstr + + Loads the class named S0. If the class is not defined, invokes + autoload and may raise an error. D:T = LdClsCns S0:ConstSstr S1:ConstSstr [L:Label] @@ -460,18 +497,26 @@ LdClsMethod LdPropAddr LdClsPropAddr LdObjMethod -LdObjClass -LdCachedClass + +D:ClassPtr = LdObjClass S0:Obj + + Load the Class* out of the object in S0 and put it in D. + +D:ClassPtr = LdCachedClass S0:ConstSstr + + Loads the Class* whose name is S0 out of target cache. Does not + invoke autoload if the class is not defined and returns a null + ClassPtr. D:FuncPtr = LdFunc S0:Str - Loads the Func* whose name is S0. Fatal if the named function is not defined, - and the function autoloader fails to define it. + Loads the Func* whose name is S0. Fatal if the named function is + not defined, and the function autoloader fails to define it. D:FuncPtr = LdFixedFunc S0:ConstStr - Loads the Func* whose name is S0. Fatal if the named function is not defined, - and the function autoloader fails to define it. + Loads the Func* whose name is S0. Fatal if the named function is + not defined, and the function autoloader fails to define it. LdCurFuncPtr @@ -789,4 +834,4 @@ D:Cell = CGetProp S0:ConstTCA S1:ConstCls S2:PtrToGen S3:Gen S4:PtrToCell D:Cell = CGetElem S0:ConstTCA S1:PtrToCell S2:Gen S3:PtrToCell Get element as cell. Calls the helper specified by S0, passing a base (S1), - key (S2), and pointer to MInstrState. \ No newline at end of file + key (S2), and pointer to MInstrState. diff --git a/src/runtime/vm/class.cpp b/src/runtime/vm/class.cpp index 2b36b66adb9..c482aad5edb 100644 --- a/src/runtime/vm/class.cpp +++ b/src/runtime/vm/class.cpp @@ -51,7 +51,7 @@ Class::InstanceCounts Class::s_instanceCounts; ReadWriteMutex Class::s_instanceCountsLock(RankInstanceCounts); Class::InstanceBitsMap Class::s_instanceBits; ReadWriteMutex Class::s_instanceBitsLock(RankInstanceBits); -bool Class::s_instanceBitsInit = false; +std::atomic Class::s_instanceBitsInit{false}; static const StringData* manglePropName(const StringData* className, const StringData* propName, @@ -573,14 +573,9 @@ bool Class::verifyPersistent() const { return true; } -/* - * Initializes s_instanceBits based on data collected during any warmup - * requests that have happened so far. Must only be called while holding the - * write lease. - */ void Class::initInstanceBits() { assert(Transl::Translator::WriteLease().amOwner()); - if (s_instanceBitsInit) return; + if (s_instanceBitsInit.load(std::memory_order_acquire)) return; // First, grab a write lock on s_instanceCounts and grab the current set of // counts as quickly as possible to minimize blocking other threads still @@ -647,7 +642,7 @@ void Class::initInstanceBits() { c->setInstanceBitsAndParents(); } - atomic_release_store(&s_instanceBitsInit, true); + s_instanceBitsInit.store(true, std::memory_order_release); } void Class::profileInstanceOf(const StringData* name) { @@ -655,7 +650,7 @@ void Class::profileInstanceOf(const StringData* name) { unsigned inc = 1; Class* c = Unit::lookupClass(name); if (c && (c->attrs() & AttrInterface)) { - // Favor traits and interfaces + // Favor interfaces inc = 250; } InstanceCounts::accessor acc; @@ -669,13 +664,15 @@ void Class::profileInstanceOf(const StringData* name) { } bool Class::haveInstanceBit(const StringData* name) { - assert(s_instanceBitsInit); + assert(Transl::Translator::WriteLease().amOwner()); + assert(s_instanceBitsInit.load(std::memory_order_acquire)); return mapContains(s_instanceBits, name); } bool Class::getInstanceBitMask(const StringData* name, int& offset, uint8& mask) { - assert(s_instanceBitsInit); + assert(Transl::Translator::WriteLease().amOwner()); + assert(s_instanceBitsInit.load(std::memory_order_acquire)); const size_t bitWidth = sizeof(mask) * CHAR_BIT; unsigned bit; if (!mapGet(s_instanceBits, name, &bit)) return false; diff --git a/src/runtime/vm/class.h b/src/runtime/vm/class.h index 5e64ad6882d..0889c1a9e4c 100644 --- a/src/runtime/vm/class.h +++ b/src/runtime/vm/class.h @@ -22,6 +22,7 @@ #ifdef USE_JEMALLOC # include #endif +#include #include #include @@ -771,6 +772,17 @@ public: public: static hphp_hash_map s_extClassHash; + + /* + * During warmup, we profile the most common classes involved in + * instanceof checks in order to set up a bitmask for each class to + * allow these checks to be performed quickly by the JIT. + * + * initInstanceBits() must be called by the first translation which + * uses instance bits, while holding the write lease. The accessors + * for instance bits (haveInstanceBit, getInstanceBitMask) also + * require holding the write lease. + */ static size_t instanceBitsOff() { return offsetof(Class, m_instanceBits); } static void profileInstanceOf(const StringData* name); static void initInstanceBits(); @@ -789,7 +801,7 @@ private: static ReadWriteMutex s_instanceCountsLock; static InstanceBitsMap s_instanceBits; static ReadWriteMutex s_instanceBitsLock; - static bool s_instanceBitsInit; + static std::atomic s_instanceBitsInit; struct TraitMethod { ClassPtr m_trait; diff --git a/src/runtime/vm/translator/hopt/codegen.cpp b/src/runtime/vm/translator/hopt/codegen.cpp index 69816d645b3..7f204072b78 100644 --- a/src/runtime/vm/translator/hopt/codegen.cpp +++ b/src/runtime/vm/translator/hopt/codegen.cpp @@ -221,6 +221,32 @@ pathloop: } } +ConditionCode cmpOpToCC(Opcode opc) { + switch (opc) { + case OpGt: return CC_G; + case OpGte: return CC_GE; + case OpLt: return CC_L; + case OpLte: return CC_LE; + case OpEq: return CC_E; + case OpNeq: return CC_NE; + case OpSame: return CC_E; + case OpNSame: return CC_NE; + case InstanceOf: return CC_NZ; + case NInstanceOf: return CC_Z; + case InstanceOfBitmask: return CC_NZ; + case NInstanceOfBitmask: return CC_Z; + case IsSet: return CC_NZ; + case IsType: return CC_NZ; + case IsNSet: return CC_Z; + case IsNType: return CC_Z; + default: always_assert(0); + } +} + +const char* getContextName(Class* ctx) { + return ctx ? ctx->name()->data() : ":anonymous:"; +} + } // unnamed namespace ////////////////////////////////////////////////////////////////////// @@ -268,7 +294,7 @@ ArgDesc::ArgDesc(SSATmp* tmp, bool val) : m_imm(-1) { m_kind = Imm; } -const Func* CodeGenerator::getCurFunc() { +const Func* CodeGenerator::getCurFunc() const { if (m_lastMarker) { return m_lastMarker->getFunc(); } @@ -286,8 +312,6 @@ Address CodeGenerator::cgInst(IRInstruction* inst) { #undef OPC default: - std::cerr << "CodeGenerator: unimplemented support for opcode " << - opcodeName(opc) << '\n'; assert(0); return nullptr; } @@ -309,20 +333,16 @@ NOOP_OPCODE(DefActRec) NOOP_OPCODE(AssertStk) NOOP_OPCODE(Nop) -PUNT_OPCODE(JmpInstanceOfD) -PUNT_OPCODE(JmpNInstanceOfD) PUNT_OPCODE(JmpIsSet) PUNT_OPCODE(JmpIsType) PUNT_OPCODE(JmpIsNSet) PUNT_OPCODE(JmpIsNType) -PUNT_OPCODE(NInstanceOfD) PUNT_OPCODE(IsSet) PUNT_OPCODE(IsNSet) PUNT_OPCODE(IsNType) PUNT_OPCODE(LdCurFuncPtr) PUNT_OPCODE(LdFuncCls) PUNT_OPCODE(IsType) -PUNT_OPCODE(InstanceOfD) #undef NOOP_OPCODE #undef PUNT_OPCODE @@ -345,17 +365,6 @@ void CodeGenerator::cgDefLabel(IRInstruction* inst) { } } -static ConditionCode cmpOpToCC[JmpNSame - JmpGt + 1] = { - CC_G, // OpGt - CC_GE, // OpGte - CC_L, - CC_LE, - CC_E, - CC_NE, - CC_E, // OpSame - CC_NE // OpNSame -}; - Address CodeGenerator::emitFwdJcc(Asm& a, ConditionCode cc, LabelInstruction* label) { @@ -397,10 +406,10 @@ Address CodeGenerator::emitSmashableFwdJmp(LabelInstruction* label, return start; } -// Patch with servie request REQ_BIND_JMPCC_FIRST/SECOND +// Patch with service request REQ_BIND_JMPCC_FIRST/SECOND Address CodeGenerator::emitSmashableFwdJccAtEnd(ConditionCode cc, - LabelInstruction* label, - SSATmp* toSmash) { + LabelInstruction* label, + SSATmp* toSmash) { Address start = m_as.code.frontier; if (toSmash) { m_tx64->prepareForSmash(m_as, TranslatorX64::kJmpLen + @@ -414,6 +423,13 @@ Address CodeGenerator::emitSmashableFwdJccAtEnd(ConditionCode cc, return start; } +void CodeGenerator::emitJccDirectExit(IRInstruction* inst, + ConditionCode cc) { + SSATmp* toSmash = inst->getTCA() == kIRDirectJccJmpActive + ? inst->getDst() : nullptr; + emitSmashableFwdJccAtEnd(cc, inst->getLabel(), toSmash); +} + // Patch with service request REQ_BIND_JCC Address CodeGenerator::emitSmashableFwdJcc(ConditionCode cc, LabelInstruction* label, @@ -463,8 +479,7 @@ void CodeGenerator::cgJcc(IRInstruction* inst) { SSATmp* src1 = inst->getSrc(0); SSATmp* src2 = inst->getSrc(1); Opcode opc = inst->getOpcode(); - ConditionCode cc = cmpOpToCC[opc - JmpGt]; - LabelInstruction* label = inst->getLabel(); + ConditionCode cc = cmpOpToCC(queryJmpToQueryOp(opc)); Type::Tag src1Type = src1->getType(); Type::Tag src2Type = src2->getType(); @@ -508,9 +523,8 @@ void CodeGenerator::cgJcc(IRInstruction* inst) { } } } - SSATmp* toSmash = inst->getTCA() == kIRDirectJccJmpActive ? - inst->getDst() : NULL; - emitSmashableFwdJccAtEnd(cc, label, toSmash); + + emitJccDirectExit(inst, cc); } void CodeGenerator::cgJmpGt (IRInstruction* inst) { cgJcc(inst); } @@ -522,47 +536,12 @@ void CodeGenerator::cgJmpNeq (IRInstruction* inst) { cgJcc(inst); } void CodeGenerator::cgJmpSame (IRInstruction* inst) { cgJcc(inst); } void CodeGenerator::cgJmpNSame(IRInstruction* inst) { cgJcc(inst); } -void CodeGenerator::cgCallHelper(Asm& a, - TCA addr, - SSATmp* dst, - SyncOptions sync, - ArgGroup& args) { - PhysReg dstReg0 = InvalidReg; - PhysReg dstReg1 = InvalidReg; - if (dst) { - dstReg0 = dst->getReg(0); - dstReg1 = dst->getReg(1); - } - return cgCallHelper(a, Transl::Call(addr), dstReg0, dstReg1, sync, args); -} - -void CodeGenerator::cgCallHelper(Asm& a, - TCA addr, - PhysReg dstReg, - SyncOptions sync, - ArgGroup& args) { - cgCallHelper(a, Transl::Call(addr), dstReg, InvalidReg, sync, args); -} - -void CodeGenerator::cgCallHelper(Asm& a, - const Transl::Call& call, - PhysReg dstReg0, - PhysReg dstReg1, - SyncOptions sync, - ArgGroup& args) { - assert(int(args.size()) <= kNumRegisterArgs); - - // We don't want to include the destination registers defined by this - // instruction when saving the caller-saved registers. - auto const regsToSave = (m_curInst->getLiveOutRegs() & kCallerSaved) - .remove(dstReg0).remove(dstReg1); - PhysRegSaverParity<1> regSaver(a, regsToSave); - - // Assign registers to the arguments - for (size_t i = 0; i < args.size(); i++) { - args[i].setDstReg(argNumToRegName[i]); - } - +/** + * Once the arg sources and dests are all assigned; emit moves and exchanges + * to put all the args in desired registers. + */ +typedef Transl::X64Assembler Asm; +static void shuffleArgs(Asm& a, ArgGroup& args, IRInstruction* inst) { // First schedule arg moves for (size_t i = 0; i < args.size(); ++i) { // We don't support memory-to-register moves currently. @@ -624,13 +603,8 @@ void CodeGenerator::cgCallHelper(Asm& a, a. addq (argDesc1->getImm(), howTo[i].m_reg1); } } - if (m_curTrace->isMain()) { - if (m_curInst->isNative()) { - TRACE(3, "[counter] 1 reg move in cgCallHelper\n"); - } - } } - if (m_curInst->isNative()) { + if (inst->isNative()) { int numBetweenCaller = 0; for (size_t i = 0; i < howTo.size(); ++i) { if (kCallerSaved.contains(howTo[i].m_reg1) && @@ -638,10 +612,6 @@ void CodeGenerator::cgCallHelper(Asm& a, ++numBetweenCaller; } } - if (numBetweenCaller > 0) { - TRACE(3, "[counter] %d moves are between caller-saved regs\n", - numBetweenCaller); - } } // Handle const-to-register moves and type shifting for (size_t i = 0; i < args.size(); ++i) { @@ -654,6 +624,56 @@ void CodeGenerator::cgCallHelper(Asm& a, a. movq (0xbadbadbadbadbad, args[i].getDstReg()); } } +} + +void CodeGenerator::cgCallHelper(Asm& a, + TCA addr, + SSATmp* dst, + SyncOptions sync, + ArgGroup& args) { + PhysReg dstReg0 = InvalidReg; + PhysReg dstReg1 = InvalidReg; + if (dst) { + dstReg0 = dst->getReg(0); + dstReg1 = dst->getReg(1); + } + return cgCallHelper(a, Transl::Call(addr), dstReg0, dstReg1, sync, args); +} + +void CodeGenerator::cgCallHelper(Asm& a, + TCA addr, + PhysReg dstReg, + SyncOptions sync, + ArgGroup& args) { + cgCallHelper(a, Transl::Call(addr), dstReg, InvalidReg, sync, args); +} + +void CodeGenerator::cgCallHelper(Asm& a, + const Transl::Call& call, + PhysReg dstReg0, + PhysReg dstReg1, + SyncOptions sync, + ArgGroup& args) { + assert(int(args.size()) <= kNumRegisterArgs); + + // We don't want to include the destination registers defined by this + // instruction when saving the caller-saved registers. + auto const regSaver = [&]() -> PhysRegSaverParity<1> { + RegSet rs = m_curInst->getLiveOutRegs() & kCallerSaved; + for (auto& dst : m_curInst->getDsts()) { + for (int i = 0; i < dst->numAllocatedRegs(); ++i) { + rs.remove(dst->getReg(i)); + } + } + return PhysRegSaverParity<1>(a, rs); + }(); + + // Assign registers to the arguments + for (size_t i = 0; i < args.size(); i++) { + args[i].setDstReg(argNumToRegName[i]); + } + + shuffleArgs(a, args, m_curInst); // do the call; may use a trampoline m_tx64->emitCall(a, call); @@ -667,11 +687,6 @@ void CodeGenerator::cgCallHelper(Asm& a, // grab the return value if any if (dstReg0 != InvalidReg && dstReg0 != reg::rax) { a. movq (reg::rax, dstReg0); - if (m_curTrace->isMain()) { - if (m_curInst->isNative()) { - TRACE(3, "[counter] 1 reg move in cgCallHelper\n"); - } - } } if (dstReg1 != InvalidReg) { if (dstReg1 != reg::rdx) { @@ -1200,6 +1215,159 @@ void CodeGenerator::cgOpGte(IRInstruction* inst) { /////////////////////////////////////////////////////////////////////////////// +static bool instanceOfHelper(const Class* objClass, const Class* testClass) { + return testClass && objClass->classof(testClass); +} + +static bool instanceOfHelperIFace(const Class* objClass, + const Class* testClass) { + return testClass && objClass->classof(testClass->preClass()); +} + +void CodeGenerator::emitInstanceCheck(IRInstruction* inst) { + const bool ifaceHint = inst->getSrc(2)->getConstValAsBool(); + cgCallHelper(m_as, + TCA(ifaceHint ? instanceOfHelperIFace : instanceOfHelper), + rax, + kNoSyncPoint, + ArgGroup() + .ssa(inst->getSrc(0)) + .ssa(inst->getSrc(1))); +} + +void CodeGenerator::cgInstanceOfCommon(IRInstruction* inst) { + auto reg = rbyte(inst->getDst()->getReg()); + auto& a = m_as; + + emitInstanceCheck(inst); + if (al != reg) { + a. movb (al, reg); + } +} + +void CodeGenerator::cgInstanceOf(IRInstruction* inst) { + cgInstanceOfCommon(inst); +} + +void CodeGenerator::cgNInstanceOf(IRInstruction* inst) { + cgInstanceOfCommon(inst); +} + +void CodeGenerator::cgJmpInstanceOf(IRInstruction* inst) { + auto& a = m_as; + emitInstanceCheck(inst); + a. testb (al, al); + emitJccDirectExit(inst, CC_NZ); +} + +void CodeGenerator::cgJmpNInstanceOf(IRInstruction* inst) { + auto& a = m_as; + emitInstanceCheck(inst); + a. testb (al, al); + emitJccDirectExit(inst, CC_Z); +} + +/* + * Check instanceof using instance bitmasks. + * + * Note it's not necessary to check whether the test class is defined: + * if it doesn't exist than the candidate can't be an instance of it + * and will fail this check. + */ +void CodeGenerator::emitInstanceBitmaskCheck(IRInstruction* inst) { + auto const rObjClass = inst->getSrc(0)->getReg(0); + auto const testClassName = inst->getSrc(1)->getConstValAsStr(); + auto& a = m_as; + + int offset; + uint8_t mask; + if (!Class::getInstanceBitMask(testClassName, offset, mask)) { + always_assert(!"cgInstanceOfBitmask had no bitmask"); + } + a. testb (int8_t(mask), rObjClass[offset]); +} + +void CodeGenerator::cgInstanceOfBitmask(IRInstruction* inst) { + auto& a = m_as; + emitInstanceBitmaskCheck(inst); + a. setnz (rbyte(inst->getDst()->getReg())); +} + +void CodeGenerator::cgNInstanceOfBitmask(IRInstruction* inst) { + auto& a = m_as; + emitInstanceBitmaskCheck(inst); + a. setz (rbyte(inst->getDst()->getReg())); +} + +void CodeGenerator::cgJmpInstanceOfBitmask(IRInstruction* inst) { + emitInstanceBitmaskCheck(inst); + emitJccDirectExit(inst, CC_NZ); +} + +void CodeGenerator::cgJmpNInstanceOfBitmask(IRInstruction* inst) { + emitInstanceBitmaskCheck(inst); + emitJccDirectExit(inst, CC_Z); +} + +/* + * Check instanceof using the superclass vector on the end of the + * Class entry. + */ +void CodeGenerator::cgExtendsClass(IRInstruction* inst) { + auto const rObjClass = inst->getSrc(0)->getReg(); + auto const testClass = inst->getSrc(1)->getConstValAsClass(); + auto rTestClass = inst->getSrc(1)->getReg(); + auto const rdst = rbyte(inst->getDst()->getReg()); + auto& a = m_as; + + Label out; + Label notExact; + Label falseLabel; + + if (rTestClass == InvalidReg) { // TODO(#2031606) + rTestClass = rScratch; // careful below about asm-x64 smashing this + a. movq (testClass, rTestClass); + } + + // Test if it is the exact same class. TODO(#2044801): we should be + // doing this control flow at the IR level. + if (!(testClass->attrs() & AttrAbstract)) { + if (Class::alwaysLowMem()) { + a. cmpl (r32(rTestClass), r32(rObjClass)); + } else { + a. cmpq (rTestClass, rObjClass); + } + a. jne8 (notExact); + a. movb (1, rdst); + a. jmp8 (out); + } + + auto const vecOffset = Class::classVecOff() + + sizeof(Class*) * (testClass->classVecLen() - 1); + + // Check the length of the class vectors---if the candidate's is at + // least as long as the potential base (testClass) it might be a + // subclass. +asm_label(a, notExact); + a. cmpl (testClass->classVecLen(), + rObjClass[Class::classVecLenOff()]); + a. jb8 (falseLabel); + + // If it's a subclass, rTestClass must be at the appropriate index. + if (Class::alwaysLowMem()) { + a. cmpl (r32(rTestClass), rObjClass[vecOffset]); + } else { + a. cmpq (rTestClass, rObjClass[vecOffset]); + } + a. sete (rdst); + a. jmp8 (out); + +asm_label(a, falseLabel); + a. xorl (r32(rdst), r32(rdst)); + +asm_label(a, out); +} + void CodeGenerator::cgConv(IRInstruction* inst) { Type::Tag toType = inst->getTypeParam(); Type::Tag fromType = inst->getSrc(0)->getType(); @@ -1441,26 +1609,17 @@ void CodeGenerator::cgLdObjMethod(IRInstruction* inst) { } void CodeGenerator::cgLdObjClass(IRInstruction* inst) { - SSATmp* dst = inst->getDst(); - SSATmp* obj = inst->getSrc(0); + auto dstReg = inst->getDst()->getReg(); + auto objReg = inst->getSrc(0)->getReg(); + auto& a = m_as; - // TODO:MP assert copied from translatorx64. Update and make it work - // assert(obj->getType() == Type::Obj); - auto dstReg = dst->getReg(); - auto objReg = obj->getReg(); - m_as.load_reg64_disp_reg64(objReg, ObjectData::getVMClassOffset(), dstReg); + a. loadq (objReg[ObjectData::getVMClassOffset()], dstReg); } void CodeGenerator::cgLdCachedClass(IRInstruction* inst) { - SSATmp* dst = inst->getDst(); - SSATmp* className = inst->getSrc(0); - assert(className->isConst() && className->getType() == Type::StaticStr); - - auto dstReg = dst->getReg(); - const StringData* classNameString = className->getConstValAsStr(); - TargetCache::allocKnownClass(classNameString); - TargetCache::CacheHandle ch = TargetCache::allocKnownClass(classNameString); - m_as.load_reg64_disp_reg64(rVmTl, ch, dstReg); + const StringData* classNameString = inst->getSrc(0)->getConstValAsStr(); + auto ch = TargetCache::allocKnownClass(classNameString); + m_as. loadq (rVmTl[ch], inst->getDst()->getReg()); } void CodeGenerator::cgRetVal(IRInstruction* inst) { @@ -1855,7 +2014,7 @@ void CodeGenerator::cgExitTrace(IRInstruction* inst) { // Patch the original jcc;jmp, don't emit another IRInstruction* jcc = toSmash->getInstruction(); Opcode opc = jcc->getOpcode(); - ConditionCode cc = cmpOpToCC[opc - JmpGt]; + ConditionCode cc = cmpOpToCC(queryJmpToQueryOp(opc)); uint64_t taken = pc->getConstValAsInt(); uint64_t notTaken = notTakenPC->getConstValAsInt(); @@ -3567,12 +3726,6 @@ void CodeGenerator::cgLdPropAddr(IRInstruction* inst) { m_as.lea_reg64_disp_reg64(objReg, offset, dstReg); } -// Copied from translator-x64.cpp -static const char* getContextName() { - Class* ctx = arGetContextClass(curFrame()); - return ctx ? ctx->name()->data() : ":anonymous:"; -} - void CodeGenerator::cgLdClsMethod(IRInstruction* inst) { SSATmp* dst = inst->getDst(); SSATmp* cls = inst->getSrc(0); @@ -3617,7 +3770,9 @@ void CodeGenerator::cgLdClsMethodCache(IRInstruction* inst) { const StringData* method = methodName->getConstValAsStr(); const NamedEntity* ne = (NamedEntity*)baseClass->getConstValAsRawInt(); TargetCache::CacheHandle ch = - TargetCache::StaticMethodCache::alloc(cls, method, getContextName()); + TargetCache::StaticMethodCache::alloc(cls, + method, + getContextName(getCurClass())); auto funcDestReg = dst->getReg(0); auto classDestReg = dst->getReg(1); @@ -3750,9 +3905,6 @@ void CodeGenerator::cgLdClsCns(IRInstruction* inst) { void CodeGenerator::cgJmpZeroHelper(IRInstruction* inst, ConditionCode cc) { SSATmp* src = inst->getSrc(0); - LabelInstruction* label = inst->getLabel(); - SSATmp* toSmash = inst->getTCA() == kIRDirectJccJmpActive ? - inst->getDst() : NULL; auto srcReg = src->getReg(); if (src->isConst()) { @@ -3779,7 +3931,8 @@ void CodeGenerator::cgJmpZeroHelper(IRInstruction* inst, m_as.test_reg64_reg64(srcReg, srcReg); } } - emitSmashableFwdJccAtEnd(cc, label, toSmash); + + emitJccDirectExit(inst, cc); } void CodeGenerator::cgJmpZero(IRInstruction* inst) { diff --git a/src/runtime/vm/translator/hopt/codegen.h b/src/runtime/vm/translator/hopt/codegen.h index cafcdcaa761..d9edf4ab4f0 100644 --- a/src/runtime/vm/translator/hopt/codegen.h +++ b/src/runtime/vm/translator/hopt/codegen.h @@ -157,8 +157,10 @@ private: int64 (*arr_cmp_arr)(ArrayData*, ArrayData*)); void cgJmpZeroHelper(IRInstruction* inst, ConditionCode cc); - private: + void emitInstanceCheck(IRInstruction*); + void cgInstanceOfCommon(IRInstruction*); + void emitInstanceBitmaskCheck(IRInstruction*); void emitTraceCall(CodeGenerator::Asm& as, int64 pcOff); void emitTraceRet(CodeGenerator::Asm& as); void emitCheckStack(CodeGenerator::Asm& as, SSATmp* sp, uint32 numElems, @@ -203,14 +205,15 @@ private: Address emitSmashableFwdJmp(LabelInstruction* label, SSATmp* toSmash); Address emitSmashableFwdJccAtEnd(ConditionCode cc, LabelInstruction* label, SSATmp* toSmash); + void emitJccDirectExit(IRInstruction*, ConditionCode); Address emitSmashableFwdJcc(ConditionCode cc, LabelInstruction* label, SSATmp* toSmash); void emitGuardOrFwdJcc(IRInstruction* inst, ConditionCode cc, LabelInstruction* label); void emitContVarEnvHelperCall(SSATmp* fp, TCA helper); - const Func* getCurFunc(); - Class* getCurClass() { return getCurFunc()->cls(); } + const Func* getCurFunc() const; + Class* getCurClass() const { return getCurFunc()->cls(); } void recordSyncPoint(Asm& as, SyncOptions sync = kSyncPoint); Address getDtorGeneric(); Address getDtorTyped(); diff --git a/src/runtime/vm/translator/hopt/hhbctranslator.cpp b/src/runtime/vm/translator/hopt/hhbctranslator.cpp index 7f69c9faa9d..5abc5289d4e 100644 --- a/src/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/src/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -53,6 +53,7 @@ const NamedEntityPair& HhbcTranslator::lookupNamedEntityPairId(int id) { } SSATmp* HhbcTranslator::push(SSATmp* tmp) { + assert(tmp); m_evalStack.push(tmp); return tmp; } @@ -1233,7 +1234,7 @@ void HhbcTranslator::emitFPushObjMethodD(int32 numParams, * given the Object and the method slot, which is the same as func's. */ if (!(func->attrs() & AttrPrivate)) { - SSATmp* clsTmp = m_tb->genLdObjClass(objOrCls); + SSATmp* clsTmp = m_tb->gen(LdObjClass, objOrCls); funcTmp = m_tb->genLdClsMethod(clsTmp, func->methodSlot()); if (res == MethodLookup::MethodFoundNoThis) { m_tb->genDecRef(objOrCls); @@ -1563,7 +1564,7 @@ void HhbcTranslator::emitVerifyParamType(int32 paramId, if (param->getType() != Type::Obj) { PUNT(VerifyParamType_nonobj); } - SSATmp* objClass = m_tb->genLdObjClass(param); + SSATmp* objClass = m_tb->gen(LdObjClass, param); if (tc.isObject()) { if (!(param->getType() == Type::Obj || @@ -1573,7 +1574,7 @@ void HhbcTranslator::emitVerifyParamType(int32 paramId, return; } } else { - if (!tc.check(frame_local(curFrame(), paramId), getCurFunc())) { + if (!tc.check(frame_local(curFrame() /* FIXME */, paramId), getCurFunc())) { spillStack(); emitInterpOne(Type::None); return; @@ -1588,10 +1589,65 @@ void HhbcTranslator::emitVerifyParamType(int32 paramId, void HhbcTranslator::emitInstanceOfD(int classNameStrId) { const StringData* className = lookupStringId(classNameStrId); - TRACE(3, "%u: InstanceOfD %s\n", m_bcOff, className->data()); SSATmp* src = popC(); - push(m_tb->genInstanceOfD(src, - m_tb->genDefConst(className))); + + /* + * InstanceOfD is always false if it's not an object. + * + * We're prepared to generate translations for known non-object + * types, but if it's Gen/Cell we're going to PUNT because it's + * natural to translate that case with control flow TODO(#2020251) + */ + if (!src->isA(Type::Obj)) { + // If it's a Cell, it might still be an object. All other types + // push false. + if (!Type::isMoreRefined(src->getType(), Type::Cell)) { + PUNT(InstanceOfD_Cell); + } + push(m_tb->genDefConst(false)); + m_tb->genDecRef(src); + return; + } + + SSATmp* objClass = m_tb->gen(LdObjClass, src); + SSATmp* ssaClassName = m_tb->genDefConst(className); + + Class::initInstanceBits(); + const bool haveBit = Class::haveInstanceBit(className); + + Class* const maybeCls = Unit::lookupClass(className); + const bool isNormalClass = maybeCls && + !(maybeCls->attrs() & + (AttrTrait | AttrInterface)); + const bool isUnique = RuntimeOption::RepoAuthoritative && + maybeCls && (maybeCls->attrs() & AttrUnique); + + /* + * If the class is unique or a parent of the current context, we + * don't need to load it out of target cache because it must + * already exist and be defined. + * + * Otherwise, we only use LdCachedClass---instanceof with an + * undefined class doesn't invoke autoload. + */ + SSATmp* checkClass = + isUnique || (maybeCls && getCurClass() && + getCurClass()->classof(maybeCls)) + ? m_tb->genDefConst(maybeCls) + : m_tb->gen(LdCachedClass, ssaClassName); + + push( + haveBit ? m_tb->gen(InstanceOfBitmask, + objClass, + ssaClassName) : + isUnique && isNormalClass ? m_tb->gen(ExtendsClass, + objClass, + checkClass) : + m_tb->gen(InstanceOf, + objClass, + checkClass, + m_tb->genDefConst(maybeCls && !isNormalClass)) + ); m_tb->genDecRef(src); } @@ -1654,7 +1710,7 @@ void HhbcTranslator::emitAGet(SSATmp* classSrc) { if (Type::isString(srcType)) { push(m_tb->genLdCls(classSrc)); } else if (srcType == Type::Obj) { - push(m_tb->genLdObjClass(classSrc)); + push(m_tb->gen(LdObjClass, classSrc)); } else { assert(0); } diff --git a/src/runtime/vm/translator/hopt/ir.cpp b/src/runtime/vm/translator/hopt/ir.cpp index e31e60b5dce..ecf4cff7492 100644 --- a/src/runtime/vm/translator/hopt/ir.cpp +++ b/src/runtime/vm/translator/hopt/ir.cpp @@ -168,22 +168,29 @@ bool IRInstruction::mayReenterHelper() const { return false; } -Opcode queryNegateTable[] = { - OpLte, // OpGt - OpLt, // OpGte - OpGte, // OpLt - OpGt, // OpLte - OpNeq, // OpEq - OpEq, // OpNeq - OpNSame, // OpSame - OpSame, // OpNSame - NInstanceOfD, // InstanceOfD - InstanceOfD, // NInstanceOfD - IsNSet, // IsSet - IsNType, // IsType - IsSet, // IsNSet - IsType // IsNType -}; +Opcode negateQueryOp(Opcode opc) { + assert(isQueryOp(opc)); + + switch (opc) { + case OpGt: return OpLte; + case OpGte: return OpLt; + case OpLt: return OpGte; + case OpLte: return OpGt; + case OpEq: return OpNeq; + case OpNeq: return OpEq; + case OpSame: return OpNSame; + case OpNSame: return OpSame; + case InstanceOf: return NInstanceOf; + case NInstanceOf: return InstanceOf; + case InstanceOfBitmask: return NInstanceOfBitmask; + case NInstanceOfBitmask: return InstanceOfBitmask; + case IsSet: return IsNSet; + case IsType: return IsNType; + case IsNSet: return IsSet; + case IsNType: return IsType; + default: always_assert(0); + } +} Opcode queryCommuteTable[] = { OpLt, // OpGt @@ -197,9 +204,9 @@ Opcode queryCommuteTable[] = { }; const char* Type::Strings[(int)Type::TAG_ENUM_COUNT] = { - #define IRT(type, name) name, +#define IRT(type, name) name, IR_TYPES - #undef IRT +#undef IRT }; // Objects compared with strings may involve calling a user-defined diff --git a/src/runtime/vm/translator/hopt/ir.h b/src/runtime/vm/translator/hopt/ir.h index 47b869c733c..03f6cd765d1 100644 --- a/src/runtime/vm/translator/hopt/ir.h +++ b/src/runtime/vm/translator/hopt/ir.h @@ -119,8 +119,11 @@ enum OpcodeFlag : uint64_t { /* convert from src operand's type to destination type */ \ OPC(Conv, (HasDest|CanCSE|CallsNative)) \ \ - /* query operators returning bool */ \ - /* comparisons (binary) */ \ + /* predicates that can't be branch-fused */ \ + OPC(ExtendsClass, (HasDest|CanCSE)) \ + \ + /* branch-fusable query operators returning bool */ \ + /* (TODO(#2058842): enum order currently matters here) */ \ OPC(OpGt, (HasDest|CanCSE)) \ OPC(OpGte, (HasDest|CanCSE)) \ OPC(OpLt, (HasDest|CanCSE)) \ @@ -133,9 +136,10 @@ enum OpcodeFlag : uint64_t { OPC(OpSame, (HasDest|CanCSE|CallsNative)) \ OPC(OpNSame, (HasDest|CanCSE|CallsNative)) \ \ - /* XXX TODO check instanceof's hasEffects, isNative, RefCount, MayReenter */ \ - OPC(InstanceOfD, (HasDest|CanCSE)) \ - OPC(NInstanceOfD, (HasDest|CanCSE)) \ + OPC(InstanceOf, (HasDest|CanCSE|CallsNative)) \ + OPC(NInstanceOf, (HasDest|CanCSE|CallsNative)) \ + OPC(InstanceOfBitmask, (HasDest|CanCSE)) \ + OPC(NInstanceOfBitmask,(HasDest|CanCSE)) \ \ /* isset, empty, and istype queries (unary) */ \ OPC(IsSet, (HasDest|CanCSE)) \ @@ -155,8 +159,12 @@ enum OpcodeFlag : uint64_t { OPC(JmpNeq, (HasDest|Essential)) \ OPC(JmpSame, (HasDest|Essential)) \ OPC(JmpNSame, (HasDest|Essential)) \ - OPC(JmpInstanceOfD, (HasDest|Essential)) \ - OPC(JmpNInstanceOfD, (HasDest|Essential)) \ + OPC(JmpInstanceOf, (HasDest|Essential|CallsNative)) \ + OPC(JmpNInstanceOf, (HasDest|Essential|CallsNative)) \ + OPC(JmpInstanceOfBitmask, \ + (HasDest|Essential)) \ + OPC(JmpNInstanceOfBitmask, \ + (HasDest|Essential)) \ OPC(JmpIsSet, (HasDest|Essential)) \ OPC(JmpIsType, (HasDest|Essential)) \ OPC(JmpIsNSet, (HasDest|Essential)) \ @@ -342,14 +350,11 @@ enum Opcode { }; inline bool isCmpOp(Opcode opc) { - return (opc >= OpGt && opc <= NInstanceOfD); -} - -inline Opcode cmpToJmpOp(Opcode opc) { - assert(isCmpOp(opc)); - return (Opcode)(JmpGt + (opc - OpGt)); + return (opc >= OpGt && opc <= OpNSame); } +// A "query op" is any instruction returning Type::Bool that is both +// branch-fusable and negateable. inline bool isQueryOp(Opcode opc) { return (opc >= OpGt && opc <= IsNType); } @@ -372,13 +377,22 @@ inline Opcode queryJmpToQueryOp(Opcode opc) { return Opcode(OpGt + (opc - JmpGt)); } -extern Opcode queryNegateTable[]; - -inline Opcode negateQueryOp(Opcode opc) { - assert(isQueryOp(opc)); - return queryNegateTable[opc - OpGt]; +/* + * Right now branch fusion is too indiscriminate to handle fusing + * with potentially expensive-to-repeat operations. TODO(#2053369) + */ +inline bool disableBranchFusion(Opcode opc) { + return opc == InstanceOf || + opc == NInstanceOf || + opc == InstanceOfBitmask || + opc == NInstanceOfBitmask; } +/* + * Return the opcode that corresponds to negation of opc. + */ +Opcode negateQueryOp(Opcode opc); + extern Opcode queryCommuteTable[]; inline Opcode commuteQueryOp(Opcode opc) { @@ -1210,8 +1224,9 @@ public: * Right now, we only spill both at the same time and only Spill and * Reload instructions need to deal with SSATmps that are spilled. */ - bool hasReg(uint32 i) const { return !m_isSpilled && - m_regs[i] != reg::noreg; } + bool hasReg(uint32 i = 0) const { + return !m_isSpilled && m_regs[i] != InvalidReg; + } /* * The maximum number of registers this SSATmp may need allocated. diff --git a/src/runtime/vm/translator/hopt/jumpsopts.cpp b/src/runtime/vm/translator/hopt/jumpsopts.cpp index bea933bca57..8b36e3184d7 100644 --- a/src/runtime/vm/translator/hopt/jumpsopts.cpp +++ b/src/runtime/vm/translator/hopt/jumpsopts.cpp @@ -23,7 +23,10 @@ namespace HPHP { namespace VM { namespace JIT { // to their target trace at TraceExit, TraceExitType::NormalCc static bool jccCanBeDirectExit(Opcode opc) { // JmpGt .. JmpNSame are contiguous and all use cgJcc - return (JmpGt <= opc && opc <= JmpNSame); + return (JmpGt <= opc && opc <= JmpNSame) || + opc == JmpInstanceOf || opc == JmpNInstanceOf || + opc == JmpInstanceOfBitmask || opc == JmpNInstanceOfBitmask; + // TODO(#2053369): JmpIsType, etc } // If main trace ends with an unconditional jump, copy the target of diff --git a/src/runtime/vm/translator/hopt/linearscan.cpp b/src/runtime/vm/translator/hopt/linearscan.cpp index cc8611b9795..11b58ea7204 100644 --- a/src/runtime/vm/translator/hopt/linearscan.cpp +++ b/src/runtime/vm/translator/hopt/linearscan.cpp @@ -769,6 +769,12 @@ void LinearScan::computePreColoringHint() { assert(arg == 4); break; } + case InstanceOf: + case NInstanceOf: + case JmpInstanceOf: + case JmpNInstanceOf: + normalHint(2); + break; default: break; } diff --git a/src/runtime/vm/translator/hopt/simplifier.cpp b/src/runtime/vm/translator/hopt/simplifier.cpp index f5c7447ae5e..a9b78389dc2 100644 --- a/src/runtime/vm/translator/hopt/simplifier.cpp +++ b/src/runtime/vm/translator/hopt/simplifier.cpp @@ -89,8 +89,6 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) { case OpSame: return simplifySame(src1, src2); case OpNSame: return simplifyNSame(src1, src2); case Concat: return simplifyConcat(src1, src2); - case InstanceOfD: return simplifyInstanceOfD(src1, src2, false); - case NInstanceOfD:return simplifyInstanceOfD(src1, src2, true); case Mov: return simplifyMov(src1); case LdClsPropAddr: @@ -122,8 +120,10 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) { // TODO(#2049201): reuse the logic in simplifyCmp. return nullptr; case Jmp_: - case JmpInstanceOfD: - case JmpNInstanceOfD: + case JmpInstanceOf: + case JmpNInstanceOf: + case JmpInstanceOfBitmask: + case JmpNInstanceOfBitmask: case JmpIsSet: case JmpIsType: case JmpIsNSet: @@ -183,13 +183,13 @@ SSATmp* Simplifier::simplifyMov(SSATmp* src) { } SSATmp* Simplifier::simplifyNot(SSATmp* src) { - // const XORs are handled in simplifyXor() - assert(!src->isConst()); - assert(src->getType() == Type::Bool); - IRInstruction* inst = src->getInstruction()->getSrc(0)->getInstruction(); + IRInstruction* inst = src->getInstruction(); Opcode op = inst->getOpcode(); + // TODO: Add more algebraic simplification rules for NOT switch (op) { + case Conv: + return simplifyNot(inst->getSrc(0)); case OpXor: { // !!X --> bool(X) if (isNotInst(inst->getSrc(0))) { @@ -206,7 +206,24 @@ SSATmp* Simplifier::simplifyNot(SSATmp* src) { case OpNeq: case OpSame: case OpNSame: - return m_tb->genCmp(negateQueryOp(op), inst->getSrc(0), inst->getSrc(1)); + // XXX: this could technically be losing a ConvToBool, except + // that we kinda know "not" instructions (Xor with 1) are always + // going to be followed by ConvToBool. + // + // TODO(#2058865): This would make more sense with a real Not + // instruction and allowing boolean output types for query ops. + return m_tb->genCmp(negateQueryOp(op), + inst->getSrc(0), + inst->getSrc(1)); + case InstanceOf: + case NInstanceOf: + case InstanceOfBitmask: + case NInstanceOfBitmask: + // TODO: combine this with the above check and use isQueryOp or + // add an isNegatable. + return m_tb->gen(negateQueryOp(op), + inst->getNumSrcs(), + inst->getSrcs().begin()); // TODO !(X | non_zero) --> 0 default: (void)op; } @@ -901,15 +918,6 @@ SSATmp* Simplifier::simplifyConv(IRInstruction* inst) { return NULL; } -SSATmp* Simplifier::simplifyInstanceOfD(SSATmp* src1, - SSATmp* src2, - bool negate) { - if (src1->getType() != Type::Obj) { - return genDefBool(false); - } - return NULL; -} - SSATmp* Simplifier::simplifyLdClsPropAddr(SSATmp* cls, SSATmp* clsName, SSATmp* propName) { @@ -1030,7 +1038,7 @@ SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) { } // Fuse jumps with query operators. - if (isQueryOp(srcOpcode)) { + if (isQueryOp(srcOpcode) && !disableBranchFusion(srcOpcode)) { SSARange ssas = srcInst->getSrcs(); return m_tb->gen(queryToJmpOp( inst->getOpcode() == JmpZero diff --git a/src/runtime/vm/translator/hopt/simplifier.h b/src/runtime/vm/translator/hopt/simplifier.h index 13a9c736d4a..f666913cbfa 100644 --- a/src/runtime/vm/translator/hopt/simplifier.h +++ b/src/runtime/vm/translator/hopt/simplifier.h @@ -62,7 +62,6 @@ private: SSATmp* simplifyIsType(IRInstruction*); SSATmp* simplifyConcat(SSATmp* src1, SSATmp* src2); SSATmp* simplifyConv(IRInstruction*); - SSATmp* simplifyInstanceOfD(SSATmp* src1, SSATmp* src2, bool negate); SSATmp* simplifyUnbox(IRInstruction*); SSATmp* simplifyUnboxPtr(IRInstruction*); SSATmp* simplifyLdClsPropAddr(SSATmp* cls, SSATmp* clsName, SSATmp* propName); diff --git a/src/runtime/vm/translator/hopt/tracebuilder.cpp b/src/runtime/vm/translator/hopt/tracebuilder.cpp index 15234b2ce9e..f7fceab6c17 100644 --- a/src/runtime/vm/translator/hopt/tracebuilder.cpp +++ b/src/runtime/vm/translator/hopt/tracebuilder.cpp @@ -591,11 +591,6 @@ SSATmp* TraceBuilder::genLdHome(uint32 id) { return optimizeInst(&inst); } -// Helper to lookup class* by name, only using thread's cache -SSATmp* TraceBuilder::genLdCachedClass(SSATmp* className) { - return gen(LdCachedClass, className); -} - SSATmp* TraceBuilder::genLdCls(SSATmp* className) { return gen(LdCls, className); } @@ -662,10 +657,7 @@ SSATmp* TraceBuilder::genQueryOp(Opcode queryOpc, SSATmp* addr) { return gen(queryOpc, addr); } -SSATmp* TraceBuilder::genLdObjClass(SSATmp* obj) { - return gen(LdObjClass, obj); -} - +// TODO(#2058871): move this to hhbctranslator Trace* TraceBuilder::genVerifyParamType(SSATmp* objClass, SSATmp* className, const Class* constraintClass, @@ -673,15 +665,11 @@ Trace* TraceBuilder::genVerifyParamType(SSATmp* objClass, // do NOT use genLdCls() since don't want to load class if it isn't loaded SSATmp* constraint = constraintClass ? genDefConst(constraintClass) - : genLdCachedClass(className); + : gen(LdCachedClass, className); gen(JmpNSame, getLabel(exitTrace), objClass, constraint); return exitTrace; } -SSATmp* TraceBuilder::genInstanceOfD(SSATmp* src, SSATmp* className) { - return gen(InstanceOfD, src, className); -} - Local getLocalFromHomeOpnd(SSATmp* srcHome) { // Invariant: only LdHome instructions generate home values IRInstruction* ldHomeInstruction = srcHome->getInstruction(); diff --git a/src/runtime/vm/translator/hopt/tracebuilder.h b/src/runtime/vm/translator/hopt/tracebuilder.h index 211df66b387..7d2e376ab2d 100644 --- a/src/runtime/vm/translator/hopt/tracebuilder.h +++ b/src/runtime/vm/translator/hopt/tracebuilder.h @@ -125,7 +125,6 @@ public: bool isValueAvailable(SSATmp* tmp) const; SSATmp* genLdHome(uint32 id); - SSATmp* genLdCachedClass(SSATmp* classNameOpnd); SSATmp* genLdCls(SSATmp* classNameOpnd); SSATmp* genLdClsCns(SSATmp* cnsName, SSATmp* cls, Trace* exitTrace); void genCheckClsCnsDefined(SSATmp* cns, Trace* exitTrace); @@ -188,7 +187,6 @@ public: SSATmp* baseClass, Trace* slowPathExit); SSATmp* genLdObjMethod(const StringData* methodName, SSATmp* obj); - SSATmp* genLdObjClass(SSATmp* obj); SSATmp* genCall(SSATmp* actRec, uint32 returnBcOffset, SSATmp* func, @@ -217,7 +215,6 @@ public: SSATmp* genQueryOp(Opcode queryOpc, SSATmp* addr); Trace* genVerifyParamType(SSATmp* objClass, SSATmp* className, const Class* constraint, Trace* exitTrace); - SSATmp* genInstanceOfD(SSATmp* src, SSATmp* className); void genNativeImpl(); diff --git a/src/runtime/vm/translator/hopt/type.cpp b/src/runtime/vm/translator/hopt/type.cpp index bed2630bc2c..ed5644ba575 100644 --- a/src/runtime/vm/translator/hopt/type.cpp +++ b/src/runtime/vm/translator/hopt/type.cpp @@ -49,7 +49,9 @@ Type::Tag outputType(const IRInstruction* inst) { case FreeActRec: return Type::StkPtr; case GenericRetDecRefs: return Type::StkPtr; case GuardStk: return Type::StkPtr; - case InstanceOfD: return Type::Bool; + case InstanceOf: return Type::Bool; + case InstanceOfBitmask: return Type::Bool; + case ExtendsClass: return Type::Bool; case InterpOne: return Type::StkPtr; case IsNSet: return Type::Bool; case IsNType: return Type::Bool; @@ -77,7 +79,8 @@ Type::Tag outputType(const IRInstruction* inst) { case NewArray: return Type::Arr; case NewObj: return Type::StkPtr; case NewTuple: return Type::Arr; - case NInstanceOfD: return Type::Bool; + case NInstanceOf: return Type::Bool; + case NInstanceOfBitmask: return Type::Bool; case RetAdjustStack: return Type::StkPtr; case SpillStack: return Type::StkPtr; case UnboxPtr: return Type::PtrToCell; @@ -122,8 +125,10 @@ Type::Tag outputType(const IRInstruction* inst) { case JmpNZero: case JmpSame: case JmpNSame: - case JmpInstanceOfD: - case JmpNInstanceOfD: + case JmpInstanceOf: + case JmpNInstanceOf: + case JmpInstanceOfBitmask: + case JmpNInstanceOfBitmask: case JmpIsSet: case JmpIsType: case JmpIsNSet: diff --git a/src/runtime/vm/translator/physreg.h b/src/runtime/vm/translator/physreg.h index e752c32bd26..dc6c746e7c3 100644 --- a/src/runtime/vm/translator/physreg.h +++ b/src/runtime/vm/translator/physreg.h @@ -224,7 +224,7 @@ static_assert(std::has_trivial_destructor::value, ////////////////////////////////////////////////////////////////////// template -struct PhysRegSaverParity : private boost::noncopyable { +struct PhysRegSaverParity { PhysRegSaverParity(X64Assembler& a_, RegSet s_) : a(a_), s(s_) { s.forEach([&] (PhysReg pr) { a. push (pr); @@ -235,6 +235,11 @@ struct PhysRegSaverParity : private boost::noncopyable { } } + PhysRegSaverParity(const PhysRegSaverParity&) = delete; + PhysRegSaverParity(PhysRegSaverParity&&) = default; + PhysRegSaverParity& operator=(const PhysRegSaverParity&) = delete; + PhysRegSaverParity& operator=(PhysRegSaverParity&&) = default; + ~PhysRegSaverParity() { if ((s.size() & 1) == StackParity) { // See above; stack parity. diff --git a/src/runtime/vm/unit.cpp b/src/runtime/vm/unit.cpp index c3c7dc15b98..d5b0ca3224c 100644 --- a/src/runtime/vm/unit.cpp +++ b/src/runtime/vm/unit.cpp @@ -378,6 +378,7 @@ bool Unit::compileTimeFatal(const StringData*& msg, int& line) const { Class* Unit::defClass(const PreClass* preClass, bool failIsFatal /* = true */) { + // TODO(#2054448): ARMv8 Class* const* clsList = preClass->namedEntity()->clsList(); Class* top = *clsList; if (top) { @@ -493,7 +494,7 @@ Class* Unit::defClass(const PreClass* preClass, } newClass->m_nextClass = top; - if (atomic_acquire_load(&Class::s_instanceBitsInit)) { + if (Class::s_instanceBitsInit.load(std::memory_order_acquire)) { // If the instance bitmap has already been set up, we can just initialize // our new class's bits and add ourselves to the class list normally. newClass->setInstanceBits(); @@ -504,7 +505,9 @@ Class* Unit::defClass(const PreClass* preClass, // must add the new class to the class list before dropping the lock to // ensure its bits are initialized when the time comes. ReadLock l(Class::s_instanceBitsLock); - if (Class::s_instanceBitsInit) newClass->setInstanceBits(); + if (Class::s_instanceBitsInit.load(std::memory_order_acquire)) { + newClass->setInstanceBits(); + } atomic_release_store(const_cast(clsList), newClass.get()); } newClass.get()->incAtomicCount(); diff --git a/src/runtime/vm/unit.h b/src/runtime/vm/unit.h index b87cab940d1..dc34152d6ae 100644 --- a/src/runtime/vm/unit.h +++ b/src/runtime/vm/unit.h @@ -501,13 +501,13 @@ struct Unit { bool failIsFatal = true); static Class *lookupClass(const NamedEntity *ne) { - Class *cls = *ne->clsList(); + Class *cls = *ne->clsList(); // TODO(#2054448): ARMv8 if (LIKELY(cls != NULL)) cls = cls->getCached(); return cls; } static Class *lookupUniqueClass(const NamedEntity *ne) { - Class *cls = *ne->clsList(); + Class *cls = *ne->clsList(); // TODO(#2054448): ARMv8 if (LIKELY(cls != NULL)) { if (cls->attrs() & AttrUnique && RuntimeOption::RepoAuthoritative) { return cls; diff --git a/src/util/asm-x64.h b/src/util/asm-x64.h index 29025e5a19f..1c81abcf640 100644 --- a/src/util/asm-x64.h +++ b/src/util/asm-x64.h @@ -134,8 +134,9 @@ struct RegRIP { // Go from a RegNumber to the same physical register of a given // size. -inline Reg8 rbyte(RegNumber r) { return Reg8(int(r)); } inline Reg8 rbyte(Reg32 r) { return Reg8(int(r)); } +inline Reg8 rbyte(RegNumber r) { return Reg8(int(r)); } +inline Reg32 r32(Reg8 r) { return Reg32(int(r)); } inline Reg32 r32(RegNumber r) { return Reg32(int(r)); } inline Reg64 r64(RegNumber r) { return Reg64(int(r)); } @@ -1155,6 +1156,7 @@ struct X64Assembler { CC(z, CC_Z) \ CC(ne, CC_NE) \ CC(nz, CC_NZ) \ + CC(b, CC_B) \ CC(be, CC_BE) \ CC(nbe, CC_NBE) \ CC(s, CC_S) \ -- 2.11.4.GIT