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:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
CASE(PackCont) \
|
||||
CASE(ContRetC) \
|
||||
CASE(ContCheck) \
|
||||
CASE(ContNext) \
|
||||
CASE(ContSend) \
|
||||
CASE(ContRaise) \
|
||||
CASE(ContValid) \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 }},
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário