Added IterBreakV, MIter{Init,InitK,Next,NextK,Free} and fixed memory tracking bug.

Added IR opcodes to perform MIter* instructions in JIT.  Added IterBreakV bytecode
operation to break out of multiple loops containing iterators.  Emitter and assembler
were modified to support such use.
Esse commit está contido em:
Paul Bissonnette
2013-06-11 11:09:56 -07:00
commit de Sara Golemon
commit 0d5d5bca72
28 arquivos alterados com 759 adições e 132 exclusões
+48 -24
Ver Arquivo
@@ -306,6 +306,7 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define DEC_MA std::vector<uchar>
#define DEC_BLA std::vector<Label*>&
#define DEC_SLA std::vector<StrOff>&
#define DEC_ILA std::vector<IterPair>&
#define DEC_IVA int32_t
#define DEC_HA int32_t
#define DEC_IA int32_t
@@ -375,6 +376,7 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define POP_HA_MA(i)
#define POP_HA_BLA(i)
#define POP_HA_SLA(i)
#define POP_HA_ILA(i)
#define POP_HA_IVA(i)
#define POP_HA_IA(i)
#define POP_HA_I64A(i)
@@ -458,6 +460,19 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define IMPL3_BLA IMPL_BLA(a3)
#define IMPL4_BLA IMPL_BLA(a4)
#define IMPL_ILA(var) do { \
auto& ue = getUnitEmitter(); \
ue.emitInt32(var.size()); \
for (auto& i : var) { \
ue.emitInt32(i.kind); \
ue.emitInt32(i.id); \
} \
} while(0)
#define IMPL1_ILA IMPL_ILA(a1)
#define IMPL2_ILA IMPL_ILA(a2)
#define IMPL3_ILA IMPL_ILA(a3)
#define IMPL4_ILA IMPL_ILA(a4)
#define IMPL_SLA(var) do { \
auto& ue = getUnitEmitter(); \
ue.emitInt32(var.size()); \
@@ -618,6 +633,11 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef IMPL2_SLA
#undef IMPL3_SLA
#undef IMPL4_SLA
#undef IMPL_ILA
#undef IMPL1_ILA
#undef IMPL2_ILA
#undef IMPL3_ILA
#undef IMPL4_ILA
#undef IMPL_IVA
#undef IMPL1_IVA
#undef IMPL2_IVA
@@ -2024,31 +2044,16 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
return false;
}
// "continue N" breaks out of N-1 loops and jumps to the top of the Nth.
// So whether this is break or continue, free N-1 Iters.
for (uint64_t i = 0; i < destLevel; ++i) {
if (m_controlTargets[i].m_itId != -1) {
if (m_controlTargets[i].m_itRef) {
e.MIterFree(m_controlTargets[i].m_itId);
} else {
e.IterFree(m_controlTargets[i].m_itId);
}
}
if (bs->is(Statement::KindOfBreakStatement)) {
// break N levels for a break
emitIterBreak(e, destLevel+1,
m_controlTargets[destLevel].m_brkTarg);
} else {
// break N-1 levels for a continue
emitIterBreak(e, destLevel,
m_controlTargets[destLevel].m_cntTarg);
}
// Only free the Nth-level Iter for break statements.
if (bs->is(Statement::KindOfBreakStatement)) {
if (m_controlTargets[destLevel].m_itId != -1) {
if (m_controlTargets[destLevel].m_itRef) {
e.MIterFree(m_controlTargets[destLevel].m_itId);
} else {
e.IterFree(m_controlTargets[destLevel].m_itId);
}
}
e.Jmp(m_controlTargets[destLevel].m_brkTarg);
} else {
e.Jmp(m_controlTargets[destLevel].m_cntTarg);
}
return false;
}
@@ -4280,6 +4285,25 @@ void EmitterVisitor::emitCGet(Emitter& e) {
}
}
void EmitterVisitor::emitIterBreak(Emitter& e, uint64_t n, Label& targ) {
std::vector<Emitter::IterPair> immItrList;
for (uint64_t level = 0; level < n; ++level) {
if (m_controlTargets[level].m_itId != -1) {
immItrList.push_back(Emitter::IterPair(m_controlTargets[level].m_itRef
? KindOfMIter : KindOfIter,
m_controlTargets[level]
.m_itId));
}
}
if (immItrList.size()) {
e.IterBreak(immItrList, targ);
} else {
e.Jmp(targ);
}
}
void EmitterVisitor::emitVGet(Emitter& e) {
if (checkIfStackEmpty("VGet*")) return;
LocationGuard loc(e, m_tempLoc);
@@ -6460,7 +6484,7 @@ class ForeachIterGuard {
public:
ForeachIterGuard(EmitterVisitor& ev,
Id iterId,
EmitterVisitor::IterKind kind)
IterKind kind)
: m_ev(ev)
{
m_ev.pushIterScope(iterId, kind);
+9 -5
Ver Arquivo
@@ -89,6 +89,12 @@ public:
Id str;
Label* dest;
};
struct IterPair {
IterPair(IterKind k, Id i) : kind(k), id(i) {}
IterKind kind;
Id id;
};
#define O(name, imm, pop, push, flags) \
void name(imm);
#define NA
@@ -103,6 +109,7 @@ public:
#define MA std::vector<uchar>
#define BLA std::vector<Label*>&
#define SLA std::vector<StrOff>&
#define ILA std::vector<IterPair>&
#define IVA int32_t
#define HA int32_t
#define IA int32_t
@@ -122,6 +129,7 @@ public:
#undef MA
#undef BLA
#undef SLA
#undef ILA
#undef IVA
#undef HA
#undef IA
@@ -374,11 +382,6 @@ public:
EXCEPTION_COMMON_IMPL(IncludeTimeFatalException);
};
enum IterKind {
KindOfIter = 0,
KindOfMIter = 1
};
void pushIterScope(Id id, IterKind kind) {
m_pendingIters.emplace_back(id, kind);
}
@@ -605,6 +608,7 @@ public:
void emitStringSwitch(Emitter& e, SwitchStatementPtr s,
std::vector<Label>& caseLabels, Label& done,
const SwitchState& state);
void emitIterBreak(Emitter& e, uint64_t n, Label& targ);
void markElem(Emitter& e);
void markNewElem(Emitter& e);
+5
Ver Arquivo
@@ -167,6 +167,11 @@ void Peephole::buildJumpTargets() {
foreachSwitchTarget(instr, [&](Offset& o) {
m_jumpTargets.insert(pos + o);
});
} else if (*instr == OpIterBreak) {
uint32_t veclen = *(uint32_t *)(instr + 1);
assert(veclen > 0);
Offset target = *(Offset *)((uint32_t *)(instr + 1) + 2 * veclen + 1);
m_jumpTargets.insert(pos + target);
} else {
Offset target = instrJumpTarget((Op*)m_ue.m_bc, pos);
if (target != InvalidAbsoluteOffset) {
+40 -1
Ver Arquivo
@@ -755,7 +755,8 @@ number of cells from the stack. In most cases the immediate vector arguments
are ordered such that the loc-desc comes first (deepest in the stack), with the
last M-vector element last (shallowest in the stack). However, classrefs that
are part of the BaseSC and BaseSL loc-desc inputs always come last (the cell
input to BaseSC comes first though).
input to BaseSC comes first though). Instuctions accepting an immediate vector
containing a list of iterators and iterator types use the notation "<iter-vec>".
In addition to describing each instruction, this instruction set documentation
also describes several operations that encapsulate fundamental, but non-trivial
@@ -3468,6 +3469,14 @@ CIterFree <iterator id> [] -> []
Cuf iterator free. This instruction frees the specified iterator
variable.
IterBreak <iter-vec> <rel offset> [] -> []
Iterator break. Frees vectors in %1 in left to right order then transfers
control to the locaton specified by %2. Surprise checks are performed
before iterators are freed so that in the event of an exception iterators
are not double freed. Note that as with normal jumps surprise checks will
only be performed if %2 is negative.
12. Include, eval, and define instructions
------------------------------------------
@@ -4408,3 +4417,33 @@ class B extends A { public static function f() { return parent::x; } }
ClsCnsD "x" "A"
RetC
foreach ($arr1 as $v1) {
foreach($arr2 as $v2) {
if ($v1 == $v2) {
break 2;
}
}
}
CGetL $arr1
IterInit 0 endFor1 $v1
startFor1:
CGetL $arr2
IterInit 1 endFor2 $v2
startFor2:
CGetL $v1
CGetL $v2
Eq
JmpNZ notEqual
IterBreak <(Iter) 1, (Iter) 2> endFor1
notEqual:
IterNext 1 startFor2 $v2
endFor2:
IterNext 1 startFor1 $v1
endFor1:
+10
Ver Arquivo
@@ -1590,6 +1590,8 @@ D:Bool = IterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = IterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
D:Bool = WIterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = WIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
D:Bool = MIterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = MIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
Initializes the iterator variable whose index is given by S2. This
instruction creates the appropriate iterator for the array or object
@@ -1610,6 +1612,9 @@ D:Bool = WIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
The WIterInit and WIterInitK instructions copy referenced array
elements by reference, and non-referenced array elements by value.
The MIterInit and MIterInitK instructions always copy the array element
by reference.
This instruction has the ConsumesRC property because it either
decrements the reference count of s0 or stores a reference to S0
into the new iterator.
@@ -1618,6 +1623,8 @@ D:Bool = IterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = IterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
D:Bool = WIterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
D:Bool = MIterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = MIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
Advances the iterator variable whose index is given by S1. S2 and S3
are local variable indices. S0 points to the stack frame containing
@@ -1633,8 +1640,11 @@ D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
element by value.
The WIterInit and WIterInitK instructions copy referenced array
elements by reference, and non-referenced array elements by value.
The MIterNext and MIterNextK instructions always copy the array element
by reference.
IterFree<IterId> S0:FramePtr
MIterFree<IterId> S0:FramePtr
Free the iterator variable whose index is given by S1 in the stack
frame pointed to by S0.
+86 -56
Ver Arquivo
@@ -713,48 +713,6 @@ bool Iter::init(TypedValue* c1) {
return hasElems;
}
bool Iter::minit(TypedValue* v1) {
assert(v1->m_type == KindOfRef);
bool hasElems = true;
TypedValue* rtv = v1->m_data.pref->tv();
if (rtv->m_type == KindOfArray) {
ArrayData* ad = rtv->m_data.parr;
if (!ad->empty()) {
MArrayIter& mi = marr();
(void) new (&mi) MArrayIter(v1->m_data.pref);
mi.advance();
} else {
hasElems = false;
}
} else if (rtv->m_type == KindOfObject) {
if (rtv->m_data.pobj->isCollection()) {
raise_error("Collection elements cannot be taken by reference");
}
bool isIterator;
Object obj = rtv->m_data.pobj->iterableObject(isIterator);
if (isIterator) {
raise_error("An iterator cannot be used with foreach by reference");
}
Class* ctx = arGetContextClass(g_vmContext->getFP());
CStrRef ctxStr = ctx ? ctx->nameRef() : null_string;
Array iterArray = obj->o_toIterArray(ctxStr, true);
if (iterArray->empty()) {
hasElems = false;
} else {
ArrayData* ad = iterArray.detach();
MArrayIter& mi = marr();
(void) new (&mi) MArrayIter(ad);
mi.advance();
}
} else {
if (!hphpiCompat) {
raise_warning("Invalid argument supplied for foreach()");
}
hasElems = false;
}
return hasElems;
}
bool Iter::next() {
assert(arr().getIterType() == ArrayIter::TypeArray ||
arr().getIterType() == ArrayIter::TypeIterator);
@@ -775,20 +733,6 @@ bool Iter::next() {
return true;
}
bool Iter::mnext() {
MArrayIter &mi = marr();
if (!mi.advance()) {
// If after advancing the iterator we have reached the end, free
// the iterator and fall through to the next instruction.
mi.~MArrayIter();
return false;
} else {
// If after advancing the iterator we have not reached the end,
// jump to the location specified by the second immediate argument.
return true;
}
}
void Iter::free() {
assert(arr().getIterType() == ArrayIter::TypeArray ||
arr().getIterType() == ArrayIter::TypeIterator);
@@ -1378,5 +1322,91 @@ template int64_t iter_next_key<true>(Iter* dest,
TypedValue* valOut,
TypedValue* keyOut);
///////////////////////////////////////////////////////////////////////////////
// MIter functions
HOT_FUNC
int64_t new_miter_array_key(Iter* dest, RefData* v1,
TypedValue* valOut, TypedValue* keyOut) {
TRACE(2, "%s: I %p, ad %p\n", __func__, dest, v1);
TypedValue* rtv = v1->tv();
ArrayData* ad = rtv->m_data.parr;
if (UNLIKELY(ad->empty())) {
return 0LL;
}
(void) new (&dest->marr()) MArrayIter(v1);
dest->marr().advance();
tvAsVariant(valOut).assignRef(dest->marr().val());
if (keyOut) {
tvAsVariant(keyOut).assignVal(dest->marr().key());
}
return 1LL;
}
HOT_FUNC
int64_t new_miter_object(Iter* dest, RefData* ref, Class* ctx,
TypedValue* valOut, TypedValue* keyOut) {
ObjectData *obj = ref->tv()->m_data.pobj;
if (obj->isCollection()) {
raise_error("Collection elements cannot be taken by reference");
}
bool isIterator;
Object itObj = obj->iterableObject(isIterator);
if (isIterator) {
raise_error("An iterator cannot be used with foreach by reference");
}
TRACE(2, "%s: I %p, obj %p, ctx %p, iterate as array\n",
__func__, dest, obj, ctx);
CStrRef ctxStr = ctx ? ctx->nameRef() : null_string;
Array iterArray(itObj->o_toIterArray(ctxStr, true));
ArrayData* ad = iterArray.detach();
(void) new (&dest->marr()) MArrayIter(ad);
if (UNLIKELY(!dest->marr().advance())) {
// Iterator was empty; call the destructor on the iterator we just
// constructed.
dest->marr().~MArrayIter();
return 0LL;
}
tvAsVariant(valOut).assignRef(dest->marr().val());
if (keyOut) {
tvAsVariant(keyOut).assignVal(dest->marr().key());
}
return 1LL;
}
int64_t new_miter_other(Iter* dest, RefData* data) {
TRACE(2, "%s: I %p, data %p, invalid type\n",
__func__, dest, data);
// TODO(#2570852): we should really issue a warning here
return 0LL;
}
HOT_FUNC
int64_t miter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) {
TRACE(2, "miter_next_key: I %p\n", iter);
MArrayIter& marr = iter->marr();
if (UNLIKELY(!marr.advance())) {
marr.~MArrayIter();
return 0LL;
}
tvAsVariant(valOut).assignRef(marr.val());
if (keyOut) {
tvAsVariant(keyOut).assignVal(marr.key());
}
return 1LL;
}
///////////////////////////////////////////////////////////////////////////////
}
+8 -2
Ver Arquivo
@@ -536,9 +536,7 @@ struct Iter {
CufIter& cuf() { return m_u.cufiter; }
bool init(TypedValue* c1);
bool minit(TypedValue* v1);
bool next();
bool mnext();
void free();
void mfree();
void cfree();
@@ -567,6 +565,14 @@ int64_t iter_next(Iter* dest, TypedValue* val);
template <bool withRef>
int64_t iter_next_key(Iter* dest, TypedValue* val, TypedValue* key);
int64_t new_miter_array_key(Iter* dest, RefData* arr, TypedValue* val,
TypedValue* key);
int64_t new_miter_object(Iter* dest, RefData* obj, Class* ctx,
TypedValue* val, TypedValue* key);
int64_t new_miter_other(Iter* dest, RefData* data);
int64_t miter_next_key(Iter* dest, TypedValue* val, TypedValue* key);
///////////////////////////////////////////////////////////////////////////////
}
+1 -1
Ver Arquivo
@@ -487,7 +487,7 @@ private:
bool initIterator(PC& pc, PC& origPc, Iter* it,
Offset offset, Cell* c1);
bool initIteratorM(PC& pc, PC& origPc, Iter* it,
Offset offset, Var* v1);
Offset offset, Var* v1, TypedValue* val, TypedValue* key);
void jmpSurpriseCheck(Offset o);
template<Op op> void jmpOpImpl(PC& pc);
#define O(name, imm, pusph, pop, flags) \
+5 -4
Ver Arquivo
@@ -20,7 +20,6 @@
#define __STDC_LIMIT_MACROS
#endif
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include "hphp/runtime/base/memory/memory_manager.h"
#include "hphp/runtime/base/memory/smart_allocator.h"
@@ -33,6 +32,8 @@
#include "hphp/util/process.h"
#include "hphp/util/trace.h"
#include <stdint.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
@@ -325,12 +326,12 @@ inline void* MemoryManager::smartRealloc(void* ptr, size_t nbytes) {
SweepNode* next = n->next;
SweepNode* prev = n->prev;
SweepNode* n2 = (SweepNode*) realloc(n, nbytes + sizeof(SweepNode));
// ensure that we have not exceeded the per request memory limit (#2529805)
refreshStatsHelper();
if (n2 != n) {
// block moved; must re-link to sweeplist
next->prev = prev->next = n2;
if (UNLIKELY(m_stats.usage > m_stats.maxBytes)) {
refreshStatsHelper();
}
}
return n2 + 1;
}
+48
Ver Arquivo
@@ -879,6 +879,46 @@ std::vector<uchar> read_immvector(AsmState& as, int& stackCount) {
return ret;
}
// Read in a vector of iterators the format for this vector is:
// <(TYPE) ID, (TYPE) ID, ...>
// Where TYPE := Iter | MIter | CIter
// and ID := Integer
std::vector<uint32_t> read_itervec(AsmState& as) {
std::vector<uint32_t> ret;
as.in.skipSpaceTab();
as.in.expect('<');
std::string word;
char *end;
for (;;) {
as.in.expectWs('(');
if (!as.in.readword(word)) as.error("Was expecting Iterator type.");
if (!word.compare("Iter")) ret.push_back(KindOfIter);
else if (!word.compare("MIter")) ret.push_back(KindOfMIter);
else if (!word.compare("CIter")) ret.push_back(KindOfCIter);
else as.error("Unknown iterator type `" + word + "'");
as.in.expectWs(')');
as.in.skipSpaceTab();
if (!as.in.readword(word)) as.error("Was expecting iterator id.");
uint32_t iterId = folly::to<uint32_t>(word);
if (!isdigit(word.back())) {
if (word.back() == '>') break;
if (word.back() != ',') as.error("Was expecting `,'.");
} else {
as.in.skipSpaceTab();
if (as.in.peek() == '>') { as.in.getc(); break; }
as.in.expect(',');
}
}
return ret;
}
// Jump tables are lists of labels.
std::vector<std::string> read_jmpvector(AsmState& as) {
std::vector<std::string> ret;
@@ -978,6 +1018,14 @@ OpcodeParserMap opcode_parsers;
as.ue->emitByte(vecImm[i]); \
}
#define IMM_ILA do { \
std::vector<uint32_t> vecImm = read_itervec(as); \
as.ue->emitInt32(vecImm.size() / 2); \
for (auto& i : vecImm) { \
as.ue->emitInt32(i); \
} \
} while (0)
#define IMM_BLA do { \
std::vector<std::string> vecImm = read_jmpvector(as); \
as.ue->emitInt32(vecImm.size()); \
+59 -14
Ver Arquivo
@@ -45,6 +45,7 @@
#include "hphp/runtime/base/shared/shared_variant.h"
#include "hphp/runtime/vm/debug/debug.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/vm/treadmill.h"
#include "hphp/runtime/vm/php_debug.h"
#include "hphp/runtime/vm/debugger_hook.h"
@@ -202,6 +203,13 @@ Transl::Translator* tx() {
#define DECODE_HA(var) DECODE_IVA(var)
#define DECODE_IA(var) DECODE_IVA(var)
#define DECODE_ITER_LIST(typeList, idList, vecLen) \
DECODE(int32_t, vecLen); \
assert(vecLen > 0); \
Id* typeList = (Id*)pc; \
Id* idList = (Id*)pc + 1; \
pc += 2 * vecLen * sizeof(Id);
#define SYNC() m_pc = pc
//=============================================================================
@@ -4036,6 +4044,37 @@ inline void OPTBLD_INLINE VMExecutionContext::iopJmpNZ(PC& pc) {
jmpOpImpl<OpJmpNZ>(pc);
}
#define FREE_ITER_LIST(typeList, idList, vecLen) do { \
int iterIndex; \
for (iterIndex = 0; iterIndex < 2 * veclen; iterIndex += 2) { \
Id iterType = typeList[iterIndex]; \
Id iterId = idList[iterIndex]; \
\
Iter *iter = frame_iter(m_fp, iterId); \
\
switch (iterType) { \
case KindOfIter: iter->free(); break; \
case KindOfMIter: iter->mfree(); break; \
case KindOfCIter: iter->cfree(); break; \
} \
} \
} while(0)
inline void OPTBLD_INLINE VMExecutionContext::iopIterBreak(PC& pc) {
PC savedPc = pc;
NEXT();
DECODE_ITER_LIST(iterTypeList, iterIdList, veclen);
DECODE_JMP(Offset, offset);
jmpSurpriseCheck(offset); // we do this early so iterators are still dirty if
// we have an exception
FREE_ITER_LIST(iterTypeList, iterIdList, veclen);
pc = savedPc + offset;
}
#undef FREE_ITER_LIST
enum class SwitchMatch {
NORMAL, // value was converted to an int: match normally
NONZERO, // can't be converted to an int: match first nonzero case
@@ -6170,12 +6209,26 @@ inline void OPTBLD_INLINE VMExecutionContext::iopWIterInitK(PC& pc) {
}
}
inline bool VMExecutionContext::initIteratorM(PC& pc, PC& origPc, Iter* it,
Offset offset, Var* v1) {
bool hasElems = it->minit(v1);
Offset offset, Var* v1,
TypedValue *val,
TypedValue *key) {
bool hasElems = false;
TypedValue* rtv = v1->m_data.pref->tv();
if (rtv->m_type == KindOfArray) {
hasElems = new_miter_array_key(it, v1->m_data.pref, val, key);
} else if (rtv->m_type == KindOfObject) {
Class* ctx = arGetContextClass(g_vmContext->getFP());
hasElems = new_miter_object(it, v1->m_data.pref, ctx, val, key);
} else {
hasElems = new_miter_other(it, v1->m_data.pref);
}
if (!hasElems) {
ITER_SKIP(offset);
}
m_stack.popV();
return hasElems;
}
@@ -6190,9 +6243,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMIterInit(PC& pc) {
assert(v1->m_type == KindOfRef);
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
if (initIteratorM(pc, origPc, it, offset, v1)) {
tvAsVariant(tv1).assignRef(it->marr().val());
}
initIteratorM(pc, origPc, it, offset, v1, tv1, nullptr);
}
inline void OPTBLD_INLINE VMExecutionContext::iopMIterInitK(PC& pc) {
@@ -6207,10 +6258,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMIterInitK(PC& pc) {
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
TypedValue* tv2 = frame_local(m_fp, key);
if (initIteratorM(pc, origPc, it, offset, v1)) {
tvAsVariant(tv1).assignRef(it->marr().val());
tvAsVariant(tv2) = it->marr().key();
}
initIteratorM(pc, origPc, it, offset, v1, tv1, tv2);
}
inline void OPTBLD_INLINE VMExecutionContext::iopIterNext(PC& pc) {
@@ -6283,9 +6331,8 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMIterNext(PC& pc) {
DECODE_HA(val);
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
if (it->mnext()) {
if (miter_next_key(it, tv1, nullptr)) {
ITER_SKIP(offset);
tvAsVariant(tv1).assignRef(it->marr().val());
}
}
@@ -6299,10 +6346,8 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMIterNextK(PC& pc) {
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
TypedValue* tv2 = frame_local(m_fp, key);
if (it->mnext()) {
if (miter_next_key(it, tv1, tv2)) {
ITER_SKIP(offset);
tvAsVariant(tv1).assignRef(it->marr().val());
tvAsVariant(tv2) = it->marr().key();
}
}
+34 -3
Ver Arquivo
@@ -146,6 +146,9 @@ int immSize(const Op* opcode, int idx) {
} else if (itype == BLA) {
prefixes = 1;
vecElemSz = sizeof(Offset);
} else if (itype == ILA) {
prefixes = 1;
vecElemSz = 2 * sizeof(uint32_t);
} else {
assert(itype == SLA);
prefixes = 1;
@@ -161,7 +164,7 @@ int immSize(const Op* opcode, int idx) {
bool immIsVector(Op opcode, int idx) {
ArgType type = immType(opcode, idx);
return (type == MA || type == BLA || type == SLA);
return (type == MA || type == BLA || type == SLA || type == ILA);
}
bool hasImmVector(Op opcode) {
@@ -303,6 +306,14 @@ Offset* instrJumpOffset(Op* instr) {
};
assert(!isSwitch(*instr));
if (Op(*instr) == OpIterBreak) {
uint32_t veclen = *(uint32_t *)(instr + 1);
assert(veclen > 0);
Offset* target = (Offset *)((uint32_t *)(instr + 1) + 2 * veclen + 1);
return target;
}
int mask = jumpMask[uint8_t(*instr)];
if (mask == 0) {
return nullptr;
@@ -340,7 +351,7 @@ int numSuccs(const Op* instr) {
if (isSwitch(*instr)) {
return *(int*)(instr + 1);
}
if (*instr == OpJmp) return 1;
if (*instr == OpJmp || *instr == OpIterBreak) return 1;
return 0;
}
if (instrJumpOffset(const_cast<Op*>(instr))) return 2;
@@ -817,6 +828,24 @@ std::string instrToString(const Op* it, const Unit* u /* = NULL */) {
out << ">"; \
} while (false)
#define READIVEC() do { \
int sz = readData<int>(it); \
out << " <"; \
const char* sep = ""; \
for (int i = 0; i < sz; ++i) { \
out << sep; \
IterKind k = (IterKind)readData<Id>(it); \
switch(k) { \
case KindOfIter: out << "(Iter) "; break; \
case KindOfMIter: out << "(MIter) "; break; \
case KindOfCIter: out << "(CIter) "; break; \
} \
out << readData<Id>(it); \
sep = ", "; \
} \
out << ">"; \
} while (false)
#define ONE(a) H_##a
#define TWO(a, b) H_##a; H_##b
#define THREE(a, b, c) H_##a; H_##b; H_##c;
@@ -825,6 +854,7 @@ std::string instrToString(const Op* it, const Unit* u /* = NULL */) {
#define H_MA READVEC()
#define H_BLA READSVEC()
#define H_SLA READSVEC()
#define H_ILA READIVEC()
#define H_IVA READIVA()
#define H_I64A READ(int64_t)
#define H_HA READV()
@@ -859,6 +889,7 @@ OPCODES
#undef H_MA
#undef H_BLA
#undef H_SLA
#undef H_ILA
#undef H_IVA
#undef H_I64A
#undef H_HA
@@ -926,7 +957,7 @@ ImmVector getImmVector(const Op* opcode) {
void* vp = getImmPtr(opcode, k);
return ImmVector::createFromStream(
static_cast<const uint8_t*>(vp));
} else if (t == BLA || t == SLA) {
} else if (t == BLA || t == SLA || t == ILA) {
void* vp = getImmPtr(opcode, k);
return ImmVector::createFromStream(
static_cast<const int32_t*>(vp));
+21 -13
Ver Arquivo
@@ -32,20 +32,21 @@ struct Unit;
// The types in this macro for MA, BLA, and SLA are meaningless since
// they are never read out of ArgUnion (they use ImmVector and
// ImmVectorO).
#define ARGTYPES \
ARGTYPE(NA, void*) /* unused */ \
ARGTYPEVEC(MA, int32_t) /* Member vector immediate */ \
ARGTYPEVEC(BLA,Offset) /* Bytecode address vector immediate */ \
ARGTYPEVEC(SLA,Id) /* litstrid/offset pair vector */ \
ARGTYPE(IVA, int32_t) /* variable size: 8 or 32-bit integer */ \
ARGTYPE(I64A, int64_t) /* 64-bit Integer */ \
ARGTYPE(HA, int32_t) /* Local variable ID: 8 or 32-bit int */ \
/* TODO(jdelong): rename HA to LA */ \
#define ARGTYPES \
ARGTYPE(NA, void*) /* unused */ \
ARGTYPEVEC(MA, int32_t) /* Member vector immediate */ \
ARGTYPEVEC(BLA,Offset) /* Bytecode address vector immediate */ \
ARGTYPEVEC(SLA,Id) /* litstrid/offset pair vector */ \
ARGTYPEVEC(ILA,Id) /* IterKind/IterId pair vector */ \
ARGTYPE(IVA, int32_t) /* variable size: 8 or 32-bit integer */ \
ARGTYPE(I64A, int64_t) /* 64-bit Integer */ \
ARGTYPE(HA, int32_t) /* Local variable ID: 8 or 32-bit int */ \
/* TODO(jdelong): rename HA to LA */ \
ARGTYPE(IA, int32_t) /* Iterator variable ID: 8 or 32-bit int */ \
ARGTYPE(DA, double) /* Double */ \
ARGTYPE(SA, Id) /* litStr ID */ \
ARGTYPE(AA, Id) /* static array ID */ \
ARGTYPE(BA, Offset) /* Bytecode address */ \
ARGTYPE(DA, double) /* Double */ \
ARGTYPE(SA, Id) /* litStr ID */ \
ARGTYPE(AA, Id) /* static array ID */ \
ARGTYPE(BA, Offset) /* Bytecode address */ \
ARGTYPE(OA, unsigned char) /* Opcode */
enum ArgType {
@@ -305,6 +306,12 @@ enum IncDecOp {
IncDec_invalid
};
enum IterKind {
KindOfIter = 0,
KindOfMIter = 1,
KindOfCIter = 2,
};
// Each of the setop ops maps to a binary bytecode op. We have reasons
// for using distinct bitwise representations, though. This macro records
// their correspondence for mapping either direction.
@@ -520,6 +527,7 @@ enum SetOpOp {
O(IterFree, ONE(IA), NOV, NOV, NF) \
O(MIterFree, ONE(IA), NOV, NOV, NF) \
O(CIterFree, ONE(IA), NOV, NOV, NF) \
O(IterBreak, TWO(ILA,BA), NOV, NOV, CF_TF) \
O(Incl, NA, ONE(CV), ONE(CV), CF) \
O(InclOnce, NA, ONE(CV), ONE(CV), CF) \
O(Req, NA, ONE(CV), ONE(CV), CF) \
+80
Ver Arquivo
@@ -5332,6 +5332,53 @@ void CodeGenerator::cgIterInitCommon(IRInstruction* inst) {
}
}
void CodeGenerator::cgMIterInit(IRInstruction* inst) {
cgMIterInitCommon(inst);
}
void CodeGenerator::cgMIterInitK(IRInstruction* inst) {
cgMIterInitCommon(inst);
}
void CodeGenerator::cgMIterInitCommon(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(1)].reg();
int64_t iterOffset = this->iterOffset(inst->src(2));
int64_t valLocalOffset = localOffset(inst->src(3));
SSATmp* src = inst->src(0);
ArgGroup args(m_regs);
args.addr(fpReg, iterOffset).ssa(src);
assert(src->type().isBoxed());
if (src->type().innerType().isArray()) {
args.addr(fpReg, valLocalOffset);
if (inst->op() == MIterInitK) {
args.addr(fpReg, localOffset(inst->src(4)));
} else {
args.imm(0);
}
cgCallHelper(m_as, (TCA)new_miter_array_key, inst->dst(),
SyncOptions::kSyncPoint, args);
} else if (src->type() == Type::BoxedObj) {
args.immPtr(curClass()).addr(fpReg, valLocalOffset);
if (inst->op() == MIterInitK) {
args.addr(fpReg, localOffset(inst->src(4)));
} else {
args.imm(0);
}
// new_miter_object decrefs its src object if it propagates an
// exception out, so we use kSyncPointAdjustOne, which adjusts the
// stack pointer by 1 stack element on an unwind, skipping over
// the src object.
cgCallHelper(m_as, (TCA)new_miter_object, inst->dst(),
SyncOptions::kSyncPointAdjustOne, args);
} else {
cgCallHelper(m_as, (TCA)new_miter_other, inst->dst(),
SyncOptions::kSyncPoint, args);
}
}
void CodeGenerator::cgIterNext(IRInstruction* inst) {
cgIterNextCommon(inst);
}
@@ -5365,10 +5412,36 @@ void CodeGenerator::cgIterNextCommon(IRInstruction* inst) {
cgCallHelper(m_as, helperAddr, inst->dst(), SyncOptions::kSyncPoint, args);
}
void CodeGenerator::cgMIterNext(IRInstruction* inst) {
cgMIterNextCommon(inst);
}
void CodeGenerator::cgMIterNextK(IRInstruction* inst) {
cgMIterNextCommon(inst);
}
void CodeGenerator::cgMIterNextCommon(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].reg();
ArgGroup args(m_regs);
args.addr(fpReg, iterOffset(inst->src(1)))
.addr(fpReg, localOffset(inst->src(2)));
if (inst->op() == MIterNextK) {
args.addr(fpReg, localOffset(inst->src(3)));
} else {
args.imm(0);
}
cgCallHelper(m_as, (TCA)miter_next_key, inst->dst(),
SyncOptions::kSyncPoint, args);
}
void iterFreeHelper(Iter* iter) {
iter->free();
}
void miterFreeHelper(Iter* iter) {
iter->mfree();
}
void citerFreeHelper(Iter* iter) {
iter->cfree();
}
@@ -5380,6 +5453,13 @@ void CodeGenerator::cgIterFree(IRInstruction* inst) {
ArgGroup(m_regs).addr(fpReg, offset));
}
void CodeGenerator::cgMIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].reg();
int64_t offset = iterOffset(inst->extra<MIterFree>()->iterId);
cgCallHelper(m_as, (TCA)miterFreeHelper, InvalidReg, SyncOptions::kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset));
}
void CodeGenerator::cgDecodeCufIter(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(1)].reg();
int64_t offset = iterOffset(inst->extra<DecodeCufIter>()->iterId);
+2
Ver Arquivo
@@ -306,6 +306,8 @@ private:
void cgIterNextCommon(IRInstruction* inst);
void cgIterInitCommon(IRInstruction* inst);
void cgMIterNextCommon(IRInstruction* inst);
void cgMIterInitCommon(IRInstruction* inst);
void cgLdFuncCachedCommon(IRInstruction* inst);
TargetCache::CacheHandle cgLdClsCachedCommon(IRInstruction* inst);
void emitFwdJcc(ConditionCode cc, Block* target);
+1
Ver Arquivo
@@ -363,6 +363,7 @@ X(LdLoc, LocalId);
X(StLoc, LocalId);
X(StLocNT, LocalId);
X(IterFree, IterId);
X(MIterFree, IterId);
X(CIterFree, IterId);
X(DecodeCufIter, IterId);
X(CufIterSpillFrame, FPushCufData);
+107 -4
Ver Arquivo
@@ -856,10 +856,25 @@ SSATmp* HhbcTranslator::emitIterInitCommon(int offset, Lambda genFunc) {
return emitJmpCondHelper(offset, true, res);
}
template<class Lambda>
SSATmp* HhbcTranslator::emitMIterInitCommon(int offset, Lambda genFunc) {
auto trace = getExitTrace();
SSATmp* src = topV();
Type type = src->type();
assert(type.isBoxed());
gen(LdRef, type.innerType(), trace, src);
SSATmp* res = genFunc(src);
SSATmp* out = popV();
gen(DecRef, out);
return emitJmpCondHelper(offset, true, res);
}
void HhbcTranslator::emitIterInit(uint32_t iterId,
int offset,
uint32_t valLocalId) {
emitIterInitCommon(offset, [=] (SSATmp* src) {
emitIterInitCommon(offset, [&] (SSATmp* src) {
return gen(
IterInit,
Type::Bool,
@@ -875,7 +890,7 @@ void HhbcTranslator::emitIterInitK(uint32_t iterId,
int offset,
uint32_t valLocalId,
uint32_t keyLocalId) {
emitIterInitCommon(offset, [=] (SSATmp* src) {
emitIterInitCommon(offset, [&] (SSATmp* src) {
return gen(
IterInitK,
Type::Bool,
@@ -920,7 +935,7 @@ void HhbcTranslator::emitWIterInit(uint32_t iterId,
int offset,
uint32_t valLocalId) {
emitIterInitCommon(
offset, [=] (SSATmp* src) {
offset, [&] (SSATmp* src) {
return gen(
WIterInit,
Type::Bool,
@@ -938,7 +953,7 @@ void HhbcTranslator::emitWIterInitK(uint32_t iterId,
uint32_t valLocalId,
uint32_t keyLocalId) {
emitIterInitCommon(
offset, [=] (SSATmp* src) {
offset, [&] (SSATmp* src) {
return gen(
WIterInitK,
Type::Bool,
@@ -980,10 +995,74 @@ void HhbcTranslator::emitWIterNextK(uint32_t iterId,
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitMIterInit(uint32_t iterId,
int offset,
uint32_t valLocalId) {
emitMIterInitCommon(offset, [&] (SSATmp* src) {
return gen(
MIterInit,
Type::Bool,
src,
m_tb->fp(),
cns(iterId),
cns(valLocalId)
);
});
}
void HhbcTranslator::emitMIterInitK(uint32_t iterId,
int offset,
uint32_t valLocalId,
uint32_t keyLocalId) {
emitMIterInitCommon(offset, [&] (SSATmp* src) {
return gen(
MIterInitK,
Type::Bool,
src,
m_tb->fp(),
cns(iterId),
cns(valLocalId),
cns(keyLocalId)
);
});
}
void HhbcTranslator::emitMIterNext(uint32_t iterId,
int offset,
uint32_t valLocalId) {
SSATmp* res = gen(
MIterNext,
Type::Bool,
m_tb->fp(),
cns(iterId),
cns(valLocalId)
);
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitMIterNextK(uint32_t iterId,
int offset,
uint32_t valLocalId,
uint32_t keyLocalId) {
SSATmp* res = gen(
MIterNextK,
Type::Bool,
m_tb->fp(),
cns(iterId),
cns(valLocalId),
cns(keyLocalId)
);
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitIterFree(uint32_t iterId) {
gen(IterFree, IterId(iterId), m_tb->fp());
}
void HhbcTranslator::emitMIterFree(uint32_t iterId) {
gen(MIterFree, IterId(iterId), m_tb->fp());
}
void HhbcTranslator::emitDecodeCufIter(uint32_t iterId, int offset) {
SSATmp* src = popC();
Type type = src->type();
@@ -1002,6 +1081,30 @@ void HhbcTranslator::emitCIterFree(uint32_t iterId) {
gen(CIterFree, IterId(iterId), m_tb->fp());
}
void HhbcTranslator::emitIterBreak(const ImmVector& iv,
uint32_t offset,
bool breakTracelet,
bool noSurprise) {
bool backward = (offset - (int32_t)bcOff()) < 0;
if (backward && !noSurprise) {
gen(ExitWhenSurprised, getExitSlowTrace());
}
int iterIndex;
for (iterIndex = 0; iterIndex < iv.size(); iterIndex += 2) {
IterKind iterKind = (IterKind)iv.vec32()[iterIndex];
Id iterId = iv.vec32()[iterIndex + 1];
switch (iterKind) {
case KindOfIter: gen(IterFree, IterId(iterId), m_tb->fp()); break;
case KindOfMIter: gen(MIterFree, IterId(iterId), m_tb->fp()); break;
case KindOfCIter: gen(CIterFree, IterId(iterId), m_tb->fp()); break;
}
}
if (!breakTracelet) return;
gen(Jmp_, getExitTrace(offset));
}
typedef std::map<int, int> ContParamMap;
/*
* mapContParams builds a mapping between named locals in origFunc and
+15
Ver Arquivo
@@ -356,6 +356,16 @@ struct HhbcTranslator {
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitMIterInit(uint32_t iterId, int targetOffset, uint32_t valLocalId);
void emitMIterInitK(uint32_t iterId,
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitMIterNext(uint32_t iterId, int targetOffset, uint32_t valLocalId);
void emitMIterNextK(uint32_t iterId,
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitWIterInit(uint32_t iterId, int targetOffset, uint32_t valLocalId);
void emitWIterInitK(uint32_t iterId,
int targetOffset,
@@ -368,8 +378,11 @@ struct HhbcTranslator {
uint32_t keyLocalId);
void emitIterFree(uint32_t iterId);
void emitMIterFree(uint32_t iterId);
void emitDecodeCufIter(uint32_t iterId, int targetOffset);
void emitCIterFree(uint32_t iterId);
void emitIterBreak(const ImmVector& iv, uint32_t offset, bool breakTracelet,
bool noSurprise);
void emitVerifyParamType(uint32_t paramId);
// continuations
@@ -662,6 +675,8 @@ private:
SSATmp* emitIterInitCommon(int offset, Lambda genFunc);
BCMarker makeMarker(Offset bcOff);
void updateMarker();
template<class Lambda>
SSATmp* emitMIterInitCommon(int offset, Lambda genFunc);
SSATmp* staticTVCns(const TypedValue*);
Type interpOutputType(const NormalizedInstruction&) const;
+14
Ver Arquivo
@@ -508,7 +508,21 @@ 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(MIterInit, D(Bool), S(BoxedCell) \
S(FramePtr) \
C(Int) \
C(Int), E|N|Mem|Refs|CRc) \
O(MIterInitK, D(Bool), S(BoxedCell) \
S(FramePtr) \
C(Int) \
C(Int) \
C(Int), E|N|Mem|Refs|CRc) \
O(MIterNext, D(Bool), S(FramePtr) \
C(Int) C(Int), E|N|Mem|Refs) \
O(MIterNextK, D(Bool), S(FramePtr) \
C(Int) C(Int) C(Int), E|N|Mem|Refs) \
O(IterFree, ND, S(FramePtr), E|N|Mem|Refs) \
O(MIterFree, ND, S(FramePtr), E|N|Mem|Refs) \
O(DecodeCufIter, D(Bool), S(Arr,Obj,Str) \
S(FramePtr), E|N|Mem|Refs) \
O(CIterFree, ND, S(FramePtr), E|N|Mem|Refs) \
+50
Ver Arquivo
@@ -1361,6 +1361,42 @@ Translator::translateIterNextK(const NormalizedInstruction& i) {
i.imm[3].u_IVA);
}
void
Translator::translateMIterInit(const NormalizedInstruction& i) {
HHIR_EMIT(MIterInit,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA);
}
void
Translator::translateMIterInitK(const NormalizedInstruction& i) {
HHIR_EMIT(MIterInitK,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA,
i.imm[3].u_IVA);
}
void
Translator::translateMIterNext(const NormalizedInstruction& i) {
HHIR_EMIT(MIterNext,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA);
}
void
Translator::translateMIterNextK(const NormalizedInstruction& i) {
HHIR_EMIT(MIterNextK,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA,
i.imm[3].u_IVA);
}
void
Translator::translateWIterInit(const NormalizedInstruction& i) {
HHIR_EMIT(WIterInit,
@@ -1403,6 +1439,20 @@ Translator::translateIterFree(const NormalizedInstruction& i) {
HHIR_EMIT(IterFree, i.imm[0].u_IVA);
}
void
Translator::translateMIterFree(const NormalizedInstruction& i) {
HHIR_EMIT(MIterFree, i.imm[0].u_IVA);
}
void
Translator::translateIterBreak(const NormalizedInstruction& i) {
assert(i.breaksTracelet);
HHIR_EMIT(IterBreak, i.immVec, i.offset() + i.imm[1].u_BA, i.breaksTracelet,
i.noSurprise);
}
void
Translator::translateDecodeCufIter(const NormalizedInstruction& i) {
+6
Ver Arquivo
@@ -117,6 +117,10 @@
CASE(WIterInitK) \
CASE(WIterNext) \
CASE(WIterNextK) \
CASE(MIterInit) \
CASE(MIterInitK) \
CASE(MIterNext) \
CASE(MIterNextK) \
CASE(ReqDoc) \
CASE(DefCls) \
CASE(DefFunc) \
@@ -155,6 +159,8 @@
CASE(BindS) \
CASE(BindG) \
CASE(IterFree) \
CASE(MIterFree) \
CASE(IterBreak) \
CASE(FPassV) \
CASE(UnsetN) \
CASE(DecodeCufIter) \
+1
Ver Arquivo
@@ -1220,6 +1220,7 @@ static const struct {
{ OpIterFree, {None, None, OutNone, 0 }},
{ OpMIterFree, {None, None, OutNone, 0 }},
{ OpCIterFree, {None, None, OutNone, 0 }},
{ OpIterBreak, {None, None, OutNone, 0 }},
/*** 12. Include, eval, and define instructions ***/
+1
Ver Arquivo
@@ -1004,6 +1004,7 @@ opcodeControlFlowInfo(const Op instr) {
case OpWIterInit: // Ditto
case OpWIterInitK: // Ditto
case OpDecodeCufIter: // Ditto
case OpIterBreak:
case OpThrow:
case OpUnwind:
case OpEval:
+4 -5
Ver Arquivo
@@ -469,11 +469,12 @@ bool FuncChecker::checkImmediates(const char* name, const Op* instr) {
}
break;
}
case ILA:
case BLA:
case SLA: { // vec of offsets for Switch/SSwitch
case SLA: { // vec of offsets for Switch/SSwitch/IterBreak
int len = *(int*)pc;
if (len < 1) {
error("invalid length of jump table %d at Offset %d\n",
error("invalid length of immediate vector %d at Offset %d\n",
len, offset(pc));
return false;
}
@@ -917,9 +918,7 @@ bool FuncChecker::checkFlow() {
ok &= checkInputs(&cur, pc, b);
if (isTF(pc)) ok &= checkTerminal(&cur, pc);
if (isFF(pc)) ok &= checkFpi(&cur, pc, b);
// TODO(#1097182) Iterator checking is disabled, because
// systemlib currently doesn't pass it.
/* if (isIter(pc)) ok &= checkIter(&cur, pc); */
if (isIter(pc)) ok &= checkIter(&cur, pc);
ok &= checkOutputs(&cur, pc, b);
}
ok &= checkSuccEdges(b, &cur);
+68
Ver Arquivo
@@ -0,0 +1,68 @@
.main {
FPushFuncD 0 "main"
FCall 0
PopR
Int 1
RetC
}
.function main() {
.numiters 2;
NewArray
String "hello"
String "world"
AddElemC
String "this"
String "is"
AddElemC
String "a"
String "test"
AddElemC
SetL $0
PopC
CGetL $0
IterInitK 0 endOuter $1 $2
startOuter:
String "\n"
CGetL2 $1
Concat
Print
PopC
String "\n"
CGetL2 $2
Concat
Print
PopC
CGetL $0
IterInitK 1 endInner $3 $4
startInner:
String "\n"
CGetL2 $3
Concat
Print
PopC
String "\n"
CGetL2 $4
Concat
Print
PopC
IterBreak <(Iter) 1, (Iter) 0> endOuter
IterNextK 1 startInner $3 $4
endInner:
IterNextK 0 startOuter $1 $2
endOuter:
Int 1
RetC
}
+4
Ver Arquivo
@@ -0,0 +1,4 @@
world
hello
world
hello
+24
Ver Arquivo
@@ -0,0 +1,24 @@
<?php
function main() {
$arr = null;
print "start iter loop\n";
foreach ($arr as $x => $y) {
print "fail";
}
print "end iter loop\n";
$ref = &$arr;
print "start witer loop\n";
foreach ($arr as $x => $y) {
print "fail";
}
print "end witer loop\n";
print "start miter loop\n";
foreach ($arr as $x => &$y) {
print "fail";
}
print "end of miter loop\n";
}
main();
@@ -0,0 +1,8 @@
start iter loop
HipHop Warning: Invalid argument supplied for foreach() in %s on line 8
end iter loop
start witer loop
HipHop Warning: Invalid argument supplied for foreach() in %s on line 14
end witer loop
start miter loop
end of miter loop