Add bytecodes to decode a function, and push an actrec for a decoded function

array_filter and friends only need to evaluate the callback once,
so add bytecodes to support that.

Depends on D817883
Esse commit está contido em:
mwilliams
2013-05-24 08:43:43 -07:00
commit de sgolemon
commit 78ca8ecbaa
27 arquivos alterados com 390 adições e 61 exclusões
+26 -9
Ver Arquivo
@@ -650,13 +650,14 @@ protected by a fault funclet that unsets i by calling IterFree or MIterFree so
that when the unwinder or dispatcher pops the current frame, each iterator
variable is uninitialized.
13) The iterator variable referenced by IterInit* or MIterInit* must be in
the uninitialized state when the instruction executes. An iterator variable
referenced by IterNext* and IterFree must be in the "iter-initialized" state,
and an iterator variable referenced by MIterNext* or MIterFree must be in the
"miter-initialized" state. Note that IterInit* and MIterInit* conditionally
initializes the iterator variable, and IterNext* and MIterNext* conditionally
frees the iterator variable.
13) The iterator variable referenced by IterInit* or MIterInit* or
DecodeCufIter must be in the uninitialized state when the instruction
executes. An iterator variable referenced by IterNext* and IterFree must be
in the "iter-initialized" state, an iterator variable referenced by MIterNext*
or MIterFree must be in the "miter-initialized" state, and an iterator variable
referenced by FPushCufIter or CIterFree must be in the citer-initialized state.
Note that IterInit* and MIterInit* conditionally initialize the iterator variable,
and IterNext* and MIterNext* conditionally free the iterator variable.
Instruction set
@@ -1703,6 +1704,17 @@ FPushCtorD <num params> <litstr id> [] -> [C]
passed (given by %1) and a reference to the FPI structure for the constructor
for class x.
DecodeCufIter <iterator id> [C] -> [C]
This instruction looks up $1 as a callable, and writes enough information
to iterator %1 for FPushDecoded to be able to push an actrec, as if it had
been given the callable.
If the function is successfully decoded, pushes true, otherwise, pushes
false, and sets up iter to call a function that does nothing, and returns
Null. No warning is raised.
FPushCufIter <num params> <iterator id> [] -> []
FPI push the result of a previous DecodeCufIter. No warning is raised.
FPushCuf <num params> [C] -> []
FPushCufF <num params> [C] -> []
@@ -3463,6 +3475,11 @@ MIterFree <iterator id> [] -> []
only needed for guarding against exceptions and implementing break and
return control flow statements inside iterator loops.
CIterFree <iterator id> [] -> []
Cuf iterator free. This instruction frees the specified iterator
variable.
12. Include, eval, and define instructions
------------------------------------------
@@ -3782,8 +3799,8 @@ ensure that the iterator variable is freed when a foreach loop exits abnormally
through an exception.
Simple break statements and continue statements are implemented using the Jmp*,
IterFree, and MIterFree instructions. Dynamic break is implemented using an
unnamed local (to store the 'break count') and a chain of basic blocks, where
IterFree, MIterFree and CIterFree instructions. Dynamic break is implemented using
an unnamed local (to store the 'break count') and a chain of basic blocks, where
each block decrements the unnamed local variable and compares it with 0, and
then decides where to jump next.
+17 -1
Ver Arquivo
@@ -918,6 +918,17 @@ D:StkPtr = SpillFrame<numArgs,invName> S0:StkPtr
Defines the fields for an activation record and writes them to the
stack pointed to by S1.
D:StkPtr = CufIterSpillFrame<numArgs,iterId> S0:StkPtr
S1:FramePtr
Operands:
S0 - caller stack pointer
S1 - caller frame pointer
Defines the fields for an activation record using data from
the iterator iterId, and writes them to the stack pointed to by S1
D:FramePtr = FreeActRec S0:FramePtr
Load the saved frame pointer from the activation record pointed to
@@ -1595,7 +1606,12 @@ D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
The WIterInit and WIterInitK instructions copy referenced array
elements by reference, and non-referenced array elements by value.
IterFree S0:FramePtr S1:ConstInt
IterFree<IterId> S0:FramePtr
Free the iterator variable whose index is given by S1 in the stack
frame pointed to by S0.
CIterFree<IterId> S0:FramePtr
Free the iterator variable whose index is given by S1 in the stack
frame pointed to by S0.
+11
Ver Arquivo
@@ -567,6 +567,13 @@ MArrayIter::~MArrayIter() {
}
}
CufIter::~CufIter() {
if (m_ctx && !(uintptr_t(m_ctx) & 1)) {
decRefObj((ObjectData*)m_ctx);
}
if (m_name) decRefStr(m_name);
}
bool Iter::init(TypedValue* c1) {
assert(c1->m_type != KindOfRef);
bool hasElems = true;
@@ -701,6 +708,10 @@ void Iter::mfree() {
marr().~MArrayIter();
}
void Iter::cfree() {
cuf().~CufIter();
}
/*
* iter_value_cell* will store a copy of the current value at the address
* given by 'out'. iter_value_cell* will increment the refcount of the current
+28 -1
Ver Arquivo
@@ -444,6 +444,30 @@ class MArrayIter : public FullPos {
friend struct Iter;
};
class CufIter {
public:
CufIter() {}
~CufIter();
const Func* func() const { return m_func; }
void* ctx() const { return m_ctx; }
StringData* name() const { return m_name; }
void setFunc(const Func* f) { m_func = f; }
void setCtx(ObjectData* obj) { m_ctx = obj; }
void setCtx(const Class* cls) {
m_ctx = cls ? (void*)((char*)cls + 1) : nullptr;
}
void setName(StringData* name) { m_name = name; }
static uint32_t funcOff() { return offsetof(CufIter, m_func); }
static uint32_t ctxOff() { return offsetof(CufIter, m_ctx); }
static uint32_t nameOff() { return offsetof(CufIter, m_name); }
private:
const Func* m_func;
void* m_ctx;
StringData* m_name;
};
struct Iter {
ArrayIter& arr() {
return *(ArrayIter*)m_u;
@@ -451,16 +475,19 @@ struct Iter {
MArrayIter& marr() {
return *(MArrayIter*)m_u;
}
CufIter& cuf() { return *(CufIter*)m_u; }
bool init(TypedValue* c1);
bool minit(TypedValue* v1);
bool next();
bool mnext();
void free();
void mfree();
void cfree();
private:
// C++ won't let you have union members with constructors. So we get to
// implement unions by hand.
char m_u[MAX(sizeof(ArrayIter), sizeof(MArrayIter))];
char m_u[MAX(MAX(sizeof(ArrayIter), sizeof(MArrayIter)), sizeof(CufIter))];
} __attribute__ ((aligned(16)));
bool interp_init_iterator(Iter* it, TypedValue* c1);
+73
Ver Arquivo
@@ -5902,6 +5902,72 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFPushCtorD(PC& pc) {
ar->setVarEnv(nullptr);
}
inline void OPTBLD_INLINE VMExecutionContext::iopDecodeCufIter(PC& pc) {
NEXT();
DECODE_IA(itId);
Iter* it = frame_iter(m_fp, itId);
CufIter &cit = it->cuf();
ObjectData* obj = nullptr;
HPHP::Class* cls = nullptr;
StringData* invName = nullptr;
TypedValue *func = m_stack.topTV();
ActRec* ar = m_fp;
if (m_fp->m_func->isBuiltin()) {
ar = getOuterVMFrame(ar);
}
const Func* f = vm_decode_function(tvAsVariant(func),
ar, false,
obj, cls, invName,
true);
bool ret = true;
if (f == nullptr) {
f = SystemLib::s_nullFunc;
obj = nullptr;
cls = nullptr;
invName = nullptr;
ret = false;
}
cit.setFunc(f);
if (obj) {
cit.setCtx(obj);
obj->incRefCount();
} else {
cit.setCtx(cls);
}
cit.setName(invName);
tvAsVariant(m_stack.top()) = ret;
}
inline void OPTBLD_INLINE VMExecutionContext::iopFPushCufIter(PC& pc) {
NEXT();
DECODE_IVA(numArgs);
DECODE_IA(itId);
Iter* it = frame_iter(m_fp, itId);
auto f = it->cuf().func();
auto o = it->cuf().ctx();
auto n = it->cuf().name();
ActRec* ar = m_stack.allocA();
arSetSfp(ar, m_fp);
ar->m_func = f;
ar->m_this = (ObjectData*)o;
if (o && !(uintptr_t(o) & 1)) ar->m_this->incRefCount();
if (n) {
ar->setInvName(n);
n->incRefCount();
} else {
ar->setVarEnv(nullptr);
}
ar->initNumArgs(numArgs, false /* isFPushCtor */);
}
inline void OPTBLD_INLINE VMExecutionContext::doFPushCuf(PC& pc,
bool forward,
bool safe) {
@@ -6667,6 +6733,13 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMIterFree(PC& pc) {
it->mfree();
}
inline void OPTBLD_INLINE VMExecutionContext::iopCIterFree(PC& pc) {
NEXT();
DECODE_IA(itId);
Iter* it = frame_iter(m_fp, itId);
it->cfree();
}
inline void OPTBLD_INLINE inclOp(VMExecutionContext *ec, PC &pc,
InclOpFlags flags) {
NEXT();
+1
Ver Arquivo
@@ -499,6 +499,7 @@ bool pushesActRec(Opcode opcode) {
case OpFPushClsMethodD:
case OpFPushCtor:
case OpFPushCtorD:
case OpFPushCufIter:
case OpFPushCuf:
case OpFPushCufF:
case OpFPushCufSafe:
+3
Ver Arquivo
@@ -505,6 +505,7 @@ enum SetOpOp {
O(FPushClsMethodD, THREE(IVA,SA,SA), NOV, NOV, NF) \
O(FPushCtor, ONE(IVA), ONE(AV), ONE(CV), NF) \
O(FPushCtorD, TWO(IVA,SA), NOV, ONE(CV), NF) \
O(FPushCufIter, TWO(IVA,IA), NOV, NOV, NF) \
O(FPushCuf, ONE(IVA), ONE(CV), NOV, NF) \
O(FPushCufF, ONE(IVA), ONE(CV), NOV, NF) \
O(FPushCufSafe, ONE(IVA), TWO(CV,CV), TWO(CV,CV), NF) \
@@ -539,6 +540,8 @@ enum SetOpOp {
O(WIterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(IterFree, ONE(IA), NOV, NOV, NF) \
O(MIterFree, ONE(IA), NOV, NOV, NF) \
O(DecodeCufIter, ONE(IA), ONE(CV), ONE(CV), NF) \
O(CIterFree, ONE(IA), NOV, NOV, NF) \
O(Incl, NA, ONE(CV), ONE(CV), CF) \
O(InclOnce, NA, ONE(CV), ONE(CV), CF) \
O(Req, NA, ONE(CV), ONE(CV), CF) \
+58 -4
Ver Arquivo
@@ -2566,6 +2566,11 @@ int CodeGenerator::getIterOffset(SSATmp* tmp) {
return -cellsToBytes(((index + 1) * kNumIterCells + func->numLocals()));
}
int CodeGenerator::getIterOffset(uint32_t id) {
const Func* func = getCurFunc();
return -cellsToBytes(((id + 1) * kNumIterCells + func->numLocals()));
}
void CodeGenerator::cgStLoc(IRInstruction* inst) {
cgStore(m_regs[inst->src(0)].getReg(),
getLocalOffset(inst->extra<StLoc>()->locId),
@@ -3142,6 +3147,44 @@ void CodeGenerator::cgDecRefNZOrBranch(IRInstruction* inst) {
cgDecRefWork(inst, true);
}
void CodeGenerator::cgCufIterSpillFrame(IRInstruction* inst) {
auto const sp = inst->src(0);
auto const fp = inst->src(1);
auto const nArgs = inst->extra<CufIterSpillFrame>()->args;
auto const iterId = inst->extra<CufIterSpillFrame>()->iterId;
auto const itOff = getIterOffset(iterId);
const int64_t spOffset = -kNumActRecCells * sizeof(Cell);
auto spReg = m_regs[sp].getReg();
auto fpReg = m_regs[fp].getReg();
m_as.loadq (fpReg[itOff + CufIter::funcOff()], m_rScratch);
m_as.storeq (m_rScratch, spReg[spOffset + int(AROFF(m_func))]);
m_as.loadq (fpReg[itOff + CufIter::ctxOff()], m_rScratch);
m_as.storeq (m_rScratch, spReg[spOffset + int(AROFF(m_this))]);
m_as.shrq (1, m_rScratch);
ifThen(m_as, CC_NBE, [this] {
m_as.shlq(1, m_rScratch);
emitIncRef(m_as, m_rScratch);
});
m_as.loadq (fpReg[itOff + CufIter::nameOff()], m_rScratch);
m_as.testq (m_rScratch, m_rScratch);
ifThen(m_as, CC_NZ, [this] {
m_as.cmpl(RefCountStaticValue, m_rScratch[FAST_REFCOUNT_OFFSET]);
ifThen(m_as, CC_NE, [&] { emitIncRef(m_as, m_rScratch); });
m_as.orq (ActRec::kInvNameBit, m_rScratch);
});
m_as.storeq (m_rScratch, spReg[spOffset + int(AROFF(m_invName))]);
m_as.storeq (fpReg, spReg[spOffset + int(AROFF(m_savedRbp))]);
m_as.storel (nArgs, spReg[spOffset + int(AROFF(m_numArgsAndCtorFlag))]);
emitAdjustSp(spReg,
m_regs[inst->dst()].getReg(),
spOffset);
}
void CodeGenerator::cgSpillFrame(IRInstruction* inst) {
auto const sp = inst->src(0);
auto const fp = inst->src(1);
@@ -3196,9 +3239,9 @@ void CodeGenerator::cgSpillFrame(IRInstruction* inst) {
uintptr_t invName = !magicName
? 0
: reinterpret_cast<uintptr_t>(magicName) | ActRec::kInvNameBit;
m_as.store_imm64_disp_reg64(invName,
spOffset + int(AROFF(m_invName)),
spReg);
m_as.store_imm64_disp_reg64(invName,
spOffset + int(AROFF(m_invName)),
spReg);
// actRec->m_func and possibly actRec->m_cls
// Note m_cls is unioned with m_this and may overwrite previous value
if (func->type().isNull()) {
@@ -5141,13 +5184,24 @@ void iterFreeHelper(Iter* iter) {
iter->free();
}
void citerFreeHelper(Iter* iter) {
iter->cfree();
}
void CodeGenerator::cgIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].getReg();
int64_t offset = getIterOffset(inst->src(1));
int64_t offset = getIterOffset(inst->extra<IterFree>()->iterId);
cgCallHelper(m_as, (TCA)iterFreeHelper, InvalidReg, kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset));
}
void CodeGenerator::cgCIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].getReg();
int64_t offset = getIterOffset(inst->extra<CIterFree>()->iterId);
cgCallHelper(m_as, (TCA)citerFreeHelper, InvalidReg, kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset));
}
void CodeGenerator::cgIncStat(IRInstruction *inst) {
Stats::emitInc(m_as,
Stats::StatCounter(inst->src(0)->getValInt()),
+1
Ver Arquivo
@@ -323,6 +323,7 @@ private:
Address getDtorGeneric();
Address getDtorTyped();
int getIterOffset(SSATmp* tmp);
int getIterOffset(uint32_t id);
void emitReqBindAddr(const Func* func, TCA& dest, Offset offset);
void emitAdjustSp(PhysReg spReg, PhysReg dstReg, int64_t adjustment);
+34
Ver Arquivo
@@ -126,6 +126,37 @@ struct LocalId : IRExtraData {
uint32_t locId;
};
struct IterId : IRExtraData {
explicit IterId(uint32_t id)
: iterId(id)
{}
bool cseEquals(IterId o) const { return iterId == o.iterId; }
size_t cseHash() const { return std::hash<uint32_t>()(iterId); }
std::string show() const { return folly::to<std::string>(iterId); }
uint32_t iterId;
};
struct FPushCufData : IRExtraData {
FPushCufData(uint32_t a, int32_t id)
: args(a), iterId(id)
{}
bool cseEquals(FPushCufData o) const {
return iterId == o.iterId && args == o.args;
}
size_t cseHash() const {
return std::hash<uint32_t>()(iterId) ^ std::hash<uint32_t>()(args);
}
std::string show() const {
return folly::to<std::string>(iterId, ',', args);
}
uint32_t args;
uint32_t iterId;
};
struct ConstData : IRExtraData {
template<class T>
explicit ConstData(T data)
@@ -272,6 +303,9 @@ X(DecRefLoc, LocalId);
X(LdLoc, LocalId);
X(StLoc, LocalId);
X(StLocNT, LocalId);
X(IterFree, IterId);
X(CIterFree, IterId);
X(CufIterSpillFrame, FPushCufData);
X(DefConst, ConstData);
X(LdConst, ConstData);
X(SpillFrame, ActRecInfo);
@@ -957,7 +957,11 @@ void HhbcTranslator::emitWIterNextK(uint32_t iterId,
}
void HhbcTranslator::emitIterFree(uint32_t iterId) {
gen(IterFree, m_tb->getFp(), cns(iterId));
gen(IterFree, IterId(iterId), m_tb->getFp());
}
void HhbcTranslator::emitCIterFree(uint32_t iterId) {
gen(CIterFree, IterId(iterId), m_tb->getFp());
}
void HhbcTranslator::emitCreateCont(bool getArgs,
@@ -1539,6 +1543,15 @@ void HhbcTranslator::emitFPassV() {
gen(DecRef, tmp);
}
void HhbcTranslator::emitFPushCufIter(int32_t numParams,
int32_t itId) {
auto sp = spillStack();
m_fpiStack.emplace(sp, m_tb->getSpOffset());
gen(CufIterSpillFrame,
FPushCufData(numParams, itId),
sp, m_tb->getFp());
}
void HhbcTranslator::emitFPushCufOp(Op op, Class* cls, StringData* invName,
const Func* callee, int numArgs) {
const Func* curFunc = getCurFunc();
@@ -235,10 +235,11 @@ struct HhbcTranslator {
void emitFPassCOp();
void emitFPassR();
void emitFPassV();
void emitFPushCufIter(int32_t numParams, int32_t itId);
void emitFPushCufOp(Op op, Class* cls, StringData* invName,
const Func* func, int numArgs);
void emitFPushActRec(SSATmp* func, SSATmp* objOrClass, int32_t numArgs,
const StringData* invName);
const StringData* invName = nullptr);
void emitFPushFuncD(int32_t numParams, int32_t funcId);
void emitFPushFuncU(int32_t numParams,
int32_t funcId,
@@ -351,6 +352,7 @@ struct HhbcTranslator {
uint32_t keyLocalId);
void emitIterFree(uint32_t iterId);
void emitCIterFree(uint32_t iterId);
void emitVerifyParamType(uint32_t paramId);
// continuations
+4 -1
Ver Arquivo
@@ -368,6 +368,8 @@ O(SpillFrame, D(StkPtr), S(StkPtr) \
S(FramePtr) \
S(Func,FuncCls,FuncCtx,Null) \
S(Ctx,Cls,InitNull), CRc) \
O(CufIterSpillFrame, D(StkPtr), S(StkPtr) \
S(FramePtr), NF) \
O(ExceptionBarrier, D(StkPtr), S(StkPtr), E) \
O(ReqBindJmp, ND, NA, T|E) \
O(ReqBindJmpNoIR, ND, NA, T|E) \
@@ -471,7 +473,8 @@ O(WIterNext, D(Bool), S(FramePtr) \
C(Int) C(Int), E|N|Mem|Refs) \
O(WIterNextK, D(Bool), S(FramePtr) \
C(Int) C(Int) C(Int), E|N|Mem|Refs) \
O(IterFree, ND, S(FramePtr) C(Int), E|N|Mem|Refs) \
O(IterFree, ND, S(FramePtr), E|N|Mem|Refs) \
O(CIterFree, ND, S(FramePtr), E|N|Mem|Refs) \
O(DefMIStateBase, D(PtrToCell), NA, NF) \
O(BaseG, D(PtrToGen), C(TCA) \
S(Str) \
@@ -1059,6 +1059,12 @@ TranslatorX64::irTranslateFPassV(const Tracelet& t,
HHIR_EMIT(FPassV);
}
void
TranslatorX64::irTranslateFPushCufIter(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(FPushCufIter, i.imm[0].u_IVA, i.imm[1].u_IA);
}
void
TranslatorX64::irTranslateFPushCufOp(const Tracelet& t,
const NormalizedInstruction& i) {
@@ -1449,6 +1455,13 @@ TranslatorX64::irTranslateIterFree(const Tracelet& t,
HHIR_EMIT(IterFree, i.imm[0].u_IVA);
}
void
TranslatorX64::irTranslateCIterFree(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(CIterFree, i.imm[0].u_IVA);
}
// PSEUDOINSTR_DISPATCH is a switch() fragment that routes opcodes to their
// shared handlers, as per the PSEUDOINSTRS macro.
#define PSEUDOINSTR_DISPATCH(func) \
@@ -457,6 +457,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
opc == CallArray ||
opc == SpillStack ||
opc == SpillFrame ||
opc == CufIterSpillFrame ||
opc == ExceptionBarrier ||
opc == RetAdjustStack ||
opc == InterpOne ||
@@ -129,6 +129,7 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
}
case SpillFrame:
case CufIterSpillFrame:
return getStackValue(inst->src(0),
// pushes an ActRec
index - kNumActRecCells);
@@ -229,6 +229,7 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
}
case SpillFrame:
case CufIterSpillFrame:
m_spValue = inst->dst();
m_spOffset += kNumActRecCells;
break;
+3 -2
Ver Arquivo
@@ -11943,8 +11943,9 @@ bool TranslatorX64::dumpTCData() {
INTERP_OP(SetOpM) \
INTERP_OP(IncDecM) \
INTERP_OP(BindM) \
INTERP_OP(UnsetM)
INTERP_OP(UnsetM) \
INTERP_OP(FPushCufIter) \
INTERP_OP(CIterFree)
// Define the trivial analyze methods
#define PLAN(Op, Spt) \
+2 -1
Ver Arquivo
@@ -556,7 +556,8 @@ private:
CASE(Strlen) \
CASE(IncStat) \
CASE(ArrayIdx) \
CASE(FPushCufIter) \
CASE(CIterFree) \
// These are instruction-like functions which cover more than one
// opcode.
#define PSEUDOINSTRS \
+6 -1
Ver Arquivo
@@ -1292,6 +1292,8 @@ static const struct {
kNumActRecCells }},
{ OpFPushCtorD, {None, Stack1|FStack,OutObject,
kNumActRecCells + 1 }},
{ OpFPushCufIter,{None, FStack, OutFDesc,
kNumActRecCells }},
{ OpFPushCuf, {Stack1, FStack, OutFDesc,
kNumActRecCells - 1 }},
{ OpFPushCufF, {Stack1, FStack, OutFDesc,
@@ -1325,6 +1327,7 @@ static const struct {
Stack1, OutArray, -2 }},
{ OpCufSafeReturn,{StackTop3|DontGuardAny,
Stack1, OutUnknown, -2 }},
{ OpDecodeCufIter,{Stack1, Stack1, OutBoolean, 0 }},
/*** 11. Iterator instructions ***/
@@ -1342,6 +1345,7 @@ static const struct {
{ OpWIterNextK, {None, Local, OutUnknown, 0 }},
{ OpIterFree, {None, None, OutNone, 0 }},
{ OpMIterFree, {None, None, OutNone, 0 }},
{ OpCIterFree, {None, None, OutNone, 0 }},
/*** 12. Include, eval, and define instructions ***/
@@ -3566,7 +3570,8 @@ std::unique_ptr<Tracelet> Translator::analyze(SrcKey sk,
tas.varEnvTaint();
}
DynLocation* outputs[] = { ni->outStack, ni->outLocal, ni->outLocal2,
DynLocation* outputs[] = { ni->outStack,
ni->outLocal, ni->outLocal2,
ni->outStack2, ni->outStack3 };
for (size_t i = 0; i < sizeof(outputs) / sizeof(*outputs); ++i) {
if (outputs[i]) {
+1 -1
Ver Arquivo
@@ -274,7 +274,7 @@ class NormalizedInstruction {
vector<DynLocation*> inputs;
DynLocation* outStack;
DynLocation* outLocal;
DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK, and
DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK,
// MIterNextK
DynLocation* outStack2; // Used for CGetL2
DynLocation* outStack3; // Used for CGetL3
+1 -1
Ver Arquivo
@@ -102,7 +102,7 @@ inline bool isRet(PC pc) {
}
inline bool isIter(PC pc) {
return Op(*pc) >= OpIterInit && Op(*pc) <= OpMIterFree;
return Op(*pc) >= OpIterInit && Op(*pc) <= OpCIterFree;
}
inline int getImmIva(PC pc) {
+5 -2
Ver Arquivo
@@ -707,7 +707,8 @@ bool FuncChecker::checkIter(State* cur, PC pc) {
bool ok = true;
if (Op(*pc) == OpIterInit || Op(*pc) == OpIterInitK ||
Op(*pc) == OpWIterInit || Op(*pc) == OpWIterInitK ||
Op(*pc) == OpMIterInit || Op(*pc) == OpMIterInitK) {
Op(*pc) == OpMIterInit || Op(*pc) == OpMIterInitK ||
Op(*pc) == OpDecodeCufIter) {
if (cur->iters[id]) {
verify_error(
"IterInit* or MIterInit* <%d> trying to double-initialize\n", id);
@@ -718,7 +719,9 @@ bool FuncChecker::checkIter(State* cur, PC pc) {
verify_error("Cannot access un-initialized iter %d\n", id);
ok = false;
}
if (Op(*pc) == OpIterFree || Op(*pc) == OpMIterFree) {
if (Op(*pc) == OpIterFree ||
Op(*pc) == OpMIterFree ||
Op(*pc) == OpCIterFree) {
cur->iters[id] = false;
}
}
+1
Ver Arquivo
@@ -41,6 +41,7 @@ These are the allowed extensions:
* .hphp_opts - Options passed to hphp when generating a bytecode repo.
* .diff - The diff for .expect tests.
* .hhas - HipHop Assembly.
* .norepo - don't run the test in repo mode
You must have one `.php`; one and only one of `.expect`, `.expectf`, and
`.expectregex`; and the rest are optional.
+9 -2
Ver Arquivo
@@ -4,9 +4,13 @@ require_once "support.hhas";
class X {
function __destruct() { var_dump(__METHOD__); }
static function test($x) { return $x !== true; }
static function s_test($x) { return $x !== true; }
function o_test($x) { return $x === $this->p; }
function __call($n, $a) { return $a[0] == $n; }
}
function filt($x) { return $x === true; }
function test() {
$a = array();
var_dump(test2($a, "foo", new X));
@@ -23,7 +27,10 @@ function test() {
var_dump(fast_array_filter($a, null));
var_dump(fast_array_filter($a, function($a) { return !$a; }));
var_dump(fast_array_filter($a, array('X', 'test')));
var_dump(fast_array_filter($a, array('X', 's_test')));
var_dump(fast_array_filter($a, array(new X, 'o_test')));
var_dump(fast_array_filter($a, array(new X, 'bar')));
var_dump(fast_array_filter($a, 'filt'));
var_dump(fast_array_map(function($a) { return $a.$a; }, $a));
var_dump(fast_array_map(function&(&$a) { return $a; }, $a));
@@ -36,6 +36,22 @@ array(4) {
[3]=>
bool(false)
}
string(13) "X::__destruct"
array(0) {
}
string(13) "X::__destruct"
array(3) {
["foo"]=>
&string(3) "bar"
[1]=>
int(0)
[2]=>
bool(true)
}
array(1) {
[2]=>
bool(true)
}
array(5) {
[0]=>
string(2) "11"
+57 -33
Ver Arquivo
@@ -24,70 +24,94 @@
}
.function fast_array_filter($arr, $func) {
.numiters 1;
.numiters 2;
NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 0 endloop $v $k
IssetL $func
JmpZ loop_n
IsArrayL $func
JmpNZ loop_a
loop_s: CGetL $func
FPushFunc 1
FPassL 0 $v
FCall 1
UnboxR
JmpZ skip_s
SetWithRefLM <L:$res EL:$k> $v
skip_s: WIterNextK 0 loop_s $v $k
Jmp endloop
JmpZ null_func
loop_n: CGetL $v
JmpZ skip_n
SetWithRefLM <L:$res EL:$k> $v
skip_n: WIterNextK 0 loop_n $v $k
Jmp endloop
loop_a: CGetL $func
FPushCuf 1
CGetL $func
DecodeCufIter 0
JmpZ bad_func
.try_fault kill_iter_0 {
CGetL $arr
WIterInitK 1 endloop_a $v $k
.try_fault kill_iter_1 {
loop_a: FPushCufIter 1 0
FPassL 0 $v
FCall 1
UnboxR
JmpZ skip_a
SetWithRefLM <L:$res EL:$k> $v
skip_a: WIterNextK 0 loop_a $v $k
endloop: CGetL $res
skip_a: WIterNextK 1 loop_a $v $k
}
}
endloop_a:CIterFree 0
endloop_n:CGetL $res
RetC
null_func:CGetL $arr
WIterInitK 1 endloop_n $v $k
.try_fault kill_iter_1 {
loop_n: CGetL $v
JmpZ skip_n
SetWithRefLM <L:$res EL:$k> $v
skip_n: WIterNextK 1 loop_n $v $k
}
Jmp endloop_n
bad_func: Null
RetC
kill_iter_0:
CIterFree 0
Unwind
kill_iter_1:
IterFree 0
Unwind
}
.function fast_array_map($func, $arr) {
.numiters 1;
.numiters 2;
IssetL $func
JmpZ ident
CGetL $func
DecodeCufIter 0
JmpZ bad_func
.try_fault kill_iter_0 {
NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 0 endloop $v $k
WIterInitK 1 endloop $v $k
loop_x: CGetL $func
FPushCuf 1
.try_fault kill_iter_1 {
loop_x: FPushCufIter 1 0
FPassL 0 $v
FCall 1
SetWithRefRM <L:$res EL:$k>
WIterNextK 0 loop_x $v $k
endloop: CGetL $res
WIterNextK 1 loop_x $v $k
}
}
endloop: CIterFree 0
CGetL $res
RetC
ident: CGetL $arr
RetC
bad_func: Null
RetC
kill_iter_0:
CIterFree 0
Unwind
kill_iter_1:
IterFree 0
Unwind
}