Kill m_received, transfer values thru stack

Continuation's m_received field is used to transfer values and
exceptions to the Continuation by send() and raise() methods. It has a
valid value only for a short duration of time, between
ContNext/ContSend/ContRaise and UnpackCont opcodes, when no other
operations could possibly occur. Let's free these 16 bytes of memory and
pass the value thru the VM stack.

To achieve this goal, ContEnter was modified to accept a value to be
transferred on the stack. The existence of the value on the stack is
then acknowledged by the UnpackCont opcode, which is the first opcode
executed after ContEnter enters the continuation body.

ContNext then becomes a trivial Null and is thus removed, ContSend just
teleports the value from local 0 to the stack (I will experiment in a
follow up diff with replacing ContSent by CGetL+UnsetL). ContRaise has
to update the label, but the eventual plan is to enter into unwinder
directly from the raise() method.
Esse commit está contido em:
Jan Oravec
2013-06-24 11:09:51 -07:00
commit de Sara Golemon
commit f0dcca8b29
14 arquivos alterados com 48 adições e 89 exclusões
+3 -3
Ver Arquivo
@@ -3951,7 +3951,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
assert(m_evalStack.size() == 0);
e.ContExit();
// emit return label for ContRaise
// emit return label for raise()
e.Null();
m_yieldLabels[2 * y->getLabel() - 1].set(e);
@@ -3959,7 +3959,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
assert(m_evalStack.size() == 1);
e.Throw();
// emit return label for ContNext/ContSend
// emit return label for next()/send()
e.Null();
m_yieldLabels[2 * y->getLabel()].set(e);
@@ -7016,7 +7016,7 @@ static void emitContinuationMethod(UnitEmitter& ue, FuncEmitter* fe,
const Offset ehStart = ue.bcPos();
static Op mOps[] = {
OpContNext,
OpNull,
OpContSend,
OpContRaise,
};
+8 -12
Ver Arquivo
@@ -3687,11 +3687,12 @@ CreateCont <function name> [] -> [C]
Continuation will store a reference to the function named by the string
immediate to be used as its body.
ContEnter [] -> []
ContEnter [C] -> []
This instruction may only appear in non-static methods of the Continuation
class. It transfers control flow to the beginning of the continuation body
associated with the $this object of the Continuation object.
associated with the $this object of the Continuation object. The value on the
stack is sent to the Continuation to be retrieved by UnpackCont.
ContExit [] -> []
@@ -3726,22 +3727,17 @@ ContCheck <check started> [] -> []
object. If the continuation is finished, already running, or not yet started
and <check started> is enabled, an exception will be thrown.
ContNext [] -> []
Prepare continuation for iteration. $this must be a Continuation object
that passed ContCheck<false> check.
ContSend [] -> []
ContSend [] -> [C]
Prepare continuation to receive a value. $this must be a Continuation object
that passed ContCheck<true> check. The value in local 0 is stored in the
continuation.
that passed ContCheck<true> check. The value in local 0 is moved to the stack
for sending to the Continuation by ContEnter.
ContRaise [] -> []
ContRaise [] -> [C:Obj:Exception]
Prepare continuation to receive a thrown exception. $this must be a
Continuation object that passed ContCheck<true> check. The exception in
local 0 is stored in the continuation.
local 0 is moved to the stack for sending to the Continuation by ContEnter.
ContValid [] -> [C:Bool]
+1 -1
Ver Arquivo
@@ -1522,7 +1522,7 @@ ContRaiseCheck S0:Obj -> L
ContPreNext S0:Obj -> L
Performs operations needed for the ContNext hhbc opcode. This
Performs operations needed for the Continuation::next() method. This
includes checking m_done and m_running---if either is not the case
branches to the label L.
-1
Ver Arquivo
@@ -49,7 +49,6 @@ c_Continuation::c_Continuation(Class* cb) :
m_label(0),
m_index(-1LL),
m_value(Variant::NullInit()),
m_received(Variant::NullInit()),
m_origFunc(nullptr) {
o_subclassData.u16 = 0;
}
-1
Ver Arquivo
@@ -133,7 +133,6 @@ public:
int32_t m_label;
int64_t m_index;
Variant m_value;
Variant m_received;
Func *m_origFunc;
ActRec* m_arPtr;
p_ContinuationWaitHandle m_waitHandle;
+12 -16
Ver Arquivo
@@ -6782,8 +6782,9 @@ static inline c_Continuation* this_continuation(ActRec* fp) {
void VMExecutionContext::iopContEnter(PC& pc) {
NEXT();
// The stack must be empty! Or else generatorStackBase() won't work!
assert(m_stack.top() == (TypedValue*)m_fp - m_fp->m_func->numSlotsInFrame());
// The stack must have one cell! Or else generatorStackBase() won't work!
assert(m_stack.top() + 1 ==
(TypedValue*)m_fp - m_fp->m_func->numSlotsInFrame());
// Do linkage of the continuation's AR.
assert(m_fp->hasThis());
@@ -6819,10 +6820,8 @@ inline void OPTBLD_INLINE VMExecutionContext::iopUnpackCont(PC& pc) {
NEXT();
c_Continuation* cont = frame_continuation(m_fp);
// Return the received value
TypedValue* recv_fr = cont->m_received.asTypedValue();
tvTeleport(recv_fr, m_stack.allocTV());
tvWriteNull(recv_fr);
// check sanity of received value
assert(tvIsPlausible(m_stack.topC()));
// Return the label in a stack cell
TypedValue* label = m_stack.allocTV();
@@ -6862,26 +6861,23 @@ inline void OPTBLD_INLINE VMExecutionContext::iopContCheck(PC& pc) {
cont->preNext();
}
inline void OPTBLD_INLINE VMExecutionContext::iopContNext(PC& pc) {
NEXT();
c_Continuation* cont = this_continuation(m_fp);
tvWriteNull(cont->m_received.asTypedValue());
}
inline void OPTBLD_INLINE VMExecutionContext::iopContSend(PC& pc) {
NEXT();
c_Continuation* cont = this_continuation(m_fp);
tvTeleport(frame_local(m_fp, 0), cont->m_received.asTypedValue());
// prepare value to be sent by ContEnter
tvTeleport(frame_local(m_fp, 0), m_stack.allocTV());
tvWriteUninit(frame_local(m_fp, 0));
}
inline void OPTBLD_INLINE VMExecutionContext::iopContRaise(PC& pc) {
NEXT();
c_Continuation* cont = this_continuation(m_fp);
tvTeleport(frame_local(m_fp, 0), cont->m_received.asTypedValue());
tvWriteUninit(frame_local(m_fp, 0));
assert(cont->m_label);
--cont->m_label;
// prepare exception to be sent by ContEnter
tvTeleport(frame_local(m_fp, 0), m_stack.allocTV());
tvWriteUninit(frame_local(m_fp, 0));
}
inline void OPTBLD_INLINE VMExecutionContext::iopContValid(PC& pc) {
+3 -4
Ver Arquivo
@@ -547,15 +547,14 @@ enum SetOpOp {
O(NativeImpl, NA, NOV, NOV, CF_TF) \
O(CreateCl, TWO(IVA,SA), CVMANY, ONE(CV), NF) \
O(CreateCont, ONE(SA), NOV, ONE(CV), NF) \
O(ContEnter, NA, NOV, NOV, CF) \
O(ContEnter, NA, ONE(CV), NOV, CF) \
O(ContExit, NA, NOV, NOV, CF) \
O(UnpackCont, NA, NOV, TWO(CV,CV), NF) \
O(PackCont, ONE(IVA), ONE(CV), NOV, NF) \
O(ContRetC, NA, ONE(CV), NOV, CF_TF) \
O(ContCheck, ONE(IVA), NOV, NOV, NF) \
O(ContNext, NA, NOV, NOV, NF) \
O(ContSend, NA, NOV, NOV, NF) \
O(ContRaise, NA, NOV, NOV, NF) \
O(ContSend, NA, NOV, ONE(CV), NF) \
O(ContRaise, NA, NOV, ONE(CV), NF) \
O(ContValid, NA, NOV, ONE(CV), NF) \
O(ContCurrent, NA, NOV, ONE(CV), NF) \
O(ContStopped, NA, NOV, NOV, NF) \
+16 -38
Ver Arquivo
@@ -1077,10 +1077,8 @@ void HhbcTranslator::emitCreateCont(Id funNameStrId) {
}
void HhbcTranslator::emitContEnter(int32_t returnBcOffset) {
// The stack should always be clean here; this only appears in generated
// methods we control.
assert(m_evalStack.size() == 0);
assert(m_stackDeficit == 0);
// make sure the value to be sent is on the actual stack
spillStack();
assert(curClass());
SSATmp* cont = gen(LdThis, m_tb->fp());
@@ -1120,10 +1118,6 @@ void HhbcTranslator::emitUnpackCont() {
gen(AssertLoc, Type::Obj, LocalId(0), m_tb->fp());
auto const cont = ldLoc(0);
auto const valOffset = cns(CONTOFF(m_received));
push(gen(LdProp, Type::Cell, cont, valOffset));
gen(StProp, cont, valOffset, m_tb->genDefNull());
push(gen(LdRaw, Type::Int, cont, cns(RawMemSlot::ContLabel)));
}
@@ -1164,40 +1158,24 @@ void HhbcTranslator::emitContCheck(bool checkStarted) {
gen(ContPreNext, getExitSlowTrace(), cont);
}
void HhbcTranslator::emitContNext() {
assert(curClass());
SSATmp* cont = gen(LdThis, m_tb->fp());
if (RuntimeOption::EvalHHIRGenerateAsserts) {
// We're guaranteed to have a Null in m_received at this point
auto const oldVal = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_received)));
gen(DbgAssertType, Type::InitNull, oldVal);
}
}
void HhbcTranslator::emitContSendImpl(bool raise) {
assert(curClass());
SSATmp* cont = gen(LdThis, m_tb->fp());
if (RuntimeOption::EvalHHIRGenerateAsserts) {
// We're guaranteed to have a Null in m_received at this point
auto const oldVal = gen(LdProp, Type::Cell, cont, cns(CONTOFF(m_received)));
gen(DbgAssertType, Type::InitNull, oldVal);
}
gen(AssertLoc, Type::Cell, LocalId(0), m_tb->fp());
gen(StProp, cont, cns(CONTOFF(m_received)), ldLoc(0));
gen(StLoc, LocalId(0), m_tb->fp(), m_tb->genDefUninit());
if (raise) {
SSATmp* label = gen(LdRaw, Type::Int, cont, cns(RawMemSlot::ContLabel));
label = gen(OpSub, label, cns(1));
gen(StRaw, cont, cns(RawMemSlot::ContLabel), label);
}
}
void HhbcTranslator::emitContSend() {
emitContSendImpl(false);
assert(curClass());
// prepare value to be sent by ContEnter
push(gen(LdLoc, Type::Cell, LocalId(0), m_tb->fp()));
gen(StLoc, LocalId(0), m_tb->fp(), m_tb->genDefUninit());
}
void HhbcTranslator::emitContRaise() {
emitContSendImpl(true);
assert(curClass());
SSATmp* cont = gen(LdThis, m_tb->fp());
SSATmp* label = gen(LdRaw, Type::Int, cont, cns(RawMemSlot::ContLabel));
label = gen(OpSub, label, cns(1));
gen(StRaw, cont, cns(RawMemSlot::ContLabel), label);
// prepare value to be sent by ContEnter
push(gen(LdLoc, Type::Cell, LocalId(0), m_tb->fp()));
gen(StLoc, LocalId(0), m_tb->fp(), m_tb->genDefUninit());
}
void HhbcTranslator::emitContValid() {
-2
Ver Arquivo
@@ -381,8 +381,6 @@ struct HhbcTranslator {
void emitPackCont(int64_t labelId);
void emitContRetC();
void emitContCheck(bool checkStarted);
void emitContNext();
void emitContSendImpl(bool raise);
void emitContSend();
void emitContRaise();
void emitContValid();
-4
Ver Arquivo
@@ -568,10 +568,6 @@ void Translator::translateContCheck(const NormalizedInstruction& i) {
HHIR_EMIT(ContCheck, i.imm[0].u_IVA);
}
void Translator::translateContNext(const NormalizedInstruction& i) {
HHIR_EMIT(ContNext);
}
void Translator::translateContSend(const NormalizedInstruction& i) {
HHIR_EMIT(ContSend);
}
+1 -1
Ver Arquivo
@@ -100,7 +100,7 @@ struct VMRegAnchor : private boost::noncopyable {
auto prevAr = g_vmContext->getOuterVMFrame(ar);
const Func* prevF = prevAr->m_func;
vmsp() = ar->m_func->isGenerator() ?
Stack::generatorStackBase(ar) :
Stack::generatorStackBase(ar) - 1 :
(TypedValue*)ar - ar->numArgs();
assert(g_vmContext->m_stack.isValidAddress((uintptr_t)vmsp()));
vmpc() = prevF->unit()->at(prevF->base() + ar->m_soff);
-1
Ver Arquivo
@@ -134,7 +134,6 @@
CASE(PackCont) \
CASE(ContRetC) \
CASE(ContCheck) \
CASE(ContNext) \
CASE(ContSend) \
CASE(ContRaise) \
CASE(ContValid) \
+1 -1
Ver Arquivo
@@ -1644,7 +1644,7 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
// Move rVmSp to the right place: just past all locals
int frameCells = func->numSlotsInFrame();
if (func->isGenerator()) {
frameCells = 0;
frameCells = 1;
} else {
emitLea(a, rVmFp, -cellsToBytes(frameCells), rVmSp);
}
+3 -4
Ver Arquivo
@@ -1264,15 +1264,14 @@ static const struct {
/*** 14. Continuation instructions ***/
{ OpCreateCont, {None, Stack1, OutObject, 1 }},
{ OpContEnter, {None, None, OutNone, 0 }},
{ OpContEnter, {Stack1, None, OutNone, -1 }},
{ OpContExit, {None, None, OutNone, 0 }},
{ OpUnpackCont, {Local, StackTop2, OutInt64, 2 }},
{ OpPackCont, {Local|Stack1, None, OutNone, -1 }},
{ OpContRetC, {Local|Stack1, None, OutNone, -1 }},
{ OpContCheck, {None, None, OutNone, 0 }},
{ OpContNext, {None, None, OutNone, 0 }},
{ OpContSend, {Local, None, OutNone, 0 }},
{ OpContRaise, {Local, None, OutNone, 0 }},
{ OpContSend, {Local, Stack1, OutUnknown, 1 }},
{ OpContRaise, {Local, Stack1, OutUnknown, 1 }},
{ OpContValid, {None, Stack1, OutBoolean, 1 }},
{ OpContCurrent, {None, Stack1, OutUnknown, 1 }},
{ OpContStopped, {None, None, OutNone, 0 }},