Change semantics of DecodeCufIter

Previously it returned a bool to say whether or not it had
succeeded, but always initialized the iter. The other iterators
branch on failure.

This adds the branch target which brings it closer to the other
iterators, and avoids the need to do a (pointless) CIterFree on
the failure path just to keep the validator happy.

I've also added a translation for it.
Esse commit está contido em:
mwilliams
2013-05-31 09:42:50 -07:00
commit de sgolemon
commit b1eedaa49f
19 arquivos alterados com 211 adições e 78 exclusões
+7 -1
Ver Arquivo
@@ -1616,9 +1616,15 @@ IterFree<IterId> S0:FramePtr
Free the iterator variable whose index is given by S1 in the stack
frame pointed to by S0.
D:Bool = DecodeCufIter<IterId> S0:{Arr|Obj|Str} S1:FramePtr
Decode S0 as a callable, and write the decoded values to the iterator
specified by IterId. Returns true iff it successfully decoded the
callable. Does not raise warnings, or throw exceptions.
CIterFree<IterId> S0:FramePtr
Free the iterator variable whose index is given by S1 in the stack
Free the iterator variable whose index is given by IterId in the stack
frame pointed to by S0.
+7 -2
Ver Arquivo
@@ -1160,7 +1160,7 @@ void parse_numiters(AsmState& as) {
void parse_function_body(AsmState&, int nestLevel = 0);
/*
* directive-fault : identifier '{' function-body
* directive-fault : identifier integer? '{' function-body
* ;
*/
void parse_fault(AsmState& as, int nestLevel) {
@@ -1170,6 +1170,11 @@ void parse_fault(AsmState& as, int nestLevel) {
if (!as.in.readword(label)) {
as.error("expected label name after .try_fault");
}
int iterId = -1;
as.in.skipWhitespace();
if (as.in.peek() != '{') {
iterId = read_opcode_arg<int32_t>(as);
}
as.in.expectWs('{');
parse_function_body(as, nestLevel + 1);
@@ -1177,7 +1182,7 @@ void parse_fault(AsmState& as, int nestLevel) {
eh.m_ehtype = EHEnt::EHType_Fault;
eh.m_base = start;
eh.m_past = as.ue->bcPos();
eh.m_iterId = -1; // only used for debug printing
eh.m_iterId = iterId;
as.addLabelEHFault(label, as.fe->ehtab().size() - 1);
}
+12 -16
Ver Arquivo
@@ -5903,6 +5903,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFPushCtorD(PC& pc) {
inline void OPTBLD_INLINE VMExecutionContext::iopDecodeCufIter(PC& pc) {
NEXT();
DECODE_IA(itId);
DECODE(Offset, offset);
Iter* it = frame_iter(m_fp, itId);
CufIter &cit = it->cuf();
@@ -5919,26 +5920,21 @@ inline void OPTBLD_INLINE VMExecutionContext::iopDecodeCufIter(PC& pc) {
const Func* f = vm_decode_function(tvAsVariant(func),
ar, false,
obj, cls, invName,
true);
false);
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();
pc += offset;
} else {
cit.setCtx(cls);
cit.setFunc(f);
if (obj) {
cit.setCtx(obj);
obj->incRefCount();
} else {
cit.setCtx(cls);
}
cit.setName(invName);
}
cit.setName(invName);
tvAsVariant(m_stack.top()) = ret;
m_stack.popC();
}
inline void OPTBLD_INLINE VMExecutionContext::iopFPushCufIter(PC& pc) {
+2 -1
Ver Arquivo
@@ -479,8 +479,9 @@ void Func::prettyPrint(std::ostream& out) const {
if (params[i].funcletOff() != InvalidAbsoluteOffset) {
out << " DV for parameter " << i << " at " << params[i].funcletOff();
if (params[i].phpCode()) {
out << " = " << params[i].phpCode()->data() << std::endl;
out << " = " << params[i].phpCode()->data();
}
out << std::endl;
}
}
const EHEntVec& ehtab = shared()->m_ehtab;
+1 -1
Ver Arquivo
@@ -537,9 +537,9 @@ enum SetOpOp {
O(IterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(MIterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(WIterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(DecodeCufIter, TWO(IA,BA), ONE(CV), 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) \
+7
Ver Arquivo
@@ -5269,6 +5269,13 @@ void CodeGenerator::cgIterFree(IRInstruction* inst) {
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);
cgCallHelper(m_as, (TCA)decodeCufIterHelper, inst->dst(), kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset).typedValue(inst->src(0)));
}
void CodeGenerator::cgCIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].reg();
int64_t offset = iterOffset(inst->extra<CIterFree>()->iterId);
+1
Ver Arquivo
@@ -312,6 +312,7 @@ X(StLoc, LocalId);
X(StLocNT, LocalId);
X(IterFree, IterId);
X(CIterFree, IterId);
X(DecodeCufIter, IterId);
X(CufIterSpillFrame, FPushCufData);
X(DefConst, ConstData);
X(LdConst, ConstData);
+14
Ver Arquivo
@@ -963,6 +963,20 @@ void HhbcTranslator::emitIterFree(uint32_t iterId) {
gen(IterFree, IterId(iterId), m_tb->getFp());
}
void HhbcTranslator::emitDecodeCufIter(uint32_t iterId, int offset) {
SSATmp* src = popC();
Type type = src->type();
if (type.subtypeOfAny(Type::Arr, Type::Str, Type::Obj)) {
SSATmp* res = gen(DecodeCufIter, Type::Bool,
IterId(iterId), src, m_tb->getFp());
gen(DecRef, src);
emitJmpCondHelper(offset, true, res);
} else {
gen(DecRef, src);
emitJmp(offset, true, false);
}
}
void HhbcTranslator::emitCIterFree(uint32_t iterId) {
gen(CIterFree, IterId(iterId), m_tb->getFp());
}
+1
Ver Arquivo
@@ -353,6 +353,7 @@ struct HhbcTranslator {
uint32_t keyLocalId);
void emitIterFree(uint32_t iterId);
void emitDecodeCufIter(uint32_t iterId, int targetOffset);
void emitCIterFree(uint32_t iterId);
void emitVerifyParamType(uint32_t paramId);
+2
Ver Arquivo
@@ -481,6 +481,8 @@ O(WIterNext, D(Bool), S(FramePtr) \
O(WIterNextK, D(Bool), S(FramePtr) \
C(Int) C(Int) C(Int), E|N|Mem|Refs) \
O(IterFree, 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) \
O(DefMIStateBase, D(PtrToCell), NA, NF) \
O(BaseG, D(PtrToGen), C(TCA) \
+7
Ver Arquivo
@@ -1461,6 +1461,13 @@ TranslatorX64::irTranslateIterFree(const Tracelet& t,
HHIR_EMIT(IterFree, i.imm[0].u_IVA);
}
void
TranslatorX64::irTranslateDecodeCufIter(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(DecodeCufIter, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA);
}
void
TranslatorX64::irTranslateCIterFree(const Tracelet& t,
const NormalizedInstruction& i) {
@@ -18,6 +18,7 @@
#include "hphp/runtime/vm/jit/targetcache.h"
#include "hphp/runtime/eval/runtime/file_repository.h"
#include "hphp/runtime/vm/event_hook.h"
#include "hphp/runtime/base/builtin_functions.h"
#include "hphp/runtime/base/stats.h"
namespace HPHP {
@@ -269,4 +270,33 @@ void functionEnterHelper(const ActRec* ar) {
sp = g_vmContext->m_stack.top();
}
HOT_FUNC_VM
int64_t decodeCufIterHelper(Iter* it, TypedValue func) {
DECLARE_FRAME_POINTER(framePtr);
ObjectData* obj = nullptr;
HPHP::Class* cls = nullptr;
StringData* invName = nullptr;
auto ar = (ActRec*)framePtr->m_savedRbp;
if (LIKELY(ar->m_func->isBuiltin())) {
ar = g_vmContext->getOuterVMFrame(ar);
}
const Func* f = vm_decode_function(tvAsVariant(&func),
ar, false,
obj, cls, invName,
false);
if (UNLIKELY(!f)) return false;
CufIter &cit = it->cuf();
cit.setFunc(f);
if (obj) {
cit.setCtx(obj);
obj->incRefCount();
} else {
cit.setCtx(cls);
}
cit.setName(invName);
return true;
}
} } // HPHP::Transl
+2
Ver Arquivo
@@ -445,6 +445,7 @@ private:
CASE(IterFree) \
CASE(FPassV) \
CASE(UnsetN) \
CASE(DecodeCufIter) \
// These are instruction-like functions which cover more than one
// opcode.
@@ -828,6 +829,7 @@ const size_t kMaxNumTrampolines = kTrampolinesBlockSize /
void fcallHelperThunk() asm ("__fcallHelperThunk");
void funcBodyHelperThunk() asm ("__funcBodyHelperThunk");
void functionEnterHelper(const ActRec* ar);
int64_t decodeCufIterHelper(Iter* it, TypedValue func);
// These could be static but are used in hopt/codegen.cpp
void raiseUndefVariable(StringData* nm);
+1 -1
Ver Arquivo
@@ -1333,7 +1333,7 @@ static const struct {
Stack1, OutArray, -2 }},
{ OpCufSafeReturn,{StackTop3|DontGuardAny,
Stack1, OutUnknown, -2 }},
{ OpDecodeCufIter,{Stack1, Stack1, OutBoolean, 0 }},
{ OpDecodeCufIter,{Stack1, None, OutNone, -1 }},
/*** 11. Iterator instructions ***/
+1
Ver Arquivo
@@ -1021,6 +1021,7 @@ opcodeControlFlowInfo(const Opcode instr) {
case OpMIterInitK: // Ditto
case OpWIterInit: // Ditto
case OpWIterInitK: // Ditto
case OpDecodeCufIter: // Ditto
case OpThrow:
case OpUnwind:
case OpEval:
-3
Ver Arquivo
@@ -730,9 +730,6 @@ bool FuncChecker::checkIter(State* cur, PC pc) {
"IterInit* or MIterInit* <%d> trying to double-initialize\n", id);
ok = false;
}
if (Op(*pc) == OpDecodeCufIter) {
cur->iters[id] = true;
}
} else {
if (!cur->iters[id]) {
verify_error("Cannot access un-initialized iter %d\n", id);
+7 -6
Ver Arquivo
@@ -3,6 +3,7 @@
require_once "support.hhas";
class X {
private $p = true;
function __destruct() { var_dump(__METHOD__); }
static function s_test($x) { return $x !== true; }
function o_test($x) { return $x === $this->p; }
@@ -25,12 +26,12 @@ function test() {
$a = array(1, 'foo' => 'bar', 0, true, false);
$b =& $a['foo'];
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', '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("No function", fast_array_filter($a, null));
var_dump("Closure", fast_array_filter($a, function($a) { return !$a; }));
var_dump("Static Method", fast_array_filter($a, array('X', 's_test')));
var_dump("Dynamic Method", fast_array_filter($a, array(new X, 'o_test')));
var_dump("__call", fast_array_filter($a, array(new X, 'bar')));
var_dump("string", 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));
@@ -1,6 +1,8 @@
array(1) {
["foo"]=>
object(X)#1 (0) {
object(X)#1 (1) {
["p":"X":private]=>
bool(true)
}
}
string(13) "X::__destruct"
@@ -12,6 +14,7 @@ array(1) {
[0]=>
&string(3) "bar"
}
string(11) "No function"
array(3) {
[0]=>
int(1)
@@ -20,12 +23,14 @@ array(3) {
[2]=>
bool(true)
}
string(7) "Closure"
array(2) {
[1]=>
int(0)
[3]=>
bool(false)
}
string(13) "Static Method"
array(4) {
[0]=>
int(1)
@@ -37,9 +42,13 @@ array(4) {
bool(false)
}
string(13) "X::__destruct"
array(0) {
string(14) "Dynamic Method"
array(1) {
[2]=>
bool(true)
}
string(13) "X::__destruct"
string(6) "__call"
array(3) {
["foo"]=>
&string(3) "bar"
@@ -48,6 +57,7 @@ array(3) {
[2]=>
bool(true)
}
string(6) "string"
array(1) {
[2]=>
bool(true)
+97 -45
Ver Arquivo
@@ -23,39 +23,59 @@
RetC
}
.function fast_array_filter($arr, $func) {
.function fast_array_filter($arr = no_args, $func = no_func, $res = entry) {
.numiters 2;
NewArray
SetL $res
PopC
# if we get here, a value was supplied for $res
String "array_filter() expects at most 2 parameters"
Jmp warning
no_args: String "array_filter() expects at least 1 parameter, 0 given"
Jmp warning
not_array:String "array_filter() expects parameter 1 to be array"
Jmp warning
bad_func: String "array_filter() expects parameter 2 to be a valid callback"
Jmp warning
warning: Cns "E_USER_WARNING"
FCallBuiltin 2 2 "trigger_error"
PopR
Null
RetC
entry: IsArrayL $arr
JmpZ not_array
IssetL $func
JmpZ null_func
JmpZ no_func
CGetL $func
DecodeCufIter 0
JmpZ bad_func
DecodeCufIter 0 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 1 loop_a $v $k
}
NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 1 endloop_a $v $k
.try_fault kill_iter_1 1 {
loop_a: FPushCufIter 1 0
FPassL 0 $v
FCall 1
UnboxR
JmpZ skip_a
SetWithRefLM <L:$res EL:$k> $v
skip_a: WIterNextK 1 loop_a $v $k
}
}
endloop_a:CIterFree 0
endloop_n:CGetL $res
RetC
null_func:CGetL $arr
no_func: NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 1 endloop_n $v $k
.try_fault kill_iter_1 {
.try_fault kill_iter_1_only 1 {
loop_n: CGetL $v
JmpZ skip_n
SetWithRefLM <L:$res EL:$k> $v
@@ -63,55 +83,87 @@ 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
IterFree 1
Unwind
kill_iter_1_only:
IterFree 1
Unwind
}
.function fast_array_map($func, $arr) {
.function fast_array_map($func = no_args, $arr = one_arg, $res = one_array) {
.numiters 2;
IssetL $func
# If we get here, a value was supplied for $res
# so we bail out to the c++ implementation
FPushFuncD 1 "__builtin_array_map"
FCallBuiltin 0 0 "func_get_args"
UnboxR
FPassC 0
FCallArray
UnboxR
RetC
no_args: String "array_map() expects at least 2 parameters, 0 given"
Jmp warning
one_arg: String "array_map() expects at least 2 parameters, 1 given"
Jmp warning
not_array_citer_free:
CIterFree 0
not_array:String "array_map() expects parameter 2 to be array"
Jmp warning
bad_func: String "array_map() expects parameter 1 to be a valid callback"
Jmp warning
warning: Cns "E_USER_WARNING"
FCallBuiltin 2 2 "trigger_error"
PopR
Null
RetC
# but if we get here, there was only one array,
# so we use the fast, php version
one_array:IssetL $func
JmpZ ident
CGetL $func
DecodeCufIter 0
JmpZ bad_func
DecodeCufIter 0 bad_func
.try_fault kill_iter_0 {
NewArray
SetL $res
PopC
IsArrayL $arr
JmpZ not_array_citer_free
CGetL $arr
WIterInitK 1 endloop $v $k
NewArray
SetL $res
PopC
.try_fault kill_iter_1 {
loop_x: FPushCufIter 1 0
FPassL 0 $v
FCall 1
SetWithRefRM <L:$res EL:$k>
WIterNextK 1 loop_x $v $k
}
CGetL $arr
WIterInitK 1 endloop $v $k
.try_fault kill_iter_1 1 {
loop_x: FPushCufIter 1 0
FPassL 0 $v
FCall 1
SetWithRefRM <L:$res EL:$k>
WIterNextK 1 loop_x $v $k
}
}
endloop: CIterFree 0
CGetL $res
RetC
ident: CGetL $arr
ident: IsArrayL $arr
JmpZ not_array
CGetL $arr
RetC
bad_func: Null
RetC
kill_iter_0:
CIterFree 0
Unwind
kill_iter_1:
IterFree 0
IterFree 1
Unwind
}