Remove Continuation from local 0

No emitted bytecode relies on Continuation being stored in local 0
anymore. Stop using local 0 for this purpose and compute offset
to the Continuation at JIT time. 16 bytes of memory freed.

At this point all locals of Continuation construction wrapper share the
same indices with their respective locals of Continuation body, which
should allow further optimizations.
Esse commit está contido em:
Mirek Klimos
2013-07-17 22:32:40 -07:00
commit de Sara Golemon
commit 90b7170582
21 arquivos alterados com 853 adições e 968 exclusões
Arquivo normal → Arquivo executável
+1 -15
Ver Arquivo
@@ -2243,8 +2243,6 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
if (m_curFunc->isGenerator()) {
assert(!retV);
m_metaInfo.addKnownDataType(
KindOfObject, false, m_ue.bcPos(), false, 1);
assert(m_evalStack.size() == 1);
e.ContRetC();
return false;
@@ -3147,10 +3145,6 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
SimpleFunctionCallPtr call(
static_pointer_cast<SimpleFunctionCall>(node));
ExpressionListPtr params = call->getParams();
auto inputIsAnObject = [&](int inputIndex) {
m_metaInfo.addKnownDataType(KindOfObject, /* predicted */ false,
m_ue.bcPos(), false, inputIndex);
};
if (call->isFatalFunction()) {
if (params && params->getCount() == 1) {
@@ -3241,7 +3235,6 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
} else if (call->isCompilerCallToFunction("hphp_unpack_continuation")) {
assert(!params || params->getCount() == 0);
int yieldLabelCount = call->getFunctionScope()->getYieldLabelCount();
inputIsAnObject(0);
emitContinuationSwitch(e, yieldLabelCount);
return false;
} else if (call->isCompilerCallToFunction("hphp_create_continuation")) {
@@ -3273,7 +3266,6 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
assert(params && params->getCount() == 1);
visit((*params)[0]);
emitConvertToCell(e);
inputIsAnObject(1);
assert(m_evalStack.size() == 1);
e.ContRetC();
e.Null();
@@ -3971,16 +3963,12 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
int64_t normalLabel = 2 * y->getLabel();
int64_t exceptLabel = normalLabel - 1;
// pack continuation and set the return label
// suspend continuation and set the return label
if (keyExp) {
assert(m_evalStack.size() == 2);
m_metaInfo.addKnownDataType(
KindOfObject, false, m_ue.bcPos(), false, 2);
e.ContSuspendK(normalLabel);
} else {
assert(m_evalStack.size() == 1);
m_metaInfo.addKnownDataType(
KindOfObject, false, m_ue.bcPos(), false, 1);
e.ContSuspend(normalLabel);
}
@@ -5620,8 +5608,6 @@ void EmitterVisitor::emitPostponedMeths() {
if (currentPositionIsReachable()) {
e.Null();
if (p.m_meth->getFunctionScope()->isGenerator()) {
m_metaInfo.addKnownDataType(
KindOfObject, false, m_ue.bcPos(), false, 1);
assert(m_evalStack.size() == 1);
e.ContRetC();
} else {
+1 -4
Ver Arquivo
@@ -344,10 +344,7 @@ bool FunctionScope::isClosure() const {
}
bool FunctionScope::isGenerator() const {
assert(!getOrigGenStmt() ||
(ParserBase::IsContinuationName(name()) &&
m_paramNames.size() == 1 &&
m_paramNames[0] == CONTINUATION_OBJECT_NAME));
assert(!getOrigGenStmt() || ParserBase::IsContinuationName(name()));
return !!getOrigGenStmt();
}
@@ -105,15 +105,6 @@ void SimpleVariable::coalesce(SimpleVariablePtr other) {
m_name = m_sym->getName();
}
string SimpleVariable::getNamePrefix() const {
bool needsCont = getFunctionScope()->isGenerator();
bool isHidden = m_sym && m_sym->isHidden();
return (needsCont &&
m_name != CONTINUATION_OBJECT_NAME &&
!isHidden) ?
string(TYPED_CONTINUATION_OBJECT_NAME) + "->" : string("");
}
/*
This simple variable is about to go out of scope.
Is it ok to kill the last assignment?
-2
Ver Arquivo
@@ -59,8 +59,6 @@ public:
void updateSymbol(SimpleVariablePtr src);
void coalesce(SimpleVariablePtr other);
private:
std::string getNamePrefix() const;
std::string m_name;
std::string m_docComment;
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+5 -9
Ver Arquivo
@@ -113,7 +113,7 @@
using namespace HPHP::Compiler;
extern void prepare_generator(Parser *_p, Token &stmt, Token &params);
extern void prepare_generator(Parser *_p, Token &stmt);
extern void create_generator(Parser *_p, Token &out, Token &params,
Token &name, const std::string &genName,
const char *clsname, Token *modifiers,
@@ -844,12 +844,10 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
if (funcContext.isGenerator) {
string genName = newContinuationName(funcName);
Token new_params;
prepare_generator(this, stmt, new_params);
prepare_generator(this, stmt);
func = NEW_STMT(FunctionStatement, exp, ref->num(), genName,
dynamic_pointer_cast<ExpressionList>(new_params->exp),
ret.typeAnnotationName(),
ExpressionListPtr(), ret.typeAnnotationName(),
dynamic_pointer_cast<StatementList>(stmt->stmt),
attribute, comment, ExpressionListPtr());
out->stmt = func;
@@ -1225,12 +1223,10 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
genName = newContinuationName(funcName + "@" + m_clsName);
}
Token new_params;
prepare_generator(this, stmt, new_params);
prepare_generator(this, stmt);
ModifierExpressionPtr exp2 = Construct::Clone(exp);
mth = NEW_STMT(MethodStatement, exp2, ref->num(), genName,
dynamic_pointer_cast<ExpressionList>(new_params->exp),
ret.typeAnnotationName(),
ExpressionListPtr(), ret.typeAnnotationName(),
dynamic_pointer_cast<StatementList>(stmt->stmt),
attribute, comment, ExpressionListPtr());
out->stmt = mth;
@@ -380,9 +380,6 @@ void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
MethodStatementRawPtr orig = getOrigGeneratorFunc();
VariableTablePtr variables = funcScope->getVariables();
Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
cont->setHidden();
orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure);
orig->getFunctionScope()->setContainsBareThis(
funcScope->containsBareThis(), funcScope->containsRefThis());
+1 -4
Ver Arquivo
@@ -245,10 +245,7 @@ void CmdNext::cleanupStepCont() {
// continuation, or we'll stop when we get back into it, we know the object
// will remain alive.
void* CmdNext::getContinuationTag(ActRec* fp) {
TypedValue* tv = frame_local(fp, 0);
assert(tv->m_type == HPHP::KindOfObject);
assert(dynamic_cast<c_Continuation*>(tv->m_data.pobj));
c_Continuation* cont = static_cast<c_Continuation*>(tv->m_data.pobj);
c_Continuation* cont = frame_continuation(fp);
TRACE(2, "CmdNext: continuation tag %p for %s\n", cont,
cont->t_getorigfuncname()->data());
return cont;
Arquivo normal → Arquivo executável
+1 -14
Ver Arquivo
@@ -42,8 +42,6 @@ p_Continuation f_hphp_create_continuation(CStrRef clsname,
///////////////////////////////////////////////////////////////////////////////
static StaticString s___cont__("__cont__");
c_Continuation::c_Continuation(Class* cb)
: ExtObjectData(cb)
, m_label(0)
@@ -57,13 +55,6 @@ c_Continuation::c_Continuation(Class* cb)
c_Continuation::~c_Continuation() {
ActRec* ar = actRec();
// The first local is the object itself, and it wasn't increffed at creation
// time (see createContinuation()). Overwrite its type to exempt it from
// refcounting here.
TypedValue* contLocal = frame_local(ar, 0);
assert(contLocal->m_data.pobj == this);
contLocal->m_type = KindOfNull;
if (ar->hasVarEnv()) {
ar->getVarEnv()->detach(ar);
} else {
@@ -191,17 +182,13 @@ void c_Continuation::copyContinuationVars(ActRec* fp) {
skipThis = definedVariables.exists(s_this, true);
for (ArrayIter iter(definedVariables); !iter.end(); iter.next()) {
if (iter.first().getStringData()->same(s___cont__.get())) {
continue;
}
dupContVar(iter.first().getStringData(),
const_cast<TypedValue *>(iter.secondRef().asTypedValue()));
}
} else {
const Func *genFunc = actRec()->m_func;
skipThis = genFunc->lookupVarId(thisStr) != kInvalidId;
// skip local 0 because that's the old continuation
for (Id i = 1; i < genFunc->numNamedLocals(); ++i) {
for (Id i = 0; i < genFunc->numNamedLocals(); ++i) {
dupContVar(genFunc->localVarName(i), frame_local(fp, i));
}
}
+3 -20
Ver Arquivo
@@ -2067,10 +2067,7 @@ Array VMExecutionContext::debugBacktrace(bool skip /* = false */,
String funcname = const_cast<StringData*>(fp->m_func->name());
if (fp->m_func->isGenerator()) {
// retrieve the original function name from the inner continuation
TypedValue* tv = frame_local(fp, 0);
assert(tv->m_type == HPHP::KindOfObject);
funcname = static_cast<c_Continuation*>(
tv->m_data.pobj)->t_getorigfuncname();
funcname = frame_continuation(fp)->t_getorigfuncname();
}
if (fp->m_func->isClosureBody()) {
@@ -6605,17 +6602,9 @@ static inline c_Continuation* createCont(const Func* origFunc,
// we enter the generator body.
ActRec* ar = cont->actRec();
ar->m_func = genFunc;
ar->initNumArgs(1);
ar->initNumArgs(0);
ar->setVarEnv(nullptr);
TypedValue* contLocal = frame_local(ar, 0);
contLocal->m_type = KindOfObject;
contLocal->m_data.pobj = cont;
// Do not incref the continuation here! Doing so will create a reference
// cycle, since this reference is a local in the continuation frame and thus
// will be decreffed when the continuation is destroyed. The corresponding
// non-decref is in ~c_Continuation.
return cont;
}
@@ -6728,13 +6717,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopCreateCont(PC& pc) {
ret->m_data.pobj = cont;
}
static inline c_Continuation* frame_continuation(ActRec* fp) {
ObjectData* obj = frame_local(fp, 0)->m_data.pobj;
assert(dynamic_cast<c_Continuation*>(obj));
return static_cast<c_Continuation*>(obj);
}
static inline c_Continuation* this_continuation(ActRec* fp) {
static inline c_Continuation* this_continuation(const ActRec* fp) {
ObjectData* obj = fp->getThis();
assert(dynamic_cast<c_Continuation*>(obj));
return static_cast<c_Continuation*>(obj);
Arquivo normal → Arquivo executável
Ver Arquivo
+1 -1
Ver Arquivo
@@ -899,7 +899,7 @@ class RawMemSlot {
}
static RawMemSlot& GetContEntry() {
static RawMemSlot m(
Func::prologueTableOff() + sizeof(HPHP::Transl::TCA),
Func::prologueTableOff(),
Transl::sz::qword, Type::TCA);
return m;
}
Arquivo normal → Arquivo executável
+6 -14
Ver Arquivo
@@ -1266,10 +1266,10 @@ static const struct {
{ OpCreateCont, {None, Stack1, OutObject, 1 }},
{ OpContEnter, {Stack1, None, OutNone, -1 }},
{ OpUnpackCont, {Local, StackTop2, OutInt64, 2 }},
{ OpContSuspend, {Local|Stack1, None, OutNone, -1 }},
{ OpContSuspendK,{Local|StackTop2, None, OutNone, -2 }},
{ OpContRetC, {Local|Stack1, None, OutNone, -1 }},
{ OpUnpackCont, {None, StackTop2, OutInt64, 2 }},
{ OpContSuspend, {Stack1, None, OutNone, -1 }},
{ OpContSuspendK,{StackTop2, None, OutNone, -2 }},
{ OpContRetC, {Stack1, None, OutNone, -1 }},
{ OpContCheck, {None, None, OutNone, 0 }},
{ OpContRaise, {None, None, OutNone, 0 }},
{ OpContValid, {None, Stack1, OutBoolean, 1 }},
@@ -1862,19 +1862,11 @@ void getInputsImpl(SrcKey startSk,
addMVectorInputs(*ni, currentStackOffset, inputs);
}
if (input & Local) {
// Many of the continuation instructions read local 0. All other
// instructions that take a Local have its index at their first
// All instructions that take a Local have its index at their first
// immediate.
int loc;
auto insertAt = inputs.end();
switch (ni->op()) {
case OpUnpackCont:
case OpContSuspend:
case OpContSuspendK:
case OpContRetC:
loc = 0;
break;
case OpSetWithRefLM:
insertAt = inputs.begin();
// fallthrough
@@ -2751,7 +2743,7 @@ Translator::getOperandConstraintCategory(NormalizedInstruction* instr,
case OpContSuspendK:
case OpContRetC:
// The stack input is teleported to the continuation's m_value field
return opndIdx == 0 ? DataTypeGeneric : DataTypeSpecific;
return DataTypeGeneric;
case OpContHandle:
// This always calls the interpreter
+2 -5
Ver Arquivo
@@ -360,11 +360,8 @@ HphpArray* get_static_locals(const ActRec* ar) {
assert(dynamic_cast<c_Closure*>(closureLoc->m_data.pobj));
return static_cast<c_Closure*>(closureLoc->m_data.pobj)->getStaticLocals();
} else if (ar->m_func->isGeneratorFromClosure()) {
TypedValue* contLoc = frame_local(ar, 0);
assert(dynamic_cast<c_Continuation*>(contLoc->m_data.pobj));
c_Continuation* cont = static_cast<c_Continuation*>(contLoc->m_data.pobj);
TypedValue* closureLoc = frame_local(ar, cont->m_origFunc->numParams() + 1);
c_Continuation* cont = frame_continuation(ar);
TypedValue* closureLoc = frame_local(ar, cont->m_origFunc->numParams());
assert(dynamic_cast<c_Closure*>(closureLoc->m_data.pobj));
return static_cast<c_Closure*>(closureLoc->m_data.pobj)->getStaticLocals();
} else {
+9
Ver Arquivo
@@ -16,6 +16,7 @@
#ifndef incl_HPHP_VM_RUNTIME_H_
#define incl_HPHP_VM_RUNTIME_H_
#include "hphp/runtime/ext/ext_continuation.h"
#include "hphp/runtime/vm/event_hook.h"
#include "hphp/runtime/vm/func.h"
#include "hphp/runtime/vm/funcdict.h"
@@ -80,6 +81,14 @@ frame_local_inner(const ActRec* fp, int n) {
return ret->m_type == KindOfRef ? ret->m_data.pref->tv() : ret;
}
inline c_Continuation*
frame_continuation(const ActRec* fp) {
size_t arOffset = c_Continuation::getArOffset(fp->m_func);
ObjectData* obj = (ObjectData*)((char*)fp - arOffset);
assert(dynamic_cast<c_Continuation*>(obj));
return static_cast<c_Continuation*>(obj);
}
/*
* 'Unwinding' versions of the below frame_free_locals_* functions
* zero locals and the $this pointer.
+3 -3
Ver Arquivo
@@ -660,7 +660,7 @@ bool TestParserStmt::TestYieldStatement() {
WithOpt w1(Option::EnableHipHopSyntax);
V("<?php function foo() { yield break;}",
"function ($" CONTINUATION_OBJECT_NAME ") {\n"
"function () {\n"
"hphp_unpack_continuation();\n"
"return;\n"
"}\n"
@@ -670,7 +670,7 @@ bool TestParserStmt::TestYieldStatement() {
"}\n");
V("<?php function foo() { yield 123;}",
"function ($" CONTINUATION_OBJECT_NAME ") {\n"
"function () {\n"
"hphp_unpack_continuation();\n"
"yield 123;\n"
"}\n"
@@ -685,7 +685,7 @@ bool TestParserStmt::TestYieldStatement() {
"return hphp_create_continuation"
"(__CLASS__, 'foo$continuation', __METHOD__);\n"
"}\n"
"public function ($" CONTINUATION_OBJECT_NAME ") {\n"
"public function () {\n"
"hphp_unpack_continuation();\n"
"yield 123;\n"
"yield 456;\n"
+3 -12
Ver Arquivo
@@ -574,10 +574,7 @@ array(3) {
[2]=>
array(1) {
["args"]=>
array(1) {
[0]=>
object(Continuation)#2 (0) {
}
array(0) {
}
}
}
@@ -674,10 +671,7 @@ array(3) {
[2]=>
array(1) {
["args"]=>
array(1) {
[0]=>
object(Continuation)#2 (0) {
}
array(0) {
}
}
}
@@ -766,10 +760,7 @@ array(3) {
[2]=>
array(1) {
["args"]=>
array(1) {
[0]=>
object(Continuation)#2 (0) {
}
array(0) {
}
}
}
@@ -4,10 +4,7 @@ array(3) {
["function"]=>
string(12) "my_generator"
["args"]=>
array(1) {
[0]=>
object(Continuation)#1 (0) {
}
array(0) {
}
}
[1]=>
@@ -54,10 +51,7 @@ array(3) {
["type"]=>
string(2) "::"
["args"]=>
array(1) {
[0]=>
object(Continuation)#1 (0) {
}
array(0) {
}
}
[1]=>
+9 -18
Ver Arquivo
@@ -48,7 +48,7 @@ array(5) {
}
int(4)
string(32) "Continuation is already finished"
array(13) {
array(12) {
["b"]=>
int(2)
["g"]=>
@@ -61,23 +61,20 @@ array(13) {
int(4)
["e"]=>
int(5)
["__cont__"]=>
object(Continuation)#5 (0) {
}
["c"]=>
int(3)
["j"]=>
int(10)
["k"]=>
int(11)
["h"]=>
int(8)
["a"]=>
int(1)
["k"]=>
int(11)
["l"]=>
int(12)
}
array(13) {
array(12) {
["b"]=>
int(3735928559)
["g"]=>
@@ -90,24 +87,21 @@ array(13) {
int(4)
["e"]=>
int(5)
["__cont__"]=>
object(Continuation)#5 (0) {
}
["c"]=>
int(3)
["j"]=>
int(10)
["k"]=>
int(11)
["h"]=>
int(8)
["a"]=>
object(stdClass)#6 (0) {
}
["k"]=>
int(11)
["l"]=>
int(12)
}
array(13) {
array(12) {
["b"]=>
int(3735928559)
["g"]=>
@@ -120,21 +114,18 @@ array(13) {
int(3126047761)
["e"]=>
int(3126047761)
["__cont__"]=>
object(Continuation)#5 (0) {
}
["c"]=>
object(Continuation)#5 (0) {
}
["j"]=>
int(10)
["k"]=>
int(11)
["h"]=>
int(8)
["a"]=>
object(stdClass)#6 (0) {
}
["k"]=>
int(11)
["l"]=>
int(12)
}
Arquivo normal → Arquivo executável
+1 -10
Ver Arquivo
@@ -142,7 +142,7 @@ static void on_constant(Parser *_p, Token &out, Token &name, Token &value) {
///////////////////////////////////////////////////////////////////////////////
// continuation transformations
void prepare_generator(Parser *_p, Token &stmt, Token &params) {
void prepare_generator(Parser *_p, Token &stmt) {
// 1. add prologue and epilogue to original body and store it back to "stmt"
{
// hphp_unpack_continuation()
@@ -158,15 +158,6 @@ void prepare_generator(Parser *_p, Token &stmt, Token &params) {
stmt.reset();
_p->finishStatement(stmt, stmts2); stmt = 1;
}
// 2. prepare a single continuation parameter list and store it in "params"
{
Token type; type.setText("Continuation");
Token var; var.setText(CONTINUATION_OBJECT_NAME);
params.reset();
type.reset();
_p->onParam(params, NULL, type, var, false, NULL, NULL, NULL);
}
}
// create a generator function with original name and parameters
-3
Ver Arquivo
@@ -37,9 +37,6 @@
m_xhpAttributes.reset(); \
} \
#define CONTINUATION_OBJECT_NAME "__cont__"
#define TYPED_CONTINUATION_OBJECT_NAME "__typedCont__"
#define FOREACH_VAR_PREFIX "__foreach__"
#define NAMESPACE_SEP '\\'
namespace HPHP {