new opcode CreateCl

This is the bottleneck for closure perf. Instead of all the code in the prologue, it turned out the expensive thing about closures was constructing them. This bypasses all the roundabout copying involved with __construct and makes a new opcode to do the same thing.
Esse commit está contido em:
ptarjan
2013-04-12 19:49:25 -07:00
commit de Sara Golemon
commit dd36937f94
18 arquivos alterados com 182 adições e 121 exclusões
+4 -87
Ver Arquivo
@@ -1773,7 +1773,6 @@ void EmitterVisitor::visit(FileScopePtr file) {
emitPostponedPinits();
emitPostponedSinits();
emitPostponedCinits();
emitPostponedClosureCtors();
Peephole peephole(m_ue, m_metaInfo);
m_metaInfo.setForUnit(m_ue);
}
@@ -3801,16 +3800,11 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
// We're still at the closure definition site. Emit code to instantiate
// the new anonymous class, with the use variables as arguments.
ExpressionListPtr valuesList(ce->getClosureValues());
Offset fpiStart = m_ue.bcPos();
e.FPushCtorD(useCount, className);
{
FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
for (int i = 0; i < useCount; ++i) {
emitFuncCallArg(e, (*valuesList)[i], i);
}
for (int i = 0; i < useCount; ++i) {
emitBuiltinCallArg(e, (*valuesList)[i], i, useVars[i].second);
}
e.FCall(useCount);
emitPop(e);
e.CreateCl(useCount, className);
// From here on out, we're just building metadata for the closure.
// Instance variables.
@@ -3821,15 +3815,6 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
&uninit, KindOfInvalid);
}
// The constructor. This is entirely generated; all it does is stash its
// arguments in the object's instance variables.
static const StringData* ctorName =
StringData::GetStaticString("__construct");
FuncEmitter* ctor = m_ue.newMethodEmitter(ctorName, pce);
pce->addMethod(ctor);
m_postponedClosureCtors.push_back(
PostponedClosureCtor(useVars, ce, ctor));
// The __invoke method. This is the body of the closure, preceded by
// code that pulls the object's instance variables into locals.
static const StringData* invokeName =
@@ -5638,74 +5623,6 @@ void EmitterVisitor::emitPostponedCinits() {
}
}
void EmitterVisitor::emitPostponedClosureCtors() {
while (!m_postponedClosureCtors.empty()) {
PostponedClosureCtor& ctor = m_postponedClosureCtors.front();
ClosureUseVarVec& useVars = ctor.m_useVars;
FuncEmitter* fe = ctor.m_fe;
const Location* sLoc = ctor.m_expr->getLocation().get();
fe->init(sLoc->line0, sLoc->line1, m_ue.bcPos(), AttrPublic, false, nullptr);
unsigned n = useVars.size();
Emitter e(ctor.m_expr, m_ue, *this);
FuncFinisher ff(this, e, fe);
if (n > 0) {
for (unsigned i = 0; i < n; ++i) {
// To ensure that we get a new local for every use var, we call
// appendParam with an artificial uniquified name. Because there's no
// user code here, the fact that the variable has a made-up name in the
// metadata doesn't matter.
std::ostringstream num;
num << i;
FuncEmitter::ParamInfo pi;
pi.setRef(useVars[i].second);
fe->appendParam(StringData::GetStaticString(num.str()), pi);
if (i) {
m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
false, 0, 0);
}
e.CheckThis();
m_evalStack.push(StackSym::H);
m_evalStack.push(StackSym::T);
m_evalStack.setString(useVars[i].first);
markProp(e);
emitVirtualLocal(i);
if (useVars[i].second) {
emitVGet(e);
emitBind(e);
} else {
emitCGet(e);
emitSet(e);
}
emitPop(e);
}
}
// call parent::__construct()
static StringData* s___construct = StringData::GetStaticString("__construct");
e.String(s___construct);
static StringData* s_Closure = StringData::GetStaticString("Closure");
e.String(s_Closure);
e.AGetC();
Offset fpiStart = m_ue.bcPos();
e.FPushClsMethodF(0);
{
FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
}
e.FCall(0);
e.PopR();
e.Null();
if (n > 0) {
m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::GuardedThis,
false, 0, 0);
}
e.RetC();
m_postponedClosureCtors.pop_front();
}
}
void EmitterVisitor::emitVirtualLocal(int localId,
DataType dt /* = KindOfUnknown */) {
prepareEvalStack();
+6
Ver Arquivo
@@ -3617,6 +3617,12 @@ AKExists [C C] -> [C:Bool]
or object, and raises a warning if $2 is not a string, integer, or
null.
CreateCl <num args> <class name> [F..F] -> [C]
Creates an instance of <class name> and pushes it on the stack.
<class name> must be a subclass of "Closure".
14. Continuation creation and execution
---------------------------------------
+8 -1
Ver Arquivo
@@ -1200,7 +1200,14 @@ FreeSpill S0:ConstInt
Deallocates S0 slots of spill space on the stack.
16. Continuations
16. Continuations & Closures
CreateCl S0:Int S1:Str S2:StkPtr S3:StkPtr
Creates an object of class S1 (which must be a subclass of Closure),
copies S0 number of parameters from S2 into the properties on the object,
saves the $this and Func* from ActRec S3 into the object,
and returns the object.
CreateCont
FillContLocals
+18 -10
Ver Arquivo
@@ -32,16 +32,14 @@ c_Closure::~c_Closure() {
}
void c_Closure::t___construct() {
VM::ActRec* ar;
{
// like calling CallerFrame twice
VM::Transl::VMRegAnchor _;
VMExecutionContext* context = g_vmContext;
VM::ActRec* me = context->getFP();
VM::ActRec* childClosure = context->getPrevVMState(me);
ar = context->getPrevVMState(childClosure);
}
raise_error("Can't create a Closure directly");
}
/**
* sp points to the last use variable on the stack.
* returns the closure so that translator-x64 can just return "rax".
*/
c_Closure* c_Closure::init(int numArgs, VM::ActRec* ar, TypedValue* sp) {
static StringData* invokeName = StringData::GetStaticString("__invoke");
VM::Func* invokeFunc = getVMClass()->lookupMethod(invokeName);
@@ -60,6 +58,16 @@ void c_Closure::t___construct() {
// Change my __invoke's m_cls to be the same as my creator's
VM::Class* scope = ar->m_func->cls();
m_func = invokeFunc->cloneAndSetClass(scope);
// copy the props to instance variables
assert(m_cls->numDeclProperties() == numArgs);
TypedValue* beforeCurUseVar = sp + numArgs;
TypedValue* curProperty = propVec();
for (int i = 0; i < numArgs; i++) {
// teleport the references in here so we don't incref
*curProperty++ = *--beforeCurUseVar;
}
return this;
}
ObjectData* c_Closure::clone() {
+2
Ver Arquivo
@@ -44,6 +44,8 @@ class c_Closure : public ExtObjectData {
public: c_Closure *create();
public:
public: ObjectData* clone();
c_Closure* init(int numArgs, VM::ActRec* ar, TypedValue* sp);
ObjectData* getThisOrClass() { return m_thisOrClass; }
const VM::Func* getInvokeFunc() { return m_func; }
HphpArray* getStaticLocals();
+12
Ver Arquivo
@@ -6683,6 +6683,18 @@ inline void OPTBLD_INLINE VMExecutionContext::iopParent(PC& pc) {
m_stack.pushClass(parent);
}
inline void OPTBLD_INLINE VMExecutionContext::iopCreateCl(PC& pc) {
NEXT();
DECODE_IVA(numArgs);
DECODE_LITSTR(clsName);
Class* cls = Unit::loadClass(clsName);
c_Closure* cl = static_cast<c_Closure*>(newInstance(cls));
c_Closure* cl2 = cl->init(numArgs, m_fp, m_stack.top());
m_stack.ndiscard(numArgs);
assert(cl == cl2);
m_stack.pushObject(cl2);
}
template<bool isMethod>
c_Continuation*
VMExecutionContext::createContinuation(ActRec* fp,
+1
Ver Arquivo
@@ -552,6 +552,7 @@ enum SetOpOp {
O(Parent, NA, NOV, ONE(AV), NF) \
O(LateBoundCls, NA, NOV, ONE(AV), NF) \
O(NativeImpl, NA, NOV, NOV, CF_TF) \
O(CreateCl, TWO(IVA,SA), FMANY, ONE(CV), NF) \
O(CreateCont, TWO(IVA,SA), NOV, ONE(CV), NF) \
O(ContEnter, NA, NOV, NOV, CF) \
O(ContExit, NA, NOV, NOV, CF) \
+61 -11
Ver Arquivo
@@ -28,6 +28,7 @@
#include "runtime/base/runtime_option.h"
#include "runtime/base/string_data.h"
#include "runtime/base/types.h"
#include "runtime/ext/ext_closure.h"
#include "runtime/ext/ext_continuation.h"
#include "runtime/vm/bytecode.h"
#include "runtime/vm/runtime.h"
@@ -3260,7 +3261,7 @@ void CodeGenerator::emitSpillActRec(SSATmp* sp,
}
HOT_FUNC_VM ActRec*
cgNewInstanceHelper(Class* cls,
irNewInstanceHelper(Class* cls,
int numArgs,
Cell* sp,
ActRec* prevAr) {
@@ -3275,7 +3276,7 @@ cgNewInstanceHelper(Class* cls,
}
HOT_FUNC_VM static ActRec*
cgNewInstanceHelperCached(CacheHandle cacheHandle,
irNewInstanceHelperCached(CacheHandle cacheHandle,
const StringData* clsName,
int numArgs,
Cell* sp,
@@ -3283,11 +3284,13 @@ cgNewInstanceHelperCached(CacheHandle cacheHandle,
Cell* obj = sp - 1; // this is where the newly allocated object will go
ActRec* ar = (ActRec*)(uintptr_t(obj) - sizeof(ActRec));
Instance* newObj = newInstanceHelperCached((Class**)handleToPtr(cacheHandle),
clsName,
numArgs,
ar,
prevAr);
Instance* newObj = newInstanceHelperCached(
static_cast<Class**>(handleToPtr(cacheHandle)),
clsName,
numArgs,
ar,
prevAr
);
// store obj into the stack
obj->m_data.pobj = newObj;
obj->m_type = KindOfObject;
@@ -3306,15 +3309,25 @@ static inline ALWAYS_INLINE Class* getKnownClass(Class** classCache,
return cls;
}
static Instance*
newInstanceHelperNoCtorCachedInstance(Class** classCache,
const StringData* clsName) {
Class* cls = getKnownClass(classCache, clsName);
Instance* newObj = newInstance(cls);
newObj->incRefCount();
return newObj;
}
static Cell*
newInstanceHelperNoCtorCached(CacheHandle cacheHandle,
const StringData* clsName,
Cell* sp) {
Cell* obj = sp - 1; // this is where the newly allocated object will go
Class* cls = getKnownClass((Class**)handleToPtr(cacheHandle), clsName);
Instance* newObj = newInstance(cls);
newObj->incRefCount();
Instance* newObj = newInstanceHelperNoCtorCachedInstance(
(Class**)handleToPtr(cacheHandle),
clsName
);
// store obj into the stack
obj->m_data.pobj = newObj;
@@ -3334,7 +3347,7 @@ void CodeGenerator::cgNewObjCached(IRInstruction* inst) {
CacheHandle ch = allocKnownClass(classNameString);
ActRec* (*helper)(CacheHandle, const StringData*, int, Cell*, ActRec*)
= cgNewInstanceHelperCached;
= irNewInstanceHelperCached;
ArgGroup args;
args.imm(ch)
.ssa(clsName)
@@ -3366,6 +3379,43 @@ void CodeGenerator::cgNewObjNoCtorCached(IRInstruction* inst) {
args);
}
void CodeGenerator::cgCreateCl(IRInstruction* inst) {
SSATmp* dst = inst->getDst();
SSATmp* numParams = inst->getSrc(0);
SSATmp* clsName = inst->getSrc(1);
SSATmp* sp = inst->getSrc(2);
SSATmp* fp = inst->getSrc(3);
const StringData* classNameString = clsName->getValStr();
CacheHandle ch = allocKnownClass(classNameString);
Class** classCache = static_cast<Class**>(handleToPtr(ch));
Instance* (*helper)(Class** classCache, const StringData* clsName) =
newInstanceHelperNoCtorCachedInstance;
cgCallHelper(
m_as,
(TCA)helper,
rScratch,
kSyncPoint,
ArgGroup()
.immPtr<Class*>(classCache)
.ssa(clsName)
);
cgCallHelper(
m_as,
Transl::Call(getMethodPtr(&c_Closure::init)),
dst->getReg(),
kSyncPoint,
ArgGroup()
.reg(rScratch)
.ssa(numParams)
.ssa(fp)
.ssa(sp)
);
}
void CodeGenerator::cgCall(IRInstruction* inst) {
SSATmp* actRec = inst->getSrc(0);
SSATmp* returnBcOffset = inst->getSrc(1);
+1 -1
Ver Arquivo
@@ -458,7 +458,7 @@ private:
};
using namespace HPHP::VM::Transl::TargetCache;
ActRec* cgNewInstanceHelper(Class* cls,
ActRec* irNewInstanceHelper(Class* cls,
int numArgs,
Cell* sp,
ActRec* prevAr);
@@ -16,6 +16,7 @@
#include "runtime/vm/translator/hopt/hhbctranslator.h"
#include "util/trace.h"
#include "runtime/ext/ext_closure.h"
#include "runtime/ext/ext_continuation.h"
#include "runtime/vm/translator/translator-runtime.h"
#include "runtime/vm/translator/translator-x64.h"
@@ -1294,6 +1295,21 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
m_tb->genNewObjCached(numParams, className);
}
void HhbcTranslator::emitCreateCl(int32_t numParams, int32_t funNameStrId) {
const StringData* clsName = lookupStringId(funNameStrId);
TRACE(3, "%u: CreateCl %d %s\n", m_bcOff, numParams, clsName->data());
spillStack();
SSATmp* closure = m_tb->gen(
CreateCl,
m_tb->genDefConst(numParams),
m_tb->genDefConst(clsName),
m_tb->getSp(),
m_tb->getFp()
);
discard(numParams);
push(closure);
}
void HhbcTranslator::emitFPushFuncD(int32_t numParams, int32_t funcId) {
TRACE(3, "%u: FPushFuncD %d %d\n", m_bcOff, numParams, funcId);
const NamedEntityPair& nep = lookupNamedEntityPairId(funcId);
@@ -232,6 +232,7 @@ struct HhbcTranslator {
const StringData* methName);
void emitFPushCtorD(int32_t numParams, int32_t classNameStrId);
void emitFPushCtor(int32_t numParams);
void emitCreateCl(int32_t numParams, int32_t classNameStrId);
void emitFCallArray();
void emitFCall(uint32_t numParams,
Offset returnBcOffset,
+4
Ver Arquivo
@@ -301,6 +301,10 @@ O(NewObjCached, D(StkPtr), C(Int) \
S(StkPtr), E|Mem|N|Refs|PRc|Er) \
O(NewObjNoCtorCached, D(StkPtr), S(Str) \
S(StkPtr), E|Mem|N|Refs|PRc|Er) \
O(CreateCl, D(Obj), C(Int) \
S(Str) \
S(StkPtr) \
S(StkPtr), Mem|N) \
O(NewArray, D(Arr), C(Int), E|Mem|N|PRc) \
O(NewTuple, D(Arr), C(Int) S(StkPtr), E|Mem|N|PRc|CRc) \
O(LdRaw, DParam, SUnk, NF) \
@@ -1010,6 +1010,11 @@ void TranslatorX64::irTranslateFPushCtorD(const Tracelet& t,
HHIR_EMIT(FPushCtorD, (i.imm[0].u_IVA), (i.imm[1].u_SA));
}
void TranslatorX64::irTranslateCreateCl(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(CreateCl, (i.imm[0].u_IVA), (i.imm[1].u_SA));
}
// static void fatalNullThis() { raise_error(Strings::FATAL_NULL_THIS); }
void
+1 -1
Ver Arquivo
@@ -75,7 +75,7 @@ static CallMap s_callMap({
{NewArray, (TCA)new_array, DSSA, SNone, {{SSA, 0}}},
{NewTuple, (TCA)new_tuple, DSSA, SNone,
{{SSA, 0}, {SSA, 1}}},
{NewObj, (TCA)cgNewInstanceHelper, DSSA, SSync,
{NewObj, (TCA)irNewInstanceHelper, DSSA, SSync,
{{SSA, 0}, {SSA, 1}, {SSA, 2}, {SSA, 3}}},
{PrintStr, (TCA)print_string, DNone, SNone, {{SSA, 0}}},
{PrintInt, (TCA)print_int, DNone, SNone, {{SSA, 0}}},
@@ -9338,6 +9338,33 @@ TranslatorX64::emitFPushCtorDFast(const NormalizedInstruction& i,
emitVStackStore(a, i, rax, arOff + AROFF(m_this));
}
void
TranslatorX64::translateCreateCl(const Tracelet& t,
const NormalizedInstruction& i) {
int getArgs = i.imm[0].u_IVA;
const StringData* clsName = curUnit()->lookupLitstrId(i.imm[1].u_SA);
LazyScratchReg clsCache(m_regMap);
TargetCache::CacheHandle classCh = TargetCache::allocKnownClass(clsName);
clsCache.alloc();
a. lea_reg64_disp_reg64(rVmTl, classCh, r(clsCache));
EMIT_RCALL(a, i,
newInstanceHelperNoCtorCached,
R(clsCache),
IMM(uintptr_t(clsName)));
EMIT_RCALL(a, i,
getMethodPtr(&c_Closure::init),
R(rax),
IMM(getArgs),
R(rVmFp),
RPLUS(rVmSp, vstackOffset(i, 0)));
m_regMap.bind(rax, i.outStack->location, KindOfObject, RegInfo::DIRTY);
}
static void fatalNullThis() {
raise_error(Strings::FATAL_NULL_THIS);
}
@@ -12379,6 +12406,7 @@ bool TranslatorX64::dumpTCData() {
SUPPORTED_OP(ContCurrent) \
SUPPORTED_OP(FPushCtor) \
SUPPORTED_OP(FPushCtorD) \
SUPPORTED_OP(CreateCl) \
SUPPORTED_OP(StaticLocInit) \
/*
* Always-interp instructions,
@@ -615,6 +615,7 @@ MINSTRS
CASE(InterfaceExists) \
CASE(TraitExists) \
CASE(Dup) \
CASE(CreateCl) \
CASE(CreateCont) \
CASE(ContEnter) \
CASE(ContExit) \
+11 -9
Ver Arquivo
@@ -1209,6 +1209,7 @@ static const struct {
{ OpParent, {None, Stack1, OutClassRef, 1 }},
{ OpLateBoundCls,{None, Stack1, OutClassRef, 1 }},
{ OpNativeImpl, {None, None, OutNone, 0 }},
{ OpCreateCl, {BStackN, Stack1, OutObject, 1 }},
/*** 14. Continuation instructions ***/
@@ -1252,15 +1253,16 @@ int getStackDelta(const NormalizedInstruction& ni) {
int hiddenStackInputs = 0;
initInstrInfo();
Opcode op = ni.op();
if (op == OpFCall) {
int numArgs = ni.imm[0].u_IVA;
return 1 - numArgs - kNumActRecCells;
}
if (op == OpFCallBuiltin) {
return 1 - ni.imm[0].u_IVA;
}
if (op == OpNewTuple) {
return 1 - ni.imm[0].u_IVA;
switch (op) {
case OpFCall: {
int numArgs = ni.imm[0].u_IVA;
return 1 - numArgs - kNumActRecCells;
}
case OpFCallBuiltin:
case OpNewTuple:
case OpCreateCl:
return 1 - ni.imm[0].u_IVA;
}
const InstrInfo& info = instrInfo[op];
if (info.in & MVector) {
+2 -1
Ver Arquivo
@@ -605,7 +605,8 @@ const FlavorDesc* FuncChecker::sig(PC pc) {
return vectorSig(pc, CV);
case OpFCall: // ONE(IVA), FMANY, ONE(RV)
case OpFCallArray:// NA, ONE(FV), ONE(RV)
case OpFCallBuiltin: //TWO(IVA, SA) FMANY, ONE(RV)
case OpFCallBuiltin: //TWO(IVA, SA) FMANY, ONE(RV)
case OpCreateCl: // TWO(IVA,SA), FMANY, ONE(CV)
for (int i = 0, n = instrNumPops(pc); i < n; ++i) {
m_tmp_sig[i] = FV;
}