Inline the outer functions for continuations with no arguments

We store a bunch of data into an ActRec on the stack, then
call a php-level function that calls a C++ function to copy it into an
ActRec on the heap.  This should hopefully be a little better.
Esse commit está contido em:
jdelong
2013-04-14 10:46:32 -07:00
commit de Sara Golemon
commit b26e59a920
22 arquivos alterados com 343 adições e 122 exclusões
+18 -1
Ver Arquivo
@@ -1275,7 +1275,24 @@ D:Obj = CreateCl S0:ConstCls S1:ConstInt S2:FramePtr S3:StkPtr
object. Saves the $this and Func* from ActRec S2 into the object,
and returns the object.
CreateCont
D:Obj = CreateCont S0:TCA S1:FramePtr S2:ConstBool S3:ConstFunc S4:ConstFunc
Create a continuation object. Arguments:
S0 - function pointer to C++ helper
S1 - current frame pointer
S2 - whether the CreateCont hhbc op had getArgs set to true (which means
the function body may call func_get_args, etc).
S3 - the "original" function
S4 - the generator function
D:Obj = InlineCreateCont<origFunc,genFunc> S0:{Obj,Null}
Create a zero-argument continuation object. S0 is the this pointer
for the continuation if it was a method, otherwise null. Used to
weaken the use of the frame in CreateCont when inlining an 'outer'
generator function.
FillContLocals
FillContThis
+1 -19
Ver Arquivo
@@ -41,7 +41,7 @@
"Iterator",
"Awaitable"
],
"footer": " public: void setCalledClass(CStrRef cls) {\n const_assert(!hhvm);\n m_called_class = cls;\n }\nprotected: virtual bool php_sleep(Variant &ret);\npublic:\n inline void preNext() {\n if (m_done) {\n throw_exception(Object(SystemLib::AllocExceptionObject(\n \"Continuation is already finished\")));\n }\n if (m_running) {\n throw_exception(Object(SystemLib::AllocExceptionObject(\n \"Continuation is already running\")));\n }\n m_running = true;\n ++m_index;\n }\n\n inline void startedCheck() {\n if (m_index < 0LL) {\n throw_exception(\n Object(SystemLib::AllocExceptionObject(\"Need to call next() first\")));\n }\n }\n\npublic:\n Object m_obj;\n Array m_args;\n int64_t m_index;\n Variant m_value;\n Variant m_received;\n String m_origFuncName;\n bool m_done;\n bool m_running;\n bool m_should_throw;\n\n int m_localsOffset;\n VM::Func *m_vmFunc;\n int64_t m_label\n VM::ActRec* m_arPtr;\n\n p_ContinuationWaitHandle m_waitHandle;\n\n SmartPtr<HphpArray> m_VMStatics;\n\n String& getCalledClass() { not_reached(); }\n\n HphpArray* getStaticLocals();\n static size_t sizeForLocalsAndIters(int nLocals, int nIters) {\n return (sizeof(c_Continuation) + sizeof(TypedValue) * nLocals +\n sizeof(VM::Iter) * nIters + sizeof(VM::ActRec));\n }\n VM::ActRec* actRec() {\n return m_arPtr;\n }\n TypedValue* locals() {\n return (TypedValue*)(uintptr_t(this) + m_localsOffset);\n }",
"footer": "",
"funcs": [
{
"name": "__construct",
@@ -49,24 +49,6 @@
"type": null
},
"args": [
{
"name": "func",
"type": "Int64"
},
{
"name": "origFuncName",
"type": "String"
},
{
"name": "obj",
"type": "Variant",
"value": "null"
},
{
"name": "args",
"type": "VariantMap",
"value": "null_array"
}
],
"flags": [
]
+7 -1
Ver Arquivo
@@ -427,6 +427,13 @@ public:
static void getElem(TypedValue* base, TypedValue* key, TypedValue* dest);
template<bool isMethod>
static c_Continuation* createContinuationHelper(
const VM::Func* origFunc,
const VM::Func* genFunc,
ObjectData* thisPtr,
ArrayData* args,
VM::Class* frameStaticCls);
template<bool isMethod>
static c_Continuation* createContinuation(ActRec* fp, bool getArgs,
const VM::Func* origFunc,
const VM::Func* genFunc);
@@ -797,7 +804,6 @@ private:
class Silencer {
public:
Silencer() : m_active(false) {}
explicit Silencer(bool);
~Silencer() { if (m_active) disableHelper(); }
+7 -18
Ver Arquivo
@@ -69,20 +69,7 @@ c_Continuation::~c_Continuation() {
}
}
void c_Continuation::t___construct(
int64_t func, CStrRef origFuncName, CVarRef obj, CArrRef args) {
m_vmFunc = (VM::Func*)func;
assert(m_vmFunc);
m_origFuncName = origFuncName;
if (!obj.isNull()) {
m_obj = obj.toObject();
assert(!m_obj.isNull());
} else {
assert(m_obj.isNull());
}
m_args = args;
}
void c_Continuation::t___construct() {}
void c_Continuation::t_update(int64_t label, CVarRef value) {
m_label = label;
@@ -156,24 +143,26 @@ void c_Continuation::t_raised() {
String c_Continuation::t_getorigfuncname() {
String called_class;
String origFunc(const_cast<StringData*>(m_origFuncName));
if (actRec()->hasThis()) {
called_class = actRec()->getThis()->getVMClass()->name()->data();
} else if (actRec()->hasClass()) {
called_class = actRec()->getClass()->name()->data();
}
if (called_class.size() == 0) {
return m_origFuncName;
return origFunc;
}
/*
Replace the class name in m_origFuncName with the LSB class. This
produces more useful traces.
*/
size_t method_pos = m_origFuncName.find("::");
size_t method_pos = origFunc.find("::");
if (method_pos != std::string::npos) {
return concat3(called_class, "::", m_origFuncName.substr(method_pos+2));
return concat3(called_class, "::", origFunc.substr(method_pos+2));
} else {
return m_origFuncName;
return origFunc;
}
}
+22 -4
Ver Arquivo
@@ -42,11 +42,29 @@ class c_Continuation : public ExtObjectData {
this_->m_vmFunc->numIterators()))(this_);
}
// need to implement
public:
explicit c_Continuation(VM::Class* cls = c_Continuation::s_cls);
~c_Continuation();
void t___construct(int64_t func, CStrRef origFuncName, CVarRef obj = uninit_null(), CArrRef args = null_array);
public:
void init(const VM::Func* vmFunc,
const StringData* origFuncName,
ObjectData* thisPtr,
ArrayData* args) noexcept {
m_vmFunc = const_cast<VM::Func*>(vmFunc);
assert(m_vmFunc);
m_origFuncName = origFuncName;
assert(m_origFuncName->isStatic());
if (thisPtr != nullptr) {
m_obj = thisPtr;
} else {
assert(m_obj.isNull());
}
m_args = args;
}
void t___construct();
void t_update(int64_t label, CVarRef value);
Object t_getwaithandle();
int64_t t_getlabel();
@@ -108,7 +126,7 @@ public:
int64_t m_index;
Variant m_value;
Variant m_received;
String m_origFuncName;
const StringData* m_origFuncName;
bool m_done;
bool m_running;
bool m_should_throw;
+50 -35
Ver Arquivo
@@ -6667,53 +6667,40 @@ inline void OPTBLD_INLINE VMExecutionContext::iopCreateCl(PC& pc) {
template<bool isMethod>
c_Continuation*
VMExecutionContext::createContinuation(ActRec* fp,
bool getArgs,
const Func* origFunc,
const Func* genFunc) {
Object obj;
Array args;
if (fp->hasThis()) {
obj = fp->getThis();
}
if (getArgs) {
args = hhvm_get_frame_args(fp);
}
static const StringData* closure = StringData::GetStaticString("{closure}");
const StringData* origName =
origFunc->isClosureBody() ? closure : origFunc->fullName();
int nLocals = genFunc->numLocals();
int nIters = genFunc->numIterators();
Class* genClass = SystemLib::s_ContinuationClass;
c_Continuation* cont = c_Continuation::alloc(genClass, nLocals, nIters);
VMExecutionContext::createContinuationHelper(const Func* origFunc,
const Func* genFunc,
ObjectData* thisPtr,
ArrayData* args,
Class* frameStaticCls) {
auto const cont = c_Continuation::alloc(
SystemLib::s_ContinuationClass,
genFunc->numLocals(),
genFunc->numIterators()
);
cont->incRefCount();
cont->setNoDestruct();
try {
cont->t___construct(uintptr_t(genFunc),
StrNR(const_cast<StringData*>(origName)),
obj, args);
} catch (...) {
decRefObj(cont);
throw;
}
static auto const closureName = StringData::GetStaticString("{closure}");
auto const origName = origFunc->isClosureBody() ? closureName
: origFunc->fullName();
cont->init(genFunc, origName, thisPtr, args);
// The ActRec corresponding to the generator body lives as long as the object
// does. We set it up once, here, and then just change FP to point to it when
// we enter the generator body.
ActRec* ar = cont->actRec();
if (isMethod) {
Class* cls = frameStaticClass(fp);
if (origFunc->isClosureBody()) {
genFunc = genFunc->cloneAndSetClass(fp->m_func->cls());
genFunc = genFunc->cloneAndSetClass(origFunc->cls());
}
if (obj.get()) {
ObjectData* objData = obj.get();
ar->setThis(objData);
objData->incRefCount();
if (thisPtr) {
ar->setThis(thisPtr);
thisPtr->incRefCount();
} else {
ar->setClass(cls);
ar->setClass(frameStaticCls);
}
} else {
ar->setThis(nullptr);
@@ -6734,6 +6721,28 @@ VMExecutionContext::createContinuation(ActRec* fp,
return cont;
}
template<bool isMethod>
c_Continuation*
VMExecutionContext::createContinuation(ActRec* fp,
bool getArgs,
const Func* origFunc,
const Func* genFunc) {
ObjectData* const thisPtr = fp->hasThis() ? fp->getThis() : nullptr;
Array args;
if (getArgs) {
args = hhvm_get_frame_args(fp);
}
return createContinuationHelper<isMethod>(
origFunc,
genFunc,
thisPtr,
args.get(),
frameStaticClass(fp)
);
}
static inline void setContVar(const Func* genFunc,
const StringData* name,
TypedValue* src,
@@ -6797,6 +6806,12 @@ VMExecutionContext::fillContinuationVars(ActRec* fp,
return cont;
}
// Explicitly instantiate for hhbctranslator.o
template c_Continuation* VMExecutionContext::createContinuation<true>(
ActRec*, bool, const Func*, const Func*);
template c_Continuation* VMExecutionContext::createContinuation<false>(
ActRec*, bool, const Func*, const Func*);
inline void OPTBLD_INLINE VMExecutionContext::iopCreateCont(PC& pc) {
NEXT();
DECODE_IVA(getArgs);
+29
Ver Arquivo
@@ -749,6 +749,9 @@ void CodeGenerator::cgCallNative(Asm& a, IRInstruction* inst) {
case VecKeyIS:
argGroup.vectorKeyIS(src);
break;
case Immed:
always_assert(0 && "We can't generate a native call for this");
break;
}
}
@@ -3216,6 +3219,32 @@ void CodeGenerator::cgNewObjNoCtorCached(IRInstruction* inst) {
args);
}
void CodeGenerator::cgInlineCreateCont(IRInstruction* inst) {
auto const& data = *inst->getExtra<InlineCreateCont>();
auto const helper = data.origFunc->isMethod()
? &VMExecutionContext::createContinuationHelper<true>
: &VMExecutionContext::createContinuationHelper<false>;
cgCallHelper(
m_as,
reinterpret_cast<TCA>(helper),
inst->getDst(),
kSyncPoint,
ArgGroup()
.immPtr(data.origFunc)
.immPtr(data.genFunc)
.ssa(inst->getSrc(0))
.immPtr(nullptr) // getArgs array
// Deliberately ignoring frameStaticClass parameter, because
// it's unused if we have a $this pointer, and we don't inline
// functions with a null $this.
);
if (data.origFunc->isMethod()) {
// We can't support a null $this.
assert(inst->getSrc(0)->isA(Type::Obj));
}
}
void CodeGenerator::cgCall(IRInstruction* inst) {
SSATmp* actRec = inst->getSrc(0);
SSATmp* returnBcOffset = inst->getSrc(1);
+2
Ver Arquivo
@@ -399,6 +399,8 @@ struct ArgGroup {
return imm(uintptr_t(ptr));
}
ArgGroup& immPtr(std::nullptr_t) { return imm(0); }
ArgGroup& reg(PhysReg reg) {
m_args.push_back(ArgDesc(ArgDesc::Reg, PhysReg(reg), -1));
return *this;
+43 -5
Ver Arquivo
@@ -61,7 +61,7 @@ struct DceFlags {
*/
void incWeakUse() {
if (m_weakUseCount + 1 > kMaxWeakUseCount) {
always_assert(!"currently there's only one instruction "
always_assert(!"currently there's only two instructions "
"using this machinery, so this shouldn't "
"happen ... ");
return;
@@ -368,7 +368,7 @@ void sinkIncRefs(Trace* trace, IRFactory* irFactory, DceState& state) {
* of a DefInlineFP. In this case we can kill both, which may allow
* removing a SpillFrame as well.
*/
void optimizeActRecs(Trace* trace, DceState& state) {
void optimizeActRecs(Trace* trace, DceState& state, IRFactory* factory) {
FTRACE(5, "AR:vvvvvvvvvvvvvvvvvvvvv\n");
SCOPE_EXIT { FTRACE(5, "AR:^^^^^^^^^^^^^^^^^^^^^\n"); };
@@ -389,6 +389,22 @@ void optimizeActRecs(Trace* trace, DceState& state) {
}
break;
case CreateCont:
{
auto const frameInst = inst->getSrc(1)->inst();
if (frameInst->op() == DefInlineFP) {
auto const spInst = frameInst->getSrc(0)->inst();
if (spInst->op() == SpillFrame &&
spInst->getSrc(3)->type().subtypeOfAny(Type::Obj, Type::Null)) {
FTRACE(5, "CreateCont ({}): weak use of frame {}\n",
inst->getIId(),
frameInst->getIId());
state[frameInst].incWeakUse();
}
}
}
break;
case InlineReturn:
{
auto frameUses = inst->getSrc(0)->getUseCount();
@@ -423,7 +439,7 @@ void optimizeActRecs(Trace* trace, DceState& state) {
switch (inst->op()) {
case DecRefKillThis:
{
auto fp = inst->getSrc(1);
auto const fp = inst->getSrc(1);
if (state[fp->inst()].isDead()) {
FTRACE(5, "DecRefKillThis ({}) -> DecRef\n", inst->getIId());
inst->setOpcode(DecRef);
@@ -433,9 +449,31 @@ void optimizeActRecs(Trace* trace, DceState& state) {
}
break;
case CreateCont:
{
auto const fp = inst->getSrc(1);
if (state[fp->inst()].isDead()) {
FTRACE(5, "CreateCont ({}) -> InlineCreateCont\n", inst->getIId());
CreateContData data;
data.origFunc = inst->getSrc(3)->getValFunc();
data.genFunc = inst->getSrc(4)->getValFunc();
assert(fp->inst()->getSrc(0)->inst()->op() == SpillFrame);
auto const thisPtr = fp->inst()->getSrc(0)->inst()->getSrc(3);
factory->replace(
inst,
InlineCreateCont,
data,
thisPtr
);
}
}
break;
case InlineReturn:
{
auto fp = inst->getSrc(0);
auto const fp = inst->getSrc(0);
if (state[fp->inst()].isDead()) {
FTRACE(5, "InlineReturn ({}) setDead\n", inst->getIId());
state[inst].setDead();
@@ -591,7 +629,7 @@ void eliminateDeadCode(Trace* trace, IRFactory* irFactory) {
// Optimize unused inlined activation records. It's not necessary
// to look at non-main traces for this.
optimizeActRecs(trace, state);
optimizeActRecs(trace, state, irFactory);
// now remove instructions whose id == DEAD
removeDeadInstructions(trace, state);
+22 -14
Ver Arquivo
@@ -837,29 +837,37 @@ void HhbcTranslator::emitIterFree(uint32_t iterId) {
void HhbcTranslator::emitCreateCont(bool getArgs,
Id funNameStrId) {
/* Runtime-determined slow path punts to TranslatorX64 for now */
m_tb->genExitOnVarEnv(getExitSlowTrace());
m_tb->gen(ExitOnVarEnv, getExitSlowTrace()->front(), m_tb->getFp());
const StringData* genName = lookupStringId(funNameStrId);
const Func* origFunc = getCurFunc();
const Func* genFunc = origFunc->getGeneratorBody(genName);
int origLocals = origFunc->numLocals();
int genLocals = genFunc->numLocals();
auto const genName = lookupStringId(funNameStrId);
auto const origFunc = getCurFunc();
auto const genFunc = origFunc->getGeneratorBody(genName);
auto const origLocals = origFunc->numLocals();
auto const genLocals = genFunc->numLocals();
TCA helper = origFunc->isMethod() ?
(TCA)&VMExecutionContext::createContinuation<true> :
(TCA)&VMExecutionContext::createContinuation<false>;
SSATmp* cont = m_tb->gen(CreateCont, cns(helper), m_tb->getFp(),
cns(getArgs), cns(origFunc), cns(genFunc));
auto const cont = m_tb->gen(
CreateCont,
cns(
origFunc->isMethod() ?
(TCA)&VMExecutionContext::createContinuation<true> :
(TCA)&VMExecutionContext::createContinuation<false>
),
m_tb->getFp(),
cns(getArgs),
cns(origFunc),
cns(genFunc)
);
TranslatorX64::ContParamMap params;
if (origLocals <= TranslatorX64::kMaxInlineContLocals &&
TranslatorX64::mapContParams(params, origFunc, genFunc)) {
static const StringData* thisStr = StringData::GetStaticString("this");
static auto const thisStr = StringData::GetStaticString("this");
Id thisId = kInvalidId;
bool fillThis = origFunc->isNonClosureMethod() && !origFunc->isStatic() &&
const bool fillThis = origFunc->isNonClosureMethod() &&
!origFunc->isStatic() &&
((thisId = genFunc->lookupVarId(thisStr)) != kInvalidId) &&
(origFunc->lookupVarId(thisStr) == kInvalidId);
SSATmp* locals = m_tb->gen(LdContLocalsPtr, cont);
for (int i = 0; i < origLocals; ++i) {
SSATmp* loc = m_tb->genIncRef(m_tb->genLdAssertedLoc(i, Type::Gen));
+23 -5
Ver Arquivo
@@ -633,6 +633,25 @@ void IRInstruction::convertToMov() {
assert(m_numDsts == 1);
}
void IRInstruction::become(IRFactory* factory, IRInstruction* other) {
assert(other->isTransient() || m_numDsts == other->m_numDsts);
assert(!canCSE());
auto& arena = factory->arena();
// Copy all but m_iid, m_block, m_listNode, and don't clone
// dests---the whole point of become() is things still point to us.
m_op = other->m_op;
m_typeParam = other->m_typeParam;
m_id = other->m_id;
m_liveRegs = other->m_liveRegs;
m_taken = other->m_taken;
m_tca = other->m_tca;
m_numSrcs = other->m_numSrcs;
m_extra = other->m_extra ? cloneExtra(m_op, other->m_extra, arena) : nullptr;
m_srcs = new (arena) SSATmp*[m_numSrcs];
std::copy(other->m_srcs, other->m_srcs + m_numSrcs, m_srcs);
}
IRInstruction* IRInstruction::clone(IRFactory* factory) const {
return factory->cloneInstruction(this);
}
@@ -889,11 +908,10 @@ static void printConst(std::ostream& os, IRInstruction* inst) {
os << "NamedEntity(" << ne << ")";
} else if (t.subtypeOf(Type::TCA)) {
TCA tca = c->as<TCA>();
char* nameRaw = Util::getNativeFunctionName(tca);
SCOPE_EXIT { free(nameRaw); };
std::string name(nameRaw);
boost::algorithm::trim(name);
os << folly::format("TCA: {}({})", tca, name);
auto name = Util::getNativeFunctionName(tca);
SCOPE_EXIT { free(name); };
os << folly::format("TCA: {}({})", tca,
boost::trim_copy(std::string(name)));
} else if (t.subtypeOf(Type::None)) {
os << "None:" << c->as<int64_t>();
} else if (t.isPtr()) {
+35 -4
Ver Arquivo
@@ -26,9 +26,16 @@
#include <forward_list>
#include <cassert>
#include <type_traits>
#include <boost/noncopyable.hpp>
#include <boost/checked_delete.hpp>
#include <boost/type_traits/has_trivial_destructor.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/intrusive/list.hpp>
#include "folly/Range.h"
#include "folly/Conv.h"
#include "util/asm-x64.h"
#include "util/trace.h"
#include "runtime/ext/ext_continuation.h"
@@ -40,9 +47,6 @@
#include "runtime/base/types.h"
#include "runtime/vm/func.h"
#include "runtime/vm/class.h"
#include "folly/Range.h"
#include "folly/Conv.h"
#include <boost/intrusive/list.hpp>
namespace HPHP {
// forward declaration
@@ -430,6 +434,7 @@ O(CreateCont, D(Obj), C(TCA) \
C(Bool) \
C(Func) \
C(Func), E|N|Mem|PRc) \
O(InlineCreateCont, D(Obj), S(Obj,Null), E|N|PRc) \
O(FillContLocals, ND, S(FramePtr) \
C(Func) \
C(Func) \
@@ -735,6 +740,11 @@ private:
uintptr_t m_dataBits;
};
struct CreateContData : IRExtraData {
const Func* origFunc;
const Func* genFunc;
};
/*
* EdgeData is linked list node that tracks the set of Jmp_'s that pass values
* to a particular block. Each such Jmp_ has one node, and the block points
@@ -778,7 +788,6 @@ struct StackOffset : IRExtraData {
int32_t offset;
};
struct BCOffset : IRExtraData {
explicit BCOffset(Offset offset) : offset(offset) {}
@@ -817,6 +826,7 @@ X(ReDefSP, StackOffset);
X(ReDefGeneratorSP, StackOffset);
X(DefSP, StackOffset);
X(DefInlineFP, BCOffset);
X(InlineCreateCont, CreateContData);
#undef X
@@ -1733,9 +1743,29 @@ struct IRInstruction {
/*
* Replace an instruction in place with a Mov. Used when we have
* proven that the instruction's side effects are not needed.
*
* TODO: replace with become
*/
void convertToMov();
/*
* Turns this instruction into the target instruction, without
* changing stable fields (IId, current block, list fields). The
* existing destination SSATmp(s) will continue to think they came
* from this instruction.
*
* The target instruction may be transient---we'll clone anything we
* need to keep, using factory for any needed memory.
*
* Note: if you want to use this to replace a CSE-able instruction
* you're probably going to have a bad time. For now it's a
* precondition that the current instruction can't CSE.
*
* Pre: other->isTransient() || numDsts() == other->numDsts()
* Pre: !canCSE()
*/
void become(IRFactory*, IRInstruction* other);
/*
* Deep-copy an IRInstruction, using factory to allocate memory for
* the IRInstruction itself, and its srcs/dests.
@@ -1767,6 +1797,7 @@ struct IRInstruction {
m_dst = newDst;
m_numDsts = newDst ? 1 : 0;
}
/*
* Returns the ith dest of this instruction. i == 0 is treated specially: if
* the instruction has no dests, getDst(0) will return nullptr, and if the
+25 -4
Ver Arquivo
@@ -178,10 +178,28 @@ public:
);
}
template<class T>
T* cloneInstruction(const T* inst) {
T* newInst = new (m_arena) T(m_arena, inst,
IRInstruction::IId(m_nextInstId++));
/*
* Replace an existing IRInstruction with a new one.
*
* This may involve making more allocations in the arena, but the
* actual IRInstruction* itself, its IId, etc, will stay unchanged.
*
* This function takes arguments in the same format as gen().
*/
template<class... Args>
void replace(IRInstruction* old, Args... args) {
makeInstruction(
[&] (IRInstruction* replacement) { old->become(this, replacement); },
args...
);
}
/*
* Clone an instruction and its sources.
*/
IRInstruction* cloneInstruction(const IRInstruction* inst) {
auto newInst = new (m_arena) IRInstruction(
m_arena, inst, IRInstruction::IId(m_nextInstId++));
if (newInst->modifiesStack()) {
assert(newInst->naryDst());
// The instruction is an opcode that modifies the stack, returning a new
@@ -198,6 +216,9 @@ public:
return newInst;
}
/*
* Some helpers for creating specific instruction patterns.
*/
IRInstruction* defLabel();
IRInstruction* defLabel(unsigned numDst);
template<typename T> SSATmp* defConst(T val) {
+13 -2
Ver Arquivo
@@ -1108,7 +1108,9 @@ TranslatorX64::irTranslateFCallBuiltin(const Tracelet& t,
HHIR_EMIT(FCallBuiltin, numArgs, numNonDefault, funcId);
}
static bool shouldIRInline(const Func* func, const Tracelet& callee) {
static bool shouldIRInline(const Func* curFunc,
const Func* func,
const Tracelet& callee) {
if (!RuntimeOption::EvalHHIREnableGenTimeInlining) {
return false;
}
@@ -1243,6 +1245,15 @@ static bool shouldIRInline(const Func* func, const Tracelet& callee) {
}
}
/*
* Continuation allocation functions that take no arguments.
*/
resetCursor();
if (current == OpCreateCont && cursor->imm[0].u_IVA == 0) {
next();
if (atRet()) return accept("zero-arg continuation creator");
}
/*
* Anything that just puts a value on the stack with no inputs, and
* then returns it, after possibly doing some comparison with
@@ -1281,7 +1292,7 @@ TranslatorX64::irTranslateFCall(const Tracelet& t,
*/
if (i.calleeTrace) {
if (!m_hhbcTrans->isInlining() &&
shouldIRInline(i.funcd, *i.calleeTrace)) {
shouldIRInline(curFunc(), i.funcd, *i.calleeTrace)) {
m_hhbcTrans->beginInlining(numArgs, i.funcd, returnBcOffset);
static const bool shapeStats = Stats::enabledAny() &&
getenv("HHVM_STATS_INLINESHAPE");
@@ -668,6 +668,8 @@ void LinearScan::computePreColoringHint() {
m_preColoringHint.add(inst->getSrc(arg.srcIdx), 0, reg++);
m_preColoringHint.add(inst->getSrc(arg.srcIdx), 1, reg++);
break;
case Immed:
break;
}
}
return;
@@ -41,18 +41,22 @@ static const DestType DNone = DestType::None;
*
* Opcode
* The opcode that uses the call
*
* Func
* A value describing the function to call:
* (TCA)<function pointer> - Raw function pointer
* {FSSA, idx} - Use a const TCA from inst->getSrc(idx)
*
* Dest
* DSSA - The helper returns a single-register value
* DTV - The helper returns a TypedValue in two registers
* DNone - The helper does not return a value
*
* SyncPoint
* SNone - The helper does not need a sync point
* SSync - The helper needs a normal sync point
* SSyncAdj1 - The helper needs a sync point that skips top of stack on unwind
*
* Args
* A list of tuples describing the arguments to pass to the helper
* {SSA, idx} - Pass the value in inst->getSrc(idx)
@@ -61,6 +65,8 @@ static const DestType DNone = DestType::None;
* {VecKeyS, idx} - Like TV, but Str values are passed as a raw
* StringData*, in a single register
* {VecKeyIS, idx} - Like VecKeyS, including Int
* {Immed} - There's no precoloring to do here, an internal immediate is
* used in cgFoo for this function
*/
static CallMap s_callMap({
/* Opcode, Func, Dest, SyncPoint, Args */
@@ -167,6 +173,8 @@ static CallMap s_callMap({
/* Continuation support helpers */
{CreateCont, {FSSA, 0}, DSSA, SNone,
{{SSA, 1}, {SSA, 2}, {SSA, 3}, {SSA, 4}}},
{InlineCreateCont, nullptr, DSSA, SSync,
{{Immed}, {Immed}, {SSA, 0}}},
{FillContLocals, (TCA)&VMExecutionContext::fillContinuationVars,
DNone, SNone,
{{SSA, 0}, {SSA, 1}, {SSA, 2}, {SSA, 3}}},
@@ -51,6 +51,7 @@ enum ArgType : unsigned {
TV,
VecKeyS,
VecKeyIS,
Immed,
};
struct Arg {
ArgType type;
@@ -166,6 +166,8 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) {
case SpillStack: return simplifySpillStack(inst);
case Call: return simplifyCall(inst);
case ExitOnVarEnv: return simplifyExitOnVarEnv(inst);
default:
return nullptr;
}
@@ -275,6 +277,16 @@ SSATmp* Simplifier::simplifyCall(IRInstruction* inst) {
return nullptr;
}
// We never inline functions that could have a VarEnv, so an
// ExitOnVarEnv that has a frame based on DefInlineFP can be removed.
SSATmp* Simplifier::simplifyExitOnVarEnv(IRInstruction* inst) {
auto const frameInst = inst->getSrc(0)->inst();
if (frameInst->op() == DefInlineFP) {
inst->convertToNop();
}
return nullptr;
}
SSATmp* Simplifier::simplifyLdCtx(IRInstruction* inst) {
const Func* func = inst->getSrc(1)->getValFunc();
if (func->isStatic()) {
+1 -2
Ver Arquivo
@@ -93,14 +93,13 @@ private:
SSATmp* simplifyGetCtxFwdCall(IRInstruction* inst);
SSATmp* simplifySpillStack(IRInstruction* inst);
SSATmp* simplifyCall(IRInstruction* inst);
private:
SSATmp* genDefInt(int64_t val);
SSATmp* genDefDbl(double val);
SSATmp* genDefBool(bool val);
SSATmp* simplifyCmp(Opcode opName, SSATmp* src1, SSATmp* src2);
SSATmp* simplifyCondJmp(IRInstruction*);
SSATmp* simplifyQueryJmp(IRInstruction*);
SSATmp* simplifyExitOnVarEnv(IRInstruction*);
private:
TraceBuilder* const m_tb;
+2 -6
Ver Arquivo
@@ -24389,12 +24389,8 @@ const char *g_class_map[] = {
"/**\n * ( excerpt from http://php.net/manual/en/class.continuation.php )\n *\n *\n */",
"iterator", "awaitable", NULL,
(const char *)0x10006040, "__construct", "", (const char*)0, (const char*)0,
"/**\n * ( excerpt from http://php.net/manual/en/continuation.construct.php )\n *\n *\n * @func int\n * @origFuncName\n * string\n * @obj mixed\n * @args map\n */",
(const char *)0x8 /* KindOfNull */, (const char *)0x2000, "func", "", (const char *)0xa /* KindOfInt64 */, "", (const char *)0, "", (const char *)0, NULL,
(const char *)0x2000, "origFuncName", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
(const char *)0x2000, "obj", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "N;", (const char *)2, "null", (const char *)4, NULL,
(const char *)0x2000, "args", "", (const char *)0x20 /* KindOfArray */, "N;", (const char *)2, "null", (const char *)4, NULL,
NULL,
"/**\n * ( excerpt from http://php.net/manual/en/continuation.construct.php )\n *\n *\n */",
(const char *)0x8 /* KindOfNull */, NULL,
NULL,
NULL,
(const char *)0x10006040, "update", "", (const char*)0, (const char*)0,
+19 -2
Ver Arquivo
@@ -10,6 +10,13 @@ class CGetM {
function getX() {
return $this->x;
}
public function genVarious() {
$local = $this->getX();
yield "a";
yield $local;
yield "c";
}
}
function foo() {
@@ -19,6 +26,16 @@ function foo() {
yield "\n";
}
foreach (foo() as $x) {
echo $x;
function main() {
foreach (foo() as $x) {
echo $x;
}
$blah = new CGetM;
foreach ($blah->genVarious() as $x) {
echo $x;
}
}
main();
echo "\n";
@@ -1 +1,2 @@
asdasd
aasdasdc