vector and map get/set/isset specialization in IR

specialized the IR instruction CGetM/SetM/IssetM for Vector and Map. IR now generates specialized code in those situation similar to what is done with Array.
Esse commit está contido em:
Dario Russi
2013-06-05 09:29:33 -07:00
commit de sgolemon
commit fe8b84f542
13 arquivos alterados com 428 adições e 75 exclusões
+26
Ver Arquivo
@@ -1753,6 +1753,14 @@ D:Cell = ArrayGet S0:ConstTCA S1:Arr S2:{Int|Str}
Get element with key S2 from base S1.
D:Cell = VectorGet S0:ConstTCA S1:Obj<Vector> S2:{Int}
Get element with key S2 from base S1. S1 is a Vector instance.
D:Cell = MapGet S0:ConstTCA S1:Obj<Map> S2:{Int|Str}
Get element with key S2 from base S1. S1 is a Map instance.
D:Cell = CGetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
Get element with key S2 from S1. S3 should point to an MInstrState
@@ -1781,6 +1789,14 @@ ArraySet S0:ConstTCA S1:Arr S2:{Int|Str} S3:Cell S4:BoxedArr
operation, it will be replaced with the new Array resulting from the set
operation.
VectorSet S0:ConstTCA S1:Obj<Vector> S2:{Int} S3:Cell
Set element with key S2 in S1 to S3. S1 is a Vector instance.
MapSet S0:ConstTCA S1:Obj<Map> S2:{Int|Str} S3:Cell
Set element with key S2 in S1 to S3. S1 is a Map instance.
D:{CountedStr|Nullptr} = SetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
D:{CountedStr|Nullptr} D:StkPtr = SetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
@@ -1830,6 +1846,16 @@ D:Bool = ArrayIsset S0:ConstTCA S1:Arr S2:{Int|Str}
Returns true iff the element at key S2 in the base S1 is set.
D:Bool = VectorIsset S0:ConstTCA S1:Obj<Vector> S2:{Int}
Returns true iff the element at key S2 in the base S1 is set. S1 is a
Vector instance.
D:Bool = MapIsset S0:ConstTCA S1:Obj<Map> S2:{Int|Str}
Returns true iff the element at key S2 in the base S1 is set. S1 is a
Map instance.
D:Bool = IssetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
Returns true iff the element at key S2 in S1 is set.
+7 -2
Ver Arquivo
@@ -816,7 +816,7 @@ mcodeMaybePropName(MemberCode mcode) {
}
inline bool
mcodeMaybeArrayKey(MemberCode mcode) {
mcodeMaybeArrayOrMapKey(MemberCode mcode) {
return mcode == MEC || mcode == MEL || mcode == MET || mcode == MEI;
}
@@ -830,7 +830,12 @@ mcodeMaybeArrayIntKey(MemberCode mcode) {
return mcode == MEC || mcode == MEL || mcode == MEI;
}
inline bool
mcodeMaybeVectorKey(MemberCode mcode) {
return mcode == MEC || mcode == MEL || mcode == MEI;
}
}
}
#endif
+6
Ver Arquivo
@@ -439,12 +439,16 @@ CALL_OPCODE(ElemX)
CALL_STK_OPCODE(ElemDX)
CALL_STK_OPCODE(ElemUX)
CALL_OPCODE(ArrayGet)
CALL_OPCODE(VectorGet)
CALL_OPCODE(MapGet)
CALL_OPCODE(CGetElem)
CALL_STK_OPCODE(VGetElem)
CALL_STK_OPCODE(BindElem)
CALL_STK_OPCODE(SetWithRefElem)
CALL_STK_OPCODE(SetWithRefNewElem)
CALL_OPCODE(ArraySet)
CALL_OPCODE(VectorSet)
CALL_OPCODE(MapSet)
CALL_OPCODE(ArraySetRef)
CALL_STK_OPCODE(SetElem)
CALL_STK_OPCODE(UnsetElem)
@@ -453,6 +457,8 @@ CALL_STK_OPCODE(IncDecElem)
CALL_STK_OPCODE(SetNewElem)
CALL_STK_OPCODE(BindNewElem)
CALL_OPCODE(ArrayIsset)
CALL_OPCODE(VectorIsset)
CALL_OPCODE(MapIsset)
CALL_OPCODE(IssetElem)
CALL_OPCODE(EmptyElem)
+18 -1
Ver Arquivo
@@ -433,6 +433,12 @@ private:
void emitArraySet(SSATmp* key, SSATmp* value);
void emitArrayGet(SSATmp* key);
void emitArrayIsset();
void emitVectorSet(SSATmp* key, SSATmp* value);
void emitVectorGet(SSATmp* key);
void emitVectorIsset();
void emitMapSet(SSATmp* key, SSATmp* value);
void emitMapGet(SSATmp* key);
void emitMapIsset();
// Misc Helpers
void numberStackInputs();
@@ -460,10 +466,21 @@ private:
SSATmp* genStk(Opcode op, IRTrace* taken, Srcs... srcs);
/* Various predicates about the current instruction */
bool isSimpleArrayOp();
bool isSimpleBase();
bool isSingleMember();
enum class SimpleOp {
// the opcode is not in a simple form or not on a proper collection type
None,
// simple opcode on Array
Array,
// simple opcode on Vector* (c_Vector*)
Vector,
// simple opcode on Map* (c_Map*)
Map
};
SimpleOp isSimpleCollectionOp();
bool generateMVal() const;
bool needFirstRatchet() const;
bool needFinalRatchet() const;
+2
Ver Arquivo
@@ -476,6 +476,8 @@ bool IRInstruction::isLoad() const {
case VGetProp:
case VGetPropStk:
case ArrayGet:
case VectorGet:
case MapGet:
case CGetElem:
case VGetElem:
case VGetElemStk:
+20
Ver Arquivo
@@ -553,6 +553,12 @@ O_STK(ElemUX, D(PtrToGen), C(TCA) \
O(ArrayGet, D(Cell), C(TCA) \
S(Arr) \
S(Int,Str), C|N|PRc|Refs|Mem|Er)\
O(VectorGet, D(Cell), C(TCA) \
S(Obj) \
S(Int), E|N|Mem|Refs|Er) \
O(MapGet, D(Cell), C(TCA) \
S(Obj) \
S(Int,Str), E|N|Mem|Refs|Er) \
O(CGetElem, D(Cell), C(TCA) \
S(PtrToGen) \
S(Cell) \
@@ -570,6 +576,14 @@ O(ArraySet, D(Arr), C(TCA) \
S(Arr) \
S(Int,Str) \
S(Cell), E|N|PRc|CRc|Refs|Mem|K) \
O(VectorSet, ND, C(TCA) \
S(Obj) \
S(Int) \
S(Cell), E|N|Mem|Refs|Er) \
O(MapSet, ND, C(TCA) \
S(Obj) \
S(Int,Str) \
S(Cell), E|N|Mem|Refs|Er) \
O(ArraySetRef, ND, C(TCA) \
S(Arr) \
S(Int,Str) \
@@ -608,6 +622,12 @@ O_STK(BindNewElem, ND, S(PtrToGen) \
O(ArrayIsset, D(Bool), C(TCA) \
S(Arr) \
S(Int,Str), E|N|Mem|Refs|Er) \
O(VectorIsset, D(Bool), C(TCA) \
S(Obj) \
S(Int), E|N|Mem|Refs) \
O(MapIsset, D(Bool), C(TCA) \
S(Obj) \
S(Int,Str), E|N|Mem|Refs) \
O(IssetElem, D(Bool), C(TCA) \
S(PtrToGen) \
S(Cell) \
+10
Ver Arquivo
@@ -215,6 +215,10 @@ static CallMap s_callMap({
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
{ArrayGet, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {SSA, 2}}},
{VectorGet, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {SSA, 2}}},
{MapGet, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {SSA, 2}}},
{CGetElem, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
{VGetElem, {FSSA, 0}, DSSA, SSync,
@@ -225,6 +229,10 @@ static CallMap s_callMap({
{{SSA, 1}, {TV, 2}, {SSA, 3}, {SSA, 4}}},
{ArraySet, {FSSA, 0}, DSSA, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
{VectorSet, {FSSA, 0}, DNone, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
{MapSet, {FSSA, 0}, DNone, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
{ArraySetRef, {FSSA, 0}, DSSA, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}, {SSA, 4}}},
{SetElem, {FSSA, 0}, DSSA, SSync,
@@ -241,6 +249,8 @@ static CallMap s_callMap({
{BindNewElem, (TCA)bindNewElemIR, DNone, SSync,
{{SSA, 0}, {SSA, 1}, {SSA, 2}}},
{ArrayIsset, {FSSA, 0}, DSSA, SSync, {{SSA, 1}, {SSA, 2}}},
{VectorIsset, {FSSA, 0}, DSSA, SSync, {{SSA, 1}, {SSA, 2}}},
{MapIsset, {FSSA, 0}, DSSA, SSync, {{SSA, 1}, {SSA, 2}}},
{IssetElem, {FSSA, 0}, DSSA, SSync,
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
{EmptyElem,{FSSA, 0}, DSSA, SSync,
+3 -2
Ver Arquivo
@@ -223,10 +223,11 @@ RuntimeType::setValueType(DataType newInner) const {
RuntimeType
RuntimeType::setKnownClass(const Class* klass) const {
assert(isObject());
assert(isObject() || (isRef() && innerType() == KindOfObject));
RuntimeType rtt;
rtt.m_kind = VALUE;
rtt.m_kind = this->m_kind;
rtt.m_value.outerType = outerType();
rtt.m_value.innerType = innerType();
rtt.m_value.klass = m_value.klass;
rtt.m_value.knownClass = klass;
rtt.consistencyCheck();
+3 -1
Ver Arquivo
@@ -229,7 +229,9 @@ class RuntimeType {
assert(m_value.innerType != KindOfStaticString &&
m_value.outerType != KindOfStaticString);
assert(m_value.knownClass == nullptr ||
m_value.outerType == KindOfObject);
m_value.outerType == KindOfObject ||
(m_value.outerType == KindOfRef &&
m_value.innerType == KindOfObject));
}
}
+58 -10
Ver Arquivo
@@ -34,6 +34,7 @@
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/base/types.h"
#include "hphp/runtime/ext/ext_continuation.h"
#include "hphp/runtime/ext/ext_collections.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/vm/bytecode.h"
#include "hphp/runtime/vm/jit/annotation.h"
@@ -249,7 +250,9 @@ Translator::locPhysicalOffset(Location l, const Func* f) {
return -((l.offset + 1) * iterInflator + localsToSkip);
}
RuntimeType Translator::liveType(Location l, const Unit& u) {
RuntimeType Translator::liveType(Location l,
const Unit& u,
bool specialize) {
Cell *outer;
switch (l.space) {
case Location::Stack:
@@ -284,11 +287,13 @@ RuntimeType Translator::liveType(Location l, const Unit& u) {
}
}
assert(IS_REAL_TYPE(outer->m_type));
return liveType(outer, l);
return liveType(outer, l, specialize);
}
RuntimeType
Translator::liveType(const Cell* outer, const Location& l) {
Translator::liveType(const Cell* outer,
const Location& l,
bool specialize) {
always_assert(analysisDepth() == 0);
if (!outer) {
@@ -298,26 +303,30 @@ Translator::liveType(const Cell* outer, const Location& l) {
DataType outerType = (DataType)outer->m_type;
assert(IS_REAL_TYPE(outerType));
DataType valueType = outerType;
DataType innerType = KindOfInvalid;
const Cell* valCell = outer;
if (outerType == KindOfRef) {
// Variant. Pick up the inner type, too.
valCell = outer->m_data.pref->tv();
DataType innerType = valCell->m_type;
innerType = valCell->m_type;
assert(IS_REAL_TYPE(innerType));
valueType = innerType;
assert(innerType != KindOfRef);
TRACE(2, "liveType Var -> %d\n", innerType);
return RuntimeType(KindOfRef, innerType);
} else {
TRACE(2, "liveType %d\n", outerType);
}
const Class *klass = nullptr;
if (valueType == KindOfObject) {
// TODO: Infer the class, too.
if (false) {
// Only infer the class if specialization requested
if (specialize) {
klass = valCell->m_data.pobj->getVMClass();
}
}
TRACE(2, "liveType %d\n", outerType);
RuntimeType retval = RuntimeType(outerType, KindOfInvalid, klass);
RuntimeType retval = RuntimeType(outerType, innerType);
if (klass != nullptr) {
retval = retval.setKnownClass(klass);
}
return retval;
}
@@ -948,7 +957,7 @@ getDynLocType(const vector<DynLocation*>& inputs,
baseType = Type::fromRuntimeType(inputs[1]->rtt);
}
const bool setElem = mcodeMaybeArrayKey(ni->immVecM[0]);
const bool setElem = mcodeMaybeArrayOrMapKey(ni->immVecM[0]);
const Type valType = Type::fromRuntimeType(inputs[0]->rtt);
if (setElem && baseType.maybe(Type::Str)) {
if (baseType.isString()) {
@@ -2936,6 +2945,44 @@ void Translator::relaxDeps(Tracelet& tclet, TraceletContext& tctxt) {
}
}
/*
* This method looks at all the uses of the tracelet dependencies in the
* instruction stream and tries to specialize the type associated
* with each location.
*/
void Translator::specializeDeps(Tracelet& tclet, TraceletContext& tctxt) {
// Process the instruction stream, look for CGetM/SetM/IssetM and if the
// instructions are for Vector types and consistent with Vector usage
// specialize the guard
for (NormalizedInstruction* instr = tclet.m_instrStream.first; instr;
instr = instr->next) {
auto op = instr->op();
if ((op == OpCGetM || op == OpIssetM) && instr->inputs.size() == 2) {
specializeCollections(instr, 0, tctxt);
} else if (op == OpSetM && instr->inputs.size() == 3) {
specializeCollections(instr, 1, tctxt);
}
}
}
void Translator::specializeCollections(NormalizedInstruction* instr,
int index,
TraceletContext& tctxt) {
if (instr->inputs[index]->isObject()
|| instr->inputs[index]->isRefToObject()) {
Location l = instr->inputs[index]->location;
auto dep = tctxt.m_dependencies.find(l);
if (dep != tctxt.m_dependencies.end()) {
RuntimeType specialized = liveType(l, *curUnit(), true);
const Class* klass = specialized.knownClass();
if (klass != nullptr
&& (klass == c_Vector::s_cls || klass == c_Map::s_cls)) {
tctxt.m_dependencies[l]->rtt = specialized;
}
}
}
}
static bool checkTaintFuncs(StringData* name) {
static const StringData* s_extract =
StringData::GetStaticString("extract");
@@ -3491,6 +3538,7 @@ breakBB:
}
relaxDeps(t, tas);
specializeDeps(t, tas);
// Mark the last instruction appropriately
assert(t.m_instrStream.last);
+11 -2
Ver Arquivo
@@ -121,6 +121,9 @@ struct DynLocation {
bool isRef() const {
return rtt.isRef();
}
bool isRefToObject() const {
return rtt.isRef() && innerType() == KindOfObject;
}
bool isValue() const {
return rtt.isValue();
}
@@ -856,6 +859,10 @@ private:
NormalizedInstruction* firstInstr,
GuardType specType,
GuardType& relxType);
void specializeDeps(Tracelet& tclet, TraceletContext& tctxt);
void specializeCollections(NormalizedInstruction* instr,
int index,
TraceletContext& tctxt);
DataTypeCategory getOperandConstraintCategory(NormalizedInstruction* instr,
size_t opndIdx);
GuardType getOperandConstraintType(NormalizedInstruction* instr,
@@ -868,8 +875,10 @@ private:
const GuardType& specType);
RuntimeType liveType(Location l, const Unit &u);
RuntimeType liveType(const Cell* outer, const Location& l);
RuntimeType liveType(Location l, const Unit &u, bool specialize = false);
RuntimeType liveType(const Cell* outer,
const Location& l,
bool specialize = false);
void consumeStackEntry(Tracelet* tlet, NormalizedInstruction* ni);
void produceStackEntry(Tracelet* tlet, NormalizedInstruction* ni);
+8 -5
Ver Arquivo
@@ -358,7 +358,7 @@ public:
Type innerType() const {
assert(isBoxed());
return Type(m_bits >> kBoxShift);
return Type(m_bits >> kBoxShift, m_class);
}
/*
@@ -391,18 +391,21 @@ public:
// Boxing Uninit returns InitNull but that logic doesn't belong
// here.
assert(not(Uninit) || equals(Cell));
return Type(m_bits << kBoxShift);
return Type(m_bits << kBoxShift, m_class);
}
// This computes the type effects of the Unbox opcode.
Type unbox() const {
assert(subtypeOf(Gen));
return (*this & Cell) | (*this & BoxedCell).innerType();
const Class* klass = m_class;
Type t = (*this & Cell) | (*this & BoxedCell).innerType();
t.m_class = klass; // keep the Class* if one was set
return t;
}
Type deref() const {
assert(isPtr());
return Type(m_bits >> kPtrShift);
return Type(m_bits >> kPtrShift, m_class);
}
Type derefIfPtr() const {
@@ -419,7 +422,7 @@ public:
Type ptr() const {
assert(!isPtr());
assert(subtypeOf(Gen));
return Type(m_bits << kPtrShift);
return Type(m_bits << kPtrShift, m_class);
}
Type specialize(const Class* klass) const {
+256 -52
Ver Arquivo
@@ -330,7 +330,7 @@ void HhbcTranslator::VectorTranslator::checkMIState() {
mcodeMaybePropName(m_ni.immVecM[0]);
// Array access with one element in the vector
const bool singleElem = isSingle && mcodeMaybeArrayKey(m_ni.immVecM[0]);
const bool singleElem = isSingle && mcodeMaybeArrayOrMapKey(m_ni.immVecM[0]);
// SetM with one vector array element
const bool simpleArraySet = isSetM && singleElem;
@@ -338,6 +338,11 @@ void HhbcTranslator::VectorTranslator::checkMIState() {
// IssetM with one vector array element and an Arr base
const bool simpleArrayIsset = isIssetM && singleElem &&
baseType.subtypeOf(Type::Arr);
// IssetM with one vector array element and a collection type
const bool simpleCollectionIsset = isIssetM && singleElem &&
baseType.strictSubtypeOf(Type::Obj) &&
(baseType.getClass() == c_Vector::s_cls ||
baseType.getClass() == c_Map::s_cls );
// UnsetM on an array with one vector element
const bool simpleArrayUnset = isUnsetM && singleElem;
@@ -349,10 +354,14 @@ void HhbcTranslator::VectorTranslator::checkMIState() {
// will use tvScratch and Obj will fatal or use tvRef.
const bool simpleArrayGet = isCGetM && singleElem &&
baseType.not(Type::Str | Type::Obj);
const bool simpleCollectionGet = isCGetM && singleElem &&
baseType.strictSubtypeOf(Type::Obj) &&
(baseType.getClass() == c_Vector::s_cls ||
baseType.getClass() == c_Map::s_cls );
if (simpleProp || singlePropSet ||
simpleArraySet || simpleArrayGet ||
simpleArrayUnset || badUnset ||
simpleArraySet || simpleArrayGet || simpleCollectionGet ||
simpleArrayUnset || badUnset || simpleCollectionIsset ||
simpleArrayIsset) {
setNoMIState();
}
@@ -409,7 +418,7 @@ void HhbcTranslator::VectorTranslator::emitMTrace() {
shape << separator;
if (mcode == MW) {
shape << "MW";
} else if (mcodeMaybeArrayKey(mcode)) {
} else if (mcodeMaybeArrayOrMapKey(mcode)) {
shape << "ME:" << rttStr(iInd);
} else if (mcodeMaybePropName(mcode)) {
shape << "MP:" << rttStr(iInd);
@@ -568,7 +577,7 @@ void HhbcTranslator::VectorTranslator::emitBaseLCR() {
IRTrace* failedRef = baseType.isBoxed() ? m_ht.getExitTrace() : nullptr;
if ((baseType.subtypeOfAny(Type::Obj, Type::BoxedObj) &&
mcodeMaybePropName(m_ni.immVecM[0])) ||
isSimpleArrayOp()) {
isSimpleCollectionOp() != SimpleOp::None) {
// In these cases we can pass the base by value, after unboxing if needed.
m_base = gen(Unbox, failedRef, getBase());
assert(m_base->isA(baseType.unbox()));
@@ -596,20 +605,43 @@ void HhbcTranslator::VectorTranslator::emitBaseLCR() {
}
}
// Is the current instruction a 1-element vector, with an Arr base and Int or
// Str key?
bool HhbcTranslator::VectorTranslator::isSimpleArrayOp() {
// Is the current instruction a 1-element simple collection (includes Array),
// operation?
HhbcTranslator::VectorTranslator::SimpleOp
HhbcTranslator::VectorTranslator::isSimpleCollectionOp() {
SSATmp* base = getInput(m_mii.valCount());
auto baseType = base->type().unbox();
HPHP::Op op = m_ni.mInstrOp();
if ((op == OpSetM || op == OpCGetM || op == OpIssetM) &&
isSimpleBase() &&
isSingleMember() &&
mcodeMaybeArrayKey(m_ni.immVecM[0]) &&
base->type().subtypeOfAny(Type::Arr, Type::BoxedArr)) {
SSATmp* key = getInput(m_mii.valCount() + 1);
return key->isA(Type::Int) || key->isA(Type::Str);
isSingleMember()) {
if (baseType.subtypeOf(Type::Arr)) {
if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1);
if (key->isA(Type::Int) || key->isA(Type::Str)) {
return SimpleOp::Array;
}
}
} else if (baseType.strictSubtypeOf(Type::Obj)) {
if (baseType.getClass() == c_Vector::s_cls) {
if (mcodeMaybeVectorKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1);
if (key->isA(Type::Int)) {
return SimpleOp::Vector;
}
}
} else if (baseType.getClass() == c_Map::s_cls) {
if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1);
if (key->isA(Type::Int) || key->isA(Type::Str)) {
return SimpleOp::Map;
}
}
}
}
}
return false;
return SimpleOp::None;
}
// "Simple" bases are stack cells and locals.
@@ -1479,6 +1511,53 @@ void HhbcTranslator::VectorTranslator::emitArrayGet(SSATmp* key) {
}
#undef HELPER_TABLE
namespace VectorHelpers {
inline TypedValue vectorGet(c_Vector* vec, int64_t key) {
TypedValue* ret = vec->at(key);
return *ret;
}
}
void HhbcTranslator::VectorTranslator::emitVectorGet(SSATmp* key) {
SSATmp* value = gen(VectorGet, m_ht.getCatchTrace(),
cns((TCA)VectorHelpers::vectorGet), m_base, key);
m_result = gen(IncRef, value);
}
template<KeyType keyType>
static inline TypedValue mapGetImpl(
c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
TypedValue* ret = map->at(key);
return *ret;
}
#define HELPER_TABLE(m) \
/* name hot keyType */ \
m(mapGetS, HOT_FUNC_VM, StrKey) \
m(mapGetI, HOT_FUNC_VM, IntKey)
#define ELEM(nm, hot, keyType) \
hot \
TypedValue nm(c_Map* map, TypedValue* key) { \
return mapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
}
namespace VectorHelpers {
HELPER_TABLE(ELEM)
}
#undef ELEM
void HhbcTranslator::VectorTranslator::emitMapGet(SSATmp* key) {
assert(key->isA(Type::Int) || key->isA(Type::Str));
KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
BUILD_OPTAB_HOT(keyType);
SSATmp* value = gen(MapGet, m_ht.getCatchTrace(),
cns((TCA)opFunc), m_base, key);
m_result = gen(IncRef, value);
}
#undef HELPER_TABLE
template <KeyType keyType>
static inline TypedValue cGetElemImpl(TypedValue* base, TypedValue keyVal,
MInstrState* mis) {
@@ -1512,15 +1591,24 @@ HELPER_TABLE(ELEM)
void HhbcTranslator::VectorTranslator::emitCGetElem() {
SSATmp* key = getKey();
if (isSimpleArrayOp()) {
SimpleOp simpleOpType = isSimpleCollectionOp();
switch (simpleOpType) {
case SimpleOp::Array:
emitArrayGet(key);
return;
break;
case SimpleOp::Vector:
emitVectorGet(key);
break;
case SimpleOp::Map:
emitMapGet(key);
break;
default:
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
BUILD_OPTAB_HOT(getKeyTypeIS(key));
m_result = gen(CGetElem, m_ht.getCatchTrace(), cns((TCA)opFunc),
m_base, key, genMisPtr());
break;
}
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
BUILD_OPTAB_HOT(getKeyTypeIS(key));
m_result = gen(CGetElem, m_ht.getCatchTrace(), cns((TCA)opFunc),
m_base, key, genMisPtr());
}
#undef HELPER_TABLE
@@ -1651,13 +1739,69 @@ void HhbcTranslator::VectorTranslator::emitArrayIsset() {
}
#undef HELPER_TABLE
void HhbcTranslator::VectorTranslator::emitIssetElem() {
if (isSimpleArrayOp()) {
emitArrayIsset();
return;
}
namespace VectorHelpers {
static inline uint64_t vectorIsset(c_Vector* vec, int64_t index) {
return vec->get(index) != nullptr;
}
}
emitIssetEmptyElem(false);
void HhbcTranslator::VectorTranslator::emitVectorIsset() {
SSATmp* key = getKey();
assert(key->isA(Type::Int));
m_result = gen(VectorIsset,
cns((TCA)VectorHelpers::vectorIsset),
m_base,
key);
}
template<KeyType keyType>
static inline uint64_t mapIssetImpl(
c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
return map->get(key) != nullptr;
}
#define HELPER_TABLE(m) \
/* name hot keyType */ \
m(mapIssetS, HOT_FUNC_VM, StrKey) \
m(mapIssetI, HOT_FUNC_VM, IntKey)
#define ELEM(nm, hot, keyType) \
hot \
uint64_t nm(c_Map* map, TypedValue* key) { \
return mapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
}
namespace VectorHelpers {
HELPER_TABLE(ELEM)
}
#undef ELEM
void HhbcTranslator::VectorTranslator::emitMapIsset() {
SSATmp* key = getKey();
assert(key->isA(Type::Int) || key->isA(Type::Str));
KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
BUILD_OPTAB_HOT(keyType);
m_result = gen(MapIsset, cns((TCA)opFunc), m_base, key);
}
#undef HELPER_TABLE
void HhbcTranslator::VectorTranslator::emitIssetElem() {
SimpleOp simpleOpType = isSimpleCollectionOp();
switch (simpleOpType) {
case SimpleOp::Array:
emitArrayIsset();
break;
case SimpleOp::Vector:
emitVectorIsset();
break;
case SimpleOp::Map:
emitMapIsset();
break;
default:
emitIssetEmptyElem(false);
break;
}
}
void HhbcTranslator::VectorTranslator::emitEmptyElem() {
@@ -1817,6 +1961,57 @@ void HhbcTranslator::VectorTranslator::emitSetWithRefNewElem() {
m_result = nullptr;
}
namespace VectorHelpers {
static inline void vectorSet(c_Vector* vec,
int64_t key,
Cell value) {
vec->set(key, &value);
}
}
void HhbcTranslator::VectorTranslator::emitVectorSet(
SSATmp* key, SSATmp* value) {
gen(VectorSet, m_ht.getCatchTrace(),
cns((TCA)VectorHelpers::vectorSet), m_base, key, value);
m_result = value;
}
template<KeyType keyType>
static inline void mapSetImpl(
c_Map* map,
typename KeyTypeTraits<keyType>::rawType key,
Cell value) {
map->set(key, &value);
}
#define HELPER_TABLE(m) \
/* name hot keyType */ \
m(mapSetS, HOT_FUNC_VM, StrKey) \
m(mapSetI, HOT_FUNC_VM, IntKey)
#define ELEM(nm, hot, keyType) \
hot \
void nm(c_Map* map, TypedValue* key, Cell value) { \
mapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
}
namespace VectorHelpers {
HELPER_TABLE(ELEM)
}
#undef ELEM
void HhbcTranslator::VectorTranslator::emitMapSet(
SSATmp* key, SSATmp* value) {
assert(key->isA(Type::Int) || key->isA(Type::Str));
KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
typedef TypedValue (*OpFunc)(c_Map*, TypedValue*, TypedValue*);
BUILD_OPTAB_HOT(keyType);
gen(MapSet, m_ht.getCatchTrace(),
cns((TCA)opFunc), m_base, key, value);
m_result = value;
}
#undef HELPER_TABLE
template <KeyType keyType>
static inline StringData* setElemImpl(TypedValue* base, TypedValue keyVal,
Cell val) {
@@ -1844,32 +2039,41 @@ void HhbcTranslator::VectorTranslator::emitSetElem() {
SSATmp* value = getValue();
SSATmp* key = getKey();
if (isSimpleArrayOp()) {
SimpleOp simpleOpType = isSimpleCollectionOp();
switch (simpleOpType) {
case SimpleOp::Array:
emitArraySet(key, value);
return;
}
// Emit the appropriate helper call.
typedef StringData* (*OpFunc)(TypedValue*, TypedValue, Cell);
BUILD_OPTAB_HOT(getKeyTypeIS(key));
m_failedSetTrace = m_ht.getCatchTrace();
SSATmp* result = genStk(SetElem, m_failedSetTrace, cns((TCA)opFunc),
m_base, key, value);
auto t = result->type();
if (t.equals(Type::Nullptr)) {
// Base is not a string. Result is always value.
m_result = value;
} else if (t.equals(Type::CountedStr)) {
// Base is a string. Stack result is a new string so we're responsible for
// decreffing value.
m_result = result;
gen(DecRef, value);
} else {
assert(t.equals(Type::CountedStr | Type::Nullptr));
// Base might be a string. Assume the result is value, then inform
// emitMPost that it needs to test the actual result.
m_result = value;
m_strTestResult = result;
break;
case SimpleOp::Vector:
emitVectorSet(key, value);
break;
case SimpleOp::Map:
emitMapSet(key, value);
break;
default:
// Emit the appropriate helper call.
typedef StringData* (*OpFunc)(TypedValue*, TypedValue, Cell);
BUILD_OPTAB_HOT(getKeyTypeIS(key));
m_failedSetTrace = m_ht.getCatchTrace();
SSATmp* result = genStk(SetElem, m_failedSetTrace, cns((TCA)opFunc),
m_base, key, value);
auto t = result->type();
if (t.equals(Type::Nullptr)) {
// Base is not a string. Result is always value.
m_result = value;
} else if (t.equals(Type::CountedStr)) {
// Base is a string. Stack result is a new string so we're responsible for
// decreffing value.
m_result = result;
gen(DecRef, value);
} else {
assert(t.equals(Type::CountedStr | Type::Nullptr));
// Base might be a string. Assume the result is value, then inform
// emitMPost that it needs to test the actual result.
m_result = value;
m_strTestResult = result;
}
break;
}
}
#undef HELPER_TABLE