From 50b8e0e99eb0449ea8f3faa41841dda52bca0dd7 Mon Sep 17 00:00:00 2001 From: Edwin Smith Date: Fri, 26 Dec 2014 08:11:03 -0800 Subject: [PATCH] vasm Branch fusion Summary: Simplifications at the vasm level: jcc{E, testb{setcc{cc, flags}}} => jcc{!cc, flags} jcc{NE, testb{setcc{cc, flags}}} => jcc{cc, flags} Removed HHIR fused-branch instructions. Reviewed By: @jdelong Differential Revision: D1758029 --- hphp/doc/ir.specification | 63 ------------- hphp/runtime/test/simplifier.cpp | 66 -------------- hphp/runtime/vm/jit/code-gen-arm.cpp | 28 ------ hphp/runtime/vm/jit/code-gen-helpers-x64.cpp | 28 ------ hphp/runtime/vm/jit/code-gen-x64.cpp | 92 ++----------------- hphp/runtime/vm/jit/code-gen-x64.h | 7 -- hphp/runtime/vm/jit/dce.cpp | 28 ------ hphp/runtime/vm/jit/extra-data.h | 14 --- hphp/runtime/vm/jit/ir-opcode.cpp | 87 ------------------ hphp/runtime/vm/jit/ir-opcode.h | 21 ----- hphp/runtime/vm/jit/irgen-resumable.cpp | 6 +- hphp/runtime/vm/jit/memory-effects.cpp | 28 ------ hphp/runtime/vm/jit/simplify.cpp | 73 --------------- hphp/runtime/vm/jit/vasm-fusion.cpp | 130 +++++++++++++++++++++++++++ hphp/runtime/vm/jit/vasm-print.h | 1 + hphp/runtime/vm/jit/vasm-x64.cpp | 1 + hphp/runtime/vm/jit/vasm.h | 1 + 17 files changed, 144 insertions(+), 530 deletions(-) create mode 100644 hphp/runtime/vm/jit/vasm-fusion.cpp diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 579b5e54fb5..c59a31b0f53 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -686,41 +686,6 @@ To string conversions: 5. Branches -There is a conditional branch instruction for each predicate above, to enable -generating efficient compare-and-branch instruction sequences. - - -| JmpGt, ND, S(Gen) S(Gen), B - -| JmpGte, ND, S(Gen) S(Gen), B - -| JmpLt, ND, S(Gen) S(Gen), B - -| JmpLte, ND, S(Gen) S(Gen), B - -| JmpEq, ND, S(Gen) S(Gen), B - -| JmpNeq, ND, S(Gen) S(Gen), B - -| JmpSame, ND, S(Gen) S(Gen), B - -| JmpNSame, ND, S(Gen) S(Gen), B - -| JmpGtInt, ND, S(Int) S(Int), B - -| JmpGteInt, ND, S(Int) S(Int), B - -| JmpLtInt, ND, S(Int) S(Int), B - -| JmpLteInt, ND, S(Int) S(Int), B - -| JmpEqInt, ND, S(Int) S(Int), B - -| JmpNeqInt, ND, S(Int) S(Int), B - - 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. - | JmpZero, ND, S(Int,Bool), B | JmpNZero, ND, S(Int,Bool), B @@ -1482,34 +1447,6 @@ generating efficient compare-and-branch instruction sequences. translation transID, which starts at bcOff. This instruction is used in exit traces that trigger profile-guided optimizations. -| ReqBindJmpGt, ND, S(Gen) S(Gen), T - -| ReqBindJmpGte, ND, S(Gen) S(Gen), T - -| ReqBindJmpLt, ND, S(Gen) S(Gen), T - -| ReqBindJmpLte, ND, S(Gen) S(Gen), T - -| ReqBindJmpEq, ND, S(Gen) S(Gen), T - -| ReqBindJmpNeq, ND, S(Gen) S(Gen), T - -| ReqBindJmpGtInt, ND, S(Int) S(Int), T - -| ReqBindJmpGteInt, ND, S(Int) S(Int), T - -| ReqBindJmpLtInt, ND, S(Int) S(Int), T - -| ReqBindJmpLteInt, ND, S(Int) S(Int), T - -| ReqBindJmpEqInt, ND, S(Int) S(Int), T - -| ReqBindJmpNeqInt, ND, S(Int) S(Int), T - -| ReqBindJmpSame, ND, S(Gen) S(Gen), T - -| ReqBindJmpNSame, ND, S(Gen) S(Gen), T - | ReqBindJmpZero, ND, S(Int,Bool), T | ReqBindJmpNZero, ND, S(Int,Bool), T diff --git a/hphp/runtime/test/simplifier.cpp b/hphp/runtime/test/simplifier.cpp index 7c09d36eb8e..57a69f3c5de 100644 --- a/hphp/runtime/test/simplifier.cpp +++ b/hphp/runtime/test/simplifier.cpp @@ -59,17 +59,6 @@ TEST(Simplifier, JumpConstFold) { EXPECT_SINGLE_OP(resultFalseZero, Jmp); EXPECT_SINGLE_OP(resultTrueNZero, Jmp); } - - // Folding query jumps. - { - auto jmpeqTaken = unit.gen(JmpEq, dummy, unit.cns(10), unit.cns(10)); - auto result = simplify(unit, jmpeqTaken, false); - EXPECT_SINGLE_OP(result, Jmp); - - auto jmpeqNTaken = unit.gen(JmpEq, dummy, unit.cns(10), unit.cns(400)); - result = simplify(unit, jmpeqNTaken, false); - EXPECT_SINGLE_OP(result, Nop); - } } TEST(Simplifier, CondJmp) { @@ -103,61 +92,6 @@ TEST(Simplifier, CondJmp) { } } -TEST(Simplifier, JumpFuse) { - BCMarker dummy = BCMarker::Dummy(); - IRUnit unit(test_context); - - { - // JmpZero(Eq(X, true)) --> JmpEq(X, false) - auto taken = unit.defBlock(); - auto lhs = unit.cns(true); - auto rhs = unit.gen(Conjure, dummy, Type::Bool); - auto eq = unit.gen(Eq, dummy, lhs, rhs->dst()); - auto jmp = unit.gen(JmpZero, dummy, taken, eq->dst()); - auto result = simplify(unit, jmp, false); - - EXPECT_EQ(nullptr, result.dst); - EXPECT_EQ(2, result.instrs.size()); - - // This is a dead Eq instruction; an artifact of weirdness in the - // implementation. Should go away. - EXPECT_FALSE(result.instrs[0]->isControlFlow()); - - EXPECT_MATCH(result.instrs[1], JmpEq, taken, rhs->dst(), unit.cns(false)); - } - - { - // JmpNZero(Neq(X:Int, Y:Int)) --> JmpNeqInt(X, Y) - auto taken = unit.defBlock(); - auto x = unit.gen(Conjure, dummy, Type::Int); - auto y = unit.gen(Conjure, dummy, Type::Int); - - auto neq = unit.gen(Neq, dummy, x->dst(), y->dst()); - auto jmp = unit.gen(JmpNZero, dummy, taken, neq->dst()); - auto result = simplify(unit, jmp, false); - - EXPECT_EQ(nullptr, result.dst); - EXPECT_EQ(2, result.instrs.size()); - EXPECT_FALSE(result.instrs[0]->isControlFlow()); // dead Neq - EXPECT_MATCH(result.instrs[1], JmpNeqInt, taken, x->dst(), y->dst()); - } - - { - // JmpNZero(Neq(X:Cls, Y:Cls)) --> JmpNeq(X, Y) - auto taken = unit.defBlock(); - auto x = unit.gen(Conjure, dummy, Type::Bool); - auto y = unit.gen(Conjure, dummy, Type::Bool); - - auto neq = unit.gen(Neq, dummy, x->dst(), y->dst()); - auto jmp = unit.gen(JmpNZero, dummy, taken, neq->dst()); - auto result = simplify(unit, jmp, false); - - EXPECT_EQ(nullptr, result.dst); - EXPECT_EQ(1, result.instrs.size()); - EXPECT_MATCH(result.instrs[0], JmpNeq, taken, x->dst(), y->dst()); - } -} - TEST(Simplifier, DoubleCmp) { IRUnit unit{test_context}; BCMarker dummy = BCMarker::Dummy(); diff --git a/hphp/runtime/vm/jit/code-gen-arm.cpp b/hphp/runtime/vm/jit/code-gen-arm.cpp index 9f0d1b6093d..f4be3430695 100644 --- a/hphp/runtime/vm/jit/code-gen-arm.cpp +++ b/hphp/runtime/vm/jit/code-gen-arm.cpp @@ -322,36 +322,8 @@ PUNT_OPCODE(NInstanceOfBitmask) PUNT_OPCODE(IsType) PUNT_OPCODE(IsScalarType) PUNT_OPCODE(IsNType) -PUNT_OPCODE(JmpGt) -PUNT_OPCODE(JmpGte) -PUNT_OPCODE(JmpLt) -PUNT_OPCODE(JmpLte) -PUNT_OPCODE(JmpEq) -PUNT_OPCODE(JmpNeq) -PUNT_OPCODE(JmpGtInt) -PUNT_OPCODE(JmpGteInt) -PUNT_OPCODE(JmpLtInt) -PUNT_OPCODE(JmpLteInt) -PUNT_OPCODE(JmpEqInt) -PUNT_OPCODE(JmpNeqInt) -PUNT_OPCODE(JmpSame) -PUNT_OPCODE(JmpNSame) PUNT_OPCODE(JmpZero) PUNT_OPCODE(JmpNZero) -PUNT_OPCODE(ReqBindJmpGt) -PUNT_OPCODE(ReqBindJmpGte) -PUNT_OPCODE(ReqBindJmpLt) -PUNT_OPCODE(ReqBindJmpLte) -PUNT_OPCODE(ReqBindJmpEq) -PUNT_OPCODE(ReqBindJmpNeq) -PUNT_OPCODE(ReqBindJmpGtInt) -PUNT_OPCODE(ReqBindJmpGteInt) -PUNT_OPCODE(ReqBindJmpLtInt) -PUNT_OPCODE(ReqBindJmpLteInt) -PUNT_OPCODE(ReqBindJmpEqInt) -PUNT_OPCODE(ReqBindJmpNeqInt) -PUNT_OPCODE(ReqBindJmpSame) -PUNT_OPCODE(ReqBindJmpNSame) PUNT_OPCODE(ReqBindJmpZero) PUNT_OPCODE(ReqBindJmpNZero) PUNT_OPCODE(JmpSSwitchDest) diff --git a/hphp/runtime/vm/jit/code-gen-helpers-x64.cpp b/hphp/runtime/vm/jit/code-gen-helpers-x64.cpp index 7f4e3546eb9..e2d1e141625 100644 --- a/hphp/runtime/vm/jit/code-gen-helpers-x64.cpp +++ b/hphp/runtime/vm/jit/code-gen-helpers-x64.cpp @@ -506,36 +506,8 @@ Vreg zeroExtendIfBool(Vout& v, const SSATmp* src, Vreg reg) { ConditionCode opToConditionCode(Opcode opc) { switch (opc) { - case JmpGt: return CC_G; - case JmpGte: return CC_GE; - case JmpLt: return CC_L; - case JmpLte: return CC_LE; - case JmpEq: return CC_E; - case JmpNeq: return CC_NE; - case JmpGtInt: return CC_G; - case JmpGteInt: return CC_GE; - case JmpLtInt: return CC_L; - case JmpLteInt: return CC_LE; - case JmpEqInt: return CC_E; - case JmpNeqInt: return CC_NE; - case JmpSame: return CC_E; - case JmpNSame: return CC_NE; case JmpZero: return CC_Z; case JmpNZero: return CC_NZ; - case ReqBindJmpGt: return CC_G; - case ReqBindJmpGte: return CC_GE; - case ReqBindJmpLt: return CC_L; - case ReqBindJmpLte: return CC_LE; - case ReqBindJmpEq: return CC_E; - case ReqBindJmpNeq: return CC_NE; - case ReqBindJmpGtInt: return CC_G; - case ReqBindJmpGteInt: return CC_GE; - case ReqBindJmpLtInt: return CC_L; - case ReqBindJmpLteInt: return CC_LE; - case ReqBindJmpEqInt: return CC_E; - case ReqBindJmpNeqInt: return CC_NE; - case ReqBindJmpSame: return CC_E; - case ReqBindJmpNSame: return CC_NE; case ReqBindJmpZero: return CC_Z; case ReqBindJmpNZero: return CC_NZ; default: diff --git a/hphp/runtime/vm/jit/code-gen-x64.cpp b/hphp/runtime/vm/jit/code-gen-x64.cpp index 5f58db260e3..89a35657217 100644 --- a/hphp/runtime/vm/jit/code-gen-x64.cpp +++ b/hphp/runtime/vm/jit/code-gen-x64.cpp @@ -497,16 +497,6 @@ Vreg CodeGenerator::emitCompare(Vout& v, IRInstruction* inst) { return sf; } -Vreg CodeGenerator::emitCompareInt(Vout& v, IRInstruction* inst) { - auto srcReg0 = srcLoc(inst, 0).reg(); - auto srcReg1 = srcLoc(inst, 1).reg(); - auto const sf = v.makeReg(); - // Note the reverse syntax in the assembler. - // This cmp will compute srcReg0 - srcReg1 - v << cmpq{srcReg1, srcReg0, sf}; - return sf; -} - void CodeGenerator::emitReqBindJcc(Vout& v, ConditionCode cc, Vreg sf, const ReqBindJccData* extra) { v << bindjcc1st{cc, sf, {extra->notTaken, extra->taken}, kCrossTraceRegs}; @@ -619,68 +609,6 @@ void CodeGenerator::cgDeleteUnwinderException(IRInstruction* inst) { v.makeVcallArgs({{exn}}), v.makeTuple({})}; } -void CodeGenerator::cgJcc(IRInstruction* inst) { - auto& v = vmain(); - auto cc = opToConditionCode(inst->op()); - auto const sf = emitCompare(v, inst); - v << jcc{cc, sf, {label(inst->next()), label(inst->taken())}}; -} - -void CodeGenerator::cgJccInt(IRInstruction* inst) { - auto& v = vmain(); - auto cc = opToConditionCode(inst->op()); - auto const sf = emitCompareInt(v, inst); - v << jcc{cc, sf, {label(inst->next()), label(inst->taken())}}; -} - -void CodeGenerator::cgReqBindJcc(IRInstruction* inst) { - // TODO(#2404427): prepareForTestAndSmash? - auto& v = vmain(); - auto const sf = emitCompare(v, inst); - emitReqBindJcc(v, opToConditionCode(inst->op()), sf, - inst->extra()); -} - -void CodeGenerator::cgReqBindJccInt(IRInstruction* inst) { - // TODO(#2404427): prepareForTestAndSmash? - auto& v = vmain(); - auto const sf = emitCompareInt(v, inst); - emitReqBindJcc(v, opToConditionCode(inst->op()), sf, - inst->extra()); -} - -void CodeGenerator::cgJmpGt(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpGte(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpLt(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpLte(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpEq(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpNeq(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpSame(IRInstruction* i) { cgJcc(i); } -void CodeGenerator::cgJmpNSame(IRInstruction* i) { cgJcc(i); } - -void CodeGenerator::cgReqBindJmpGt(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpGte(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpLt(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpLte(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpEq(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpNeq(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpSame(IRInstruction* i) { cgReqBindJcc(i); } -void CodeGenerator::cgReqBindJmpNSame(IRInstruction* i) { cgReqBindJcc(i); } - -void CodeGenerator::cgJmpGtInt(IRInstruction* i) { cgJccInt(i); } -void CodeGenerator::cgJmpGteInt(IRInstruction* i) { cgJccInt(i); } -void CodeGenerator::cgJmpLtInt(IRInstruction* i) { cgJccInt(i); } -void CodeGenerator::cgJmpLteInt(IRInstruction* i) { cgJccInt(i); } -void CodeGenerator::cgJmpEqInt(IRInstruction* i) { cgJccInt(i); } -void CodeGenerator::cgJmpNeqInt(IRInstruction* i) { cgJccInt(i); } - -void CodeGenerator::cgReqBindJmpGtInt(IRInstruction* i) { cgReqBindJccInt(i); } -void CodeGenerator::cgReqBindJmpGteInt(IRInstruction* i) { cgReqBindJccInt(i); } -void CodeGenerator::cgReqBindJmpLtInt(IRInstruction* i) { cgReqBindJccInt(i); } -void CodeGenerator::cgReqBindJmpLteInt(IRInstruction* i) { cgReqBindJccInt(i); } -void CodeGenerator::cgReqBindJmpEqInt(IRInstruction* i) { cgReqBindJccInt(i); } -void CodeGenerator::cgReqBindJmpNeqInt(IRInstruction* i) { cgReqBindJccInt(i); } - ////////////////////////////////////////////////////////////////////// void CodeGenerator::cgHalt(IRInstruction* inst) { @@ -1297,10 +1225,14 @@ void CodeGenerator::cgGteX(IRInstruction* inst) { } void CodeGenerator::emitCmpInt(IRInstruction* inst, ConditionCode cc) { - auto dstReg = dstLoc(inst, 0).reg(); + auto dst = dstLoc(inst, 0).reg(); + auto src0 = srcLoc(inst, 0).reg(); + auto src1 = srcLoc(inst, 1).reg(); auto& v = vmain(); - auto const sf = emitCompareInt(v, inst); - v << setcc{cc, sf, dstReg}; + auto sf = v.makeReg(); + // Note the reverse syntax in the assembler: will compute src0 - src1 + v << cmpq{src1, src0, sf}; + v << setcc{cc, sf, dst}; } void CodeGenerator::cgEqInt(IRInstruction* inst) { emitCmpInt(inst, CC_E); } @@ -3955,16 +3887,6 @@ void CodeGenerator::cgCheckLoc(IRInstruction* inst) { rbase[baseOff + TVOFF(m_data)], inst->taken()); } -void CodeGenerator::cgExitJcc(IRInstruction* inst) { - auto const extra = inst->extra(); - auto const& marker = inst->marker(); - auto const sk = SrcKey(getFunc(marker), extra->taken, resumed(marker)); - auto& v = vmain(); - auto const sf = emitCompare(v, inst); - v << bindexit{opToConditionCode(inst->op()), sf, sk, extra->trflags, - kCrossTraceRegs}; -} - void CodeGenerator::cgDefMIStateBase(IRInstruction* inst) { assert(dstLoc(inst, 0).reg() == rVmTl); } diff --git a/hphp/runtime/vm/jit/code-gen-x64.h b/hphp/runtime/vm/jit/code-gen-x64.h index 6a4ed4b0812..f70d2f7ff85 100644 --- a/hphp/runtime/vm/jit/code-gen-x64.h +++ b/hphp/runtime/vm/jit/code-gen-x64.h @@ -106,12 +106,6 @@ private: Vreg emitGetCtxFwdCallWithThisDyn(Vreg destCtxReg, Vreg thisReg, RDS::Handle ch); - void cgJcc(IRInstruction* inst); // helper - void cgReqBindJcc(IRInstruction* inst); // helper - void cgExitJcc(IRInstruction* inst); // helper - void cgJccInt(IRInstruction* inst); // helper - void cgReqBindJccInt(IRInstruction* inst); // helper - void cgExitJccInt(IRInstruction* inst); // helper void emitCmpInt(IRInstruction* inst, ConditionCode cc); void emitCmpEqDbl(IRInstruction* inst, ComparisonPred pred); void emitCmpRelDbl(IRInstruction* inst, ConditionCode cc, bool flipOperands); @@ -127,7 +121,6 @@ private: const ReqBindJccData*); Vreg emitCompare(Vout& v, IRInstruction* inst); - Vreg emitCompareInt(Vout& v, IRInstruction* inst); Vreg emitTestZero(Vout& v, SSATmp* src, Vloc srcLoc); template bool emitIncDec(Vout& v, Vloc dst, SSATmp* src0, Vloc loc0, diff --git a/hphp/runtime/vm/jit/dce.cpp b/hphp/runtime/vm/jit/dce.cpp index 277622d2d6d..b293988ad23 100644 --- a/hphp/runtime/vm/jit/dce.cpp +++ b/hphp/runtime/vm/jit/dce.cpp @@ -241,20 +241,6 @@ bool canDCE(IRInstruction* inst) { case LteX: case EqX: case NeqX: - case JmpGt: - case JmpGte: - case JmpLt: - case JmpLte: - case JmpEq: - case JmpNeq: - case JmpSame: - case JmpNSame: - case JmpGtInt: - case JmpGteInt: - case JmpLtInt: - case JmpLteInt: - case JmpEqInt: - case JmpNeqInt: case JmpZero: case JmpNZero: case JmpSSwitchDest: @@ -332,20 +318,6 @@ bool canDCE(IRInstruction* inst) { case ReqBindJmp: case ReqRetranslate: case ReqRetranslateOpt: - case ReqBindJmpGt: - case ReqBindJmpGte: - case ReqBindJmpLt: - case ReqBindJmpLte: - case ReqBindJmpEq: - case ReqBindJmpNeq: - case ReqBindJmpGtInt: - case ReqBindJmpGteInt: - case ReqBindJmpLtInt: - case ReqBindJmpLteInt: - case ReqBindJmpEqInt: - case ReqBindJmpNeqInt: - case ReqBindJmpSame: - case ReqBindJmpNSame: case ReqBindJmpZero: case ReqBindJmpNZero: case IncRef: diff --git a/hphp/runtime/vm/jit/extra-data.h b/hphp/runtime/vm/jit/extra-data.h index 1e0926336a5..7d961fa447b 100644 --- a/hphp/runtime/vm/jit/extra-data.h +++ b/hphp/runtime/vm/jit/extra-data.h @@ -904,20 +904,6 @@ X(LdFuncCached, LdFuncCachedData); X(LdFuncCachedSafe, LdFuncCachedData); X(LdFuncCachedU, LdFuncCachedUData); X(LdObjMethod, LdObjMethodData); -X(ReqBindJmpGt, ReqBindJccData); -X(ReqBindJmpGte, ReqBindJccData); -X(ReqBindJmpLt, ReqBindJccData); -X(ReqBindJmpLte, ReqBindJccData); -X(ReqBindJmpEq, ReqBindJccData); -X(ReqBindJmpNeq, ReqBindJccData); -X(ReqBindJmpGtInt, ReqBindJccData); -X(ReqBindJmpGteInt, ReqBindJccData); -X(ReqBindJmpLtInt, ReqBindJccData); -X(ReqBindJmpLteInt, ReqBindJccData); -X(ReqBindJmpEqInt, ReqBindJccData); -X(ReqBindJmpNeqInt, ReqBindJccData); -X(ReqBindJmpSame, ReqBindJccData); -X(ReqBindJmpNSame, ReqBindJccData); X(ReqBindJmpZero, ReqBindJccData); X(ReqBindJmpNZero, ReqBindJccData); X(InterpOne, InterpOneData); diff --git a/hphp/runtime/vm/jit/ir-opcode.cpp b/hphp/runtime/vm/jit/ir-opcode.cpp index 71d39fa6801..b74dfcc24e8 100644 --- a/hphp/runtime/vm/jit/ir-opcode.cpp +++ b/hphp/runtime/vm/jit/ir-opcode.cpp @@ -238,27 +238,8 @@ bool isDblQueryOp(Opcode opc) { } } -bool isFusableQueryOp(Opcode opc) { - return isQueryOp(opc) && !isDblQueryOp(opc) && - opc != IsType && opc != IsNType; -} - bool isQueryJmpOp(Opcode opc) { switch (opc) { - case JmpGt: - case JmpGte: - case JmpLt: - case JmpLte: - case JmpEq: - case JmpNeq: - case JmpGtInt: - case JmpGteInt: - case JmpLtInt: - case JmpLteInt: - case JmpEqInt: - case JmpNeqInt: - case JmpSame: - case JmpNSame: case JmpZero: case JmpNZero: return true; @@ -267,64 +248,8 @@ bool isQueryJmpOp(Opcode opc) { } } -Opcode queryToJmpOp(Opcode opc) { - assert(isFusableQueryOp(opc)); - switch (opc) { - case Gt: return JmpGt; - case Gte: return JmpGte; - case Lt: return JmpLt; - case Lte: return JmpLte; - case Eq: return JmpEq; - case Neq: return JmpNeq; - case GtInt: return JmpGtInt; - case GteInt: return JmpGteInt; - case LtInt: return JmpLtInt; - case LteInt: return JmpLteInt; - case EqInt: return JmpEqInt; - case NeqInt: return JmpNeqInt; - case Same: return JmpSame; - case NSame: return JmpNSame; - default: always_assert(0); - } -} - -Opcode queryJmpToQueryOp(Opcode opc) { - assert(isQueryJmpOp(opc)); - switch (opc) { - case JmpGt: return Gt; - case JmpGte: return Gte; - case JmpLt: return Lt; - case JmpLte: return Lte; - case JmpEq: return Eq; - case JmpNeq: return Neq; - case JmpGtInt: return GtInt; - case JmpGteInt: return GteInt; - case JmpLtInt: return LtInt; - case JmpLteInt: return LteInt; - case JmpEqInt: return EqInt; - case JmpNeqInt: return NeqInt; - case JmpSame: return Same; - case JmpNSame: return NSame; - default: always_assert(0); - } -} - Opcode jmpToReqBindJmp(Opcode opc) { switch (opc) { - case JmpGt: return ReqBindJmpGt; - case JmpGte: return ReqBindJmpGte; - case JmpLt: return ReqBindJmpLt; - case JmpLte: return ReqBindJmpLte; - case JmpEq: return ReqBindJmpEq; - case JmpNeq: return ReqBindJmpNeq; - case JmpGtInt: return ReqBindJmpGtInt; - case JmpGteInt: return ReqBindJmpGteInt; - case JmpLtInt: return ReqBindJmpLtInt; - case JmpLteInt: return ReqBindJmpLteInt; - case JmpEqInt: return ReqBindJmpEqInt; - case JmpNeqInt: return ReqBindJmpNeqInt; - case JmpSame: return ReqBindJmpSame; - case JmpNSame: return ReqBindJmpNSame; case JmpZero: return ReqBindJmpZero; case JmpNZero: return ReqBindJmpNZero; default: always_assert(0); @@ -407,18 +332,6 @@ Opcode queryToIntQueryOp(Opcode opc) { case LteDbl:return LteInt; case EqDbl: return EqInt; case NeqDbl:return NeqInt; - case JmpGt: return JmpGtInt; - case JmpGte: return JmpGteInt; - case JmpLt: return JmpLtInt; - case JmpLte: return JmpLteInt; - case JmpEq: return JmpEqInt; - case JmpNeq: return JmpNeqInt; - case ReqBindJmpGt: return ReqBindJmpGtInt; - case ReqBindJmpGte: return ReqBindJmpGteInt; - case ReqBindJmpLt: return ReqBindJmpLtInt; - case ReqBindJmpLte: return ReqBindJmpLteInt; - case ReqBindJmpEq: return ReqBindJmpEqInt; - case ReqBindJmpNeq: return ReqBindJmpNeqInt; default: always_assert(0); } } diff --git a/hphp/runtime/vm/jit/ir-opcode.h b/hphp/runtime/vm/jit/ir-opcode.h index 03adce59dd0..0cc697a3c03 100644 --- a/hphp/runtime/vm/jit/ir-opcode.h +++ b/hphp/runtime/vm/jit/ir-opcode.h @@ -175,33 +175,12 @@ bool isDblQueryOp(Opcode opc); Opcode queryToDblQueryOp(Opcode opc); /* - * A "fusable query op" is any instruction returning Type::Bool that - * has a corresponding "query jump op" for branch fusion. - */ -bool isFusableQueryOp(Opcode opc); - -/* * A "query jump op" is a conditional jump instruction that * corresponds to one of the fusable query op instructions. */ bool isQueryJmpOp(Opcode opc); /* - * Translate a query op into a conditional jump that does the same - * test (a "query jump op"). - * - * Pre: isFusableQueryOp(opc) - */ -Opcode queryToJmpOp(Opcode opc); - -/* - * Translate a "query jump op" to a query op. - * - * Pre: isQueryJmpOp(opc); - */ -Opcode queryJmpToQueryOp(Opcode opc); - -/* * Convert a jump operation to its corresponding conditional * ReqBindJmp. * diff --git a/hphp/runtime/vm/jit/irgen-resumable.cpp b/hphp/runtime/vm/jit/irgen-resumable.cpp index 21d2cacf215..7f7e647bf47 100644 --- a/hphp/runtime/vm/jit/irgen-resumable.cpp +++ b/hphp/runtime/vm/jit/irgen-resumable.cpp @@ -171,11 +171,13 @@ void emitAwait(HTS& env, int32_t numIters) { auto const kFailed = c_WaitHandle::STATE_FAILED; auto const state = gen(env, LdWHState, child); - gen(env, JmpEqInt, exitSlow, state, cns(env, kFailed)); + auto const failed = gen(env, EqInt, state, cns(env, kFailed)); + gen(env, JmpNZero, exitSlow, failed); env.irb->ifThenElse( [&] (Block* taken) { - gen(env, JmpEqInt, taken, state, cns(env, kSucceeded)); + auto const succeeded = gen(env, EqInt, state, cns(env, kSucceeded)); + gen(env, JmpNZero, taken, succeeded); }, [&] { // Next: the wait handle is not finished, we need to suspend if (resumed(env)) { diff --git a/hphp/runtime/vm/jit/memory-effects.cpp b/hphp/runtime/vm/jit/memory-effects.cpp index a5f0636adb6..8e5e11be8d0 100644 --- a/hphp/runtime/vm/jit/memory-effects.cpp +++ b/hphp/runtime/vm/jit/memory-effects.cpp @@ -80,21 +80,7 @@ MemEffects memory_effects_impl(const IRInstruction& inst) { // that could read or write anything as far as we know (including frame // locals). case ReqBindJmp: - case ReqBindJmpEq: - case ReqBindJmpEqInt: - case ReqBindJmpGt: - case ReqBindJmpGte: - case ReqBindJmpGteInt: - case ReqBindJmpGtInt: - case ReqBindJmpLt: - case ReqBindJmpLte: - case ReqBindJmpLteInt: - case ReqBindJmpLtInt: - case ReqBindJmpNeq: - case ReqBindJmpNeqInt: - case ReqBindJmpNSame: case ReqBindJmpNZero: - case ReqBindJmpSame: case ReqBindJmpZero: case ReqRetranslate: case ReqRetranslateOpt: @@ -520,12 +506,6 @@ MemEffects memory_effects_impl(const IRInstruction& inst) { case Ceil: case Floor: case DefLabel: - case JmpNeqInt: - case JmpGteInt: - case JmpGtInt: - case JmpLteInt: - case JmpLtInt: - case JmpEqInt: case ExceptionBarrier: case SyncABIRegs: case DecRefNZ: @@ -632,8 +612,6 @@ MemEffects memory_effects_impl(const IRInstruction& inst) { case DbgAssertRetAddr: case NSame: case Same: - case JmpNSame: - case JmpSame: case Gt: case Gte: case Eq: @@ -642,12 +620,6 @@ MemEffects memory_effects_impl(const IRInstruction& inst) { case Neq: case IncTransCounter: case LdBindAddr: - case JmpEq: - case JmpGt: - case JmpGte: - case JmpLt: - case JmpLte: - case JmpNeq: case LdClsCtor: case LdAsyncArParentChain: case GuardRefs: diff --git a/hphp/runtime/vm/jit/simplify.cpp b/hphp/runtime/vm/jit/simplify.cpp index 605355a750c..9994a58f6e3 100644 --- a/hphp/runtime/vm/jit/simplify.cpp +++ b/hphp/runtime/vm/jit/simplify.cpp @@ -1084,46 +1084,6 @@ X(NSame) #undef X -SSATmp* queryJmpImpl(State& env, const IRInstruction* inst) { - auto const src1 = inst->src(0); - auto const src2 = inst->src(1); - auto const opc = inst->op(); - // reuse the logic in cmpImpl - auto const newCmp = cmpImpl( - env, - queryJmpToQueryOp(opc), - nullptr, - src1, - src2 - ); - if (!newCmp) return nullptr; - - // Become an equivalent conditional jump and reuse that logic. - return gen(env, JmpNZero, inst->taken(), newCmp); -} - -#define X(x) \ - SSATmp* simplify##x(State& env, const IRInstruction* i) { \ - return queryJmpImpl(env, i); \ - } - -X(JmpGt) -X(JmpGte) -X(JmpLt) -X(JmpLte) -X(JmpEq) -X(JmpNeq) -X(JmpGtInt) -X(JmpGteInt) -X(JmpLtInt) -X(JmpLteInt) -X(JmpEqInt) -X(JmpNeqInt) -X(JmpSame) -X(JmpNSame) - -#undef X - SSATmp* isTypeImpl(State& env, const IRInstruction* inst) { bool const trueSense = inst->op() == IsType; auto const type = inst->typeParam(); @@ -1714,25 +1674,6 @@ SSATmp* condJmpImpl(State& env, const IRInstruction* inst) { // We can just check the int or ptr directly. Borrow the Conv's src. return gen(env, inst->op(), inst->taken(), srcInst->src(0)); } - - auto canCompareFused = [&]() { - auto src1Type = srcInst->src(0)->type(); - auto src2Type = srcInst->src(1)->type(); - return ((src1Type <= Type::Int && src2Type <= Type::Int) || - (src1Type <= Type::Bool && src2Type <= Type::Bool) || - (src1Type <= Type::Cls && src2Type <= Type::Cls)); - }; - - // Fuse jumps with query operators. - if (isFusableQueryOp(srcOpcode) && canCompareFused()) { - auto opc = queryToJmpOp(inst->op() == JmpZero - ? negateQueryOp(srcOpcode) : srcOpcode); - SrcRange ssas = srcInst->srcs(); - - return gen(env, opc, inst->maybeTypeParam(), inst->taken(), - std::make_pair(ssas.size(), ssas.begin())); - } - return nullptr; } @@ -2067,20 +2008,6 @@ SSATmp* simplifyWork(State& env, const IRInstruction* inst) { X(SpillStack) X(TakeStack) X(UnboxPtr) - X(JmpGt) - X(JmpGte) - X(JmpLt) - X(JmpLte) - X(JmpEq) - X(JmpNeq) - X(JmpGtInt) - X(JmpGteInt) - X(JmpLtInt) - X(JmpLteInt) - X(JmpEqInt) - X(JmpNeqInt) - X(JmpSame) - X(JmpNSame) X(JmpZero) X(JmpNZero) X(OrInt) diff --git a/hphp/runtime/vm/jit/vasm-fusion.cpp b/hphp/runtime/vm/jit/vasm-fusion.cpp new file mode 100644 index 00000000000..3eac7337c7f --- /dev/null +++ b/hphp/runtime/vm/jit/vasm-fusion.cpp @@ -0,0 +1,130 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 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/vasm-print.h" +#include "hphp/runtime/vm/jit/vasm-x64.h" + +TRACE_SET_MOD(vasm); + +namespace HPHP { namespace jit { + +namespace { +struct DefVisitor { + Vreg flags; + template void imm(T&) {} + template void use(T&) {} + template void across(T&) {} + template void useHint(T&, H&) {} + template void def(T&) {} + template void defHint(T&, H&) {} + template void defHint(VregSF f, H&) { flags = f; } + void def(VregSF f) { flags = f; } +}; + +// if inst is testb{r,r,d}, return true,d +bool match_testb(Vinstr& inst, Vreg r) { + return inst.op == Vinstr::testb && + inst.testb_.s0 == r && + inst.testb_.s1 == r; +} + +bool match_jcc(Vinstr& inst, Vreg flags) { + return inst.op == Vinstr::jcc && inst.jcc_.sf == flags && + (inst.jcc_.cc == CC_E || inst.jcc_.cc == CC_NE); +} + +bool sets_flags(Vinstr& inst) { + DefVisitor dv; + visitOperands(inst, dv); + return dv.flags.isValid(); +} +} + +/* + * Branch fusion: + * Analyze blocks one at a time, looking for the sequence: + * + * setcc cc, f1 => b + * ... + * testb b, b => f2 + * ... + * jcc E|NE, f2 + * + * If found, and f2 is only used by the jcc, then change the code to: + * + * setcc cc, f1 => b + * ... + * nop + * ... + * jcc !cc|cc, f1 + * + * Later, vasm-dead will clean up the nop, and the setcc if b became dead. + * + * During the search, any other instruction that has a status flag result + * will reset the pattern matcher. No instruction can "kill" flags, + * since flags are SSA variables. However the transformation we want to + * make extends the setcc flags lifetime, and we don't want it to overlap + * another flag's lifetime. + */ +void fuseBranches(Vunit& unit) { + auto blocks = sortBlocks(unit); + jit::vector uses(unit.next_vr); + for (auto b : blocks) { + for (auto& inst : unit.blocks[b].code) { + visitUses(unit, inst, [&](Vreg r) { + uses[r]++; + }); + } + } + bool should_print = false; + for (auto b : blocks) { + auto& code = unit.blocks[b].code; + ConditionCode cc; + Vreg setcc_flags, setcc_dest, testb_flags; + unsigned testb_index; + for (unsigned i = 0, n = code.size(); i < n; ++i) { + if (code[i].op == Vinstr::setcc) { + cc = code[i].setcc_.cc; + setcc_flags = code[i].setcc_.sf; + setcc_dest = code[i].setcc_.d; + continue; + } + if (setcc_flags.isValid() && + match_testb(code[i], setcc_dest) && + uses[code[i].testb_.sf] == 1) { + testb_flags = code[i].testb_.sf; + testb_index = i; + continue; + } + if (match_jcc(code[i], testb_flags)) { + code[testb_index] = nop{}; // erase the testb + auto& jcc = code[i].jcc_; + jcc.cc = jcc.cc == CC_NE ? cc : ccNegate(cc); + jcc.sf = setcc_flags; + should_print = true; + continue; + } + if (setcc_flags.isValid() && sets_flags(code[i])) { + setcc_flags = testb_flags = Vreg{}; + } + } + } + if (should_print) { + printUnit(kVasmFusionLevel, "after vasm-fusion", unit); + } +} + +}} diff --git a/hphp/runtime/vm/jit/vasm-print.h b/hphp/runtime/vm/jit/vasm-print.h index 9dd92c8e5db..0f42b46ac00 100644 --- a/hphp/runtime/vm/jit/vasm-print.h +++ b/hphp/runtime/vm/jit/vasm-print.h @@ -38,6 +38,7 @@ void printCfg(std::ostream& out, const Vunit& unit, // Tracing level constants. constexpr int kInitialVasmLevel = 1; constexpr int kVasmImmsLevel = 2; +constexpr int kVasmFusionLevel = 2; constexpr int kVasmCodeGenLevel = 2; constexpr int kVasmRegAllocLevel = 3; constexpr int kVasmARMFoldLevel = 4; diff --git a/hphp/runtime/vm/jit/vasm-x64.cpp b/hphp/runtime/vm/jit/vasm-x64.cpp index e6312bc751e..aa62030c072 100644 --- a/hphp/runtime/vm/jit/vasm-x64.cpp +++ b/hphp/runtime/vm/jit/vasm-x64.cpp @@ -1217,6 +1217,7 @@ void Vasm::finishX64(const Abi& abi, AsmInfo* asmInfo) { always_assert(!busy); busy = true; SCOPE_EXIT { busy = false; }; + fuseBranches(m_unit); optimizeExits(m_unit); lowerForX64(m_unit, abi); diff --git a/hphp/runtime/vm/jit/vasm.h b/hphp/runtime/vm/jit/vasm.h index 9bfe685c198..51379e92584 100644 --- a/hphp/runtime/vm/jit/vasm.h +++ b/hphp/runtime/vm/jit/vasm.h @@ -71,6 +71,7 @@ enum class VregKind : uint8_t { Any, Gpr, Simd, Sf }; void allocateRegisters(Vunit&, const Abi&); void optimizeExits(Vunit&); void optimizeJmps(Vunit&); +void fuseBranches(Vunit&); void removeDeadCode(Vunit&); template void foldImms(Vunit&); void lowerForARM(Vunit&); -- 2.11.4.GIT