Fix crashes with empty string as SetElem base
[hiphop-php.git] / hphp / runtime / vm / jit / vectortranslator.cpp
blobfa3b33deb6eba552828ec86e06a61ddfe20488f4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/strings.h"
18 #include "hphp/runtime/vm/member_operations.h"
19 #include "hphp/runtime/vm/jit/hhbctranslator.h"
20 #include "hphp/runtime/vm/jit/ir.h"
21 #include "hphp/runtime/vm/jit/irinstruction.h"
22 #include "hphp/runtime/vm/jit/translator-x64.h"
24 // These files do ugly things with macros so include them last
25 #include "hphp/util/assert_throw.h"
26 #include "hphp/runtime/vm/jit/vectortranslator-internal.h"
28 namespace HPHP { namespace JIT {
30 TRACE_SET_MOD(hhir);
32 using HPHP::Transl::Location;
34 static bool wantPropSpecializedWarnings() {
35 return !RuntimeOption::RepoAuthoritative ||
36 !RuntimeOption::EvalDisableSomeRepoAuthNotices;
39 bool VectorEffects::supported(Opcode op) {
40 return opcodeHasFlags(op, VectorProp | VectorElem);
42 bool VectorEffects::supported(const IRInstruction* inst) {
43 return supported(inst->op());
46 void VectorEffects::get(const IRInstruction* inst,
47 StoreLocFunc storeLocalValue,
48 SetLocTypeFunc setLocalType) {
49 // If the base for this instruction is a local address, the
50 // helper call might have side effects on the local's value
51 SSATmp* base = inst->src(vectorBaseIdx(inst));
52 IRInstruction* locInstr = base->inst();
53 if (locInstr->op() == LdLocAddr) {
54 UNUSED Type baseType = locInstr->dst()->type();
55 assert(baseType.equals(base->type()));
56 assert(baseType.isPtr() || baseType.isKnownDataType());
57 int loc = locInstr->extra<LdLocAddr>()->locId;
59 VectorEffects ve(inst);
60 if (ve.baseTypeChanged || ve.baseValChanged) {
61 storeLocalValue(loc, nullptr);
62 setLocalType(loc, ve.baseType.derefIfPtr());
67 namespace {
68 // Reduce baseType to a canonical unboxed, non-pointer form, returning (in
69 // basePtr and baseBoxed) whether the original type was a pointer or boxed,
70 // respectively. Whether or not the type is a pointer and/or boxed must be
71 // statically known, unless it's exactly equal to Gen.
72 void stripBase(Type& baseType, bool& basePtr, bool& baseBoxed) {
73 assert_not_implemented(baseType.isPtr() || baseType.notPtr());
74 basePtr = baseType.isPtr();
75 baseType = basePtr ? baseType.deref() : baseType;
76 assert_not_implemented(
77 baseType.equals(Type::Gen) || baseType.isBoxed() || baseType.notBoxed());
78 baseBoxed = baseType.isBoxed();
79 baseType = baseBoxed ? baseType.innerType() : baseType;
82 Opcode canonicalOp(Opcode op) {
83 if (op == ElemUX || op == ElemUXStk ||
84 op == UnsetElem || op == UnsetElemStk) {
85 return UnsetElem;
87 if (op == SetWithRefElem ||
88 op == SetWithRefElemStk ||
89 op == SetWithRefNewElem ||
90 op == SetWithRefNewElemStk) {
91 return SetWithRefElem;
93 return opcodeHasFlags(op, VectorProp) ? SetProp
94 : opcodeHasFlags(op, VectorElem) || op == ArraySet ? SetElem
95 : bad_value<Opcode>();
99 VectorEffects::VectorEffects(const IRInstruction* inst) {
100 int keyIdx = vectorKeyIdx(inst);
101 int valIdx = vectorValIdx(inst);
102 init(inst->op(),
103 inst->src(vectorBaseIdx(inst))->type(),
104 keyIdx == -1 ? Type::None : inst->src(keyIdx)->type(),
105 valIdx == -1 ? Type::None : inst->src(valIdx)->type());
108 VectorEffects::VectorEffects(Opcode op, Type base, Type key, Type val) {
109 init(op, base, key, val);
112 VectorEffects::VectorEffects(Opcode op,
113 SSATmp* base, SSATmp* key, SSATmp* val) {
114 auto typeOrNone =
115 [](SSATmp* val){ return val ? val->type() : Type::None; };
116 init(op, typeOrNone(base), typeOrNone(key), typeOrNone(val));
119 VectorEffects::VectorEffects(Opcode opc, const std::vector<SSATmp*>& srcs) {
120 int keyIdx = vectorKeyIdx(opc);
121 int valIdx = vectorValIdx(opc);
122 init(opc,
123 srcs[vectorBaseIdx(opc)]->type(),
124 keyIdx == -1 ? Type::None : srcs[keyIdx]->type(),
125 valIdx == -1 ? Type::None : srcs[valIdx]->type());
128 void VectorEffects::init(Opcode op, const Type origBase,
129 const Type key, const Type origVal) {
130 baseType = origBase;
131 bool basePtr, baseBoxed;
132 stripBase(baseType, basePtr, baseBoxed);
133 // Only certain types of bases are supported now, but this list may expand in
134 // the future.
135 assert_not_implemented(basePtr ||
136 baseType.subtypeOfAny(Type::Obj, Type::Arr));
138 baseTypeChanged = baseValChanged = false;
140 // Canonicalize the op to SetProp/SetElem/UnsetElem/SetWithRefElem
141 op = canonicalOp(op);
143 // We're not expecting types other than specific known data types
144 // (or for keys, Cell). (At least for keys it might work since the
145 // helpers generally operate on cells, but we're asserting anyway
146 // since this shouldn't actually happen.)
147 assert(key.equals(Type::None) || key.isKnownDataType() ||
148 key.equals(Type::Cell));
150 if ((op == SetElem || op == SetProp || op == SetWithRefElem) &&
151 baseType.maybe(Type::Null | Type::Bool | Type::Str)) {
152 // stdClass or array promotion might happen
153 auto newBase = op == SetElem ? Type::Arr : Type::Obj;
154 // If the base is known to be null, promotion will happen. If we can ever
155 // prove that the base is false or the empty string, promotion will
156 // definitely happen but those cases aren't handled yet. In a perfect world
157 // we would remove Type::Null from baseType here but that can produce types
158 // that are tricky to guard against and doesn't buy us much right now.
159 if (!baseBoxed && (!baseType.isString() || op == SetProp)) {
161 * Uses of boxed types are always guarded, in case the inner
162 * type was modified. If the base type was String, its extremely
163 * likely to still be a String, so leave it as such, and we'll
164 * exit in the rare case that it changed.
166 baseType = baseType.isNull() ? newBase : (baseType | newBase);
168 baseValChanged = !baseBoxed;
170 if (op == SetElem || op == UnsetElem || op == SetWithRefElem) {
171 if (baseType.maybe(Type::Arr)) {
172 // possible COW when modifying an array, unless the base was boxed. If the
173 // base is a box then the value of the box itself won't change, just what
174 // it points to.
175 baseValChanged = !baseBoxed;
177 if (baseType.maybe(Type::StaticArr)) {
178 // the base might get promoted to a CountedArr. We can ignore the change
179 // if the base is boxed, (for the same reasons as above).
180 baseType = baseType | Type::CountedArr;
181 baseValChanged = !baseBoxed;
185 // The final baseType should be a pointer/box iff the input was
186 baseType = baseBoxed ? baseType.box() : baseType;
187 baseType = basePtr ? baseType.ptr() : baseType;
189 baseTypeChanged = baseTypeChanged || baseType != origBase;
190 baseValChanged = baseValChanged || baseTypeChanged;
193 // vectorBaseIdx returns the src index for inst's base operand.
194 int vectorBaseIdx(Opcode opc) {
195 return opc == SetNewElem || opc == SetNewElemStk ? 0
196 : opc == BindNewElem || opc == BindNewElemStk ? 0
197 : opc == ArraySet ? 1
198 : opc == SetOpProp || opc == SetOpPropStk ? 1
199 : opcodeHasFlags(opc, VectorProp) ? 2
200 : opcodeHasFlags(opc, VectorElem) ? 1
201 : bad_value<int>();
203 int vectorBaseIdx(const IRInstruction* inst) {
204 return vectorBaseIdx(inst->op());
207 // vectorKeyIdx returns the src index for inst's key operand.
208 int vectorKeyIdx(Opcode opc) {
209 return opc == SetNewElem || opc == SetNewElemStk ? -1
210 : opc == SetWithRefNewElem || opc == SetWithRefNewElemStk ? -1
211 : opc == BindNewElem || opc == BindNewElem ? -1
212 : opc == ArraySet ? 2
213 : opc == SetOpProp || opc == SetOpPropStk ? 2
214 : opcodeHasFlags(opc, VectorProp) ? 3
215 : opcodeHasFlags(opc, VectorElem) ? 2
216 : bad_value<int>();
218 int vectorKeyIdx(const IRInstruction* inst) {
219 return vectorKeyIdx(inst->op());
222 // vectorValIdx returns the src index for inst's value operand.
223 int vectorValIdx(Opcode opc) {
224 switch (opc) {
225 case VGetProp: case VGetPropStk:
226 case IncDecProp: case IncDecPropStk:
227 case PropDX: case PropDXStk:
228 case VGetElem: case VGetElemStk:
229 case UnsetElem: case UnsetElemStk:
230 case IncDecElem: case IncDecElemStk:
231 case ElemDX: case ElemDXStk:
232 case ElemUX: case ElemUXStk:
233 return -1;
235 case ArraySet: return 3;
236 case SetNewElem: case SetNewElemStk: return 1;
237 case SetWithRefNewElem: case SetWithRefNewElemStk: return 2;
238 case BindNewElem: case BindNewElemStk: return 1;
239 case SetOpProp: case SetOpPropStk: return 3;
241 default:
242 return opcodeHasFlags(opc, VectorProp) ? 4
243 : opcodeHasFlags(opc, VectorElem) ? 3
244 : bad_value<int>();
247 int vectorValIdx(const IRInstruction* inst) {
248 return vectorValIdx(inst->op());
251 HhbcTranslator::VectorTranslator::VectorTranslator(
252 const NormalizedInstruction& ni,
253 HhbcTranslator& ht)
254 : m_ni(ni)
255 , m_ht(ht)
256 , m_tb(*m_ht.m_tb)
257 , m_irf(m_ht.m_irFactory)
258 , m_mii(getMInstrInfo(ni.mInstrOp()))
259 , m_needMIS(true)
260 , m_misBase(nullptr)
261 , m_base(nullptr)
262 , m_result(nullptr)
263 , m_strTestResult(nullptr)
264 , m_failedSetTrace(nullptr)
268 template<typename... Srcs>
269 SSATmp* HhbcTranslator::VectorTranslator::genStk(Opcode opc, IRTrace* taken,
270 Srcs... srcs) {
271 assert(opcodeHasFlags(opc, HasStackVersion));
272 assert(!opcodeHasFlags(opc, ModifiesStack));
273 std::vector<SSATmp*> srcVec({srcs...});
274 SSATmp* base = srcVec[vectorBaseIdx(opc)];
276 /* If the base is a pointer to a stack cell and the operation might change
277 * its type and/or value, use the version of the opcode that returns a new
278 * StkPtr. */
279 if (base->inst()->op() == LdStackAddr) {
280 VectorEffects ve(opc, srcVec);
281 if (ve.baseTypeChanged || ve.baseValChanged) {
282 opc = getStackModifyingOpcode(opc);
286 return gen(opc, taken, srcs...);
289 void HhbcTranslator::VectorTranslator::emit() {
290 // Assign stack slots to our stack inputs
291 numberStackInputs();
292 // Emit the base and every intermediate op
293 emitMPre();
294 // Emit the final operation
295 emitFinalMOp();
296 // Cleanup: decref inputs and scratch values
297 emitMPost();
300 // Returns a pointer to the base of the current MInstrState struct, or
301 // a null pointer if it's not needed.
302 SSATmp* HhbcTranslator::VectorTranslator::genMisPtr() {
303 if (m_needMIS) {
304 return gen(LdAddr, m_misBase, cns(kReservedRSPSpillSpace));
305 } else {
306 return gen(DefConst, Type::PtrToCell, ConstData(nullptr));
310 // Inspect the instruction we're about to translate and determine if
311 // it can be executed without using an MInstrState struct.
312 void HhbcTranslator::VectorTranslator::checkMIState() {
313 auto const& baseRtt = m_ni.inputs[m_mii.valCount()]->rtt;
314 Type baseType = Type::fromRuntimeType(baseRtt);
315 const bool isCGetM = m_ni.mInstrOp() == OpCGetM;
316 const bool isSetM = m_ni.mInstrOp() == OpSetM;
317 const bool isIssetM = m_ni.mInstrOp() == OpIssetM;
318 const bool isUnsetM = m_ni.mInstrOp() == OpUnsetM;
319 const bool isSingle = m_ni.immVecM.size() == 1;
321 assert(baseType.isBoxed() || baseType.notBoxed());
322 baseType = baseType.unbox();
324 // CGetM or SetM with no unknown property offsets
325 const bool simpleProp = !mInstrHasUnknownOffsets(m_ni, contextClass()) &&
326 (isCGetM || isSetM);
328 // SetM with only one element
329 const bool singlePropSet = isSingle && isSetM &&
330 mcodeMaybePropName(m_ni.immVecM[0]);
332 // Array access with one element in the vector
333 const bool singleElem = isSingle && mcodeMaybeArrayOrMapKey(m_ni.immVecM[0]);
335 // SetM with one vector array element
336 const bool simpleArraySet = isSetM && singleElem;
338 // IssetM with one vector array element and an Arr base
339 const bool simpleArrayIsset = isIssetM && singleElem &&
340 baseType.subtypeOf(Type::Arr);
341 // IssetM with one vector array element and a collection type
342 const bool simpleCollectionIsset = isIssetM && singleElem &&
343 baseType.strictSubtypeOf(Type::Obj) &&
344 (baseType.getClass() == c_Vector::s_cls ||
345 baseType.getClass() == c_Map::s_cls );
347 // UnsetM on an array with one vector element
348 const bool simpleArrayUnset = isUnsetM && singleElem;
350 // UnsetM on a non-standard base. Always a noop or fatal.
351 const bool badUnset = isUnsetM && baseType.not(Type::Arr | Type::Obj);
353 // CGetM on an array with a base that won't use MInstrState. Str
354 // will use tvScratch and Obj will fatal or use tvRef.
355 const bool simpleArrayGet = isCGetM && singleElem &&
356 baseType.not(Type::Str | Type::Obj);
357 const bool simpleCollectionGet = isCGetM && singleElem &&
358 baseType.strictSubtypeOf(Type::Obj) &&
359 (baseType.getClass() == c_Vector::s_cls ||
360 baseType.getClass() == c_Map::s_cls );
362 if (simpleProp || singlePropSet ||
363 simpleArraySet || simpleArrayGet || simpleCollectionGet ||
364 simpleArrayUnset || badUnset || simpleCollectionIsset ||
365 simpleArrayIsset) {
366 setNoMIState();
370 void HhbcTranslator::VectorTranslator::emitMPre() {
371 checkMIState();
373 if (HPHP::Trace::moduleEnabled(HPHP::Trace::minstr, 1)) {
374 emitMTrace();
377 if (m_needMIS) {
378 m_misBase = gen(DefMIStateBase);
379 SSATmp* uninit = m_tb.genDefUninit();
381 if (nLogicalRatchets() > 0) {
382 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef)), uninit);
383 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef2)), uninit);
387 // The base location is input 0 or 1, and the location code is stored
388 // separately from m_ni.immVecM, so input indices (iInd) and member indices
389 // (mInd) commonly differ. Additionally, W members have no corresponding
390 // inputs, so it is necessary to track the two indices separately.
391 m_iInd = m_mii.valCount();
392 emitBaseOp();
393 ++m_iInd;
395 // Iterate over all but the last member, which is consumed by a final
396 // operation.
397 for (m_mInd = 0; m_mInd < m_ni.immVecM.size() - 1; ++m_mInd) {
398 emitIntermediateOp();
399 emitRatchetRefs();
403 void HhbcTranslator::VectorTranslator::emitMTrace() {
404 auto rttStr = [this](int i) {
405 return Type::fromRuntimeType(m_ni.inputs[i]->rtt).unbox().toString();
407 std::ostringstream shape;
408 int iInd = m_mii.valCount();
409 const char* separator = "";
411 shape << opcodeToName(m_ni.mInstrOp()) << " <";
412 auto baseLoc = m_ni.immVec.locationCode();
413 shape << folly::format("{}:{} ", locationCodeString(baseLoc), rttStr(iInd));
414 ++iInd;
416 for (int mInd = 0; mInd < m_ni.immVecM.size(); ++mInd) {
417 auto mcode = m_ni.immVecM[mInd];
418 shape << separator;
419 if (mcode == MW) {
420 shape << "MW";
421 } else if (mcodeMaybeArrayOrMapKey(mcode)) {
422 shape << "ME:" << rttStr(iInd);
423 } else if (mcodeMaybePropName(mcode)) {
424 shape << "MP:" << rttStr(iInd);
425 } else {
426 not_reached();
428 if (mcode != MW) ++iInd;
429 separator = " ";
431 shape << '>';
432 gen(IncStatGrouped,
433 cns(StringData::GetStaticString("vector instructions")),
434 cns(StringData::GetStaticString(shape.str())),
435 cns(1));
438 // Build a map from (stack) input index to stack index.
439 void HhbcTranslator::VectorTranslator::numberStackInputs() {
440 // Stack inputs are pushed in the order they appear in the vector
441 // from left to right, so earlier elements in the vector are at
442 // higher offsets in the stack. m_mii.valCount() tells us how many
443 // rvals the instruction takes on the stack; they're pushed after
444 // any vector elements and we want to ignore them here.
445 bool stackRhs = m_mii.valCount() &&
446 m_ni.inputs[0]->location.space == Location::Stack;
447 int stackIdx = (int)stackRhs + m_ni.immVec.numStackValues() - 1;
448 for (unsigned i = m_mii.valCount(); i < m_ni.inputs.size(); ++i) {
449 const Location& l = m_ni.inputs[i]->location;
450 switch (l.space) {
451 case Location::Stack:
452 assert(stackIdx >= 0);
453 m_stackInputs[i] = stackIdx--;
454 break;
456 default:
457 break;
460 assert(stackIdx == (stackRhs ? 0 : -1));
462 if (stackRhs) {
463 // If this instruction does have an RHS, it will be input 0 at
464 // stack offset 0.
465 assert(m_mii.valCount() == 1);
466 m_stackInputs[0] = 0;
470 SSATmp* HhbcTranslator::VectorTranslator::getBase() {
471 assert(m_iInd == m_mii.valCount());
472 return getInput(m_iInd);
475 SSATmp* HhbcTranslator::VectorTranslator::getKey() {
476 SSATmp* key = getInput(m_iInd);
477 auto keyType = key->type();
478 assert(keyType.isBoxed() || keyType.notBoxed());
479 if (keyType.isBoxed()) {
480 key = gen(LdRef, Type::Cell, key);
482 return key;
485 SSATmp* HhbcTranslator::VectorTranslator::getValue() {
486 // If an instruction takes an rhs, it's always input 0.
487 assert(m_mii.valCount() == 1);
488 const int kValIdx = 0;
489 return getInput(kValIdx);
492 SSATmp* HhbcTranslator::VectorTranslator::getValAddr() {
493 assert(m_mii.valCount() == 1);
494 const DynLocation& dl = *m_ni.inputs[0];
495 const Location& l = dl.location;
496 if (l.space == Location::Local) {
497 assert(!mapContains(m_stackInputs, 0));
498 return m_ht.ldLocAddr(l.offset);
499 } else {
500 assert(l.space == Location::Stack);
501 assert(mapContains(m_stackInputs, 0));
502 m_ht.spillStack();
503 return m_ht.ldStackAddr(m_stackInputs[0]);
507 SSATmp* HhbcTranslator::VectorTranslator::getInput(unsigned i) {
508 const DynLocation& dl = *m_ni.inputs[i];
509 const Location& l = dl.location;
511 assert(mapContains(m_stackInputs, i) == (l.space == Location::Stack));
512 switch (l.space) {
513 case Location::Stack: {
514 SSATmp* val = m_ht.top(Type::Gen | Type::Cls, m_stackInputs[i]);
515 // Check if the type on our eval stack is at least as specific as what
516 // Transl::Translator came up with. We allow boxed types with differing
517 // inner types because of the different ways the two systems deal with
518 // reference types.
519 auto t = Type::fromRuntimeType(dl.rtt);
520 if (!val->isA(t) && !(val->isBoxed() && t.isBoxed())) {
521 FTRACE(1, "{}: hhir stack has a {} where Translator had a {}\n",
522 __func__, val->type().toString(), t.toString());
523 // They'd better not be completely unrelated types...
524 assert(t.subtypeOf(val->type()));
525 m_ht.refineType(val, t);
527 return val;
530 case Location::Local:
531 return m_ht.ldLoc(l.offset);
533 case Location::Litstr:
534 return cns(m_ht.lookupStringId(l.offset));
536 case Location::Litint:
537 return cns(l.offset);
539 case Location::This:
540 return gen(LdThis, m_tb.fp());
542 default: not_reached();
546 void HhbcTranslator::VectorTranslator::emitBaseLCR() {
547 const MInstrAttr& mia = m_mii.getAttr(m_ni.immVec.locationCode());
548 const DynLocation& base = *m_ni.inputs[m_iInd];
549 auto baseType = Type::fromRuntimeType(base.rtt);
550 assert(baseType.isKnownDataType());
552 if (base.location.isLocal()) {
553 // Check for Uninit and warn/promote to InitNull as appropriate
554 if (baseType.subtypeOf(Type::Uninit)) {
555 if (mia & MIA_warn) {
556 gen(RaiseUninitLoc, getEmptyCatchTrace(),
557 LocalId(base.location.offset));
559 if (mia & MIA_define) {
560 gen(
561 StLoc,
562 LocalId(base.location.offset),
563 m_tb.fp(),
564 m_tb.genDefInitNull()
566 baseType = Type::InitNull;
571 // If the base is a box with a type that's changed, we need to bail out of
572 // the tracelet and retranslate. Doing an exit here is a little sketchy since
573 // we may have already emitted instructions with memory effects to initialize
574 // the MInstrState. These particular stores are harmless though, and the
575 // worst outcome here is that we'll end up doing the stores twice, once for
576 // this instruction and once at the beginning of the retranslation.
577 IRTrace* failedRef = baseType.isBoxed() ? m_ht.getExitTrace() : nullptr;
578 if ((baseType.subtypeOfAny(Type::Obj, Type::BoxedObj) &&
579 mcodeMaybePropName(m_ni.immVecM[0])) ||
580 isSimpleCollectionOp() != SimpleOp::None) {
581 // In these cases we can pass the base by value, after unboxing if needed.
582 m_base = gen(Unbox, failedRef, getBase());
583 assert(m_base->isA(baseType.unbox()));
584 } else {
585 // Everything else is passed by reference. We don't have to worry about
586 // unboxing here, since all the generic helpers understand boxed bases.
587 if (baseType.isBoxed()) {
588 SSATmp* box = getBase();
589 assert(box->isA(Type::BoxedCell));
590 // Guard that the inner type hasn't changed
591 gen(LdRef, baseType.innerType(), failedRef, box);
594 if (base.location.space == Location::Local) {
595 m_base = m_ht.ldLocAddr(base.location.offset);
596 } else {
597 assert(base.location.space == Location::Stack);
598 // Make sure the stack is clean before getting a pointer to one of its
599 // elements.
600 m_ht.spillStack();
601 assert(m_stackInputs.count(m_iInd));
602 m_base = m_ht.ldStackAddr(m_stackInputs[m_iInd]);
604 assert(m_base->type().isPtr());
608 // Is the current instruction a 1-element simple collection (includes Array),
609 // operation?
610 HhbcTranslator::VectorTranslator::SimpleOp
611 HhbcTranslator::VectorTranslator::isSimpleCollectionOp() {
612 SSATmp* base = getInput(m_mii.valCount());
613 auto baseType = base->type().unbox();
614 HPHP::Op op = m_ni.mInstrOp();
615 if ((op == OpSetM || op == OpCGetM || op == OpIssetM) &&
616 isSimpleBase() &&
617 isSingleMember()) {
619 if (baseType.subtypeOf(Type::Arr)) {
620 if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
621 SSATmp* key = getInput(m_mii.valCount() + 1);
622 if (key->isA(Type::Int) || key->isA(Type::Str)) {
623 return SimpleOp::Array;
626 } else if (baseType.strictSubtypeOf(Type::Obj)) {
627 if (baseType.getClass() == c_Vector::s_cls) {
628 if (mcodeMaybeVectorKey(m_ni.immVecM[0])) {
629 SSATmp* key = getInput(m_mii.valCount() + 1);
630 if (key->isA(Type::Int)) {
631 return SimpleOp::Vector;
634 } else if (baseType.getClass() == c_Map::s_cls) {
635 if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
636 SSATmp* key = getInput(m_mii.valCount() + 1);
637 if (key->isA(Type::Int) || key->isA(Type::Str)) {
638 return SimpleOp::Map;
644 return SimpleOp::None;
647 // "Simple" bases are stack cells and locals.
648 bool HhbcTranslator::VectorTranslator::isSimpleBase() {
649 LocationCode loc = m_ni.immVec.locationCode();
650 return loc == LL || loc == LC || loc == LR;
653 bool HhbcTranslator::VectorTranslator::isSingleMember() {
654 return m_ni.immVecM.size() == 1;
657 void HhbcTranslator::VectorTranslator::emitBaseH() {
658 m_base = gen(LdThis, m_tb.fp());
661 void HhbcTranslator::VectorTranslator::emitBaseN() {
662 PUNT(emitBaseN);
665 template <bool warn, bool define>
666 static inline TypedValue* baseGImpl(TypedValue *key,
667 MInstrState* mis) {
668 TypedValue* base;
669 StringData* name = prepareKey(key);
670 VarEnv* varEnv = g_vmContext->m_globalVarEnv;
671 assert(varEnv != NULL);
672 base = varEnv->lookup(name);
673 if (base == NULL) {
674 if (warn) {
675 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
677 if (define) {
678 TypedValue tv;
679 tvWriteNull(&tv);
680 varEnv->set(name, &tv);
681 base = varEnv->lookup(name);
682 } else {
683 return const_cast<TypedValue*>(init_null_variant.asTypedValue());
686 decRefStr(name);
687 if (base->m_type == KindOfRef) {
688 base = base->m_data.pref->tv();
690 return base;
693 namespace VectorHelpers {
694 TypedValue* baseG(TypedValue key, MInstrState* mis) {
695 return baseGImpl<false, false>(&key, mis);
698 TypedValue* baseGW(TypedValue key, MInstrState* mis) {
699 return baseGImpl<true, false>(&key, mis);
702 TypedValue* baseGD(TypedValue key, MInstrState* mis) {
703 return baseGImpl<false, true>(&key, mis);
706 TypedValue* baseGWD(TypedValue key, MInstrState* mis) {
707 return baseGImpl<true, true>(&key, mis);
711 void HhbcTranslator::VectorTranslator::emitBaseG() {
712 const MInstrAttr& mia = m_mii.getAttr(m_ni.immVec.locationCode());
713 typedef TypedValue* (*OpFunc)(TypedValue, MInstrState*);
714 using namespace VectorHelpers;
715 static const OpFunc opFuncs[] = {baseG, baseGW, baseGD, baseGWD};
716 OpFunc opFunc = opFuncs[mia & MIA_base];
717 SSATmp* gblName = getBase();
718 m_base = gen(BaseG,
719 getEmptyCatchTrace(),
720 cns(reinterpret_cast<TCA>(opFunc)),
721 gblName,
722 genMisPtr());
725 void HhbcTranslator::VectorTranslator::emitBaseS() {
726 const int kClassIdx = m_ni.inputs.size() - 1;
727 SSATmp* key = getKey();
728 SSATmp* clsRef = getInput(kClassIdx);
729 m_base = gen(LdClsPropAddr,
730 clsRef,
731 key,
732 CTX());
735 void HhbcTranslator::VectorTranslator::emitBaseOp() {
736 LocationCode lCode = m_ni.immVec.locationCode();
737 switch (lCode) {
738 case LL: case LC: case LR: emitBaseLCR(); break;
739 case LH: emitBaseH(); break;
740 case LGL: case LGC: emitBaseG(); break;
741 case LNL: case LNC: emitBaseN(); break;
742 case LSL: case LSC: emitBaseS(); break;
743 default: not_reached();
747 void HhbcTranslator::VectorTranslator::emitIntermediateOp() {
748 switch (m_ni.immVecM[m_mInd]) {
749 case MEC: case MEL: case MET: case MEI: {
750 emitElem();
751 ++m_iInd;
752 break;
754 case MPC: case MPL: case MPT:
755 emitProp();
756 ++m_iInd;
757 break;
758 case MW:
759 assert(m_mii.newElem());
760 emitNewElem();
761 break;
762 default: not_reached();
767 void HhbcTranslator::VectorTranslator::emitProp() {
768 const Class* knownCls = nullptr;
769 const auto propInfo = getPropertyOffset(m_ni, contextClass(),
770 knownCls, m_mii,
771 m_mInd, m_iInd);
772 auto mia = m_mii.getAttr(m_ni.immVecM[m_mInd]);
773 if (propInfo.offset == -1 || (mia & Unset)) {
774 emitPropGeneric();
775 } else {
776 emitPropSpecialized(mia, propInfo);
780 template <MInstrAttr attrs, bool isObj>
781 static inline TypedValue* propImpl(Class* ctx, TypedValue* base,
782 TypedValue keyVal, MInstrState* mis) {
783 return Prop<WDU(attrs), isObj>(
784 mis->tvScratch, mis->tvRef, ctx, base, &keyVal);
787 #define HELPER_TABLE(m) \
788 /* name attrs isObj */ \
789 m(propC, None, false) \
790 m(propCD, Define, false) \
791 m(propCDO, Define, true) \
792 m(propCO, None, true) \
793 m(propCU, Unset, false) \
794 m(propCUO, Unset, true) \
795 m(propCW, Warn, false) \
796 m(propCWD, WarnDefine, false) \
797 m(propCWDO, WarnDefine, true) \
798 m(propCWO, Warn, true)
800 #define PROP(nm, ...) \
801 TypedValue* nm(Class* ctx, TypedValue* base, TypedValue key, \
802 MInstrState* mis) { \
803 return propImpl<__VA_ARGS__>(ctx, base, key, mis); \
805 namespace VectorHelpers {
806 HELPER_TABLE(PROP)
808 #undef PROP
810 void HhbcTranslator::VectorTranslator::emitPropGeneric() {
811 MemberCode mCode = m_ni.immVecM[m_mInd];
812 MInstrAttr mia = MInstrAttr(m_mii.getAttr(mCode) & MIA_intermediate_prop);
814 if ((mia & Unset) && m_base->type().strip().not(Type::Obj)) {
815 m_base = m_tb.genPtrToInitNull();
816 return;
819 typedef TypedValue* (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
820 SSATmp* key = getKey();
821 BUILD_OPTAB(mia, m_base->isA(Type::Obj));
822 if (mia & Define) {
823 m_base = genStk(PropDX, getCatchTrace(), cns((TCA)opFunc), CTX(),
824 m_base, key, genMisPtr());
825 } else {
826 m_base = gen(PropX, getCatchTrace(),
827 cns((TCA)opFunc), CTX(), m_base, key, genMisPtr());
830 #undef HELPER_TABLE
833 * Helper for emitPropSpecialized to check if a property is Uninit. It
834 * returns a pointer to the property's address, or init_null_variant
835 * if the property was Uninit and doDefine is false.
837 * We can omit the uninit check for properties that we know may not be
838 * uninit due to the frontend's type inference.
840 SSATmp* HhbcTranslator::VectorTranslator::checkInitProp(
841 SSATmp* baseAsObj,
842 SSATmp* propAddr,
843 PropInfo propInfo,
844 bool doWarn,
845 bool doDefine) {
846 SSATmp* key = getKey();
847 assert(key->isA(Type::StaticStr));
848 assert(baseAsObj->isA(Type::Obj));
849 assert(propAddr->type().isPtr());
851 auto const needsCheck =
852 propInfo.hphpcType == KindOfInvalid &&
853 // The m_mInd check is to avoid initializing a property to
854 // InitNull right before it's going to be set to something else.
855 (doWarn || (doDefine && m_mInd < m_ni.immVecM.size() - 1));
857 if (!needsCheck) return propAddr;
859 return m_tb.cond(m_ht.curFunc(),
860 [&] (Block* taken) {
861 gen(CheckInitMem, taken, propAddr, cns(0));
863 [&] { // Next: Property isn't Uninit. Do nothing.
864 return propAddr;
866 [&] { // Taken: Property is Uninit. Raise a warning and return
867 // a pointer to InitNull, either in the object or
868 // init_null_variant.
869 m_tb.hint(Block::Unlikely);
870 if (doWarn && wantPropSpecializedWarnings()) {
871 gen(RaiseUndefProp, m_ht.getCatchTrace(), baseAsObj, key);
873 if (doDefine) {
874 gen(
875 StProp,
876 baseAsObj,
877 cns(propInfo.offset),
878 m_tb.genDefInitNull()
880 return propAddr;
882 return cns((const TypedValue*)&init_null_variant);
887 Class* HhbcTranslator::VectorTranslator::contextClass() const {
888 return m_ht.curFunc()->cls();
891 void HhbcTranslator::VectorTranslator::emitPropSpecialized(const MInstrAttr mia,
892 PropInfo propInfo) {
893 assert(!(mia & MIA_warn) || !(mia & MIA_unset));
894 const bool doWarn = mia & MIA_warn;
895 const bool doDefine = mia & MIA_define || mia & MIA_unset;
897 SSATmp* initNull = cns((const TypedValue*)&init_null_variant);
900 * Type-inference from hphpc only tells us that this is either an object of a
901 * given class type or null. If it's not an object, it has to be a null type
902 * based on type inference. (It could be KindOfRef with an object inside,
903 * except that this isn't inferred for object properties so we're fine not
904 * checking KindOfRef in that case.)
906 * On the other hand, if m_base->isA(Type::Obj), we're operating on the base
907 * which was already guarded by tracelet guards (and may have been KindOfRef,
908 * but the Base* op already handled this). So we only need to do a type
909 * check against null here in the intermediate cases.
911 if (m_base->isA(Type::Obj)) {
912 SSATmp* propAddr = gen(LdPropAddr, m_base, cns(propInfo.offset));
913 m_base = checkInitProp(m_base, propAddr, propInfo, doWarn, doDefine);
914 } else {
915 SSATmp* baseAsObj = nullptr;
916 m_base = m_tb.cond(m_ht.curFunc(),
917 [&] (Block* taken) {
918 // baseAsObj is only available in the Next branch
919 baseAsObj = gen(LdMem, Type::Obj, taken, m_base, cns(0));
921 [&] { // Next: Base is an object. Load property address and
922 // check for uninit
923 return checkInitProp(baseAsObj,
924 gen(LdPropAddr, baseAsObj,
925 cns(propInfo.offset)),
926 propInfo,
927 doWarn,
928 doDefine);
930 [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull.
931 m_tb.hint(Block::Unlikely);
932 if (doWarn) {
933 gen(WarnNonObjProp);
935 if (doDefine) {
937 * NOTE:
939 * This case logically is supposed to do a stdClass promotion. It
940 * should ideally not be possible (since we have a known class type),
941 * except that the static compiler doesn't correctly infer object
942 * class types in some edge cases involving stdClass promotion.
944 * This is impossible to handle "correctly" if we're in the middle of
945 * a multi-dim property expression, because things further along may
946 * also have type inference telling them that object properties are
947 * at a given slot, but the object could actually be a stdClass
948 * instead of the knownCls type if we were to promote here.
950 * So, we throw a fatal error, which is what hphpc's generated C++
951 * would do in this case too.
953 * Relevant TODOs:
954 * #1789661 (this can cause bugs if bytecode.cpp promotes)
955 * #1124706 (we want to get rid of stdClass promotion in general)
957 gen(ThrowNonObjProp);
959 return initNull;
964 // At this point m_base is either a pointer to init_null_variant or
965 // a property in the object that we've verified isn't uninit.
966 assert(m_base->type().isPtr());
969 template <KeyType keyType, bool warn, bool define, bool reffy,
970 bool unset>
971 static inline TypedValue* elemImpl(TypedValue* base, TypedValue keyVal,
972 MInstrState* mis) {
973 TypedValue* key = keyPtr<keyType>(keyVal);
974 if (unset) {
975 return ElemU<keyType>(mis->tvScratch, mis->tvRef, base, key);
976 } else if (define) {
977 return ElemD<warn, reffy, keyType>(mis->tvScratch, mis->tvRef, base, key);
978 } else {
979 return Elem<warn, keyType>(mis->tvScratch, mis->tvRef, base, key);
983 #define HELPER_TABLE(m) \
984 /* name hot key attrs */ \
985 m(elemC, , AnyKey, None) \
986 m(elemCD, , AnyKey, Define) \
987 m(elemCDR, , AnyKey, DefineReffy) \
988 m(elemCU, , AnyKey, Unset) \
989 m(elemCW, , AnyKey, Warn) \
990 m(elemCWD, , AnyKey, WarnDefine) \
991 m(elemCWDR, , AnyKey, WarnDefineReffy) \
992 m(elemI, , IntKey, None) \
993 m(elemID, HOT_FUNC_VM, IntKey, Define) \
994 m(elemIDR, , IntKey, DefineReffy) \
995 m(elemIU, , IntKey, Unset) \
996 m(elemIW, , IntKey, Warn) \
997 m(elemIWD, , IntKey, WarnDefine) \
998 m(elemIWDR, , IntKey, WarnDefineReffy) \
999 m(elemS, HOT_FUNC_VM, StrKey, None) \
1000 m(elemSD, HOT_FUNC_VM, StrKey, Define) \
1001 m(elemSDR, , StrKey, DefineReffy) \
1002 m(elemSU, , StrKey, Unset) \
1003 m(elemSW, HOT_FUNC_VM, StrKey, Warn) \
1004 m(elemSWD, , StrKey, WarnDefine) \
1005 m(elemSWDR, , StrKey, WarnDefineReffy)
1007 #define ELEM(nm, hot, keyType, attrs) \
1008 hot \
1009 TypedValue* nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1010 return elemImpl<keyType, WDRU(attrs)>(base, key, mis); \
1012 namespace VectorHelpers {
1013 HELPER_TABLE(ELEM)
1015 #undef ELEM
1017 void HhbcTranslator::VectorTranslator::emitElem() {
1018 MemberCode mCode = m_ni.immVecM[m_mInd];
1019 MInstrAttr mia = MInstrAttr(m_mii.getAttr(mCode) & MIA_intermediate);
1020 SSATmp* key = getKey();
1022 const bool unset = mia & Unset;
1023 const bool define = mia & Define;
1024 assert(!(define && unset));
1025 if (unset) {
1026 SSATmp* uninit = m_tb.genPtrToUninit();
1027 Type baseType = m_base->type().strip();
1028 if (baseType.subtypeOf(Type::Str)) {
1029 m_ht.exceptionBarrier();
1030 gen(
1031 RaiseError,
1032 cns(StringData::GetStaticString(Strings::OP_NOT_SUPPORTED_STRING))
1034 m_base = uninit;
1035 return;
1037 if (baseType.not(Type::Arr | Type::Obj)) {
1038 m_base = uninit;
1039 return;
1043 typedef TypedValue* (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1044 BUILD_OPTAB_HOT(getKeyTypeIS(key), mia);
1045 if (define || unset) {
1046 m_base = genStk(define ? ElemDX : ElemUX, getCatchTrace(),
1047 cns((TCA)opFunc), m_base, key, genMisPtr());
1048 } else {
1049 m_base = gen(ElemX, getCatchTrace(),
1050 cns((TCA)opFunc), m_base, key, genMisPtr());
1053 #undef HELPER_TABLE
1055 void HhbcTranslator::VectorTranslator::emitNewElem() {
1056 PUNT(emitNewElem);
1059 void HhbcTranslator::VectorTranslator::emitRatchetRefs() {
1060 if (ratchetInd() < 0 || ratchetInd() >= int(nLogicalRatchets())) {
1061 return;
1064 m_base = m_tb.cond(m_ht.curFunc(),
1065 [&] (Block* taken) {
1066 gen(CheckInitMem, taken, m_misBase, cns(HHIR_MISOFF(tvRef)));
1068 [&] { // Next: tvRef isn't Uninit. Ratchet the refs
1069 // Clean up tvRef2 before overwriting it.
1070 if (ratchetInd() > 0) {
1071 gen(DecRefMem, Type::Gen, m_misBase, cns(HHIR_MISOFF(tvRef2)));
1073 // Copy tvRef to tvRef2. Use mmx at some point
1074 SSATmp* tvRef = gen(
1075 LdMem, Type::Gen, m_misBase, cns(HHIR_MISOFF(tvRef))
1077 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef2)), tvRef);
1079 // Reset tvRef.
1080 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef)), m_tb.genDefUninit());
1082 // Adjust base pointer.
1083 assert(m_base->type().isPtr());
1084 return gen(LdAddr, m_misBase, cns(HHIR_MISOFF(tvRef2)));
1086 [&] { // Taken: tvRef is Uninit. Do nothing.
1087 return m_base;
1092 void HhbcTranslator::VectorTranslator::emitFinalMOp() {
1093 typedef void (HhbcTranslator::VectorTranslator::*MemFun)();
1095 switch (m_ni.immVecM[m_mInd]) {
1096 case MEC: case MEL: case MET: case MEI:
1097 static MemFun elemOps[] = {
1098 # define MII(instr, ...) &HhbcTranslator::VectorTranslator::emit##instr##Elem,
1099 MINSTRS
1100 # undef MII
1102 (this->*elemOps[m_mii.instr()])();
1103 break;
1105 case MPC: case MPL: case MPT:
1106 static MemFun propOps[] = {
1107 # define MII(instr, ...) &HhbcTranslator::VectorTranslator::emit##instr##Prop,
1108 MINSTRS
1109 # undef MII
1111 (this->*propOps[m_mii.instr()])();
1112 break;
1114 case MW:
1115 assert(m_mii.getAttr(MW) & MIA_final);
1116 static MemFun newOps[] = {
1117 # define MII(instr, attrs, bS, iS, vC, fN) \
1118 &HhbcTranslator::VectorTranslator::emit##fN,
1119 MINSTRS
1120 # undef MII
1122 (this->*newOps[m_mii.instr()])();
1123 break;
1125 default: not_reached();
1129 template <KeyType keyType, bool isObj>
1130 static inline TypedValue cGetPropImpl(Class* ctx, TypedValue* base,
1131 TypedValue keyVal, MInstrState* mis) {
1132 TypedValue* key = keyPtr<keyType>(keyVal);
1133 TypedValue scratch;
1134 TypedValue* result = Prop<true, false, false, isObj, keyType>(
1135 scratch, mis->tvRef, ctx, base, key);
1137 if (result->m_type == KindOfRef) {
1138 result = result->m_data.pref->tv();
1140 tvRefcountedIncRef(result);
1141 return *result;
1144 #define HELPER_TABLE(m) \
1145 /* name hot key isObj */ \
1146 m(cGetPropC, , AnyKey, false) \
1147 m(cGetPropCO, , AnyKey, true) \
1148 m(cGetPropS, , StrKey, false) \
1149 m(cGetPropSO, HOT_FUNC_VM, StrKey, true)
1151 #define PROP(nm, hot, ...) \
1152 hot \
1153 TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
1154 MInstrState* mis) { \
1155 return cGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1157 namespace VectorHelpers {
1158 HELPER_TABLE(PROP)
1160 #undef PROP
1162 void HhbcTranslator::VectorTranslator::emitCGetProp() {
1163 assert(!m_ni.outLocal);
1165 const Class* knownCls = nullptr;
1166 const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls,
1167 m_mii, m_mInd, m_iInd);
1168 if (propInfo.offset != -1) {
1169 emitPropSpecialized(MIA_warn, propInfo);
1170 SSATmp* cellPtr = gen(UnboxPtr, m_base);
1171 SSATmp* propVal = gen(LdMem, Type::Cell, cellPtr, cns(0));
1172 m_result = gen(IncRef, propVal);
1173 return;
1176 typedef TypedValue (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
1177 SSATmp* key = getKey();
1178 BUILD_OPTAB_HOT(getKeyTypeS(key), m_base->isA(Type::Obj));
1179 m_result = gen(CGetProp, getCatchTrace(),
1180 cns((TCA)opFunc), CTX(), m_base, key, genMisPtr());
1182 #undef HELPER_TABLE
1184 template <KeyType keyType, bool isObj>
1185 static inline RefData* vGetPropImpl(Class* ctx, TypedValue* base,
1186 TypedValue keyVal, MInstrState* mis) {
1187 TypedValue* key = keyPtr<keyType>(keyVal);
1188 TypedValue* result = HPHP::Prop<false, true, false, isObj, keyType>(
1189 mis->tvScratch, mis->tvRef, ctx, base, key);
1191 if (result->m_type != KindOfRef) {
1192 tvBox(result);
1194 RefData* ref = result->m_data.pref;
1195 ref->incRefCount();
1196 return ref;
1199 #define HELPER_TABLE(m) \
1200 /* name hot key isObj */ \
1201 m(vGetPropC, , AnyKey, false) \
1202 m(vGetPropCO, , AnyKey, true) \
1203 m(vGetPropS, , StrKey, false) \
1204 m(vGetPropSO, HOT_FUNC_VM, StrKey, true)
1206 #define PROP(nm, hot, ...) \
1207 hot \
1208 RefData* nm(Class* ctx, TypedValue* base, TypedValue key, \
1209 MInstrState* mis) { \
1210 return vGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1212 namespace VectorHelpers {
1213 HELPER_TABLE(PROP)
1215 #undef PROP
1217 void HhbcTranslator::VectorTranslator::emitVGetProp() {
1218 SSATmp* key = getKey();
1219 typedef RefData* (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
1220 BUILD_OPTAB_HOT(getKeyTypeS(key), m_base->isA(Type::Obj));
1221 m_result = genStk(VGetProp, getCatchTrace(), cns((TCA)opFunc), CTX(),
1222 m_base, key, genMisPtr());
1224 #undef HELPER_TABLE
1226 template <bool useEmpty, bool isObj>
1227 static inline bool issetEmptyPropImpl(Class* ctx, TypedValue* base,
1228 TypedValue keyVal) {
1229 return HPHP::IssetEmptyProp<useEmpty, isObj>(ctx, base, &keyVal);
1232 #define HELPER_TABLE(m) \
1233 /* name useEmpty isObj */ \
1234 m(issetPropC, false, false) \
1235 m(issetPropCE, true, false) \
1236 m(issetPropCEO, true, true) \
1237 m(issetPropCO, false, true)
1239 #define ISSET(nm, ...) \
1240 /* This returns int64_t to ensure all 64 bits of rax are valid */ \
1241 uint64_t nm(Class* ctx, TypedValue* base, TypedValue key) { \
1242 return issetEmptyPropImpl<__VA_ARGS__>(ctx, base, key); \
1244 namespace VectorHelpers {
1245 HELPER_TABLE(ISSET)
1247 #undef ISSET
1249 void HhbcTranslator::VectorTranslator::emitIssetEmptyProp(bool isEmpty) {
1250 SSATmp* key = getKey();
1251 typedef uint64_t (*OpFunc)(Class*, TypedValue*, TypedValue);
1252 BUILD_OPTAB(isEmpty, m_base->isA(Type::Obj));
1253 m_result = gen(isEmpty ? EmptyProp : IssetProp, getCatchTrace(),
1254 cns((TCA)opFunc), CTX(), m_base, key);
1256 #undef HELPER_TABLE
1258 void HhbcTranslator::VectorTranslator::emitIssetProp() {
1259 emitIssetEmptyProp(false);
1262 void HhbcTranslator::VectorTranslator::emitEmptyProp() {
1263 emitIssetEmptyProp(true);
1266 template <bool isObj>
1267 static inline void setPropImpl(Class* ctx, TypedValue* base,
1268 TypedValue keyVal, Cell val) {
1269 HPHP::SetProp<false, isObj>(ctx, base, &keyVal, &val);
1272 #define HELPER_TABLE(m) \
1273 /* name isObj */ \
1274 m(setPropC, false) \
1275 m(setPropCO, true)
1277 #define PROP(nm, ...) \
1278 void nm(Class* ctx, TypedValue* base, TypedValue key, Cell val) { \
1279 setPropImpl<__VA_ARGS__>(ctx, base, key, val); \
1281 namespace VectorHelpers {
1282 HELPER_TABLE(PROP)
1284 #undef PROP
1286 void HhbcTranslator::VectorTranslator::emitSetProp() {
1287 SSATmp* value = getValue();
1289 /* If we know the class for the current base, emit a direct property set. */
1290 const Class* knownCls = nullptr;
1291 const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls,
1292 m_mii, m_mInd, m_iInd);
1293 if (propInfo.offset != -1) {
1294 emitPropSpecialized(MIA_define, propInfo);
1295 SSATmp* cellPtr = gen(UnboxPtr, m_base);
1296 SSATmp* oldVal = gen(LdMem, Type::Cell, cellPtr, cns(0));
1297 // The object owns a reference now
1298 SSATmp* increffed = gen(IncRef, value);
1299 gen(StMem, cellPtr, cns(0), value);
1300 gen(DecRef, oldVal);
1301 m_result = increffed;
1302 return;
1305 // Emit the appropriate helper call.
1306 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue, Cell);
1307 SSATmp* key = getKey();
1308 BUILD_OPTAB(m_base->isA(Type::Obj));
1309 genStk(SetProp, getCatchSetTrace(), cns((TCA)opFunc), CTX(),
1310 m_base, key, value);
1311 m_result = value;
1313 #undef HELPER_TABLE
1315 template <bool isObj>
1316 static inline TypedValue setOpPropImpl(TypedValue* base, TypedValue keyVal,
1317 Cell val, MInstrState* mis, SetOpOp op) {
1318 TypedValue* result = HPHP::SetOpProp<isObj>(
1319 mis->tvScratch, mis->tvRef, mis->ctx, op, base, &keyVal, &val);
1321 TypedValue ret;
1322 tvReadCell(result, &ret);
1323 return ret;
1326 #define HELPER_TABLE(m) \
1327 /* name isObj */ \
1328 m(setOpPropC, false) \
1329 m(setOpPropCO, true)
1331 #define SETOP(nm, ...) \
1332 TypedValue nm(TypedValue* base, TypedValue key, \
1333 Cell val, MInstrState* mis, SetOpOp op) { \
1334 return setOpPropImpl<__VA_ARGS__>(base, key, val, mis, op); \
1336 namespace VectorHelpers {
1337 HELPER_TABLE(SETOP)
1339 #undef SETOP
1341 void HhbcTranslator::VectorTranslator::emitSetOpProp() {
1342 SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
1343 SSATmp* key = getKey();
1344 SSATmp* value = getValue();
1345 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue,
1346 Cell, MInstrState*, SetOpOp);
1347 BUILD_OPTAB(m_base->isA(Type::Obj));
1348 m_tb.gen(StRaw, m_misBase, cns(RawMemSlot::MisCtx), CTX());
1349 m_result =
1350 genStk(SetOpProp, getCatchTrace(), cns((TCA)opFunc),
1351 m_base, key, value, genMisPtr(), cns(op));
1353 #undef HELPER_TABLE
1355 template <bool isObj>
1356 static inline TypedValue incDecPropImpl(TypedValue* base, TypedValue keyVal,
1357 MInstrState* mis, IncDecOp op) {
1358 TypedValue result;
1359 result.m_type = KindOfUninit;
1360 HPHP::IncDecProp<true, isObj>(
1361 mis->tvScratch, mis->tvRef, mis->ctx, op, base, &keyVal, result);
1362 assert(result.m_type != KindOfRef);
1363 return result;
1367 #define HELPER_TABLE(m) \
1368 /* name isObj */ \
1369 m(incDecPropC, false) \
1370 m(incDecPropCO, true)
1372 #define INCDEC(nm, ...) \
1373 TypedValue nm(TypedValue* base, TypedValue key, \
1374 MInstrState* mis, IncDecOp op) { \
1375 return incDecPropImpl<__VA_ARGS__>(base, key, mis, op); \
1377 namespace VectorHelpers {
1378 HELPER_TABLE(INCDEC)
1380 #undef INCDEC
1382 void HhbcTranslator::VectorTranslator::emitIncDecProp() {
1383 IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
1384 SSATmp* key = getKey();
1385 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue,
1386 MInstrState*, IncDecOp);
1387 BUILD_OPTAB(m_base->isA(Type::Obj));
1388 m_tb.gen(StRaw, m_misBase, cns(RawMemSlot::MisCtx), CTX());
1389 m_result =
1390 genStk(IncDecProp, getCatchTrace(), cns((TCA)opFunc),
1391 m_base, key, genMisPtr(), cns(op));
1393 #undef HELPER_TABLE
1395 template <bool isObj>
1396 static inline void bindPropImpl(Class* ctx, TypedValue* base, TypedValue keyVal,
1397 RefData* val, MInstrState* mis) {
1398 TypedValue* prop = HPHP::Prop<false, true, false, isObj>(
1399 mis->tvScratch, mis->tvRef, ctx, base, &keyVal);
1400 if (!(prop == &mis->tvScratch && prop->m_type == KindOfUninit)) {
1401 tvBindRef(val, prop);
1405 #define HELPER_TABLE(m) \
1406 /* name isObj */ \
1407 m(bindPropC, false) \
1408 m(bindPropCO, true)
1410 #define PROP(nm, ...) \
1411 void nm(Class* ctx, TypedValue* base, TypedValue key, \
1412 RefData* val, MInstrState* mis) { \
1413 bindPropImpl<__VA_ARGS__>(ctx, base, key, val, mis); \
1415 namespace VectorHelpers {
1416 HELPER_TABLE(PROP)
1418 #undef PROP
1420 void HhbcTranslator::VectorTranslator::emitBindProp() {
1421 SSATmp* key = getKey();
1422 SSATmp* box = getValue();
1423 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue, RefData*,
1424 MInstrState*);
1425 BUILD_OPTAB(m_base->isA(Type::Obj));
1426 genStk(BindProp, getCatchTrace(), cns((TCA)opFunc), CTX(),
1427 m_base, key, box, genMisPtr());
1428 m_result = box;
1430 #undef HELPER_TABLE
1432 template <bool isObj>
1433 static inline void unsetPropImpl(Class* ctx, TypedValue* base,
1434 TypedValue keyVal) {
1435 HPHP::UnsetProp<isObj>(ctx, base, &keyVal);
1438 #define HELPER_TABLE(m) \
1439 /* name isObj */ \
1440 m(unsetPropC, false) \
1441 m(unsetPropCO, true)
1443 #define PROP(nm, ...) \
1444 static void nm(Class* ctx, TypedValue* base, TypedValue key) { \
1445 unsetPropImpl<__VA_ARGS__>(ctx, base, key); \
1447 namespace VectorHelpers {
1448 HELPER_TABLE(PROP)
1450 #undef PROP
1452 void HhbcTranslator::VectorTranslator::emitUnsetProp() {
1453 SSATmp* key = getKey();
1455 if (m_base->type().strip().not(Type::Obj)) {
1456 // Noop
1457 return;
1460 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue);
1461 BUILD_OPTAB(m_base->isA(Type::Obj));
1462 gen(UnsetProp, cns((TCA)opFunc), CTX(), m_base, key);
1464 #undef HELPER_TABLE
1466 static inline TypedValue* checkedGetCell(ArrayData* a, StringData* key) {
1467 int64_t i;
1468 return UNLIKELY(key->isStrictlyInteger(i)) ? a->nvGetCell(i)
1469 : a->nvGetCell(key);
1472 static inline TypedValue* checkedGetCell(ArrayData* a, int64_t key) {
1473 not_reached();
1476 template<KeyType keyType, bool checkForInt>
1477 static inline TypedValue arrayGetImpl(
1478 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key) {
1479 TypedValue* ret = checkForInt ? checkedGetCell(a, key)
1480 : a->nvGetCell(key);
1481 tvRefcountedIncRef(ret);
1482 return *ret;
1485 #define HELPER_TABLE(m) \
1486 /* name hot keyType checkForInt */ \
1487 m(arrayGetS, HOT_FUNC_VM, StrKey, false) \
1488 m(arrayGetSi, HOT_FUNC_VM, StrKey, true) \
1489 m(arrayGetI, HOT_FUNC_VM, IntKey, false)
1491 #define ELEM(nm, hot, keyType, checkForInt) \
1492 hot \
1493 TypedValue nm(ArrayData* a, TypedValue* key) { \
1494 return arrayGetImpl<keyType, checkForInt>(a, keyAsRaw<keyType>(key)); \
1496 namespace VectorHelpers {
1497 HELPER_TABLE(ELEM)
1499 #undef ELEM
1501 void HhbcTranslator::VectorTranslator::emitArrayGet(SSATmp* key) {
1502 KeyType keyType;
1503 bool checkForInt;
1504 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1506 typedef TypedValue (*OpFunc)(ArrayData*, TypedValue*);
1507 BUILD_OPTAB_HOT(keyType, checkForInt);
1508 assert(m_base->isA(Type::Arr));
1509 m_result = gen(ArrayGet, cns((TCA)opFunc), m_base, key);
1511 #undef HELPER_TABLE
1513 namespace VectorHelpers {
1514 inline TypedValue vectorGet(c_Vector* vec, int64_t key) {
1515 TypedValue* ret = vec->at(key);
1516 return *ret;
1520 void HhbcTranslator::VectorTranslator::emitVectorGet(SSATmp* key) {
1521 SSATmp* value = gen(VectorGet, getCatchTrace(),
1522 cns((TCA)VectorHelpers::vectorGet), m_base, key);
1523 m_result = gen(IncRef, value);
1526 template<KeyType keyType>
1527 static inline TypedValue mapGetImpl(
1528 c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
1529 TypedValue* ret = map->at(key);
1530 return *ret;
1533 #define HELPER_TABLE(m) \
1534 /* name hot keyType */ \
1535 m(mapGetS, HOT_FUNC_VM, StrKey) \
1536 m(mapGetI, HOT_FUNC_VM, IntKey)
1538 #define ELEM(nm, hot, keyType) \
1539 hot \
1540 TypedValue nm(c_Map* map, TypedValue* key) { \
1541 return mapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1543 namespace VectorHelpers {
1544 HELPER_TABLE(ELEM)
1546 #undef ELEM
1548 void HhbcTranslator::VectorTranslator::emitMapGet(SSATmp* key) {
1549 assert(key->isA(Type::Int) || key->isA(Type::Str));
1550 KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
1552 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
1553 BUILD_OPTAB_HOT(keyType);
1554 SSATmp* value = gen(MapGet, getCatchTrace(),
1555 cns((TCA)opFunc), m_base, key);
1556 m_result = gen(IncRef, value);
1558 #undef HELPER_TABLE
1560 template <KeyType keyType>
1561 static inline TypedValue cGetElemImpl(TypedValue* base, TypedValue keyVal,
1562 MInstrState* mis) {
1563 TypedValue* key = keyPtr<keyType>(keyVal);
1564 TypedValue scratch;
1565 TypedValue* result = Elem<true, keyType>(scratch, mis->tvRef, base, key);
1567 if (result->m_type == KindOfRef) {
1568 result = result->m_data.pref->tv();
1570 tvRefcountedIncRef(result);
1571 return *result;
1574 #define HELPER_TABLE(m) \
1575 /* name hot key */ \
1576 m(cGetElemC, , AnyKey) \
1577 m(cGetElemI, , IntKey) \
1578 m(cGetElemS, HOT_FUNC_VM, StrKey)
1580 #define ELEM(nm, hot, ...) \
1581 hot \
1582 TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1583 return cGetElemImpl<__VA_ARGS__>(base, key, mis); \
1585 namespace VectorHelpers {
1586 HELPER_TABLE(ELEM)
1588 #undef ELEM
1590 void HhbcTranslator::VectorTranslator::emitCGetElem() {
1591 SSATmp* key = getKey();
1593 SimpleOp simpleOpType = isSimpleCollectionOp();
1594 switch (simpleOpType) {
1595 case SimpleOp::Array:
1596 emitArrayGet(key);
1597 break;
1598 case SimpleOp::Vector:
1599 emitVectorGet(key);
1600 break;
1601 case SimpleOp::Map:
1602 emitMapGet(key);
1603 break;
1604 default:
1605 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1606 BUILD_OPTAB_HOT(getKeyTypeIS(key));
1607 m_result = gen(CGetElem, getCatchTrace(), cns((TCA)opFunc),
1608 m_base, key, genMisPtr());
1609 break;
1612 #undef HELPER_TABLE
1614 template <KeyType keyType>
1615 static inline RefData* vGetElemImpl(TypedValue* base, TypedValue keyVal,
1616 MInstrState* mis) {
1617 TypedValue* key = keyPtr<keyType>(keyVal);
1618 TypedValue* result = HPHP::ElemD<false, true, keyType>(
1619 mis->tvScratch, mis->tvRef, base, key);
1621 if (result->m_type != KindOfRef) {
1622 tvBox(result);
1624 RefData* ref = result->m_data.pref;
1625 ref->incRefCount();
1626 return ref;
1629 #define HELPER_TABLE(m) \
1630 /* name keyType */ \
1631 m(vGetElemC, AnyKey) \
1632 m(vGetElemI, IntKey) \
1633 m(vGetElemS, StrKey)
1635 #define ELEM(nm, ...) \
1636 RefData* nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1637 return vGetElemImpl<__VA_ARGS__>(base, key, mis); \
1639 namespace VectorHelpers {
1640 HELPER_TABLE(ELEM)
1642 #undef ELEM
1644 void HhbcTranslator::VectorTranslator::emitVGetElem() {
1645 SSATmp* key = getKey();
1646 typedef RefData* (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1647 BUILD_OPTAB(getKeyTypeIS(key));
1648 m_result = genStk(VGetElem, getCatchTrace(), cns((TCA)opFunc),
1649 m_base, key, genMisPtr());
1651 #undef HELPER_TABLE
1653 template <KeyType keyType, bool isEmpty>
1654 static inline bool issetEmptyElemImpl(TypedValue* base, TypedValue keyVal,
1655 MInstrState* mis) {
1656 TypedValue* key = keyPtr<keyType>(keyVal);
1657 // mis == nullptr if we proved that it won't be used. mis->tvScratch and
1658 // mis->tvRef are ok because those params are passed by
1659 // reference.
1660 return HPHP::IssetEmptyElem<isEmpty, false, keyType>(
1661 mis->tvScratch, mis->tvRef, base, key);
1664 #define HELPER_TABLE(m) \
1665 /* name hot keyType isEmpty */ \
1666 m(issetElemC, , AnyKey, false) \
1667 m(issetElemCE, , AnyKey, true) \
1668 m(issetElemI, HOT_FUNC_VM, IntKey, false) \
1669 m(issetElemIE, , IntKey, true) \
1670 m(issetElemS, HOT_FUNC_VM, StrKey, false) \
1671 m(issetElemSE, , StrKey, true)
1673 #define ISSET(nm, hot, ...) \
1674 hot \
1675 uint64_t nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1676 return issetEmptyElemImpl<__VA_ARGS__>(base, key, mis); \
1678 namespace VectorHelpers {
1679 HELPER_TABLE(ISSET)
1681 #undef ISSET
1683 void HhbcTranslator::VectorTranslator::emitIssetEmptyElem(bool isEmpty) {
1684 SSATmp* key = getKey();
1686 typedef uint64_t (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1687 BUILD_OPTAB_HOT(getKeyTypeIS(key), isEmpty);
1688 m_result = gen(isEmpty ? EmptyElem : IssetElem, getCatchTrace(),
1689 cns((TCA)opFunc), m_base, key, genMisPtr());
1691 #undef HELPER_TABLE
1693 static inline TypedValue* checkedGet(ArrayData* a, StringData* key) {
1694 int64_t i;
1695 return UNLIKELY(key->isStrictlyInteger(i)) ? a->nvGet(i)
1696 : a->nvGet(key);
1699 static inline TypedValue* checkedGet(ArrayData* a, int64_t key) {
1700 not_reached();
1703 template<KeyType keyType, bool checkForInt>
1704 static inline uint64_t arrayIssetImpl(
1705 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key) {
1706 TypedValue* value = checkForInt ? checkedGet(a, key)
1707 : a->nvGet(key);
1708 Variant* var = &tvAsVariant(value);
1709 return var && !var->isNull();
1712 #define HELPER_TABLE(m) \
1713 /* name keyType checkForInt */ \
1714 m(arrayIssetS, StrKey, false) \
1715 m(arrayIssetSi, StrKey, true) \
1716 m(arrayIssetI, IntKey, false)
1718 #define ISSET(nm, keyType, checkForInt) \
1719 uint64_t nm(ArrayData* a, TypedValue* key) { \
1720 return arrayIssetImpl<keyType, checkForInt>(a, keyAsRaw<keyType>(key)); \
1722 namespace VectorHelpers {
1723 HELPER_TABLE(ISSET)
1725 #undef ISSET
1727 void HhbcTranslator::VectorTranslator::emitArrayIsset() {
1728 SSATmp* key = getKey();
1729 KeyType keyType;
1730 bool checkForInt;
1731 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1733 typedef uint64_t (*OpFunc)(ArrayData*, TypedValue*);
1734 BUILD_OPTAB(keyType, checkForInt);
1735 assert(m_base->isA(Type::Arr));
1736 m_result = gen(ArrayIsset, getCatchTrace(),
1737 cns((TCA)opFunc), m_base, key);
1739 #undef HELPER_TABLE
1741 namespace VectorHelpers {
1742 static inline uint64_t vectorIsset(c_Vector* vec, int64_t index) {
1743 return vec->get(index) != nullptr;
1747 void HhbcTranslator::VectorTranslator::emitVectorIsset() {
1748 SSATmp* key = getKey();
1749 assert(key->isA(Type::Int));
1750 m_result = gen(VectorIsset,
1751 cns((TCA)VectorHelpers::vectorIsset),
1752 m_base,
1753 key);
1756 template<KeyType keyType>
1757 static inline uint64_t mapIssetImpl(
1758 c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
1759 return map->get(key) != nullptr;
1762 #define HELPER_TABLE(m) \
1763 /* name hot keyType */ \
1764 m(mapIssetS, HOT_FUNC_VM, StrKey) \
1765 m(mapIssetI, HOT_FUNC_VM, IntKey)
1767 #define ELEM(nm, hot, keyType) \
1768 hot \
1769 uint64_t nm(c_Map* map, TypedValue* key) { \
1770 return mapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1772 namespace VectorHelpers {
1773 HELPER_TABLE(ELEM)
1775 #undef ELEM
1777 void HhbcTranslator::VectorTranslator::emitMapIsset() {
1778 SSATmp* key = getKey();
1779 assert(key->isA(Type::Int) || key->isA(Type::Str));
1780 KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
1782 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
1783 BUILD_OPTAB_HOT(keyType);
1784 m_result = gen(MapIsset, cns((TCA)opFunc), m_base, key);
1786 #undef HELPER_TABLE
1788 void HhbcTranslator::VectorTranslator::emitIssetElem() {
1789 SimpleOp simpleOpType = isSimpleCollectionOp();
1790 switch (simpleOpType) {
1791 case SimpleOp::Array:
1792 emitArrayIsset();
1793 break;
1794 case SimpleOp::Vector:
1795 emitVectorIsset();
1796 break;
1797 case SimpleOp::Map:
1798 emitMapIsset();
1799 break;
1800 default:
1801 emitIssetEmptyElem(false);
1802 break;
1806 void HhbcTranslator::VectorTranslator::emitEmptyElem() {
1807 emitIssetEmptyElem(true);
1810 static inline ArrayData* checkedSet(ArrayData* a, StringData* key,
1811 CVarRef value, bool copy) {
1812 int64_t i;
1813 return UNLIKELY(key->isStrictlyInteger(i)) ? a->set(i, value, copy)
1814 : a->set(key, value, copy);
1817 static inline ArrayData* checkedSet(ArrayData* a, int64_t key,
1818 CVarRef value, bool copy) {
1819 not_reached();
1822 template<KeyType keyType, bool checkForInt, bool setRef>
1823 static inline typename ShuffleReturn<setRef>::return_type arraySetImpl(
1824 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key,
1825 CVarRef value, RefData* ref) {
1826 static_assert(keyType != AnyKey, "AnyKey is not supported in arraySetMImpl");
1827 const bool copy = a->getCount() > 1;
1828 ArrayData* ret = checkForInt ? checkedSet(a, key, value, copy)
1829 : a->set(key, value, copy);
1831 return arrayRefShuffle<setRef>(a, ret, setRef ? ref->tv() : nullptr);
1834 #define HELPER_TABLE(m) \
1835 /* name hot keyType checkForInt setRef */ \
1836 m(arraySetS, HOT_FUNC_VM, StrKey, false, false) \
1837 m(arraySetSi, HOT_FUNC_VM, StrKey, true, false) \
1838 m(arraySetI, HOT_FUNC_VM, IntKey, false, false) \
1839 m(arraySetSR, , StrKey, false, true) \
1840 m(arraySetSiR, , StrKey, true, true) \
1841 m(arraySetIR, , IntKey, false, true)
1843 #define ELEM(nm, hot, keyType, checkForInt, setRef) \
1844 hot \
1845 typename ShuffleReturn<setRef>::return_type \
1846 nm(ArrayData* a, TypedValue* key, TypedValue value, RefData* ref) { \
1847 return arraySetImpl<keyType, checkForInt, setRef>( \
1848 a, keyAsRaw<keyType>(key), tvAsCVarRef(&value), ref); \
1850 namespace VectorHelpers {
1851 HELPER_TABLE(ELEM)
1853 #undef ELEM
1855 void HhbcTranslator::VectorTranslator::emitArraySet(SSATmp* key,
1856 SSATmp* value) {
1857 assert(m_iInd == m_mii.valCount() + 1);
1858 const int baseStkIdx = m_mii.valCount();
1859 assert(key->type().notBoxed());
1860 assert(value->type().notBoxed());
1861 KeyType keyType;
1862 bool checkForInt;
1863 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1864 const DynLocation& base = *m_ni.inputs[m_mii.valCount()];
1865 bool setRef = base.outerType() == KindOfRef;
1866 typedef ArrayData* (*OpFunc)(ArrayData*, TypedValue*, TypedValue, RefData*);
1867 BUILD_OPTAB_HOT(keyType, checkForInt, setRef);
1869 // No catch trace below because the helper can't throw. It may reenter to
1870 // call destructors so it has a sync point in nativecalls.cpp, but exceptions
1871 // are swallowed at destructor boundaries right now: #2182869.
1872 if (setRef) {
1873 assert(base.location.space == Location::Local ||
1874 base.location.space == Location::Stack);
1875 SSATmp* box = getInput(baseStkIdx);
1876 gen(ArraySetRef, cns((TCA)opFunc), m_base, key, value, box);
1877 // Unlike the non-ref case, we don't need to do anything to the stack
1878 // because any load of the box will be guarded.
1879 } else {
1880 SSATmp* newArr = gen(ArraySet, cns((TCA)opFunc), m_base, key, value);
1882 // Update the base's value with the new array
1883 if (base.location.space == Location::Local) {
1884 // We know it's not boxed (setRef above handles that), and
1885 // newArr has already been incref'd in the helper.
1886 gen(StLoc, LocalId(base.location.offset), m_tb.fp(), newArr);
1887 } else if (base.location.space == Location::Stack) {
1888 VectorEffects ve(newArr->inst());
1889 assert(ve.baseValChanged);
1890 assert(ve.baseType.subtypeOf(Type::Arr));
1891 m_ht.extendStack(baseStkIdx, Type::Gen);
1892 m_ht.replace(baseStkIdx, newArr);
1893 } else {
1894 not_reached();
1898 m_result = value;
1900 #undef HELPER_TABLE
1902 namespace VectorHelpers {
1903 void setWithRefElemC(TypedValue* base, TypedValue keyVal, TypedValue* val,
1904 MInstrState* mis) {
1905 base = HPHP::ElemD<false, false>(mis->tvScratch, mis->tvRef, base, &keyVal);
1906 if (base != &mis->tvScratch) {
1907 tvDup(val, base);
1908 } else {
1909 assert(base->m_type == KindOfUninit);
1913 void setWithRefNewElem(TypedValue* base, TypedValue* val,
1914 MInstrState* mis) {
1915 base = NewElem(mis->tvScratch, mis->tvRef, base);
1916 if (base != &mis->tvScratch) {
1917 tvDup(val, base);
1918 } else {
1919 assert(base->m_type == KindOfUninit);
1924 void HhbcTranslator::VectorTranslator::emitSetWithRefLElem() {
1925 SSATmp* key = getKey();
1926 SSATmp* locAddr = getValAddr();
1927 if (m_base->type().strip().subtypeOf(Type::Arr) &&
1928 !locAddr->type().deref().maybeBoxed()) {
1929 emitSetElem();
1930 assert(m_strTestResult == nullptr);
1931 } else {
1932 genStk(SetWithRefElem, getCatchTrace(),
1933 cns((TCA)VectorHelpers::setWithRefElemC),
1934 m_base, key, locAddr, genMisPtr());
1936 m_result = nullptr;
1939 void HhbcTranslator::VectorTranslator::emitSetWithRefLProp() {
1940 SPUNT(__func__);
1943 void HhbcTranslator::VectorTranslator::emitSetWithRefRElem() {
1944 emitSetWithRefLElem();
1947 void HhbcTranslator::VectorTranslator::emitSetWithRefRProp() {
1948 emitSetWithRefLProp();
1951 void HhbcTranslator::VectorTranslator::emitSetWithRefNewElem() {
1952 if (m_base->type().strip().subtypeOf(Type::Arr) &&
1953 getValue()->type().notBoxed()) {
1954 emitSetNewElem();
1955 } else {
1956 genStk(SetWithRefNewElem, getCatchTrace(),
1957 cns((TCA)VectorHelpers::setWithRefNewElem),
1958 m_base, getValAddr(), genMisPtr());
1960 m_result = nullptr;
1963 namespace VectorHelpers {
1964 static inline void vectorSet(c_Vector* vec,
1965 int64_t key,
1966 Cell value) {
1967 vec->set(key, &value);
1971 void HhbcTranslator::VectorTranslator::emitVectorSet(
1972 SSATmp* key, SSATmp* value) {
1973 gen(VectorSet, getCatchTrace(),
1974 cns((TCA)VectorHelpers::vectorSet), m_base, key, value);
1975 m_result = value;
1978 template<KeyType keyType>
1979 static inline void mapSetImpl(
1980 c_Map* map,
1981 typename KeyTypeTraits<keyType>::rawType key,
1982 Cell value) {
1983 map->set(key, &value);
1986 #define HELPER_TABLE(m) \
1987 /* name hot keyType */ \
1988 m(mapSetS, HOT_FUNC_VM, StrKey) \
1989 m(mapSetI, HOT_FUNC_VM, IntKey)
1991 #define ELEM(nm, hot, keyType) \
1992 hot \
1993 void nm(c_Map* map, TypedValue* key, Cell value) { \
1994 mapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
1996 namespace VectorHelpers {
1997 HELPER_TABLE(ELEM)
1999 #undef ELEM
2001 void HhbcTranslator::VectorTranslator::emitMapSet(
2002 SSATmp* key, SSATmp* value) {
2003 assert(key->isA(Type::Int) || key->isA(Type::Str));
2004 KeyType keyType = key->isA(Type::Int) ? IntKey : StrKey;
2006 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*, TypedValue*);
2007 BUILD_OPTAB_HOT(keyType);
2008 gen(MapSet, getCatchTrace(),
2009 cns((TCA)opFunc), m_base, key, value);
2010 m_result = value;
2012 #undef HELPER_TABLE
2014 template <KeyType keyType>
2015 static inline StringData* setElemImpl(TypedValue* base, TypedValue keyVal,
2016 Cell val) {
2017 TypedValue* key = keyPtr<keyType>(keyVal);
2018 return HPHP::SetElem<false, keyType>(base, key, &val);
2021 #define HELPER_TABLE(m) \
2022 /* name hot key */ \
2023 m(setElemC, , AnyKey) \
2024 m(setElemI, , IntKey) \
2025 m(setElemS, HOT_FUNC_VM, StrKey)
2027 #define ELEM(nm, hot, ...) \
2028 hot \
2029 StringData* nm(TypedValue* base, TypedValue key, Cell val) { \
2030 return setElemImpl<__VA_ARGS__>(base, key, val); \
2032 namespace VectorHelpers {
2033 HELPER_TABLE(ELEM)
2035 #undef ELEM
2037 void HhbcTranslator::VectorTranslator::emitSetElem() {
2038 SSATmp* value = getValue();
2039 SSATmp* key = getKey();
2041 SimpleOp simpleOpType = isSimpleCollectionOp();
2042 switch (simpleOpType) {
2043 case SimpleOp::Array:
2044 emitArraySet(key, value);
2045 break;
2046 case SimpleOp::Vector:
2047 emitVectorSet(key, value);
2048 break;
2049 case SimpleOp::Map:
2050 emitMapSet(key, value);
2051 break;
2052 default:
2053 // Emit the appropriate helper call.
2054 typedef StringData* (*OpFunc)(TypedValue*, TypedValue, Cell);
2055 BUILD_OPTAB_HOT(getKeyTypeIS(key));
2056 m_failedSetTrace = getCatchSetTrace();
2057 SSATmp* result = genStk(SetElem, m_failedSetTrace, cns((TCA)opFunc),
2058 m_base, key, value);
2059 auto t = result->type();
2060 if (t.equals(Type::Nullptr)) {
2061 // Base is not a string. Result is always value.
2062 m_result = value;
2063 } else if (t.equals(Type::CountedStr)) {
2064 // Base is a string. Stack result is a new string so we're responsible for
2065 // decreffing value.
2066 m_result = result;
2067 gen(DecRef, value);
2068 } else {
2069 assert(t.equals(Type::CountedStr | Type::Nullptr));
2070 // Base might be a string. Assume the result is value, then inform
2071 // emitMPost that it needs to test the actual result.
2072 m_result = value;
2073 m_strTestResult = result;
2075 break;
2078 #undef HELPER_TABLE
2080 template <SetOpOp op>
2081 static inline TypedValue setOpElemImpl(TypedValue* base, TypedValue keyVal,
2082 Cell val, MInstrState* mis) {
2083 TypedValue* result =
2084 HPHP::SetOpElem(mis->tvScratch, mis->tvRef, op, base, &keyVal, &val);
2086 TypedValue ret;
2087 tvReadCell(result, &ret);
2088 return ret;
2091 #define OPELEM_TABLE(m, nm, op) \
2092 /* name op */ \
2093 m(nm##op##ElemC, op)
2095 #define HELPER_TABLE(m, op) OPELEM_TABLE(m, setOp, SetOp##op)
2096 #define SETOP(nm, ...) \
2097 TypedValue nm(TypedValue* base, TypedValue key, Cell val, \
2098 MInstrState* mis) { \
2099 return setOpElemImpl<__VA_ARGS__>(base, key, val, mis); \
2101 #define SETOP_OP(op, bcOp) HELPER_TABLE(SETOP, op)
2102 namespace VectorHelpers {
2103 SETOP_OPS
2105 #undef SETOP_OP
2106 #undef SETOP
2108 void HhbcTranslator::VectorTranslator::emitSetOpElem() {
2109 SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
2110 SSATmp* key = getKey();
2111 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, Cell, MInstrState*);
2112 # define SETOP_OP(op, bcOp) HELPER_TABLE(FILL_ROW, op)
2113 BUILD_OPTAB_ARG(SETOP_OPS, op);
2114 # undef SETOP_OP
2115 m_result =
2116 genStk(SetOpElem, getCatchTrace(), cns((TCA)opFunc),
2117 m_base, key, getValue(), genMisPtr());
2119 #undef HELPER_TABLE
2121 template <IncDecOp op>
2122 static inline TypedValue incDecElemImpl(TypedValue* base, TypedValue keyVal,
2123 MInstrState* mis) {
2124 TypedValue result;
2125 HPHP::IncDecElem<true>(
2126 mis->tvScratch, mis->tvRef, op, base, &keyVal, result);
2127 assert(result.m_type != KindOfRef);
2128 return result;
2131 #define HELPER_TABLE(m, op) OPELEM_TABLE(m, incDec, op)
2132 #define INCDEC(nm, ...) \
2133 TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
2134 return incDecElemImpl<__VA_ARGS__>(base, key, mis); \
2136 #define INCDEC_OP(op) HELPER_TABLE(INCDEC, op)
2137 namespace VectorHelpers {
2138 INCDEC_OPS
2140 #undef INCDEC_OP
2141 #undef INCDEC
2143 void HhbcTranslator::VectorTranslator::emitIncDecElem() {
2144 IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
2145 SSATmp* key = getKey();
2146 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
2147 # define INCDEC_OP(op) HELPER_TABLE(FILL_ROW, op)
2148 BUILD_OPTAB_ARG(INCDEC_OPS, op);
2149 # undef INCDEC_OP
2150 m_result = genStk(IncDecElem, getCatchTrace(), cns((TCA)opFunc),
2151 m_base, key, genMisPtr());
2153 #undef HELPER_TABLE
2155 namespace VectorHelpers {
2156 void bindElemC(TypedValue* base, TypedValue keyVal, RefData* val,
2157 MInstrState* mis) {
2158 base = HPHP::ElemD<false, true>(mis->tvScratch, mis->tvRef, base, &keyVal);
2159 if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
2160 tvBindRef(val, base);
2165 void HhbcTranslator::VectorTranslator::emitBindElem() {
2166 SSATmp* key = getKey();
2167 SSATmp* box = getValue();
2168 genStk(BindElem, getCatchTrace(), cns((TCA)VectorHelpers::bindElemC),
2169 m_base, key, box, genMisPtr());
2170 m_result = box;
2173 template <KeyType keyType>
2174 static inline void unsetElemImpl(TypedValue* base, TypedValue keyVal) {
2175 TypedValue* key = keyPtr<keyType>(keyVal);
2176 HPHP::UnsetElem<keyType>(base, key);
2179 #define HELPER_TABLE(m) \
2180 /* name hot keyType */ \
2181 m(unsetElemC, , AnyKey) \
2182 m(unsetElemI, , IntKey) \
2183 m(unsetElemS, HOT_FUNC_VM, StrKey)
2185 #define ELEM(nm, hot, ...) \
2186 hot \
2187 void nm(TypedValue* base, TypedValue key) { \
2188 unsetElemImpl<__VA_ARGS__>(base, key); \
2190 namespace VectorHelpers {
2191 HELPER_TABLE(ELEM)
2193 #undef ELEM
2195 void HhbcTranslator::VectorTranslator::emitUnsetElem() {
2196 SSATmp* key = getKey();
2198 Type baseType = m_base->type().strip();
2199 if (baseType.subtypeOf(Type::Str)) {
2200 m_ht.exceptionBarrier();
2201 gen(RaiseError,
2202 cns(StringData::GetStaticString(Strings::CANT_UNSET_STRING)));
2203 return;
2205 if (baseType.not(Type::Arr | Type::Obj)) {
2206 // Noop
2207 return;
2210 typedef void (*OpFunc)(TypedValue*, TypedValue);
2211 BUILD_OPTAB_HOT(getKeyTypeIS(key));
2212 genStk(UnsetElem, getCatchTrace(), cns((TCA)opFunc), m_base, key);
2214 #undef HELPER_TABLE
2216 void HhbcTranslator::VectorTranslator::emitNotSuppNewElem() {
2217 not_reached();
2220 void HhbcTranslator::VectorTranslator::emitVGetNewElem() {
2221 SPUNT(__func__);
2224 void HhbcTranslator::VectorTranslator::emitSetNewElem() {
2225 SSATmp* value = getValue();
2226 gen(SetNewElem, getCatchSetTrace(), m_base, value);
2227 m_result = value;
2230 void HhbcTranslator::VectorTranslator::emitSetOpNewElem() {
2231 SPUNT(__func__);
2234 void HhbcTranslator::VectorTranslator::emitIncDecNewElem() {
2235 SPUNT(__func__);
2238 void HhbcTranslator::VectorTranslator::emitBindNewElem() {
2239 SSATmp* box = getValue();
2240 genStk(BindNewElem, getCatchTrace(), m_base, box, genMisPtr());
2241 m_result = box;
2244 void HhbcTranslator::VectorTranslator::emitMPost() {
2245 SSATmp* catchSp = nullptr;
2246 if (m_failedSetTrace) {
2247 catchSp = m_failedSetTrace->back()->back()->src(0);
2248 assert(catchSp->isA(Type::StkPtr));
2251 // Decref stack inputs. If we're translating a SetM or BindM, then input 0 is
2252 // both our input and output so leave its refcount alone. If m_failedSetTrace
2253 // is non-null, the final helper call may throw an InvalidSetMException. We
2254 // need to add instructions to m_failedSetTrace to finish the vector
2255 // instruction in case this happens, so any DecRefs emitted here are also
2256 // added to m_failedSetTrace.
2257 unsigned nStack =
2258 (m_ni.mInstrOp() == OpSetM || m_ni.mInstrOp() == OpBindM) ? 1 : 0;
2259 for (unsigned i = nStack; i < m_ni.inputs.size(); ++i) {
2260 const DynLocation& input = *m_ni.inputs[i];
2261 switch (input.location.space) {
2262 case Location::Stack: {
2263 ++nStack;
2264 auto input = getInput(i);
2265 if (input->isA(Type::Gen)) {
2266 gen(DecRef, input);
2267 if (m_failedSetTrace) {
2268 m_ht.genFor(m_failedSetTrace, DecRefStack,
2269 StackOffset(m_stackInputs[i]), Type::Cell, catchSp);
2272 break;
2274 case Location::Local:
2275 case Location::Litstr:
2276 case Location::Litint:
2277 case Location::This: {
2278 // Do nothing.
2279 break;
2282 default: not_reached();
2286 // Pop off all stack inputs
2287 m_ht.discard(nStack);
2289 // Push result, if one was produced. If we have a predicted result use that
2290 // instead of the real result; its validity will be guarded later in this
2291 // function.
2292 if (m_result) {
2293 m_ht.push(m_result);
2294 } else {
2295 assert(m_ni.mInstrOp() == OpUnsetM ||
2296 m_ni.mInstrOp() == OpSetWithRefLM ||
2297 m_ni.mInstrOp() == OpSetWithRefRM);
2300 // Clean up tvRef(2): during exception handling any objects required only
2301 // during vector expansion need to be DecRef'd. There may be either one
2302 // or two such scratch objects, in the case of a Set the first of which will
2303 // always be tvRef2, in all other cases if only one scratch value is present
2304 // it will be stored in tvRef.
2305 static const size_t refOffs[] = { HHIR_MISOFF(tvRef), HHIR_MISOFF(tvRef2) };
2306 for (unsigned i = 0; i < std::min(nLogicalRatchets(), 2U); ++i) {
2307 IRInstruction* inst = m_irf.gen(DecRefMem, Type::Gen, m_misBase,
2308 cns(refOffs[m_failedSetTrace ? 1 - i : i]));
2309 m_tb.add(inst);
2310 prependToTraces(inst);
2313 emitSideExits(catchSp, nStack);
2316 void HhbcTranslator::VectorTranslator::emitSideExits(SSATmp* catchSp,
2317 int nStack) {
2318 auto const nextOff = m_ht.nextBcOff();
2319 auto const op = m_ni.mInstrOp();
2320 const bool isSetWithRef = op == OpSetWithRefLM || op == OpSetWithRefRM;
2322 if (m_failedSetTrace) {
2323 assert(bool(m_result) ^ isSetWithRef);
2324 // This catch trace currently ends with an EndCatch that will fall through
2325 // if an InvalidSetMException was thrown. We need to emit code to clean up
2326 // if that happens. If we're translating a SetWithRef* bytecode we don't
2327 // have to do anything special to the stack since they have no stack
2328 // output. Otherwise we need to pop our input value and push the value from
2329 // the exception to the stack (done with a DecRefStack followed by a
2330 // SpillStack).
2332 std::vector<SSATmp*> args{
2333 catchSp, // sp from the previous SpillStack
2334 cns(nStack), // cells popped since the last SpillStack
2337 if (!isSetWithRef) {
2338 m_ht.genFor(m_failedSetTrace, DecRefStack, StackOffset(0),
2339 Type::Cell, catchSp);
2340 args.push_back(m_ht.genFor(m_failedSetTrace, LdUnwinderValue,
2341 Type::Cell));
2344 SSATmp* sp = m_ht.genFor(m_failedSetTrace, SpillStack,
2345 std::make_pair(args.size(), &args[0]));
2346 m_ht.genFor(m_failedSetTrace, DeleteUnwinderException);
2347 m_ht.genFor(m_failedSetTrace, SyncABIRegs, m_tb.fp(), sp);
2348 m_ht.genFor(m_failedSetTrace, ReqBindJmp, BCOffset(nextOff));
2351 if (m_strTestResult) {
2352 assert(!isSetWithRef);
2353 // We expected SetElem's base to not be a Str but might be wrong. Make an
2354 // exit trace to side exit to the next instruction, replacing our guess
2355 // with the correct stack output.
2357 auto toSpill = m_ht.peekSpillValues();
2358 assert(toSpill.size());
2359 assert(toSpill[0] == m_result);
2360 SSATmp* str = m_irf.gen(AssertNonNull, m_strTestResult)->dst();
2361 toSpill[0] = str;
2362 IRTrace* exit = m_ht.getExitTrace(nextOff, toSpill);
2363 exit->front()->prepend(str->inst());
2364 exit->front()->prepend(m_irf.gen(DecRef, m_result));
2365 exit->front()->prepend(
2366 m_irf.gen(IncStat, cns(Stats::TC_SetMStrGuess_Miss),
2367 cns(1), cns(false)));
2368 exit->front()->prepend(m_ht.makeMarker(m_ht.bcOff()));
2370 gen(CheckType, Type::Nullptr, exit, m_strTestResult);
2371 gen(IncStat, cns(Stats::TC_SetMStrGuess_Hit), cns(1), cns(false));
2375 bool HhbcTranslator::VectorTranslator::needFirstRatchet() const {
2376 if (m_ni.inputs[m_mii.valCount()]->valueType() == KindOfArray) {
2377 switch (m_ni.immVecM[0]) {
2378 case MEC: case MEL: case MET: case MEI: return false;
2379 case MPC: case MPL: case MPT: case MW: return true;
2380 default: not_reached();
2383 return true;
2386 bool HhbcTranslator::VectorTranslator::needFinalRatchet() const {
2387 return m_mii.finalGet();
2390 // Ratchet operations occur after each intermediate operation, except
2391 // possibly the first and last (see need{First,Final}Ratchet()). No actual
2392 // ratchet occurs after the final operation, but this means that both tvRef
2393 // and tvRef2 can contain references just after the final operation. Here we
2394 // pretend that a ratchet occurs after the final operation, i.e. a "logical"
2395 // ratchet. The reason for counting logical ratchets as part of the total is
2396 // the following case, in which the logical count is 0:
2398 // (base is array)
2399 // BaseL
2400 // IssetElemL
2401 // no logical ratchet
2403 // Following are a few more examples to make the algorithm clear:
2405 // (base is array) (base is object) (base is object)
2406 // BaseL BaseL BaseL
2407 // ElemL ElemL CGetPropL
2408 // no ratchet ratchet logical ratchet
2409 // ElemL PropL
2410 // ratchet ratchet
2411 // ElemL CGetElemL
2412 // ratchet logical ratchet
2413 // IssetElemL
2414 // logical ratchet
2416 // (base is array)
2417 // BaseL
2418 // ElemL
2419 // no ratchet
2420 // ElemL
2421 // ratchet
2422 // ElemL
2423 // logical ratchet
2424 // SetElemL
2425 // no ratchet
2426 unsigned HhbcTranslator::VectorTranslator::nLogicalRatchets() const {
2427 // If we've proven elsewhere that we don't need an MInstrState struct, we
2428 // know this translation won't need any ratchets
2429 if (!m_needMIS) return 0;
2431 unsigned ratchets = m_ni.immVecM.size();
2432 if (!needFirstRatchet()) --ratchets;
2433 if (!needFinalRatchet()) --ratchets;
2434 return ratchets;
2437 int HhbcTranslator::VectorTranslator::ratchetInd() const {
2438 return needFirstRatchet() ? int(m_mInd) : int(m_mInd) - 1;