ArrayIdx bytecode and IR instruction

- Implemented ArrayIdx bytecode and IR instruction for common pattern
Esse commit está contido em:
Sean Cannella
2013-05-17 09:18:47 -07:00
commit de Sara Golemon
commit c6dc5f366f
22 arquivos alterados com 246 adições e 28 exclusões
+11
Ver Arquivo
@@ -3162,6 +3162,17 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
e.AKExists();
return true;
}
} else if (call->isCallToFunction("hphp_array_idx")) {
if (params && params->getCount() == 3) {
visit((*params)[0]);
emitConvertToCell(e);
visit((*params)[1]);
emitConvertToCell(e);
visit((*params)[2]);
emitConvertToCell(e);
e.ArrayIdx();
return true;
}
} else if (call->isCallToFunction("strlen")) {
if (params && params->getCount() == 1) {
visit((*params)[0]);
+5
Ver Arquivo
@@ -3625,6 +3625,11 @@ CreateCl <num args> <class name> [F..F] -> [C]
defined at the point of the CreateCl opcode, or the behavior is
undefined.
ArrayIdx [C C C] -> [C]
Checks if array in $3 contains key in $2 and pushes the result onto the stack
if found. Otherwise, $1 is pushed onto the stack. A fatal error will be
thrown if $3 is not an array.
14. Continuation creation and execution
---------------------------------------
+1
Ver Arquivo
@@ -1230,6 +1230,7 @@ ArrayAdd
DefCls
DefFunc
AKExists
ArrayIdx
D:Int LdSwitchDblIndex S0:Dbl S1:Int S2:Int
D:Int LdSwitchStrIndex S0:Str S1:Int S2:Int
+30
Ver Arquivo
@@ -2039,6 +2039,36 @@
},
"args": [
]
},
{
"name": "hphp_array_idx",
"desc": "hphp_array_idx() returns the value at the given key in the given array or the given default value if it is not found. An error will be raised if the search parameter is not an array.",
"flags": [
"HasDocComment",
"HipHopSpecific",
"FunctionIsFoldable"
],
"return": {
"type": "Variant",
"desc": "Returns the value at 'key' in 'search' or 'def' if it is not found."
},
"args": [
{
"name": "key",
"type": "Variant",
"desc": "Value to check."
},
{
"name": "search",
"type": "Variant",
"desc": "An array with keys to check."
},
{
"name": "def",
"type": "Variant",
"desc": "The value to return if key is not found in search."
}
]
}
],
"classes": [
+15
Ver Arquivo
@@ -1339,5 +1339,20 @@ Variant f_i18n_loc_get_error_code() {
return s_collator->getErrorCode();
}
Variant f_hphp_array_idx(CVarRef key, CVarRef search, CVarRef def) {
if (UNLIKELY(!search.isArray())) {
raise_error("hphp_array_idx: search must be an array");
} else if (LIKELY(!key.isNull())) {
ArrayData *arr = search.getArrayData();
VarNR index = key.toKey();
if (LIKELY(!index.isNull())) {
CVarRef ret = arr->get(index, false);
return (&ret != &null_variant) ? ret : def;
}
}
return def;
}
///////////////////////////////////////////////////////////////////////////////
}
+2
Ver Arquivo
@@ -173,6 +173,8 @@ bool f_i18n_loc_set_attribute(int64_t attr, int64_t val);
bool f_i18n_loc_set_strength(int64_t strength);
Variant f_i18n_loc_get_error_code();
Variant f_hphp_array_idx(CVarRef key, CVarRef search, CVarRef def);
///////////////////////////////////////////////////////////////////////////////
}
+14
Ver Arquivo
@@ -5104,6 +5104,20 @@ inline void OPTBLD_INLINE VMExecutionContext::iopAKExists(PC& pc) {
key->m_type = KindOfBoolean;
}
inline void OPTBLD_INLINE VMExecutionContext::iopArrayIdx(PC& pc) {
NEXT();
TypedValue* def = m_stack.topTV();
TypedValue* arr = m_stack.indTV(1);
TypedValue* key = m_stack.indTV(2);
Variant result = f_hphp_array_idx(tvAsCVarRef(key),
tvAsCVarRef(arr),
tvAsCVarRef(def));
m_stack.popTV();
m_stack.popTV();
tvAsVariant(key) = result;
}
inline void OPTBLD_INLINE VMExecutionContext::iopSetL(PC& pc) {
NEXT();
DECODE_HA(local);
+1
Ver Arquivo
@@ -571,6 +571,7 @@ enum SetOpOp {
O(ContHandle, NA, ONE(CV), NOV, CF_TF) \
O(Strlen, NA, ONE(CV), ONE(CV), NF) \
O(IncStat, TWO(IVA,IVA), NOV, NOV, NF) \
O(ArrayIdx, NA, THREE(CV,CV,CV), ONE(CV), NF) \
O(HighInvalid, NA, NOV, NOV, NF) \
enum Op {
+31
Ver Arquivo
@@ -415,6 +415,7 @@ CALL_OPCODE(IncStatGrouped)
CALL_OPCODE(StaticLocInit)
CALL_OPCODE(StaticLocInitCached)
CALL_OPCODE(OpMod)
CALL_OPCODE(ArrayIdx)
// Vector instruction helpers
CALL_OPCODE(BaseG)
@@ -5233,4 +5234,34 @@ void genCodeForTrace(Trace* trace,
}
}
ALWAYS_INLINE
TypedValue& getDefaultIfNullCell(TypedValue* tv, TypedValue& def) {
if (UNLIKELY(nullptr == tv)) {
// refcount is already correct since def was never decrefed
return def;
}
tvRefcountedDecRef(&def);
TypedValue* ret = tvToCell(tv);
tvRefcountedIncRef(ret);
return *ret;
}
HOT_FUNC_VM
TypedValue arrayIdxS(ArrayData* a, StringData* key, TypedValue def) {
return getDefaultIfNullCell(a->nvGet(key), def);
}
HOT_FUNC_VM
TypedValue arrayIdxSi(ArrayData* a, StringData* key, TypedValue def) {
int64_t i;
return UNLIKELY(key->isStrictlyInteger(i)) ?
getDefaultIfNullCell(a->nvGet(i), def) :
getDefaultIfNullCell(a->nvGet(key), def);
}
HOT_FUNC_VM
TypedValue arrayIdxI(ArrayData* a, int64_t key, TypedValue def) {
return getDefaultIfNullCell(a->nvGet(key), def);
}
}}
+4
Ver Arquivo
@@ -561,6 +561,10 @@ void genCodeForTrace(Trace* trace,
const LifetimeInfo* lifetime = nullptr,
AsmInfo* asmInfo = nullptr);
TypedValue arrayIdxI(ArrayData*, int64_t, TypedValue);
TypedValue arrayIdxS(ArrayData*, StringData*, TypedValue);
TypedValue arrayIdxSi(ArrayData*, StringData*, TypedValue);
}}
#endif
@@ -24,6 +24,7 @@
#include "hphp/runtime/vm/unit.h"
#include "hphp/runtime/vm/runtime.h"
#include "hphp/runtime/vm/translator/hopt/irfactory.h"
#include "hphp/runtime/vm/translator/hopt/codegen.h" // ArrayIdx helpers
// Include last to localize effects to this file
#include "hphp/util/assert_throw.h"
@@ -1124,6 +1125,46 @@ void HhbcTranslator::emitIncStat(int32_t counter, int32_t value, bool force) {
}
}
void HhbcTranslator::emitArrayIdx() {
SSATmp* def = popC();
SSATmp* arr = popC();
SSATmp* key = popC();
if (UNLIKELY(!arr->isA(Type::Arr))) {
// raise fatal
emitInterpOne(Type::Cell, 3);
return;
}
if (UNLIKELY(key->isA(Type::Null))) {
// if the key is null it will not be found so just return the default
push(def);
gen(DecRef, arr);
gen(DecRef, key);
return;
}
if (UNLIKELY(!(key->isA(Type::Int) || key->isA(Type::Str)))) {
PUNT(ArrayIdx_unsupportedKey);
}
KeyType keyType;
bool checkForInt;
checkStrictlyInteger(key, keyType, checkForInt);
TCA opFunc;
if (checkForInt) {
opFunc = (TCA)&arrayIdxSi;
} else if (IntKey == keyType) {
opFunc = (TCA)&arrayIdxI;
} else {
assert(StrKey == keyType);
opFunc = (TCA)&arrayIdxS;
}
push(gen(ArrayIdx, cns(opFunc), arr, key, def));
gen(DecRef, arr);
gen(DecRef, key);
}
void HhbcTranslator::emitIncTransCounter() {
m_tb->gen(IncTransCounter);
}
@@ -3206,4 +3247,24 @@ void HhbcTranslator::end(int nextPc) {
m_tb->genTraceEnd(nextPc);
}
void HhbcTranslator::checkStrictlyInteger(
SSATmp*& key, KeyType& keyType, bool& checkForInt) {
checkForInt = false;
if (key->isA(Type::Int)) {
keyType = IntKey;
} else {
assert(key->isA(Type::Str));
keyType = StrKey;
if (key->isConst()) {
int64_t i;
if (key->getValStr()->isStrictlyInteger(i)) {
keyType = IntKey;
key = cns(i);
}
} else {
checkForInt = true;
}
}
}
}} // namespace HPHP::JIT
@@ -350,6 +350,7 @@ struct HhbcTranslator {
void emitStrlen();
void emitIncStat(int32_t counter, int32_t value, bool force = false);
void emitIncTransCounter();
void emitArrayIdx();
// tracelet guards
Trace* guardTypeStack(uint32_t stackIndex,
@@ -371,6 +372,9 @@ struct HhbcTranslator {
void emitInterpOne(Type type, int numPopped, int numExtraPushed = 0);
void emitInterpOneCF(int numPopped);
void checkStrictlyInteger(SSATmp*& key, KeyType& keyType,
bool& checkForInt);
private:
/*
* VectorTranslator is responsible for translating one of the vector
@@ -423,8 +427,6 @@ private:
void emitArraySet(SSATmp* key, SSATmp* value);
void emitArrayGet(SSATmp* key);
void emitArrayIsset();
void checkStrictlyInteger(SSATmp*& key, KeyType& keyType,
bool& checkForInt);
// Misc Helpers
void numberStackInputs();
+2
Ver Arquivo
@@ -581,6 +581,8 @@ O(EmptyElem, D(Bool), C(TCA) \
O(IncStat, ND, C(Int) C(Int) C(Bool), E|Mem) \
O(IncStatGrouped, ND, CStr CStr C(Int), E|N|Mem) \
O(IncTransCounter, ND, NA, E) \
O(ArrayIdx, D(Cell), C(TCA) S(Arr) S(Int,Str) S(Cell), \
E|N|CRc|PRc|Refs|Mem) \
O(DbgAssertRefCount, ND, SUnk, N|E) \
O(DbgAssertPtr, ND, S(PtrToGen), N|E) \
O(Nop, ND, NA, NF) \
@@ -686,6 +686,11 @@ void TranslatorX64::irTranslateIncStat(const Tracelet& t,
HHIR_EMIT(IncStat, i.imm[0].u_IVA, i.imm[1].u_IVA);
}
void TranslatorX64::irTranslateArrayIdx(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(ArrayIdx);
}
void TranslatorX64::irTranslateClassExists(const Tracelet& t,
const NormalizedInstruction& i) {
const StringData* clsName = i.inputs[1]->rtt.valueStringOrNull();
@@ -163,6 +163,8 @@ static CallMap s_callMap({
{{SSA, 0}}},
{CreateCl, (TCA)createClHelper, DSSA, SSync,
{{SSA, 0}, {SSA, 1}, {SSA, 2}, {SSA, 3}}},
{ArrayIdx, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
/* Switch helpers */
{LdSwitchDblIndex, (TCA)switchDoubleHelper, DSSA, SSync,
@@ -1419,26 +1419,6 @@ void HhbcTranslator::VectorTranslator::emitUnsetProp() {
}
#undef HELPER_TABLE
void HhbcTranslator::VectorTranslator::checkStrictlyInteger(
SSATmp*& key, KeyType& keyType, bool& checkForInt) {
checkForInt = false;
if (key->isA(Type::Int)) {
keyType = IntKey;
} else {
assert(key->isA(Type::Str));
keyType = StrKey;
if (key->isConst()) {
int64_t i;
if (key->getValStr()->isStrictlyInteger(i)) {
keyType = IntKey;
key = cns(i);
}
} else {
checkForInt = true;
}
}
}
static inline TypedValue* checkedGetCell(ArrayData* a, StringData* key) {
int64_t i;
return UNLIKELY(key->isStrictlyInteger(i)) ? a->nvGetCell(i)
@@ -1477,7 +1457,7 @@ HELPER_TABLE(ELEM)
void HhbcTranslator::VectorTranslator::emitArrayGet(SSATmp* key) {
KeyType keyType;
bool checkForInt;
checkStrictlyInteger(key, keyType, checkForInt);
m_ht.checkStrictlyInteger(key, keyType, checkForInt);
typedef TypedValue (*OpFunc)(ArrayData*, TypedValue*);
BUILD_OPTAB_HOT(keyType, checkForInt);
@@ -1650,7 +1630,7 @@ void HhbcTranslator::VectorTranslator::emitArrayIsset() {
SSATmp* key = getKey();
KeyType keyType;
bool checkForInt;
checkStrictlyInteger(key, keyType, checkForInt);
m_ht.checkStrictlyInteger(key, keyType, checkForInt);
typedef uint64_t (*OpFunc)(ArrayData*, TypedValue*);
BUILD_OPTAB(keyType, checkForInt);
@@ -1726,7 +1706,7 @@ void HhbcTranslator::VectorTranslator::emitArraySet(SSATmp* key,
assert(value->type().notBoxed());
KeyType keyType;
bool checkForInt;
checkStrictlyInteger(key, keyType, checkForInt);
m_ht.checkStrictlyInteger(key, keyType, checkForInt);
const DynLocation& base = *m_ni.inputs[m_mii.valCount()];
bool setRef = base.outerType() == KindOfRef;
typedef ArrayData* (*OpFunc)(ArrayData*, TypedValue*, TypedValue, RefData*);
+6 -1
Ver Arquivo
@@ -7519,7 +7519,6 @@ void TranslatorX64::translateStrlen(const Tracelet& t,
not_reached();
}
}
void TranslatorX64::translateIncStat(const Tracelet& t,
const NormalizedInstruction& i) {
int32_t counter = i.imm[0].u_IVA;
@@ -7527,6 +7526,11 @@ void TranslatorX64::translateIncStat(const Tracelet& t,
Stats::emitInc(a, Stats::StatCounter(counter), value);
}
void TranslatorX64::translateArrayIdx(const Tracelet& t,
const NormalizedInstruction& i) {
not_reached();
}
static void analyzeClassExistsImpl(NormalizedInstruction& i) {
const int nameIdx = 1;
const int autoIdx = 0;
@@ -12247,6 +12251,7 @@ bool TranslatorX64::dumpTCData() {
* Always-interp instructions,
*/ \
INTERP_OP(ContHandle) \
INTERP_OP(ArrayIdx) \
// Define the trivial analyze methods
@@ -629,6 +629,7 @@ MINSTRS
CASE(ContHandle) \
CASE(Strlen) \
CASE(IncStat) \
CASE(ArrayIdx) \
// These are instruction-like functions which cover more than one
// opcode.
+3 -2
Ver Arquivo
@@ -1263,6 +1263,9 @@ static const struct {
{ OpLateBoundCls,{None, Stack1, OutClassRef, 1 }},
{ OpNativeImpl, {None, None, OutNone, 0 }},
{ OpCreateCl, {BStackN, Stack1, OutObject, 1 }},
{ OpStrlen, {Stack1, Stack1, OutStrlen, 0 }},
{ OpIncStat, {None, None, OutNone, 0 }},
{ OpArrayIdx, {StackTop3, Stack1, OutUnknown, -2 }},
/*** 14. Continuation instructions ***/
@@ -1280,8 +1283,6 @@ static const struct {
{ OpContCurrent, {None, Stack1, OutUnknown, 1 }},
{ OpContStopped, {None, None, OutNone, 0 }},
{ OpContHandle, {Stack1, None, OutNone, -1 }},
{ OpStrlen, {Stack1, Stack1, OutStrlen, 0 }},
{ OpIncStat, {None, None, OutNone, 0 }},
};
static hphp_hash_map<Opcode, InstrInfo> instrInfo;
+8
Ver Arquivo
@@ -13216,6 +13216,14 @@ const char *g_class_map[] = {
(const char *)0xffffffff /* KindOfUnknown: $t: Variant */, NULL,
NULL,
NULL,
(const char *)0x10116040, "hphp_array_idx", "", (const char*)0, (const char*)0,
"/**\n * ( HipHop specific )\n *\n * hphp_array_idx() returns the value at the given key in the given array\n * or the given default value if it is not found. An error will be raised\n * if the search parameter is not an array.\n *\n * @key mixed Value to check.\n * @search mixed An array with keys to check.\n * @def mixed The value to return if key is not found in search.\n *\n * @return mixed Returns the value at 'key' in 'search' or 'def' if\n * it is not found.\n */",
(const char *)0xffffffff /* KindOfUnknown: $t: Variant */, (const char *)0x2000, "key", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL,
(const char *)0x2000, "search", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL,
(const char *)0x2000, "def", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL,
NULL,
NULL,
NULL,
(const char *)0x10706040, "is_bool", "", (const char*)0, (const char*)0,
"/**\n * ( excerpt from http://php.net/manual/en/function.is-bool.php )\n *\n * Finds whether the given variable is a boolean.\n *\n * @var mixed The variable being evaluated.\n *\n * @return bool Returns TRUE if var is a boolean, FALSE otherwise.\n */",
(const char *)0x9 /* KindOfBoolean */, (const char *)0x2000, "var", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL,
+25
Ver Arquivo
@@ -0,0 +1,25 @@
<?php
function main() {
$a = array('1' => '2', 'hello' => 'world', '' => 'empty');
$b = null;
var_dump(hphp_array_idx('1', $a, 3));
var_dump(hphp_array_idx('0', $a, 3));
var_dump(hphp_array_idx(1, $a, 3));
var_dump(hphp_array_idx(0, $a, 3));
var_dump(hphp_array_idx(1.01, $a, 3));
var_dump(hphp_array_idx(true, $a, 3));
var_dump(hphp_array_idx(false, $a, 3));
var_dump(hphp_array_idx('hello', $a, 3));
var_dump(hphp_array_idx('world', $a, 3));
var_dump(hphp_array_idx('', $a, 3));
var_dump(hphp_array_idx(null, $a, 3));
// should fatal
var_dump(hphp_array_idx('not_reached', $b, 3));
}
main();
+12
Ver Arquivo
@@ -0,0 +1,12 @@
string(1) "2"
int(3)
string(1) "2"
int(3)
string(1) "2"
string(1) "2"
int(3)
string(5) "world"
int(3)
string(5) "empty"
int(3)
HipHop Fatal error: hphp_array_idx: search must be an array in %s on line 22