From f11bab46b7fe51c15472bbde6960fe6110e78b1f Mon Sep 17 00:00:00 2001 From: Mirek Klimos Date: Wed, 17 Jul 2013 21:42:17 -0700 Subject: [PATCH] Eliminate Continuation in local 0: JIT The the code is being translated, we statically know offset of Continuaton from its ActRec. Use this knowledge to emit data fetches relative to ActRec rather than relative to Continuation loaded from local 0. --- hphp/doc/ir.specification | 48 ++++++++--- hphp/runtime/ext/ext_continuation.h | 0 hphp/runtime/vm/jit/code-gen.cpp | 107 +++++++++++++++++++----- hphp/runtime/vm/jit/hhbc-translator.cpp | 71 ++++++++-------- hphp/runtime/vm/jit/hhbc-translator.h | 1 + hphp/runtime/vm/jit/ir.h | 23 +++-- 6 files changed, 173 insertions(+), 77 deletions(-) mode change 100644 => 100755 hphp/doc/ir.specification mode change 100644 => 100755 hphp/runtime/ext/ext_continuation.h mode change 100644 => 100755 hphp/runtime/vm/jit/code-gen.cpp mode change 100644 => 100755 hphp/runtime/vm/jit/hhbc-translator.cpp mode change 100644 => 100755 hphp/runtime/vm/jit/hhbc-translator.h mode change 100644 => 100755 hphp/runtime/vm/jit/ir.h diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification old mode 100644 new mode 100755 index 3c3c2f155..bcdae453a --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -1537,11 +1537,6 @@ ContEnter S0:FramePtr S1:TCA S2:ConstInt S3:FramePtr object. S1 is the address to jump to. S2 is the bytecode offset in the caller to return to when the generator body yields. S3 is the current frame. -ContRaiseCheck S0:Obj -> L - - Checks whethre the continuation object S0 has raised an exception, - and if so branches to the label L. - ContPreNext S0:Obj -> L Performs operations needed for the Continuation::next() method. This @@ -1558,16 +1553,45 @@ ContSetRunning S0:Obj S1:ConstBool Sets the continuation object S0 to running if S1 is true, or sets it as stopped otherwise. -ContIncKey S0:Obj +ContArIncKey S0:FramePtr - Special-case key update for continuation S0, which increments the - key of a continuation if that continuation's key is an Int. This will - cause undefined behavior if the continuation's key is not an Int. + Special-case key update for continuation, ActRec of which is S0, + which increments the key of a continuation if that continuation's key + is an Int. This will cause undefined behavior if the continuation's key + is not an Int. -ContUpdateIdx S0:Obj S1:Int +ContArUpdateIdx S0:FramePtr S1:Int + + Updates the internal index of continuation with S1 if necessary, i.e. if + S1 is larger than the index. S0 is the pointer to the embedded ActRec. + +D:T LdContArRaw S0:FramePtr S1:ConstInt + + Loads raw data from the Continuation object ActRec of which is S0. + S1 is a constant from the RawMemSlot::Kind enum, which + describes the offset from the continuation base, and the size. + +StContArRaw S0:FramePtr S1:ConstInt S2:?? + + Stores raw data into the Continuation object ActRec of which is S0. + S1 is a constant from the RawMemSlow::Kind enum, which + describes the offset from the continutaion base, and the size. + S2 is the value. + +D:Cell LdContArValue S0:FramePtr + Loads 'value' from the Continuation object ActRec of which is S0. + +StContArValue S0:FramePtr S1:Cell + Stores 'value' into the Continuation object ActRec of which is S0. + S1 is the new value. + +D:Cell LdContArKey S0:FramePtr + Loads 'key' from the Continuation object ActRec of which is S0. + +StContArKey S0:FramePtr S1:Cell + Stores 'key' into the Continuation object ActRec of which is S0. + S1 is the new value. - Updates the internal index of continuation S0 from S1 if necessary, - i.e. if S1 is larger than the index. 17. Debugging and instrumentation diff --git a/hphp/runtime/ext/ext_continuation.h b/hphp/runtime/ext/ext_continuation.h old mode 100644 new mode 100755 diff --git a/hphp/runtime/vm/jit/code-gen.cpp b/hphp/runtime/vm/jit/code-gen.cpp old mode 100644 new mode 100755 index 403afb967..5a1287e10 --- a/hphp/runtime/vm/jit/code-gen.cpp +++ b/hphp/runtime/vm/jit/code-gen.cpp @@ -5369,13 +5369,6 @@ void CodeGenerator::cgContSetRunning(IRInstruction* inst) { } } -void CodeGenerator::cgContDone(IRInstruction* inst) { - auto contReg = m_regs[inst->src(0)].reg(); - - const Offset stateOffset = c_Continuation::stateOffset(); - m_as.storeb(0x2, contReg[stateOffset]); -} - void CodeGenerator::cgContValid(IRInstruction* inst) { auto contReg = m_regs[inst->src(0)].reg(); auto destReg = m_regs[inst->dst()].reg(); @@ -5385,31 +5378,103 @@ void CodeGenerator::cgContValid(IRInstruction* inst) { m_as.xorb(0x1, rbyte(destReg)); } -void CodeGenerator::cgContIncKey(IRInstruction* inst) { - auto contReg = m_regs[inst->src(0)].reg(); - m_as.incq(contReg[CONTOFF(m_key) + TVOFF(m_data)]); +void CodeGenerator::cgContArIncKey(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + m_as.incq(contArReg[CONTOFF(m_key) + TVOFF(m_data) - + (int64_t)c_Continuation::getArOffset(curFunc())]); } -void CodeGenerator::cgContUpdateIdx(IRInstruction* inst) { - auto contReg = m_regs[inst->src(0)].reg(); +void CodeGenerator::cgContArUpdateIdx(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + int64_t off = CONTOFF(m_index) - + (int64_t)c_Continuation::getArOffset(curFunc()); auto newIdx = inst->src(1); // this is hacky and awful oh god if (newIdx->isConst()) { auto const val = newIdx->getValRawInt(); - m_as.emitImmReg( - val, m_rScratch); - m_as.cmpq (m_rScratch, contReg[CONTOFF(m_index)]); - m_as.cload_reg64_disp_reg64( - CC_G, contReg, CONTOFF(m_index), m_rScratch); + m_as.emitImmReg(val, m_rScratch); + m_as.cmpq (m_rScratch, contArReg[off]); + m_as.cload_reg64_disp_reg64(CC_G, contArReg, off, m_rScratch); } else { auto newIdxReg = m_regs[newIdx].reg(); - m_as.loadq (contReg[CONTOFF(m_index)], m_rScratch); + m_as.loadq (contArReg[off], m_rScratch); m_as.cmpq (m_rScratch, newIdxReg); - m_as.cmov_reg64_reg64( - CC_G, newIdxReg, m_rScratch); + m_as.cmov_reg64_reg64(CC_G, newIdxReg, m_rScratch); } - m_as.storeq (m_rScratch, contReg[CONTOFF(m_index)]); + m_as.storeq (m_rScratch, contArReg[off]); +} + +void CodeGenerator::cgLdContArRaw(IRInstruction* inst) { + auto destReg = m_regs[inst->dst()].reg(); + auto contArReg = m_regs[inst->src(0)].reg(); + int64_t kind = inst->src(1)->getValInt(); + RawMemSlot& slot = RawMemSlot::Get(RawMemSlot::Kind(kind)); + + int64_t off = slot.offset() - (int64_t)c_Continuation::getArOffset(curFunc()); + switch (slot.size()) { + case sz::byte: m_as.loadzbl(contArReg[off], r32(destReg)); break; + case sz::dword: m_as.loadl(contArReg[off], r32(destReg)); break; + case sz::qword: m_as.loadq(contArReg[off], destReg); break; + default: not_implemented(); + } +} + +void CodeGenerator::cgStContArRaw(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + int64_t kind = inst->src(1)->getValInt(); + SSATmp* value = inst->src(2); + RawMemSlot& slot = RawMemSlot::Get(RawMemSlot::Kind(kind)); + + assert(value->type().equals(slot.type())); + int64_t off = slot.offset() - (int64_t)c_Continuation::getArOffset(curFunc()); + + if (value->isConst()) { + switch (slot.size()) { + case sz::byte: m_as.storeb(value->getValRawInt(), contArReg[off]); break; + case sz::dword: m_as.storel(value->getValRawInt(), contArReg[off]); break; + case sz::qword: m_as.storeq(value->getValRawInt(), contArReg[off]); break; + default: not_implemented(); + } + } else { + auto valueReg = m_regs[value].reg(); + switch (slot.size()) { + case sz::byte: m_as.storeb(rbyte(valueReg), contArReg[off]); break; + case sz::dword: m_as.storel(r32(valueReg), contArReg[off]); break; + case sz::qword: m_as.storeq(r64(valueReg), contArReg[off]); break; + default: not_implemented(); + } + } +} + +void CodeGenerator::cgLdContArValue(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + const int64_t valueOff = CONTOFF(m_value); + int64_t off = valueOff - (int64_t)c_Continuation::getArOffset(curFunc()); + cgLoad(contArReg, off, inst); +} + +void CodeGenerator::cgStContArValue(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + SSATmp* value = inst->src(1); + const int64_t valueOff = CONTOFF(m_value); + int64_t off = valueOff - (int64_t)c_Continuation::getArOffset(curFunc()); + cgStore(contArReg, off, value, true); +} + +void CodeGenerator::cgLdContArKey(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + const int64_t keyOff = CONTOFF(m_key); + int64_t off = keyOff - (int64_t)c_Continuation::getArOffset(curFunc()); + cgLoad(contArReg, off, inst); +} + +void CodeGenerator::cgStContArKey(IRInstruction* inst) { + auto contArReg = m_regs[inst->src(0)].reg(); + SSATmp* value = inst->src(1); + const int64_t keyOff = CONTOFF(m_key); + int64_t off = keyOff - (int64_t)c_Continuation::getArOffset(curFunc()); + cgStore(contArReg, off, value, true); } void CodeGenerator::cgIterInit(IRInstruction* inst) { diff --git a/hphp/runtime/vm/jit/hhbc-translator.cpp b/hphp/runtime/vm/jit/hhbc-translator.cpp old mode 100644 new mode 100755 index b67285ce9..d854ab6a6 --- a/hphp/runtime/vm/jit/hhbc-translator.cpp +++ b/hphp/runtime/vm/jit/hhbc-translator.cpp @@ -1228,76 +1228,72 @@ void HhbcTranslator::emitContReturnControl() { } void HhbcTranslator::emitUnpackCont() { - gen(AssertLoc, Type::Obj, LocalId(0), m_tb->fp()); - auto const cont = ldLoc(0); + push(gen(LdContArRaw, Type::Int, m_tb->fp(), cns(RawMemSlot::ContLabel))); +} - push(gen(LdRaw, Type::Int, cont, cns(RawMemSlot::ContLabel))); +void HhbcTranslator::emitContSuspendImpl(int64_t labelId) { + gen(ExitWhenSurprised, getExitSlowTrace()); + + // set m_value = popC(); + auto const oldValue = gen(LdContArValue, Type::Cell, m_tb->fp()); + gen(StContArValue, m_tb->fp(), popC()); + gen(DecRef, oldValue); + + // set m_label = labelId; + gen(StContArRaw, m_tb->fp(), cns(RawMemSlot::ContLabel), cns(labelId)); } void HhbcTranslator::emitContSuspend(int64_t labelId) { - gen(ExitWhenSurprised, getExitSlowTrace()); - - gen(AssertLoc, Type::Obj, LocalId(0), m_tb->fp()); - auto const cont = ldLoc(0); - auto const newVal = popC(); - auto const oldValue = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_value))); - gen(StProp, cont, cns(CONTOFF(m_value)), newVal); - gen(DecRef, oldValue); + emitContSuspendImpl(labelId); // take a fast path if this generator has no yield k => v; if (curFunc()->isPairGenerator()) { // this needs optimization - auto const idx = gen(LdRaw, Type::Int, cont, cns(RawMemSlot::ContIndex)); + auto const idx = gen(LdContArRaw, Type::Int, + m_tb->fp(), cns(RawMemSlot::ContIndex)); auto const newIdx = gen(OpAdd, idx, cns(1)); - gen(StRaw, cont, cns(RawMemSlot::ContIndex), newIdx); - auto const oldKey = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_key))); - gen(StProp, cont, cns(CONTOFF(m_key)), newIdx); + gen(StContArRaw, m_tb->fp(), cns(RawMemSlot::ContIndex), newIdx); + + auto const oldKey = gen(LdContArKey, Type::Cell, m_tb->fp()); + gen(StContArKey, m_tb->fp(), newIdx); gen(DecRef, oldKey); } else { // we're guaranteed that the key is an int - gen(ContIncKey, cont); + gen(ContArIncKey, m_tb->fp()); } - gen(StRaw, cont, cns(RawMemSlot::ContLabel), cns(labelId)); - // transfer control emitContReturnControl(); } void HhbcTranslator::emitContSuspendK(int64_t labelId) { - gen(ExitWhenSurprised, getExitSlowTrace()); + emitContSuspendImpl(labelId); - gen(AssertLoc, Type::Obj, LocalId(0), m_tb->fp()); - auto const cont = ldLoc(0); - auto const newVal = popC(); - auto const oldValue = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_value))); - gen(StProp, cont, cns(CONTOFF(m_value)), newVal); - gen(DecRef, oldValue); auto const newKey = popC(); - auto const oldKey = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_key))); - gen(StProp, cont, cns(CONTOFF(m_key)), newKey); + auto const oldKey = gen(LdContArKey, Type::Cell, m_tb->fp()); + gen(StContArKey, m_tb->fp(), newKey); gen(DecRef, oldKey); auto const keyType = newKey->type(); if (keyType.subtypeOf(Type::Int)) { - gen(ContUpdateIdx, cont, newKey); + gen(ContArUpdateIdx, m_tb->fp(), newKey); } - gen(StRaw, cont, cns(RawMemSlot::ContLabel), cns(labelId)); - // transfer control emitContReturnControl(); } void HhbcTranslator::emitContRetC() { - gen(AssertLoc, Type::Obj, LocalId(0), m_tb->fp()); - auto const cont = ldLoc(0); gen(ExitWhenSurprised, getExitSlowTrace()); - gen(ContDone, cont); - auto const newVal = popC(); - auto const oldVal = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_value))); - gen(StProp, cont, cns(CONTOFF(m_value)), newVal); - gen(DecRef, oldVal); + + // set state to done + gen(StContArRaw, m_tb->fp(), cns(RawMemSlot::ContState), + cns(c_Continuation::Done)); + + // set m_value = popC(); + auto const oldValue = gen(LdContArValue, Type::Cell, m_tb->fp()); + gen(StContArValue, m_tb->fp(), popC()); + gen(DecRef, oldValue); // transfer control emitContReturnControl(); @@ -1349,6 +1345,7 @@ void HhbcTranslator::emitContCurrent() { void HhbcTranslator::emitContStopped() { assert(curClass()); SSATmp* cont = gen(LdThis, m_tb->fp()); + gen(ContSetRunning, cont, cns(false)); } diff --git a/hphp/runtime/vm/jit/hhbc-translator.h b/hphp/runtime/vm/jit/hhbc-translator.h old mode 100644 new mode 100755 index fd4f0a395..cfcd5fae4 --- a/hphp/runtime/vm/jit/hhbc-translator.h +++ b/hphp/runtime/vm/jit/hhbc-translator.h @@ -399,6 +399,7 @@ struct HhbcTranslator { void emitContEnter(int32_t returnBcOffset); void emitUnpackCont(); void emitContReturnControl(); + void emitContSuspendImpl(int64_t labelId); void emitContSuspend(int64_t labelId); void emitContSuspendK(int64_t labelId); void emitContRetC(); diff --git a/hphp/runtime/vm/jit/ir.h b/hphp/runtime/vm/jit/ir.h old mode 100644 new mode 100755 index 66c5ec600..2ac47c142 --- a/hphp/runtime/vm/jit/ir.h +++ b/hphp/runtime/vm/jit/ir.h @@ -480,13 +480,16 @@ O(ContEnter, ND, S(FramePtr) \ S(TCA) C(Int) S(FramePtr), E|Mem) \ O(ContPreNext, ND, S(Obj), E|Mem) \ O(ContStartedCheck, ND, S(Obj), E) \ -O(ContSetRunning, ND, S(Obj) \ - C(Bool), E|Mem) \ -O(ContDone, ND, S(Obj), E|Mem) \ +O(ContSetRunning, ND, S(Obj) C(Bool), E|Mem) \ O(ContValid, D(Bool), S(Obj), E) \ -O(ContIncKey, ND, S(Obj), E|Mem) \ -O(ContUpdateIdx, ND, S(Obj) \ - S(Int), E|Mem) \ +O(ContArIncKey, ND, S(FramePtr), E|Mem) \ +O(ContArUpdateIdx, ND, S(FramePtr) S(Int), E|Mem) \ +O(LdContArRaw, DParam, S(FramePtr) C(Int), NF) \ +O(StContArRaw, ND, S(FramePtr) C(Int) S(Gen), E|Mem) \ +O(LdContArValue, DParam, S(FramePtr), NF) \ +O(StContArValue, ND, S(FramePtr) S(Gen), E|Mem|CRc|Refs) \ +O(LdContArKey, DParam, S(FramePtr), NF) \ +O(StContArKey, ND, S(FramePtr) S(Gen), E|Mem|CRc|Refs) \ O(IterInit, D(Bool), S(Arr,Obj) \ S(FramePtr) \ C(Int) \ @@ -842,7 +845,7 @@ class RawMemSlot { public: enum Kind { - ContLabel, ContIndex, ContARPtr, + ContLabel, ContIndex, ContARPtr, ContState, StrLen, FuncNumParams, ContEntry, MisCtx, MaxKind }; @@ -851,6 +854,7 @@ class RawMemSlot { case ContLabel: return GetContLabel(); case ContIndex: return GetContIndex(); case ContARPtr: return GetContARPtr(); + case ContState: return GetContState(); case StrLen: return GetStrLen(); case FuncNumParams: return GetFuncNumParams(); case ContEntry: return GetContEntry(); @@ -880,6 +884,11 @@ class RawMemSlot { static RawMemSlot m(CONTOFF(m_arPtr), Transl::sz::qword, Type::StkPtr); return m; } + static RawMemSlot& GetContState() { + static RawMemSlot m(c_Continuation::stateOffset(), + Transl::sz::byte, Type::Int); + return m; + } static RawMemSlot& GetStrLen() { static RawMemSlot m(StringData::sizeOffset(), Transl::sz::dword, Type::Int); return m;