Clean up VectorEffects::init
[hiphop-php.git] / hphp / runtime / vm / jit / vectortranslator.cpp
blob07db8bc97fda36fef684234221e041c376fae879
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(const Opcode rawOp, 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));
137 baseTypeChanged = baseValChanged = false;
139 // Canonicalize the op to SetProp/SetElem/UnsetElem/SetWithRefElem
140 auto const op = canonicalOp(rawOp);
142 /* Although it should work fine in practice, if we have a key it should
143 * either be a known DataType or Cell for now. */
144 assert(key.equals(Type::None) || key.isKnownDataType() ||
145 key.equals(Type::Cell));
147 // Deal with possible promotion to stdClass or array
148 if ((op == SetElem || op == SetProp || op == SetWithRefElem) &&
149 baseType.maybe(Type::Null | Type::Bool | Type::Str)) {
150 auto newBase = op == SetProp ? Type::Obj : Type::Arr;
152 if (baseBoxed) {
153 /* We always guard when loading from a Boxed type, so we don't *have* to
154 * fix the type when the base is boxed. But it's still a good idea to do
155 * something with it, since the inner type is used as a hint by
156 * LdRef. And since the next load of the base will be guarded anyway we
157 * can be optimistic and assume no promotion for string bases and
158 * promotion in other cases. */
159 baseType = baseType.isString() ? Type::Str : newBase;
160 } else if (baseType.isString() &&
161 (rawOp == SetElem || rawOp == SetElemStk)) {
162 /* If the base is known to be a string and the operation is exactly
163 * SetElem, we're guaranteed that either the base will end as a
164 * CountedStr or the instruction will throw an exception and side
165 * exit. */
166 baseType = Type::CountedStr;
167 } else {
168 /* Regardless of whether or not promotion happens, we know the base
169 * cannot be Null after the operation. If the base was a subtype of Null
170 * this will give newBase. */
171 baseType = (baseType - Type::Null) | newBase;
174 baseValChanged = true;
177 if ((op == SetElem || op == UnsetElem || op == SetWithRefElem) &&
178 baseType.maybe(Type::Arr | Type::Str)) {
179 /* Modifying an array or string element, even when COW doesn't kick in,
180 * produces a new SSATmp for the base. StaticArr/StaticStr may be promoted
181 * to CountedArr/CountedStr. */
182 baseValChanged = true;
183 if (baseType.maybe(Type::StaticArr)) baseType |= Type::CountedArr;
184 if (baseType.maybe(Type::StaticStr)) baseType |= Type::CountedStr;
187 // The final baseType should be a pointer/box iff the input was
188 baseType = baseBoxed ? baseType.box() : baseType;
189 baseType = basePtr ? baseType.ptr() : baseType;
191 baseTypeChanged = baseTypeChanged || baseType != origBase;
193 /* Boxed bases may have their inner value changed but the value of the box
194 * will never change. */
195 baseValChanged = !baseBoxed && (baseValChanged || baseTypeChanged);
198 // vectorBaseIdx returns the src index for inst's base operand.
199 int vectorBaseIdx(Opcode opc) {
200 return opc == SetNewElem || opc == SetNewElemStk ? 0
201 : opc == BindNewElem || opc == BindNewElemStk ? 0
202 : opc == ArraySet ? 1
203 : opc == SetOpProp || opc == SetOpPropStk ? 1
204 : opcodeHasFlags(opc, VectorProp) ? 2
205 : opcodeHasFlags(opc, VectorElem) ? 1
206 : bad_value<int>();
208 int vectorBaseIdx(const IRInstruction* inst) {
209 return vectorBaseIdx(inst->op());
212 // vectorKeyIdx returns the src index for inst's key operand.
213 int vectorKeyIdx(Opcode opc) {
214 return opc == SetNewElem || opc == SetNewElemStk ? -1
215 : opc == SetWithRefNewElem || opc == SetWithRefNewElemStk ? -1
216 : opc == BindNewElem || opc == BindNewElem ? -1
217 : opc == ArraySet ? 2
218 : opc == SetOpProp || opc == SetOpPropStk ? 2
219 : opcodeHasFlags(opc, VectorProp) ? 3
220 : opcodeHasFlags(opc, VectorElem) ? 2
221 : bad_value<int>();
223 int vectorKeyIdx(const IRInstruction* inst) {
224 return vectorKeyIdx(inst->op());
227 // vectorValIdx returns the src index for inst's value operand.
228 int vectorValIdx(Opcode opc) {
229 switch (opc) {
230 case VGetProp: case VGetPropStk:
231 case IncDecProp: case IncDecPropStk:
232 case PropDX: case PropDXStk:
233 case VGetElem: case VGetElemStk:
234 case UnsetElem: case UnsetElemStk:
235 case IncDecElem: case IncDecElemStk:
236 case ElemDX: case ElemDXStk:
237 case ElemUX: case ElemUXStk:
238 return -1;
240 case ArraySet: return 3;
241 case SetNewElem: case SetNewElemStk: return 1;
242 case SetWithRefNewElem: case SetWithRefNewElemStk: return 2;
243 case BindNewElem: case BindNewElemStk: return 1;
244 case SetOpProp: case SetOpPropStk: return 3;
246 default:
247 return opcodeHasFlags(opc, VectorProp) ? 4
248 : opcodeHasFlags(opc, VectorElem) ? 3
249 : bad_value<int>();
252 int vectorValIdx(const IRInstruction* inst) {
253 return vectorValIdx(inst->op());
256 HhbcTranslator::VectorTranslator::VectorTranslator(
257 const NormalizedInstruction& ni,
258 HhbcTranslator& ht)
259 : m_ni(ni)
260 , m_ht(ht)
261 , m_tb(*m_ht.m_tb)
262 , m_irf(m_ht.m_irFactory)
263 , m_mii(getMInstrInfo(ni.mInstrOp()))
264 , m_marker(ht.makeMarker(ht.bcOff()))
265 , m_needMIS(true)
266 , m_misBase(nullptr)
267 , m_base(nullptr)
268 , m_result(nullptr)
269 , m_strTestResult(nullptr)
270 , m_failedSetTrace(nullptr)
274 template<typename... Srcs>
275 SSATmp* HhbcTranslator::VectorTranslator::genStk(Opcode opc, IRTrace* taken,
276 Srcs... srcs) {
277 assert(opcodeHasFlags(opc, HasStackVersion));
278 assert(!opcodeHasFlags(opc, ModifiesStack));
279 std::vector<SSATmp*> srcVec({srcs...});
280 SSATmp* base = srcVec[vectorBaseIdx(opc)];
282 /* If the base is a pointer to a stack cell and the operation might change
283 * its type and/or value, use the version of the opcode that returns a new
284 * StkPtr. */
285 if (base->inst()->op() == LdStackAddr) {
286 VectorEffects ve(opc, srcVec);
287 if (ve.baseTypeChanged || ve.baseValChanged) {
288 opc = getStackModifyingOpcode(opc);
292 return gen(opc, taken, srcs...);
295 void HhbcTranslator::VectorTranslator::emit() {
296 // Assign stack slots to our stack inputs
297 numberStackInputs();
298 // Emit the base and every intermediate op
299 emitMPre();
300 // Emit the final operation
301 emitFinalMOp();
302 // Cleanup: decref inputs and scratch values
303 emitMPost();
306 // Returns a pointer to the base of the current MInstrState struct, or
307 // a null pointer if it's not needed.
308 SSATmp* HhbcTranslator::VectorTranslator::genMisPtr() {
309 if (m_needMIS) {
310 return gen(LdAddr, m_misBase, cns(kReservedRSPSpillSpace));
311 } else {
312 return gen(DefConst, Type::PtrToCell, ConstData(nullptr));
316 // Inspect the instruction we're about to translate and determine if
317 // it can be executed without using an MInstrState struct.
318 void HhbcTranslator::VectorTranslator::checkMIState() {
319 auto const& baseRtt = m_ni.inputs[m_mii.valCount()]->rtt;
320 Type baseType = Type::fromRuntimeType(baseRtt);
321 const bool isCGetM = m_ni.mInstrOp() == OpCGetM;
322 const bool isSetM = m_ni.mInstrOp() == OpSetM;
323 const bool isIssetM = m_ni.mInstrOp() == OpIssetM;
324 const bool isUnsetM = m_ni.mInstrOp() == OpUnsetM;
325 const bool isSingle = m_ni.immVecM.size() == 1;
327 assert(baseType.isBoxed() || baseType.notBoxed());
328 baseType = baseType.unbox();
330 // CGetM or SetM with no unknown property offsets
331 const bool simpleProp = !mInstrHasUnknownOffsets(m_ni, contextClass()) &&
332 (isCGetM || isSetM);
334 // SetM with only one element
335 const bool singlePropSet = isSingle && isSetM &&
336 mcodeMaybePropName(m_ni.immVecM[0]);
338 // Array access with one element in the vector
339 const bool singleElem = isSingle && mcodeMaybeArrayOrMapKey(m_ni.immVecM[0]);
341 // SetM with one vector array element
342 const bool simpleArraySet = isSetM && singleElem;
344 // IssetM with one vector array element and an Arr base
345 const bool simpleArrayIsset = isIssetM && singleElem &&
346 baseType.subtypeOf(Type::Arr);
347 // IssetM with one vector array element and a collection type
348 const bool simpleCollectionIsset = isIssetM && singleElem &&
349 baseType.strictSubtypeOf(Type::Obj) &&
350 isOptimizableCollectionClass(baseType.getClass());
352 // UnsetM on an array with one vector element
353 const bool simpleArrayUnset = isUnsetM && singleElem;
355 // UnsetM on a non-standard base. Always a noop or fatal.
356 const bool badUnset = isUnsetM && baseType.not(Type::Arr | Type::Obj);
358 // CGetM on an array with a base that won't use MInstrState. Str
359 // will use tvScratch and Obj will fatal or use tvRef.
360 const bool simpleArrayGet = isCGetM && singleElem &&
361 baseType.not(Type::Str | Type::Obj);
362 const bool simpleCollectionGet = isCGetM && singleElem &&
363 baseType.strictSubtypeOf(Type::Obj) &&
364 isOptimizableCollectionClass(baseType.getClass());
366 if (simpleProp || singlePropSet ||
367 simpleArraySet || simpleArrayGet || simpleCollectionGet ||
368 simpleArrayUnset || badUnset || simpleCollectionIsset ||
369 simpleArrayIsset) {
370 setNoMIState();
374 void HhbcTranslator::VectorTranslator::emitMPre() {
375 checkMIState();
377 if (HPHP::Trace::moduleEnabled(HPHP::Trace::minstr, 1)) {
378 emitMTrace();
381 if (m_needMIS) {
382 m_misBase = gen(DefMIStateBase);
383 SSATmp* uninit = m_tb.genDefUninit();
385 if (nLogicalRatchets() > 0) {
386 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef)), uninit);
387 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef2)), uninit);
391 // The base location is input 0 or 1, and the location code is stored
392 // separately from m_ni.immVecM, so input indices (iInd) and member indices
393 // (mInd) commonly differ. Additionally, W members have no corresponding
394 // inputs, so it is necessary to track the two indices separately.
395 m_iInd = m_mii.valCount();
396 emitBaseOp();
397 ++m_iInd;
399 // Iterate over all but the last member, which is consumed by a final
400 // operation.
401 for (m_mInd = 0; m_mInd < m_ni.immVecM.size() - 1; ++m_mInd) {
402 emitIntermediateOp();
403 emitRatchetRefs();
407 void HhbcTranslator::VectorTranslator::emitMTrace() {
408 auto rttStr = [this](int i) {
409 return Type::fromRuntimeType(m_ni.inputs[i]->rtt).unbox().toString();
411 std::ostringstream shape;
412 int iInd = m_mii.valCount();
413 const char* separator = "";
415 shape << opcodeToName(m_ni.mInstrOp()) << " <";
416 auto baseLoc = m_ni.immVec.locationCode();
417 shape << folly::format("{}:{} ", locationCodeString(baseLoc), rttStr(iInd));
418 ++iInd;
420 for (int mInd = 0; mInd < m_ni.immVecM.size(); ++mInd) {
421 auto mcode = m_ni.immVecM[mInd];
422 shape << separator;
423 if (mcode == MW) {
424 shape << "MW";
425 } else if (mcodeMaybeArrayOrMapKey(mcode)) {
426 shape << "ME:" << rttStr(iInd);
427 } else if (mcodeMaybePropName(mcode)) {
428 shape << "MP:" << rttStr(iInd);
429 } else {
430 not_reached();
432 if (mcode != MW) ++iInd;
433 separator = " ";
435 shape << '>';
436 gen(IncStatGrouped,
437 cns(StringData::GetStaticString("vector instructions")),
438 cns(StringData::GetStaticString(shape.str())),
439 cns(1));
442 // Build a map from (stack) input index to stack index.
443 void HhbcTranslator::VectorTranslator::numberStackInputs() {
444 // Stack inputs are pushed in the order they appear in the vector
445 // from left to right, so earlier elements in the vector are at
446 // higher offsets in the stack. m_mii.valCount() tells us how many
447 // rvals the instruction takes on the stack; they're pushed after
448 // any vector elements and we want to ignore them here.
449 bool stackRhs = m_mii.valCount() &&
450 m_ni.inputs[0]->location.space == Location::Stack;
451 int stackIdx = (int)stackRhs + m_ni.immVec.numStackValues() - 1;
452 for (unsigned i = m_mii.valCount(); i < m_ni.inputs.size(); ++i) {
453 const Location& l = m_ni.inputs[i]->location;
454 switch (l.space) {
455 case Location::Stack:
456 assert(stackIdx >= 0);
457 m_stackInputs[i] = stackIdx--;
458 break;
460 default:
461 break;
464 assert(stackIdx == (stackRhs ? 0 : -1));
466 if (stackRhs) {
467 // If this instruction does have an RHS, it will be input 0 at
468 // stack offset 0.
469 assert(m_mii.valCount() == 1);
470 m_stackInputs[0] = 0;
474 SSATmp* HhbcTranslator::VectorTranslator::getBase() {
475 assert(m_iInd == m_mii.valCount());
476 return getInput(m_iInd);
479 SSATmp* HhbcTranslator::VectorTranslator::getKey() {
480 SSATmp* key = getInput(m_iInd);
481 auto keyType = key->type();
482 assert(keyType.isBoxed() || keyType.notBoxed());
483 if (keyType.isBoxed()) {
484 key = gen(LdRef, Type::Cell, key);
486 return key;
489 SSATmp* HhbcTranslator::VectorTranslator::getValue() {
490 // If an instruction takes an rhs, it's always input 0.
491 assert(m_mii.valCount() == 1);
492 const int kValIdx = 0;
493 return getInput(kValIdx);
496 SSATmp* HhbcTranslator::VectorTranslator::getValAddr() {
497 assert(m_mii.valCount() == 1);
498 const DynLocation& dl = *m_ni.inputs[0];
499 const Location& l = dl.location;
500 if (l.space == Location::Local) {
501 assert(!mapContains(m_stackInputs, 0));
502 return m_ht.ldLocAddr(l.offset);
503 } else {
504 assert(l.space == Location::Stack);
505 assert(mapContains(m_stackInputs, 0));
506 m_ht.spillStack();
507 return m_ht.ldStackAddr(m_stackInputs[0]);
511 SSATmp* HhbcTranslator::VectorTranslator::getInput(unsigned i) {
512 const DynLocation& dl = *m_ni.inputs[i];
513 const Location& l = dl.location;
515 assert(mapContains(m_stackInputs, i) == (l.space == Location::Stack));
516 switch (l.space) {
517 case Location::Stack: {
518 SSATmp* val = m_ht.top(Type::Gen | Type::Cls, m_stackInputs[i]);
519 // Check if the type on our eval stack is at least as specific as what
520 // Transl::Translator came up with. We allow boxed types with differing
521 // inner types because of the different ways the two systems deal with
522 // reference types.
523 auto t = Type::fromRuntimeType(dl.rtt);
524 if (!val->isA(t) && !(val->isBoxed() && t.isBoxed())) {
525 FTRACE(1, "{}: hhir stack has a {} where Translator had a {}\n",
526 __func__, val->type().toString(), t.toString());
527 // Normally here we would make sure that Translator gave us a sensical
528 // type, but since we could be getting inputs from type-predicted
529 // instructions, we can't actually be sure that the types are going to
530 // match up. refineType is just going to ignore the Translator type
531 // if the runtime type is completely unrelated.
532 m_ht.refineType(val, t);
534 return val;
537 case Location::Local:
538 return m_ht.ldLoc(l.offset);
540 case Location::Litstr:
541 return cns(m_ht.lookupStringId(l.offset));
543 case Location::Litint:
544 return cns(l.offset);
546 case Location::This:
547 return gen(LdThis, m_tb.fp());
549 default: not_reached();
553 void HhbcTranslator::VectorTranslator::emitBaseLCR() {
554 const MInstrAttr& mia = m_mii.getAttr(m_ni.immVec.locationCode());
555 const DynLocation& base = *m_ni.inputs[m_iInd];
556 auto baseType = Type::fromRuntimeType(base.rtt);
557 assert(baseType.isKnownDataType());
559 if (base.location.isLocal()) {
560 // Check for Uninit and warn/promote to InitNull as appropriate
561 if (baseType.subtypeOf(Type::Uninit)) {
562 if (mia & MIA_warn) {
563 gen(RaiseUninitLoc, getEmptyCatchTrace(),
564 LocalId(base.location.offset));
566 if (mia & MIA_define) {
567 gen(
568 StLoc,
569 LocalId(base.location.offset),
570 m_tb.fp(),
571 m_tb.genDefInitNull()
573 baseType = Type::InitNull;
578 // If the base is a box with a type that's changed, we need to bail out of
579 // the tracelet and retranslate. Doing an exit here is a little sketchy since
580 // we may have already emitted instructions with memory effects to initialize
581 // the MInstrState. These particular stores are harmless though, and the
582 // worst outcome here is that we'll end up doing the stores twice, once for
583 // this instruction and once at the beginning of the retranslation.
584 IRTrace* failedRef = baseType.isBoxed() ? m_ht.getExitTrace() : nullptr;
585 if ((baseType.subtypeOfAny(Type::Obj, Type::BoxedObj) &&
586 mcodeMaybePropName(m_ni.immVecM[0])) ||
587 isSimpleCollectionOp() != SimpleOp::None) {
588 // In these cases we can pass the base by value, after unboxing if needed.
589 m_base = gen(Unbox, failedRef, getBase());
590 assert(m_base->isA(baseType.unbox()));
591 } else {
592 // Everything else is passed by reference. We don't have to worry about
593 // unboxing here, since all the generic helpers understand boxed bases.
594 if (baseType.isBoxed()) {
595 SSATmp* box = getBase();
596 assert(box->isA(Type::BoxedCell));
597 // Guard that the inner type hasn't changed
598 gen(LdRef, baseType.innerType(), failedRef, box);
601 if (base.location.space == Location::Local) {
602 m_base = m_ht.ldLocAddr(base.location.offset);
603 } else {
604 assert(base.location.space == Location::Stack);
605 // Make sure the stack is clean before getting a pointer to one of its
606 // elements.
607 m_ht.spillStack();
608 assert(m_stackInputs.count(m_iInd));
609 m_base = m_ht.ldStackAddr(m_stackInputs[m_iInd]);
611 assert(m_base->type().isPtr());
615 // Is the current instruction a 1-element simple collection (includes Array),
616 // operation?
617 HhbcTranslator::VectorTranslator::SimpleOp
618 HhbcTranslator::VectorTranslator::isSimpleCollectionOp() {
619 SSATmp* base = getInput(m_mii.valCount());
620 auto baseType = base->type().unbox();
621 HPHP::Op op = m_ni.mInstrOp();
622 if ((op == OpSetM || op == OpCGetM || op == OpIssetM) &&
623 isSimpleBase() &&
624 isSingleMember()) {
626 if (baseType.subtypeOf(Type::Arr)) {
627 if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
628 SSATmp* key = getInput(m_mii.valCount() + 1);
629 if (key->isA(Type::Int) || key->isA(Type::Str)) {
630 return SimpleOp::Array;
633 } else if (baseType.strictSubtypeOf(Type::Obj)) {
634 const Class* klass = baseType.getClass();
635 if (klass == c_Vector::s_cls ||
636 klass == c_Pair::s_cls) {
637 if (mcodeMaybeVectorKey(m_ni.immVecM[0])) {
638 SSATmp* key = getInput(m_mii.valCount() + 1);
639 if (key->isA(Type::Int)) {
640 return (klass == c_Vector::s_cls) ?
641 SimpleOp::Vector : SimpleOp::Pair;
644 } else if (klass == c_Map::s_cls ||
645 klass == c_StableMap::s_cls) {
646 if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
647 SSATmp* key = getInput(m_mii.valCount() + 1);
648 if (key->isA(Type::Int) || key->isA(Type::Str)) {
649 return (klass == c_Map::s_cls) ?
650 SimpleOp::Map : SimpleOp::StableMap;
656 return SimpleOp::None;
659 // "Simple" bases are stack cells and locals.
660 bool HhbcTranslator::VectorTranslator::isSimpleBase() {
661 LocationCode loc = m_ni.immVec.locationCode();
662 return loc == LL || loc == LC || loc == LR;
665 bool HhbcTranslator::VectorTranslator::isSingleMember() {
666 return m_ni.immVecM.size() == 1;
669 void HhbcTranslator::VectorTranslator::emitBaseH() {
670 m_base = gen(LdThis, m_tb.fp());
673 void HhbcTranslator::VectorTranslator::emitBaseN() {
674 PUNT(emitBaseN);
677 template <bool warn, bool define>
678 static inline TypedValue* baseGImpl(TypedValue *key,
679 MInstrState* mis) {
680 TypedValue* base;
681 StringData* name = prepareKey(key);
682 VarEnv* varEnv = g_vmContext->m_globalVarEnv;
683 assert(varEnv != NULL);
684 base = varEnv->lookup(name);
685 if (base == NULL) {
686 if (warn) {
687 raise_notice(Strings::UNDEFINED_VARIABLE, name->data());
689 if (define) {
690 TypedValue tv;
691 tvWriteNull(&tv);
692 varEnv->set(name, &tv);
693 base = varEnv->lookup(name);
694 } else {
695 return const_cast<TypedValue*>(init_null_variant.asTypedValue());
698 decRefStr(name);
699 if (base->m_type == KindOfRef) {
700 base = base->m_data.pref->tv();
702 return base;
705 namespace VectorHelpers {
706 TypedValue* baseG(TypedValue key, MInstrState* mis) {
707 return baseGImpl<false, false>(&key, mis);
710 TypedValue* baseGW(TypedValue key, MInstrState* mis) {
711 return baseGImpl<true, false>(&key, mis);
714 TypedValue* baseGD(TypedValue key, MInstrState* mis) {
715 return baseGImpl<false, true>(&key, mis);
718 TypedValue* baseGWD(TypedValue key, MInstrState* mis) {
719 return baseGImpl<true, true>(&key, mis);
723 void HhbcTranslator::VectorTranslator::emitBaseG() {
724 const MInstrAttr& mia = m_mii.getAttr(m_ni.immVec.locationCode());
725 typedef TypedValue* (*OpFunc)(TypedValue, MInstrState*);
726 using namespace VectorHelpers;
727 static const OpFunc opFuncs[] = {baseG, baseGW, baseGD, baseGWD};
728 OpFunc opFunc = opFuncs[mia & MIA_base];
729 SSATmp* gblName = getBase();
730 m_base = gen(BaseG,
731 getEmptyCatchTrace(),
732 cns(reinterpret_cast<TCA>(opFunc)),
733 gblName,
734 genMisPtr());
737 void HhbcTranslator::VectorTranslator::emitBaseS() {
738 const int kClassIdx = m_ni.inputs.size() - 1;
739 SSATmp* key = getKey();
740 SSATmp* clsRef = getInput(kClassIdx);
741 m_base = gen(LdClsPropAddr,
742 clsRef,
743 key,
744 CTX());
747 void HhbcTranslator::VectorTranslator::emitBaseOp() {
748 LocationCode lCode = m_ni.immVec.locationCode();
749 switch (lCode) {
750 case LL: case LC: case LR: emitBaseLCR(); break;
751 case LH: emitBaseH(); break;
752 case LGL: case LGC: emitBaseG(); break;
753 case LNL: case LNC: emitBaseN(); break;
754 case LSL: case LSC: emitBaseS(); break;
755 default: not_reached();
759 void HhbcTranslator::VectorTranslator::emitIntermediateOp() {
760 switch (m_ni.immVecM[m_mInd]) {
761 case MEC: case MEL: case MET: case MEI: {
762 emitElem();
763 ++m_iInd;
764 break;
766 case MPC: case MPL: case MPT:
767 emitProp();
768 ++m_iInd;
769 break;
770 case MW:
771 assert(m_mii.newElem());
772 emitNewElem();
773 break;
774 default: not_reached();
779 void HhbcTranslator::VectorTranslator::emitProp() {
780 const Class* knownCls = nullptr;
781 const auto propInfo = getPropertyOffset(m_ni, contextClass(),
782 knownCls, m_mii,
783 m_mInd, m_iInd);
784 auto mia = m_mii.getAttr(m_ni.immVecM[m_mInd]);
785 if (propInfo.offset == -1 || (mia & Unset)) {
786 emitPropGeneric();
787 } else {
788 emitPropSpecialized(mia, propInfo);
792 template <MInstrAttr attrs, bool isObj>
793 static inline TypedValue* propImpl(Class* ctx, TypedValue* base,
794 TypedValue keyVal, MInstrState* mis) {
795 return Prop<WDU(attrs), isObj>(
796 mis->tvScratch, mis->tvRef, ctx, base, &keyVal);
799 #define HELPER_TABLE(m) \
800 /* name attrs isObj */ \
801 m(propC, None, false) \
802 m(propCD, Define, false) \
803 m(propCDO, Define, true) \
804 m(propCO, None, true) \
805 m(propCU, Unset, false) \
806 m(propCUO, Unset, true) \
807 m(propCW, Warn, false) \
808 m(propCWD, WarnDefine, false) \
809 m(propCWDO, WarnDefine, true) \
810 m(propCWO, Warn, true)
812 #define PROP(nm, ...) \
813 TypedValue* nm(Class* ctx, TypedValue* base, TypedValue key, \
814 MInstrState* mis) { \
815 return propImpl<__VA_ARGS__>(ctx, base, key, mis); \
817 namespace VectorHelpers {
818 HELPER_TABLE(PROP)
820 #undef PROP
822 void HhbcTranslator::VectorTranslator::emitPropGeneric() {
823 MemberCode mCode = m_ni.immVecM[m_mInd];
824 MInstrAttr mia = MInstrAttr(m_mii.getAttr(mCode) & MIA_intermediate_prop);
826 if ((mia & Unset) && m_base->type().strip().not(Type::Obj)) {
827 m_base = m_tb.genPtrToInitNull();
828 return;
831 typedef TypedValue* (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
832 SSATmp* key = getKey();
833 BUILD_OPTAB(mia, m_base->isA(Type::Obj));
834 if (mia & Define) {
835 m_base = genStk(PropDX, getCatchTrace(), cns((TCA)opFunc), CTX(),
836 m_base, key, genMisPtr());
837 } else {
838 m_base = gen(PropX, getCatchTrace(),
839 cns((TCA)opFunc), CTX(), m_base, key, genMisPtr());
842 #undef HELPER_TABLE
845 * Helper for emitPropSpecialized to check if a property is Uninit. It
846 * returns a pointer to the property's address, or init_null_variant
847 * if the property was Uninit and doDefine is false.
849 * We can omit the uninit check for properties that we know may not be
850 * uninit due to the frontend's type inference.
852 SSATmp* HhbcTranslator::VectorTranslator::checkInitProp(
853 SSATmp* baseAsObj,
854 SSATmp* propAddr,
855 PropInfo propInfo,
856 bool doWarn,
857 bool doDefine) {
858 SSATmp* key = getKey();
859 assert(key->isA(Type::StaticStr));
860 assert(baseAsObj->isA(Type::Obj));
861 assert(propAddr->type().isPtr());
863 auto const needsCheck =
864 propInfo.hphpcType == KindOfInvalid &&
865 // The m_mInd check is to avoid initializing a property to
866 // InitNull right before it's going to be set to something else.
867 (doWarn || (doDefine && m_mInd < m_ni.immVecM.size() - 1));
869 if (!needsCheck) return propAddr;
871 return m_tb.cond(m_ht.curFunc(),
872 [&] (Block* taken) {
873 gen(CheckInitMem, taken, propAddr, cns(0));
875 [&] { // Next: Property isn't Uninit. Do nothing.
876 return propAddr;
878 [&] { // Taken: Property is Uninit. Raise a warning and return
879 // a pointer to InitNull, either in the object or
880 // init_null_variant.
881 m_tb.hint(Block::Hint::Unlikely);
882 if (doWarn && wantPropSpecializedWarnings()) {
883 gen(RaiseUndefProp, m_ht.getCatchTrace(), baseAsObj, key);
885 if (doDefine) {
886 gen(
887 StProp,
888 baseAsObj,
889 cns(propInfo.offset),
890 m_tb.genDefInitNull()
892 return propAddr;
894 return cns((const TypedValue*)&init_null_variant);
899 Class* HhbcTranslator::VectorTranslator::contextClass() const {
900 return m_ht.curFunc()->cls();
903 void HhbcTranslator::VectorTranslator::emitPropSpecialized(const MInstrAttr mia,
904 PropInfo propInfo) {
905 assert(!(mia & MIA_warn) || !(mia & MIA_unset));
906 const bool doWarn = mia & MIA_warn;
907 const bool doDefine = mia & MIA_define || mia & MIA_unset;
909 SSATmp* initNull = cns((const TypedValue*)&init_null_variant);
912 * Type-inference from hphpc only tells us that this is either an object of a
913 * given class type or null. If it's not an object, it has to be a null type
914 * based on type inference. (It could be KindOfRef with an object inside,
915 * except that this isn't inferred for object properties so we're fine not
916 * checking KindOfRef in that case.)
918 * On the other hand, if m_base->isA(Type::Obj), we're operating on the base
919 * which was already guarded by tracelet guards (and may have been KindOfRef,
920 * but the Base* op already handled this). So we only need to do a type
921 * check against null here in the intermediate cases.
923 if (m_base->isA(Type::Obj)) {
924 SSATmp* propAddr = gen(LdPropAddr, m_base, cns(propInfo.offset));
925 m_base = checkInitProp(m_base, propAddr, propInfo, doWarn, doDefine);
926 } else {
927 SSATmp* baseAsObj = nullptr;
928 m_base = m_tb.cond(m_ht.curFunc(),
929 [&] (Block* taken) {
930 // baseAsObj is only available in the Next branch
931 baseAsObj = gen(LdMem, Type::Obj, taken, m_base, cns(0));
933 [&] { // Next: Base is an object. Load property address and
934 // check for uninit
935 return checkInitProp(baseAsObj,
936 gen(LdPropAddr, baseAsObj,
937 cns(propInfo.offset)),
938 propInfo,
939 doWarn,
940 doDefine);
942 [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull.
943 m_tb.hint(Block::Hint::Unlikely);
944 if (doWarn) {
945 gen(WarnNonObjProp);
947 if (doDefine) {
949 * NOTE:
951 * This case logically is supposed to do a stdClass promotion. It
952 * should ideally not be possible (since we have a known class type),
953 * except that the static compiler doesn't correctly infer object
954 * class types in some edge cases involving stdClass promotion.
956 * This is impossible to handle "correctly" if we're in the middle of
957 * a multi-dim property expression, because things further along may
958 * also have type inference telling them that object properties are
959 * at a given slot, but the object could actually be a stdClass
960 * instead of the knownCls type if we were to promote here.
962 * So, we throw a fatal error, which is what hphpc's generated C++
963 * would do in this case too.
965 * Relevant TODOs:
966 * #1789661 (this can cause bugs if bytecode.cpp promotes)
967 * #1124706 (we want to get rid of stdClass promotion in general)
969 gen(ThrowNonObjProp);
971 return initNull;
976 // At this point m_base is either a pointer to init_null_variant or
977 // a property in the object that we've verified isn't uninit.
978 assert(m_base->type().isPtr());
981 template <KeyType keyType, bool warn, bool define, bool reffy,
982 bool unset>
983 static inline TypedValue* elemImpl(TypedValue* base, TypedValue keyVal,
984 MInstrState* mis) {
985 TypedValue* key = keyPtr<keyType>(keyVal);
986 if (unset) {
987 return ElemU<keyType>(mis->tvScratch, mis->tvRef, base, key);
988 } else if (define) {
989 return ElemD<warn, reffy, keyType>(mis->tvScratch, mis->tvRef, base, key);
990 } else {
991 return Elem<warn, keyType>(mis->tvScratch, mis->tvRef, base, key);
995 #define HELPER_TABLE(m) \
996 /* name hot keyType attrs */ \
997 m(elemC, , KeyType::Any, None) \
998 m(elemCD, , KeyType::Any, Define) \
999 m(elemCDR, , KeyType::Any, DefineReffy) \
1000 m(elemCU, , KeyType::Any, Unset) \
1001 m(elemCW, , KeyType::Any, Warn) \
1002 m(elemCWD, , KeyType::Any, WarnDefine) \
1003 m(elemCWDR, , KeyType::Any, WarnDefineReffy) \
1004 m(elemI, , KeyType::Int, None) \
1005 m(elemID, HOT_FUNC_VM, KeyType::Int, Define) \
1006 m(elemIDR, , KeyType::Int, DefineReffy) \
1007 m(elemIU, , KeyType::Int, Unset) \
1008 m(elemIW, , KeyType::Int, Warn) \
1009 m(elemIWD, , KeyType::Int, WarnDefine) \
1010 m(elemIWDR, , KeyType::Int, WarnDefineReffy) \
1011 m(elemS, HOT_FUNC_VM, KeyType::Str, None) \
1012 m(elemSD, HOT_FUNC_VM, KeyType::Str, Define) \
1013 m(elemSDR, , KeyType::Str, DefineReffy) \
1014 m(elemSU, , KeyType::Str, Unset) \
1015 m(elemSW, HOT_FUNC_VM, KeyType::Str, Warn) \
1016 m(elemSWD, , KeyType::Str, WarnDefine) \
1017 m(elemSWDR, , KeyType::Str, WarnDefineReffy)
1019 #define ELEM(nm, hot, keyType, attrs) \
1020 hot \
1021 TypedValue* nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1022 return elemImpl<keyType, WDRU(attrs)>(base, key, mis); \
1024 namespace VectorHelpers {
1025 HELPER_TABLE(ELEM)
1027 #undef ELEM
1029 void HhbcTranslator::VectorTranslator::emitElem() {
1030 MemberCode mCode = m_ni.immVecM[m_mInd];
1031 MInstrAttr mia = MInstrAttr(m_mii.getAttr(mCode) & MIA_intermediate);
1032 SSATmp* key = getKey();
1034 const bool unset = mia & Unset;
1035 const bool define = mia & Define;
1036 assert(!(define && unset));
1037 if (unset) {
1038 SSATmp* uninit = m_tb.genPtrToUninit();
1039 Type baseType = m_base->type().strip();
1040 if (baseType.subtypeOf(Type::Str)) {
1041 m_ht.exceptionBarrier();
1042 gen(
1043 RaiseError,
1044 cns(StringData::GetStaticString(Strings::OP_NOT_SUPPORTED_STRING))
1046 m_base = uninit;
1047 return;
1049 if (baseType.not(Type::Arr | Type::Obj)) {
1050 m_base = uninit;
1051 return;
1055 typedef TypedValue* (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1056 BUILD_OPTAB_HOT(getKeyTypeIS(key), mia);
1057 if (define || unset) {
1058 m_base = genStk(define ? ElemDX : ElemUX, getCatchTrace(),
1059 cns((TCA)opFunc), m_base, key, genMisPtr());
1060 } else {
1061 m_base = gen(ElemX, getCatchTrace(),
1062 cns((TCA)opFunc), m_base, key, genMisPtr());
1065 #undef HELPER_TABLE
1067 void HhbcTranslator::VectorTranslator::emitNewElem() {
1068 PUNT(emitNewElem);
1071 void HhbcTranslator::VectorTranslator::emitRatchetRefs() {
1072 if (ratchetInd() < 0 || ratchetInd() >= int(nLogicalRatchets())) {
1073 return;
1076 m_base = m_tb.cond(m_ht.curFunc(),
1077 [&] (Block* taken) {
1078 gen(CheckInitMem, taken, m_misBase, cns(HHIR_MISOFF(tvRef)));
1080 [&] { // Next: tvRef isn't Uninit. Ratchet the refs
1081 // Clean up tvRef2 before overwriting it.
1082 if (ratchetInd() > 0) {
1083 gen(DecRefMem, Type::Gen, m_misBase, cns(HHIR_MISOFF(tvRef2)));
1085 // Copy tvRef to tvRef2. Use mmx at some point
1086 SSATmp* tvRef = gen(
1087 LdMem, Type::Gen, m_misBase, cns(HHIR_MISOFF(tvRef))
1089 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef2)), tvRef);
1091 // Reset tvRef.
1092 gen(StMem, m_misBase, cns(HHIR_MISOFF(tvRef)), m_tb.genDefUninit());
1094 // Adjust base pointer.
1095 assert(m_base->type().isPtr());
1096 return gen(LdAddr, m_misBase, cns(HHIR_MISOFF(tvRef2)));
1098 [&] { // Taken: tvRef is Uninit. Do nothing.
1099 return m_base;
1104 void HhbcTranslator::VectorTranslator::emitFinalMOp() {
1105 typedef void (HhbcTranslator::VectorTranslator::*MemFun)();
1107 switch (m_ni.immVecM[m_mInd]) {
1108 case MEC: case MEL: case MET: case MEI:
1109 static MemFun elemOps[] = {
1110 # define MII(instr, ...) &HhbcTranslator::VectorTranslator::emit##instr##Elem,
1111 MINSTRS
1112 # undef MII
1114 (this->*elemOps[m_mii.instr()])();
1115 break;
1117 case MPC: case MPL: case MPT:
1118 static MemFun propOps[] = {
1119 # define MII(instr, ...) &HhbcTranslator::VectorTranslator::emit##instr##Prop,
1120 MINSTRS
1121 # undef MII
1123 (this->*propOps[m_mii.instr()])();
1124 break;
1126 case MW:
1127 assert(m_mii.getAttr(MW) & MIA_final);
1128 static MemFun newOps[] = {
1129 # define MII(instr, attrs, bS, iS, vC, fN) \
1130 &HhbcTranslator::VectorTranslator::emit##fN,
1131 MINSTRS
1132 # undef MII
1134 (this->*newOps[m_mii.instr()])();
1135 break;
1137 default: not_reached();
1141 template <KeyType keyType, bool isObj>
1142 static inline TypedValue cGetPropImpl(Class* ctx, TypedValue* base,
1143 TypedValue keyVal, MInstrState* mis) {
1144 TypedValue* key = keyPtr<keyType>(keyVal);
1145 TypedValue scratch;
1146 TypedValue* result = Prop<true, false, false, isObj, keyType>(
1147 scratch, mis->tvRef, ctx, base, key);
1149 if (result->m_type == KindOfRef) {
1150 result = result->m_data.pref->tv();
1152 tvRefcountedIncRef(result);
1153 return *result;
1156 #define HELPER_TABLE(m) \
1157 /* name hot keyType isObj */ \
1158 m(cGetPropC, , KeyType::Any, false) \
1159 m(cGetPropCO, , KeyType::Any, true) \
1160 m(cGetPropS, , KeyType::Str, false) \
1161 m(cGetPropSO, HOT_FUNC_VM, KeyType::Str, true)
1163 #define PROP(nm, hot, ...) \
1164 hot \
1165 TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
1166 MInstrState* mis) { \
1167 return cGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1169 namespace VectorHelpers {
1170 HELPER_TABLE(PROP)
1172 #undef PROP
1174 void HhbcTranslator::VectorTranslator::emitCGetProp() {
1175 assert(!m_ni.outLocal);
1177 const Class* knownCls = nullptr;
1178 const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls,
1179 m_mii, m_mInd, m_iInd);
1180 if (propInfo.offset != -1) {
1181 emitPropSpecialized(MIA_warn, propInfo);
1182 SSATmp* cellPtr = gen(UnboxPtr, m_base);
1183 SSATmp* propVal = gen(LdMem, Type::Cell, cellPtr, cns(0));
1184 m_result = gen(IncRef, propVal);
1185 return;
1188 typedef TypedValue (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
1189 SSATmp* key = getKey();
1190 BUILD_OPTAB_HOT(getKeyTypeS(key), m_base->isA(Type::Obj));
1191 m_result = gen(CGetProp, getCatchTrace(),
1192 cns((TCA)opFunc), CTX(), m_base, key, genMisPtr());
1194 #undef HELPER_TABLE
1196 template <KeyType keyType, bool isObj>
1197 static inline RefData* vGetPropImpl(Class* ctx, TypedValue* base,
1198 TypedValue keyVal, MInstrState* mis) {
1199 TypedValue* key = keyPtr<keyType>(keyVal);
1200 TypedValue* result = HPHP::Prop<false, true, false, isObj, keyType>(
1201 mis->tvScratch, mis->tvRef, ctx, base, key);
1203 if (result->m_type != KindOfRef) {
1204 tvBox(result);
1206 RefData* ref = result->m_data.pref;
1207 ref->incRefCount();
1208 return ref;
1211 #define HELPER_TABLE(m) \
1212 /* name hot keyType isObj */\
1213 m(vGetPropC, , KeyType::Any, false) \
1214 m(vGetPropCO, , KeyType::Any, true) \
1215 m(vGetPropS, , KeyType::Str, false) \
1216 m(vGetPropSO, HOT_FUNC_VM, KeyType::Str, true)
1218 #define PROP(nm, hot, ...) \
1219 hot \
1220 RefData* nm(Class* ctx, TypedValue* base, TypedValue key, \
1221 MInstrState* mis) { \
1222 return vGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1224 namespace VectorHelpers {
1225 HELPER_TABLE(PROP)
1227 #undef PROP
1229 void HhbcTranslator::VectorTranslator::emitVGetProp() {
1230 SSATmp* key = getKey();
1231 typedef RefData* (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
1232 BUILD_OPTAB_HOT(getKeyTypeS(key), m_base->isA(Type::Obj));
1233 m_result = genStk(VGetProp, getCatchTrace(), cns((TCA)opFunc), CTX(),
1234 m_base, key, genMisPtr());
1236 #undef HELPER_TABLE
1238 template <bool useEmpty, bool isObj>
1239 static inline bool issetEmptyPropImpl(Class* ctx, TypedValue* base,
1240 TypedValue keyVal) {
1241 return HPHP::IssetEmptyProp<useEmpty, isObj>(ctx, base, &keyVal);
1244 #define HELPER_TABLE(m) \
1245 /* name useEmpty isObj */ \
1246 m(issetPropC, false, false) \
1247 m(issetPropCE, true, false) \
1248 m(issetPropCEO, true, true) \
1249 m(issetPropCO, false, true)
1251 #define ISSET(nm, ...) \
1252 /* This returns int64_t to ensure all 64 bits of rax are valid */ \
1253 uint64_t nm(Class* ctx, TypedValue* base, TypedValue key) { \
1254 return issetEmptyPropImpl<__VA_ARGS__>(ctx, base, key); \
1256 namespace VectorHelpers {
1257 HELPER_TABLE(ISSET)
1259 #undef ISSET
1261 void HhbcTranslator::VectorTranslator::emitIssetEmptyProp(bool isEmpty) {
1262 SSATmp* key = getKey();
1263 typedef uint64_t (*OpFunc)(Class*, TypedValue*, TypedValue);
1264 BUILD_OPTAB(isEmpty, m_base->isA(Type::Obj));
1265 m_result = gen(isEmpty ? EmptyProp : IssetProp, getCatchTrace(),
1266 cns((TCA)opFunc), CTX(), m_base, key);
1268 #undef HELPER_TABLE
1270 void HhbcTranslator::VectorTranslator::emitIssetProp() {
1271 emitIssetEmptyProp(false);
1274 void HhbcTranslator::VectorTranslator::emitEmptyProp() {
1275 emitIssetEmptyProp(true);
1278 template <bool isObj>
1279 static inline void setPropImpl(Class* ctx, TypedValue* base,
1280 TypedValue keyVal, Cell val) {
1281 HPHP::SetProp<false, isObj>(ctx, base, &keyVal, &val);
1284 #define HELPER_TABLE(m) \
1285 /* name isObj */ \
1286 m(setPropC, false) \
1287 m(setPropCO, true)
1289 #define PROP(nm, ...) \
1290 void nm(Class* ctx, TypedValue* base, TypedValue key, Cell val) { \
1291 setPropImpl<__VA_ARGS__>(ctx, base, key, val); \
1293 namespace VectorHelpers {
1294 HELPER_TABLE(PROP)
1296 #undef PROP
1298 void HhbcTranslator::VectorTranslator::emitSetProp() {
1299 SSATmp* value = getValue();
1301 /* If we know the class for the current base, emit a direct property set. */
1302 const Class* knownCls = nullptr;
1303 const auto propInfo = getPropertyOffset(m_ni, contextClass(), knownCls,
1304 m_mii, m_mInd, m_iInd);
1305 if (propInfo.offset != -1) {
1306 emitPropSpecialized(MIA_define, propInfo);
1307 SSATmp* cellPtr = gen(UnboxPtr, m_base);
1308 SSATmp* oldVal = gen(LdMem, Type::Cell, cellPtr, cns(0));
1309 // The object owns a reference now
1310 SSATmp* increffed = gen(IncRef, value);
1311 gen(StMem, cellPtr, cns(0), value);
1312 gen(DecRef, oldVal);
1313 m_result = increffed;
1314 return;
1317 // Emit the appropriate helper call.
1318 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue, Cell);
1319 SSATmp* key = getKey();
1320 BUILD_OPTAB(m_base->isA(Type::Obj));
1321 genStk(SetProp, getCatchSetTrace(), cns((TCA)opFunc), CTX(),
1322 m_base, key, value);
1323 m_result = value;
1325 #undef HELPER_TABLE
1327 template <bool isObj>
1328 static inline TypedValue setOpPropImpl(TypedValue* base, TypedValue keyVal,
1329 Cell val, MInstrState* mis, SetOpOp op) {
1330 TypedValue* result = HPHP::SetOpProp<isObj>(
1331 mis->tvScratch, mis->tvRef, mis->ctx, op, base, &keyVal, &val);
1333 TypedValue ret;
1334 tvReadCell(result, &ret);
1335 return ret;
1338 #define HELPER_TABLE(m) \
1339 /* name isObj */ \
1340 m(setOpPropC, false) \
1341 m(setOpPropCO, true)
1343 #define SETOP(nm, ...) \
1344 TypedValue nm(TypedValue* base, TypedValue key, \
1345 Cell val, MInstrState* mis, SetOpOp op) { \
1346 return setOpPropImpl<__VA_ARGS__>(base, key, val, mis, op); \
1348 namespace VectorHelpers {
1349 HELPER_TABLE(SETOP)
1351 #undef SETOP
1353 void HhbcTranslator::VectorTranslator::emitSetOpProp() {
1354 SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
1355 SSATmp* key = getKey();
1356 SSATmp* value = getValue();
1357 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue,
1358 Cell, MInstrState*, SetOpOp);
1359 BUILD_OPTAB(m_base->isA(Type::Obj));
1360 m_tb.gen(StRaw, m_misBase, cns(RawMemSlot::MisCtx), CTX());
1361 m_result =
1362 genStk(SetOpProp, getCatchTrace(), cns((TCA)opFunc),
1363 m_base, key, value, genMisPtr(), cns(op));
1365 #undef HELPER_TABLE
1367 template <bool isObj>
1368 static inline TypedValue incDecPropImpl(TypedValue* base, TypedValue keyVal,
1369 MInstrState* mis, IncDecOp op) {
1370 TypedValue result;
1371 result.m_type = KindOfUninit;
1372 HPHP::IncDecProp<true, isObj>(
1373 mis->tvScratch, mis->tvRef, mis->ctx, op, base, &keyVal, result);
1374 assert(result.m_type != KindOfRef);
1375 return result;
1379 #define HELPER_TABLE(m) \
1380 /* name isObj */ \
1381 m(incDecPropC, false) \
1382 m(incDecPropCO, true)
1384 #define INCDEC(nm, ...) \
1385 TypedValue nm(TypedValue* base, TypedValue key, \
1386 MInstrState* mis, IncDecOp op) { \
1387 return incDecPropImpl<__VA_ARGS__>(base, key, mis, op); \
1389 namespace VectorHelpers {
1390 HELPER_TABLE(INCDEC)
1392 #undef INCDEC
1394 void HhbcTranslator::VectorTranslator::emitIncDecProp() {
1395 IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
1396 SSATmp* key = getKey();
1397 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue,
1398 MInstrState*, IncDecOp);
1399 BUILD_OPTAB(m_base->isA(Type::Obj));
1400 m_tb.gen(StRaw, m_misBase, cns(RawMemSlot::MisCtx), CTX());
1401 m_result =
1402 genStk(IncDecProp, getCatchTrace(), cns((TCA)opFunc),
1403 m_base, key, genMisPtr(), cns(op));
1405 #undef HELPER_TABLE
1407 template <bool isObj>
1408 static inline void bindPropImpl(Class* ctx, TypedValue* base, TypedValue keyVal,
1409 RefData* val, MInstrState* mis) {
1410 TypedValue* prop = HPHP::Prop<false, true, false, isObj>(
1411 mis->tvScratch, mis->tvRef, ctx, base, &keyVal);
1412 if (!(prop == &mis->tvScratch && prop->m_type == KindOfUninit)) {
1413 tvBindRef(val, prop);
1417 #define HELPER_TABLE(m) \
1418 /* name isObj */ \
1419 m(bindPropC, false) \
1420 m(bindPropCO, true)
1422 #define PROP(nm, ...) \
1423 void nm(Class* ctx, TypedValue* base, TypedValue key, \
1424 RefData* val, MInstrState* mis) { \
1425 bindPropImpl<__VA_ARGS__>(ctx, base, key, val, mis); \
1427 namespace VectorHelpers {
1428 HELPER_TABLE(PROP)
1430 #undef PROP
1432 void HhbcTranslator::VectorTranslator::emitBindProp() {
1433 SSATmp* key = getKey();
1434 SSATmp* box = getValue();
1435 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue, RefData*,
1436 MInstrState*);
1437 BUILD_OPTAB(m_base->isA(Type::Obj));
1438 genStk(BindProp, getCatchTrace(), cns((TCA)opFunc), CTX(),
1439 m_base, key, box, genMisPtr());
1440 m_result = box;
1442 #undef HELPER_TABLE
1444 template <bool isObj>
1445 static inline void unsetPropImpl(Class* ctx, TypedValue* base,
1446 TypedValue keyVal) {
1447 HPHP::UnsetProp<isObj>(ctx, base, &keyVal);
1450 #define HELPER_TABLE(m) \
1451 /* name isObj */ \
1452 m(unsetPropC, false) \
1453 m(unsetPropCO, true)
1455 #define PROP(nm, ...) \
1456 static void nm(Class* ctx, TypedValue* base, TypedValue key) { \
1457 unsetPropImpl<__VA_ARGS__>(ctx, base, key); \
1459 namespace VectorHelpers {
1460 HELPER_TABLE(PROP)
1462 #undef PROP
1464 void HhbcTranslator::VectorTranslator::emitUnsetProp() {
1465 SSATmp* key = getKey();
1467 if (m_base->type().strip().not(Type::Obj)) {
1468 // Noop
1469 return;
1472 typedef void (*OpFunc)(Class*, TypedValue*, TypedValue);
1473 BUILD_OPTAB(m_base->isA(Type::Obj));
1474 gen(UnsetProp, cns((TCA)opFunc), CTX(), m_base, key);
1476 #undef HELPER_TABLE
1478 // Keep these error handlers in sync with ArrayData::getNotFound();
1479 NEVER_INLINE
1480 static TypedValue arrayGetNotFound(int64_t k) {
1481 raise_notice("Undefined index: %" PRId64, k);
1482 TypedValue v;
1483 tvWriteNull(&v);
1484 return v;
1487 NEVER_INLINE
1488 static TypedValue arrayGetNotFound(const StringData* k) {
1489 raise_notice("Undefined index: %s", k->data());
1490 TypedValue v;
1491 tvWriteNull(&v);
1492 return v;
1495 static inline TypedValue* checkedGet(ArrayData* a, StringData* key) {
1496 int64_t i;
1497 return UNLIKELY(key->isStrictlyInteger(i)) ? a->nvGet(i) : a->nvGet(key);
1500 static inline TypedValue* checkedGet(ArrayData* a, int64_t key) {
1501 not_reached();
1504 template<KeyType keyType, bool checkForInt>
1505 static inline TypedValue arrayGetImpl(
1506 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key) {
1507 TypedValue* ret = checkForInt ? checkedGet(a, key)
1508 : a->nvGet(key);
1509 if (ret) {
1510 ret = tvToCell(ret);
1511 tvRefcountedIncRef(ret);
1512 return *ret;
1514 return arrayGetNotFound(key);
1517 #define HELPER_TABLE(m) \
1518 /* name hot keyType checkForInt */\
1519 m(arrayGetS, HOT_FUNC_VM, KeyType::Str, false) \
1520 m(arrayGetSi, HOT_FUNC_VM, KeyType::Str, true) \
1521 m(arrayGetI, HOT_FUNC_VM, KeyType::Int, false)
1523 #define ELEM(nm, hot, keyType, checkForInt) \
1524 hot \
1525 TypedValue nm(ArrayData* a, TypedValue* key) { \
1526 return arrayGetImpl<keyType, checkForInt>(a, keyAsRaw<keyType>(key)); \
1528 namespace VectorHelpers {
1529 HELPER_TABLE(ELEM)
1531 #undef ELEM
1533 void HhbcTranslator::VectorTranslator::emitArrayGet(SSATmp* key) {
1534 KeyType keyType;
1535 bool checkForInt;
1536 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1538 typedef TypedValue (*OpFunc)(ArrayData*, TypedValue*);
1539 BUILD_OPTAB_HOT(keyType, checkForInt);
1540 assert(m_base->isA(Type::Arr));
1541 m_result = gen(ArrayGet, cns((TCA)opFunc), m_base, key);
1543 #undef HELPER_TABLE
1545 namespace VectorHelpers {
1546 TypedValue vectorGet(c_Vector* vec, int64_t key) {
1547 TypedValue* ret = vec->at(key);
1548 return *ret;
1552 void HhbcTranslator::VectorTranslator::emitVectorGet(SSATmp* key) {
1553 SSATmp* value = gen(VectorGet, getCatchTrace(),
1554 cns((TCA)VectorHelpers::vectorGet), m_base, key);
1555 m_result = gen(IncRef, value);
1558 namespace VectorHelpers {
1559 TypedValue pairGet(c_Pair* pair, int64_t key) {
1560 TypedValue* ret = pair->at(key);
1561 return *ret;
1565 void HhbcTranslator::VectorTranslator::emitPairGet(SSATmp* key) {
1566 SSATmp* value = gen(PairGet, getCatchTrace(),
1567 cns((TCA)VectorHelpers::pairGet), m_base, key);
1568 m_result = gen(IncRef, value);
1571 template<KeyType keyType>
1572 static inline TypedValue mapGetImpl(
1573 c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
1574 TypedValue* ret = map->at(key);
1575 return *ret;
1578 #define HELPER_TABLE(m) \
1579 /* name hot keyType */ \
1580 m(mapGetS, HOT_FUNC_VM, KeyType::Str) \
1581 m(mapGetI, HOT_FUNC_VM, KeyType::Int)
1583 #define ELEM(nm, hot, keyType) \
1584 hot \
1585 TypedValue nm(c_Map* map, TypedValue* key) { \
1586 return mapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1588 namespace VectorHelpers {
1589 HELPER_TABLE(ELEM)
1591 #undef ELEM
1593 void HhbcTranslator::VectorTranslator::emitMapGet(SSATmp* key) {
1594 assert(key->isA(Type::Int) || key->isA(Type::Str));
1595 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
1597 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
1598 BUILD_OPTAB_HOT(keyType);
1599 SSATmp* value = gen(MapGet, getCatchTrace(),
1600 cns((TCA)opFunc), m_base, key);
1601 m_result = gen(IncRef, value);
1603 #undef HELPER_TABLE
1605 template<KeyType keyType>
1606 static inline TypedValue stableMapGetImpl(
1607 c_StableMap* map, typename KeyTypeTraits<keyType>::rawType key) {
1608 TypedValue* ret = map->at(key);
1609 return *ret;
1612 #define HELPER_TABLE(m) \
1613 /* name hot keyType */ \
1614 m(stableMapGetS, HOT_FUNC_VM, KeyType::Str) \
1615 m(stableMapGetI, HOT_FUNC_VM, KeyType::Int)
1617 #define ELEM(nm, hot, keyType) \
1618 hot \
1619 TypedValue nm(c_StableMap* map, TypedValue* key) { \
1620 return stableMapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1622 namespace VectorHelpers {
1623 HELPER_TABLE(ELEM)
1625 #undef ELEM
1627 void HhbcTranslator::VectorTranslator::emitStableMapGet(SSATmp* key) {
1628 assert(key->isA(Type::Int) || key->isA(Type::Str));
1629 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
1631 typedef TypedValue (*OpFunc)(c_StableMap*, TypedValue*);
1632 BUILD_OPTAB_HOT(keyType);
1633 SSATmp* value = gen(StableMapGet, getCatchTrace(),
1634 cns((TCA)opFunc), m_base, key);
1635 m_result = gen(IncRef, value);
1637 #undef HELPER_TABLE
1639 template <KeyType keyType>
1640 static inline TypedValue cGetElemImpl(TypedValue* base, TypedValue keyVal,
1641 MInstrState* mis) {
1642 TypedValue* key = keyPtr<keyType>(keyVal);
1643 TypedValue scratch;
1644 TypedValue* result = Elem<true, keyType>(scratch, mis->tvRef, base, key);
1646 if (result->m_type == KindOfRef) {
1647 result = result->m_data.pref->tv();
1649 tvRefcountedIncRef(result);
1650 return *result;
1653 #define HELPER_TABLE(m) \
1654 /* name hot key */ \
1655 m(cGetElemC, , KeyType::Any) \
1656 m(cGetElemI, , KeyType::Int) \
1657 m(cGetElemS, HOT_FUNC_VM, KeyType::Str)
1659 #define ELEM(nm, hot, ...) \
1660 hot \
1661 TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1662 return cGetElemImpl<__VA_ARGS__>(base, key, mis); \
1664 namespace VectorHelpers {
1665 HELPER_TABLE(ELEM)
1667 #undef ELEM
1669 void HhbcTranslator::VectorTranslator::emitCGetElem() {
1670 SSATmp* key = getKey();
1672 SimpleOp simpleOpType = isSimpleCollectionOp();
1673 switch (simpleOpType) {
1674 case SimpleOp::Array:
1675 emitArrayGet(key);
1676 break;
1677 case SimpleOp::Vector:
1678 emitVectorGet(key);
1679 break;
1680 case SimpleOp::Pair:
1681 emitPairGet(key);
1682 break;
1683 case SimpleOp::Map:
1684 emitMapGet(key);
1685 break;
1686 case SimpleOp::StableMap:
1687 emitStableMapGet(key);
1688 break;
1689 default:
1690 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1691 BUILD_OPTAB_HOT(getKeyTypeIS(key));
1692 m_result = gen(CGetElem, getCatchTrace(), cns((TCA)opFunc),
1693 m_base, key, genMisPtr());
1694 break;
1697 #undef HELPER_TABLE
1699 template <KeyType keyType>
1700 static inline RefData* vGetElemImpl(TypedValue* base, TypedValue keyVal,
1701 MInstrState* mis) {
1702 TypedValue* key = keyPtr<keyType>(keyVal);
1703 TypedValue* result = HPHP::ElemD<false, true, keyType>(
1704 mis->tvScratch, mis->tvRef, base, key);
1706 if (result->m_type != KindOfRef) {
1707 tvBox(result);
1709 RefData* ref = result->m_data.pref;
1710 ref->incRefCount();
1711 return ref;
1714 #define HELPER_TABLE(m) \
1715 /* name keyType */ \
1716 m(vGetElemC, KeyType::Any) \
1717 m(vGetElemI, KeyType::Int) \
1718 m(vGetElemS, KeyType::Str)
1720 #define ELEM(nm, ...) \
1721 RefData* nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1722 return vGetElemImpl<__VA_ARGS__>(base, key, mis); \
1724 namespace VectorHelpers {
1725 HELPER_TABLE(ELEM)
1727 #undef ELEM
1729 void HhbcTranslator::VectorTranslator::emitVGetElem() {
1730 SSATmp* key = getKey();
1731 typedef RefData* (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1732 BUILD_OPTAB(getKeyTypeIS(key));
1733 m_result = genStk(VGetElem, getCatchTrace(), cns((TCA)opFunc),
1734 m_base, key, genMisPtr());
1736 #undef HELPER_TABLE
1738 template <KeyType keyType, bool isEmpty>
1739 static inline bool issetEmptyElemImpl(TypedValue* base, TypedValue keyVal,
1740 MInstrState* mis) {
1741 TypedValue* key = keyPtr<keyType>(keyVal);
1742 // mis == nullptr if we proved that it won't be used. mis->tvScratch and
1743 // mis->tvRef are ok because those params are passed by
1744 // reference.
1745 return HPHP::IssetEmptyElem<isEmpty, false, keyType>(
1746 mis->tvScratch, mis->tvRef, base, key);
1749 #define HELPER_TABLE(m) \
1750 /* name hot keyType isEmpty */\
1751 m(issetElemC, , KeyType::Any, false) \
1752 m(issetElemCE, , KeyType::Any, true) \
1753 m(issetElemI, HOT_FUNC_VM, KeyType::Int, false) \
1754 m(issetElemIE, , KeyType::Int, true) \
1755 m(issetElemS, HOT_FUNC_VM, KeyType::Str, false) \
1756 m(issetElemSE, , KeyType::Str, true)
1758 #define ISSET(nm, hot, ...) \
1759 hot \
1760 uint64_t nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1761 return issetEmptyElemImpl<__VA_ARGS__>(base, key, mis); \
1763 namespace VectorHelpers {
1764 HELPER_TABLE(ISSET)
1766 #undef ISSET
1768 void HhbcTranslator::VectorTranslator::emitIssetEmptyElem(bool isEmpty) {
1769 SSATmp* key = getKey();
1771 typedef uint64_t (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
1772 BUILD_OPTAB_HOT(getKeyTypeIS(key), isEmpty);
1773 m_result = gen(isEmpty ? EmptyElem : IssetElem, getCatchTrace(),
1774 cns((TCA)opFunc), m_base, key, genMisPtr());
1776 #undef HELPER_TABLE
1778 template<KeyType keyType, bool checkForInt>
1779 static inline uint64_t arrayIssetImpl(
1780 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key) {
1781 TypedValue* value = checkForInt ? checkedGet(a, key)
1782 : a->nvGet(key);
1783 Variant* var = &tvAsVariant(value);
1784 return var && !var->isNull();
1787 #define HELPER_TABLE(m) \
1788 /* name keyType checkForInt */\
1789 m(arrayIssetS, KeyType::Str, false) \
1790 m(arrayIssetSi, KeyType::Str, true) \
1791 m(arrayIssetI, KeyType::Int, false)
1793 #define ISSET(nm, keyType, checkForInt) \
1794 uint64_t nm(ArrayData* a, TypedValue* key) { \
1795 return arrayIssetImpl<keyType, checkForInt>(a, keyAsRaw<keyType>(key)); \
1797 namespace VectorHelpers {
1798 HELPER_TABLE(ISSET)
1800 #undef ISSET
1802 void HhbcTranslator::VectorTranslator::emitArrayIsset() {
1803 SSATmp* key = getKey();
1804 KeyType keyType;
1805 bool checkForInt;
1806 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1808 typedef uint64_t (*OpFunc)(ArrayData*, TypedValue*);
1809 BUILD_OPTAB(keyType, checkForInt);
1810 assert(m_base->isA(Type::Arr));
1811 m_result = gen(ArrayIsset, getCatchTrace(),
1812 cns((TCA)opFunc), m_base, key);
1814 #undef HELPER_TABLE
1816 namespace VectorHelpers {
1817 uint64_t vectorIsset(c_Vector* vec, int64_t index) {
1818 return vec->get(index) != nullptr;
1822 void HhbcTranslator::VectorTranslator::emitVectorIsset() {
1823 SSATmp* key = getKey();
1824 assert(key->isA(Type::Int));
1825 m_result = gen(VectorIsset,
1826 cns((TCA)VectorHelpers::vectorIsset),
1827 m_base,
1828 key);
1831 namespace VectorHelpers {
1832 uint64_t pairIsset(c_Pair* pair, int64_t index) {
1833 return pair->get(index) != nullptr;
1837 void HhbcTranslator::VectorTranslator::emitPairIsset() {
1838 SSATmp* key = getKey();
1839 assert(key->isA(Type::Int));
1840 m_result = gen(PairIsset,
1841 cns((TCA)VectorHelpers::pairIsset),
1842 m_base,
1843 key);
1846 template<KeyType keyType>
1847 static inline uint64_t mapIssetImpl(
1848 c_Map* map, typename KeyTypeTraits<keyType>::rawType key) {
1849 return map->get(key) != nullptr;
1852 #define HELPER_TABLE(m) \
1853 /* name hot keyType */ \
1854 m(mapIssetS, HOT_FUNC_VM, KeyType::Str) \
1855 m(mapIssetI, HOT_FUNC_VM, KeyType::Int)
1857 #define ELEM(nm, hot, keyType) \
1858 hot \
1859 uint64_t nm(c_Map* map, TypedValue* key) { \
1860 return mapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1862 namespace VectorHelpers {
1863 HELPER_TABLE(ELEM)
1865 #undef ELEM
1867 void HhbcTranslator::VectorTranslator::emitMapIsset() {
1868 SSATmp* key = getKey();
1869 assert(key->isA(Type::Int) || key->isA(Type::Str));
1870 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
1872 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*);
1873 BUILD_OPTAB_HOT(keyType);
1874 m_result = gen(MapIsset, cns((TCA)opFunc), m_base, key);
1876 #undef HELPER_TABLE
1878 template<KeyType keyType>
1879 static inline uint64_t stableMapIssetImpl(
1880 c_StableMap* map, typename KeyTypeTraits<keyType>::rawType key) {
1881 return map->get(key) != nullptr;
1884 #define HELPER_TABLE(m) \
1885 /* name hot keyType */ \
1886 m(stableMapIssetS, HOT_FUNC_VM, KeyType::Str) \
1887 m(stableMapIssetI, HOT_FUNC_VM, KeyType::Int)
1889 #define ELEM(nm, hot, keyType) \
1890 hot \
1891 uint64_t nm(c_StableMap* map, TypedValue* key) { \
1892 return stableMapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1894 namespace VectorHelpers {
1895 HELPER_TABLE(ELEM)
1897 #undef ELEM
1899 void HhbcTranslator::VectorTranslator::emitStableMapIsset() {
1900 SSATmp* key = getKey();
1901 assert(key->isA(Type::Int) || key->isA(Type::Str));
1902 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
1904 typedef TypedValue (*OpFunc)(c_StableMap*, TypedValue*);
1905 BUILD_OPTAB_HOT(keyType);
1906 m_result = gen(StableMapIsset, cns((TCA)opFunc), m_base, key);
1908 #undef HELPER_TABLE
1910 void HhbcTranslator::VectorTranslator::emitIssetElem() {
1911 SimpleOp simpleOpType = isSimpleCollectionOp();
1912 switch (simpleOpType) {
1913 case SimpleOp::Array:
1914 emitArrayIsset();
1915 break;
1916 case SimpleOp::Vector:
1917 emitVectorIsset();
1918 break;
1919 case SimpleOp::Pair:
1920 emitPairIsset();
1921 break;
1922 case SimpleOp::Map:
1923 emitMapIsset();
1924 break;
1925 case SimpleOp::StableMap:
1926 emitStableMapIsset();
1927 break;
1928 default:
1929 emitIssetEmptyElem(false);
1930 break;
1934 void HhbcTranslator::VectorTranslator::emitEmptyElem() {
1935 emitIssetEmptyElem(true);
1938 static inline ArrayData* checkedSet(ArrayData* a, StringData* key,
1939 CVarRef value, bool copy) {
1940 int64_t i;
1941 return UNLIKELY(key->isStrictlyInteger(i)) ? a->set(i, value, copy)
1942 : a->set(key, value, copy);
1945 static inline ArrayData* checkedSet(ArrayData* a, int64_t key,
1946 CVarRef value, bool copy) {
1947 not_reached();
1950 template<KeyType keyType, bool checkForInt, bool setRef>
1951 static inline typename ShuffleReturn<setRef>::return_type arraySetImpl(
1952 ArrayData* a, typename KeyTypeTraits<keyType>::rawType key,
1953 CVarRef value, RefData* ref) {
1954 static_assert(keyType != KeyType::Any,
1955 "KeyType::Any is not supported in arraySetMImpl");
1956 const bool copy = a->getCount() > 1;
1957 ArrayData* ret = checkForInt ? checkedSet(a, key, value, copy)
1958 : a->set(key, value, copy);
1960 return arrayRefShuffle<setRef>(a, ret, setRef ? ref->tv() : nullptr);
1963 #define HELPER_TABLE(m) \
1964 /* name hot keyType checkForInt setRef */ \
1965 m(arraySetS, HOT_FUNC_VM, KeyType::Str, false, false) \
1966 m(arraySetSi, HOT_FUNC_VM, KeyType::Str, true, false) \
1967 m(arraySetI, HOT_FUNC_VM, KeyType::Int, false, false) \
1968 m(arraySetSR, , KeyType::Str, false, true) \
1969 m(arraySetSiR, , KeyType::Str, true, true) \
1970 m(arraySetIR, , KeyType::Int, false, true)
1972 #define ELEM(nm, hot, keyType, checkForInt, setRef) \
1973 hot \
1974 typename ShuffleReturn<setRef>::return_type \
1975 nm(ArrayData* a, TypedValue* key, TypedValue value, RefData* ref) { \
1976 return arraySetImpl<keyType, checkForInt, setRef>( \
1977 a, keyAsRaw<keyType>(key), tvAsCVarRef(&value), ref); \
1979 namespace VectorHelpers {
1980 HELPER_TABLE(ELEM)
1982 #undef ELEM
1984 void HhbcTranslator::VectorTranslator::emitArraySet(SSATmp* key,
1985 SSATmp* value) {
1986 assert(m_iInd == m_mii.valCount() + 1);
1987 const int baseStkIdx = m_mii.valCount();
1988 assert(key->type().notBoxed());
1989 assert(value->type().notBoxed());
1990 KeyType keyType;
1991 bool checkForInt;
1992 m_ht.checkStrictlyInteger(key, keyType, checkForInt);
1993 const DynLocation& base = *m_ni.inputs[m_mii.valCount()];
1994 bool setRef = base.outerType() == KindOfRef;
1995 typedef ArrayData* (*OpFunc)(ArrayData*, TypedValue*, TypedValue, RefData*);
1996 BUILD_OPTAB_HOT(keyType, checkForInt, setRef);
1998 // No catch trace below because the helper can't throw. It may reenter to
1999 // call destructors so it has a sync point in nativecalls.cpp, but exceptions
2000 // are swallowed at destructor boundaries right now: #2182869.
2001 if (setRef) {
2002 assert(base.location.space == Location::Local ||
2003 base.location.space == Location::Stack);
2004 SSATmp* box = getInput(baseStkIdx);
2005 gen(ArraySetRef, cns((TCA)opFunc), m_base, key, value, box);
2006 // Unlike the non-ref case, we don't need to do anything to the stack
2007 // because any load of the box will be guarded.
2008 } else {
2009 SSATmp* newArr = gen(ArraySet, cns((TCA)opFunc), m_base, key, value);
2011 // Update the base's value with the new array
2012 if (base.location.space == Location::Local) {
2013 // We know it's not boxed (setRef above handles that), and
2014 // newArr has already been incref'd in the helper.
2015 gen(StLoc, LocalId(base.location.offset), m_tb.fp(), newArr);
2016 } else if (base.location.space == Location::Stack) {
2017 VectorEffects ve(newArr->inst());
2018 assert(ve.baseValChanged);
2019 assert(ve.baseType.subtypeOf(Type::Arr));
2020 m_ht.extendStack(baseStkIdx, Type::Gen);
2021 m_ht.replace(baseStkIdx, newArr);
2022 } else {
2023 not_reached();
2027 m_result = value;
2029 #undef HELPER_TABLE
2031 namespace VectorHelpers {
2032 void setWithRefElemC(TypedValue* base, TypedValue keyVal, TypedValue* val,
2033 MInstrState* mis) {
2034 base = HPHP::ElemD<false, false>(mis->tvScratch, mis->tvRef, base, &keyVal);
2035 if (base != &mis->tvScratch) {
2036 tvDup(*val, *base);
2037 } else {
2038 assert(base->m_type == KindOfUninit);
2042 void setWithRefNewElem(TypedValue* base, TypedValue* val,
2043 MInstrState* mis) {
2044 base = NewElem(mis->tvScratch, mis->tvRef, base);
2045 if (base != &mis->tvScratch) {
2046 tvDup(*val, *base);
2047 } else {
2048 assert(base->m_type == KindOfUninit);
2053 void HhbcTranslator::VectorTranslator::emitSetWithRefLElem() {
2054 SSATmp* key = getKey();
2055 SSATmp* locAddr = getValAddr();
2056 if (m_base->type().strip().subtypeOf(Type::Arr) &&
2057 !locAddr->type().deref().maybeBoxed()) {
2058 emitSetElem();
2059 assert(m_strTestResult == nullptr);
2060 } else {
2061 genStk(SetWithRefElem, getCatchTrace(),
2062 cns((TCA)VectorHelpers::setWithRefElemC),
2063 m_base, key, locAddr, genMisPtr());
2065 m_result = nullptr;
2068 void HhbcTranslator::VectorTranslator::emitSetWithRefLProp() {
2069 SPUNT(__func__);
2072 void HhbcTranslator::VectorTranslator::emitSetWithRefRElem() {
2073 emitSetWithRefLElem();
2076 void HhbcTranslator::VectorTranslator::emitSetWithRefRProp() {
2077 emitSetWithRefLProp();
2080 void HhbcTranslator::VectorTranslator::emitSetWithRefNewElem() {
2081 if (m_base->type().strip().subtypeOf(Type::Arr) &&
2082 getValue()->type().notBoxed()) {
2083 emitSetNewElem();
2084 } else {
2085 genStk(SetWithRefNewElem, getCatchTrace(),
2086 cns((TCA)VectorHelpers::setWithRefNewElem),
2087 m_base, getValAddr(), genMisPtr());
2089 m_result = nullptr;
2092 namespace VectorHelpers {
2093 void vectorSet(c_Vector* vec, int64_t key, Cell value) {
2094 vec->set(key, &value);
2098 void HhbcTranslator::VectorTranslator::emitVectorSet(
2099 SSATmp* key, SSATmp* value) {
2100 gen(VectorSet, getCatchTrace(),
2101 cns((TCA)VectorHelpers::vectorSet), m_base, key, value);
2102 m_result = value;
2105 template<KeyType keyType>
2106 static inline void mapSetImpl(
2107 c_Map* map,
2108 typename KeyTypeTraits<keyType>::rawType key,
2109 Cell value) {
2110 map->set(key, &value);
2113 #define HELPER_TABLE(m) \
2114 /* name hot keyType */ \
2115 m(mapSetS, HOT_FUNC_VM, KeyType::Str) \
2116 m(mapSetI, HOT_FUNC_VM, KeyType::Int)
2118 #define ELEM(nm, hot, keyType) \
2119 hot \
2120 void nm(c_Map* map, TypedValue* key, Cell value) { \
2121 mapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
2123 namespace VectorHelpers {
2124 HELPER_TABLE(ELEM)
2126 #undef ELEM
2128 void HhbcTranslator::VectorTranslator::emitMapSet(
2129 SSATmp* key, SSATmp* value) {
2130 assert(key->isA(Type::Int) || key->isA(Type::Str));
2131 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
2133 typedef TypedValue (*OpFunc)(c_Map*, TypedValue*, TypedValue*);
2134 BUILD_OPTAB_HOT(keyType);
2135 gen(MapSet, getCatchTrace(),
2136 cns((TCA)opFunc), m_base, key, value);
2137 m_result = value;
2139 #undef HELPER_TABLE
2141 template<KeyType keyType>
2142 static inline void stableMapSetImpl(
2143 c_StableMap* map,
2144 typename KeyTypeTraits<keyType>::rawType key,
2145 Cell value) {
2146 map->set(key, &value);
2149 #define HELPER_TABLE(m) \
2150 /* name hot keyType */ \
2151 m(stableMapSetS, HOT_FUNC_VM, KeyType::Str) \
2152 m(stableMapSetI, HOT_FUNC_VM, KeyType::Int)
2154 #define ELEM(nm, hot, keyType) \
2155 hot \
2156 void nm(c_StableMap* map, TypedValue* key, Cell value) { \
2157 stableMapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
2159 namespace VectorHelpers {
2160 HELPER_TABLE(ELEM)
2162 #undef ELEM
2164 void HhbcTranslator::VectorTranslator::emitStableMapSet(
2165 SSATmp* key, SSATmp* value) {
2166 assert(key->isA(Type::Int) || key->isA(Type::Str));
2167 KeyType keyType = key->isA(Type::Int) ? KeyType::Int : KeyType::Str;
2169 typedef TypedValue (*OpFunc)(c_StableMap*, TypedValue*, TypedValue*);
2170 BUILD_OPTAB_HOT(keyType);
2171 gen(StableMapSet, getCatchTrace(),
2172 cns((TCA)opFunc), m_base, key, value);
2173 m_result = value;
2175 #undef HELPER_TABLE
2177 template <KeyType keyType>
2178 static inline StringData* setElemImpl(TypedValue* base, TypedValue keyVal,
2179 Cell val) {
2180 TypedValue* key = keyPtr<keyType>(keyVal);
2181 return HPHP::SetElem<false, keyType>(base, key, &val);
2184 #define HELPER_TABLE(m) \
2185 /* name hot keyType */ \
2186 m(setElemC, , KeyType::Any) \
2187 m(setElemI, , KeyType::Int) \
2188 m(setElemS, HOT_FUNC_VM, KeyType::Str)
2190 #define ELEM(nm, hot, ...) \
2191 hot \
2192 StringData* nm(TypedValue* base, TypedValue key, Cell val) { \
2193 return setElemImpl<__VA_ARGS__>(base, key, val); \
2195 namespace VectorHelpers {
2196 HELPER_TABLE(ELEM)
2198 #undef ELEM
2200 void HhbcTranslator::VectorTranslator::emitSetElem() {
2201 SSATmp* value = getValue();
2202 SSATmp* key = getKey();
2204 SimpleOp simpleOpType = isSimpleCollectionOp();
2205 switch (simpleOpType) {
2206 case SimpleOp::Array:
2207 emitArraySet(key, value);
2208 break;
2209 case SimpleOp::Vector:
2210 emitVectorSet(key, value);
2211 break;
2212 case SimpleOp::Map:
2213 emitMapSet(key, value);
2214 break;
2215 case SimpleOp::StableMap:
2216 emitStableMapSet(key, value);
2217 break;
2218 default:
2219 // Emit the appropriate helper call.
2220 typedef StringData* (*OpFunc)(TypedValue*, TypedValue, Cell);
2221 BUILD_OPTAB_HOT(getKeyTypeIS(key));
2222 m_failedSetTrace = getCatchSetTrace();
2223 SSATmp* result = genStk(SetElem, m_failedSetTrace, cns((TCA)opFunc),
2224 m_base, key, value);
2225 auto t = result->type();
2226 if (t.equals(Type::Nullptr)) {
2227 // Base is not a string. Result is always value.
2228 m_result = value;
2229 } else if (t.equals(Type::CountedStr)) {
2230 // Base is a string. Stack result is a new string so we're responsible for
2231 // decreffing value.
2232 m_result = result;
2233 gen(DecRef, value);
2234 } else {
2235 assert(t.equals(Type::CountedStr | Type::Nullptr));
2236 // Base might be a string. Assume the result is value, then inform
2237 // emitMPost that it needs to test the actual result.
2238 m_result = value;
2239 m_strTestResult = result;
2241 break;
2244 #undef HELPER_TABLE
2246 template <SetOpOp op>
2247 static inline TypedValue setOpElemImpl(TypedValue* base, TypedValue keyVal,
2248 Cell val, MInstrState* mis) {
2249 TypedValue* result =
2250 HPHP::SetOpElem(mis->tvScratch, mis->tvRef, op, base, &keyVal, &val);
2252 TypedValue ret;
2253 tvReadCell(result, &ret);
2254 return ret;
2257 #define OPELEM_TABLE(m, nm, op) \
2258 /* name op */ \
2259 m(nm##op##ElemC, op)
2261 #define HELPER_TABLE(m, op) OPELEM_TABLE(m, setOp, SetOp##op)
2262 #define SETOP(nm, ...) \
2263 TypedValue nm(TypedValue* base, TypedValue key, Cell val, \
2264 MInstrState* mis) { \
2265 return setOpElemImpl<__VA_ARGS__>(base, key, val, mis); \
2267 #define SETOP_OP(op, bcOp) HELPER_TABLE(SETOP, op)
2268 namespace VectorHelpers {
2269 SETOP_OPS
2271 #undef SETOP_OP
2272 #undef SETOP
2274 void HhbcTranslator::VectorTranslator::emitSetOpElem() {
2275 SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
2276 SSATmp* key = getKey();
2277 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, Cell, MInstrState*);
2278 # define SETOP_OP(op, bcOp) HELPER_TABLE(FILL_ROW, op)
2279 BUILD_OPTAB_ARG(SETOP_OPS, op);
2280 # undef SETOP_OP
2281 m_result =
2282 genStk(SetOpElem, getCatchTrace(), cns((TCA)opFunc),
2283 m_base, key, getValue(), genMisPtr());
2285 #undef HELPER_TABLE
2287 template <IncDecOp op>
2288 static inline TypedValue incDecElemImpl(TypedValue* base, TypedValue keyVal,
2289 MInstrState* mis) {
2290 TypedValue result;
2291 HPHP::IncDecElem<true>(
2292 mis->tvScratch, mis->tvRef, op, base, &keyVal, result);
2293 assert(result.m_type != KindOfRef);
2294 return result;
2297 #define HELPER_TABLE(m, op) OPELEM_TABLE(m, incDec, op)
2298 #define INCDEC(nm, ...) \
2299 TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
2300 return incDecElemImpl<__VA_ARGS__>(base, key, mis); \
2302 #define INCDEC_OP(op) HELPER_TABLE(INCDEC, op)
2303 namespace VectorHelpers {
2304 INCDEC_OPS
2306 #undef INCDEC_OP
2307 #undef INCDEC
2309 void HhbcTranslator::VectorTranslator::emitIncDecElem() {
2310 IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
2311 SSATmp* key = getKey();
2312 typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
2313 # define INCDEC_OP(op) HELPER_TABLE(FILL_ROW, op)
2314 BUILD_OPTAB_ARG(INCDEC_OPS, op);
2315 # undef INCDEC_OP
2316 m_result = genStk(IncDecElem, getCatchTrace(), cns((TCA)opFunc),
2317 m_base, key, genMisPtr());
2319 #undef HELPER_TABLE
2321 namespace VectorHelpers {
2322 void bindElemC(TypedValue* base, TypedValue keyVal, RefData* val,
2323 MInstrState* mis) {
2324 base = HPHP::ElemD<false, true>(mis->tvScratch, mis->tvRef, base, &keyVal);
2325 if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
2326 tvBindRef(val, base);
2331 void HhbcTranslator::VectorTranslator::emitBindElem() {
2332 SSATmp* key = getKey();
2333 SSATmp* box = getValue();
2334 genStk(BindElem, getCatchTrace(), cns((TCA)VectorHelpers::bindElemC),
2335 m_base, key, box, genMisPtr());
2336 m_result = box;
2339 template <KeyType keyType>
2340 static inline void unsetElemImpl(TypedValue* base, TypedValue keyVal) {
2341 TypedValue* key = keyPtr<keyType>(keyVal);
2342 HPHP::UnsetElem<keyType>(base, key);
2345 #define HELPER_TABLE(m) \
2346 /* name hot keyType */ \
2347 m(unsetElemC, , KeyType::Any) \
2348 m(unsetElemI, , KeyType::Int) \
2349 m(unsetElemS, HOT_FUNC_VM, KeyType::Str)
2351 #define ELEM(nm, hot, ...) \
2352 hot \
2353 void nm(TypedValue* base, TypedValue key) { \
2354 unsetElemImpl<__VA_ARGS__>(base, key); \
2356 namespace VectorHelpers {
2357 HELPER_TABLE(ELEM)
2359 #undef ELEM
2361 void HhbcTranslator::VectorTranslator::emitUnsetElem() {
2362 SSATmp* key = getKey();
2364 Type baseType = m_base->type().strip();
2365 if (baseType.subtypeOf(Type::Str)) {
2366 m_ht.exceptionBarrier();
2367 gen(RaiseError,
2368 cns(StringData::GetStaticString(Strings::CANT_UNSET_STRING)));
2369 return;
2371 if (baseType.not(Type::Arr | Type::Obj)) {
2372 // Noop
2373 return;
2376 typedef void (*OpFunc)(TypedValue*, TypedValue);
2377 BUILD_OPTAB_HOT(getKeyTypeIS(key));
2378 genStk(UnsetElem, getCatchTrace(), cns((TCA)opFunc), m_base, key);
2380 #undef HELPER_TABLE
2382 void HhbcTranslator::VectorTranslator::emitNotSuppNewElem() {
2383 not_reached();
2386 void HhbcTranslator::VectorTranslator::emitVGetNewElem() {
2387 SPUNT(__func__);
2390 void HhbcTranslator::VectorTranslator::emitSetNewElem() {
2391 SSATmp* value = getValue();
2392 gen(SetNewElem, getCatchSetTrace(), m_base, value);
2393 m_result = value;
2396 void HhbcTranslator::VectorTranslator::emitSetOpNewElem() {
2397 SPUNT(__func__);
2400 void HhbcTranslator::VectorTranslator::emitIncDecNewElem() {
2401 SPUNT(__func__);
2404 void HhbcTranslator::VectorTranslator::emitBindNewElem() {
2405 SSATmp* box = getValue();
2406 genStk(BindNewElem, getCatchTrace(), m_base, box, genMisPtr());
2407 m_result = box;
2410 void HhbcTranslator::VectorTranslator::emitMPost() {
2411 SSATmp* catchSp = nullptr;
2412 if (m_failedSetTrace) {
2413 catchSp = m_failedSetTrace->back()->back()->src(0);
2414 assert(catchSp->isA(Type::StkPtr));
2417 // Decref stack inputs. If we're translating a SetM or BindM, then input 0 is
2418 // both our input and output so leave its refcount alone. If m_failedSetTrace
2419 // is non-null, the final helper call may throw an InvalidSetMException. We
2420 // need to add instructions to m_failedSetTrace to finish the vector
2421 // instruction in case this happens, so any DecRefs emitted here are also
2422 // added to m_failedSetTrace.
2423 unsigned nStack =
2424 (m_ni.mInstrOp() == OpSetM || m_ni.mInstrOp() == OpBindM) ? 1 : 0;
2425 for (unsigned i = nStack; i < m_ni.inputs.size(); ++i) {
2426 const DynLocation& input = *m_ni.inputs[i];
2427 switch (input.location.space) {
2428 case Location::Stack: {
2429 ++nStack;
2430 auto input = getInput(i);
2431 if (input->isA(Type::Gen)) {
2432 gen(DecRef, input);
2433 if (m_failedSetTrace) {
2434 TracePusher tp(m_tb, m_failedSetTrace, m_marker);
2435 gen(DecRefStack, StackOffset(m_stackInputs[i]), Type::Cell, catchSp);
2438 break;
2440 case Location::Local:
2441 case Location::Litstr:
2442 case Location::Litint:
2443 case Location::This: {
2444 // Do nothing.
2445 break;
2448 default: not_reached();
2452 // Pop off all stack inputs
2453 m_ht.discard(nStack);
2455 // Push result, if one was produced. If we have a predicted result use that
2456 // instead of the real result; its validity will be guarded later in this
2457 // function.
2458 if (m_result) {
2459 m_ht.push(m_result);
2460 } else {
2461 assert(m_ni.mInstrOp() == OpUnsetM ||
2462 m_ni.mInstrOp() == OpSetWithRefLM ||
2463 m_ni.mInstrOp() == OpSetWithRefRM);
2466 // Clean up tvRef(2): during exception handling any objects required only
2467 // during vector expansion need to be DecRef'd. There may be either one
2468 // or two such scratch objects, in the case of a Set the first of which will
2469 // always be tvRef2, in all other cases if only one scratch value is present
2470 // it will be stored in tvRef.
2471 static const size_t refOffs[] = { HHIR_MISOFF(tvRef), HHIR_MISOFF(tvRef2) };
2472 for (unsigned i = 0; i < std::min(nLogicalRatchets(), 2U); ++i) {
2473 IRInstruction* inst = m_irf.gen(DecRefMem, m_marker, Type::Gen, m_misBase,
2474 cns(refOffs[m_failedSetTrace ? 1 - i : i]));
2475 m_tb.add(inst);
2476 prependToTraces(inst);
2479 emitSideExits(catchSp, nStack);
2482 void HhbcTranslator::VectorTranslator::emitSideExits(SSATmp* catchSp,
2483 int nStack) {
2484 auto const nextOff = m_ht.nextBcOff();
2485 auto const op = m_ni.mInstrOp();
2486 const bool isSetWithRef = op == OpSetWithRefLM || op == OpSetWithRefRM;
2488 if (m_failedSetTrace) {
2489 assert(bool(m_result) ^ isSetWithRef);
2490 // This catch trace currently ends with an EndCatch that will fall through
2491 // if an InvalidSetMException was thrown. We need to emit code to clean up
2492 // if that happens. If we're translating a SetWithRef* bytecode we don't
2493 // have to do anything special to the stack since they have no stack
2494 // output. Otherwise we need to pop our input value and push the value from
2495 // the exception to the stack (done with a DecRefStack followed by a
2496 // SpillStack).
2498 std::vector<SSATmp*> args{
2499 catchSp, // sp from the previous SpillStack
2500 cns(nStack), // cells popped since the last SpillStack
2503 TracePusher tp(m_tb, m_failedSetTrace, m_marker);
2504 if (!isSetWithRef) {
2505 gen(DecRefStack, StackOffset(0), Type::Cell, catchSp);
2506 args.push_back(m_ht.gen(LdUnwinderValue, Type::Cell));
2509 SSATmp* sp = gen(SpillStack, std::make_pair(args.size(), &args[0]));
2510 gen(DeleteUnwinderException);
2511 gen(SyncABIRegs, m_tb.fp(), sp);
2512 gen(ReqBindJmp, BCOffset(nextOff));
2515 if (m_strTestResult) {
2516 assert(!isSetWithRef);
2517 // We expected SetElem's base to not be a Str but might be wrong. Make an
2518 // exit trace to side exit to the next instruction, replacing our guess
2519 // with the correct stack output.
2521 auto toSpill = m_ht.peekSpillValues();
2522 assert(toSpill.size());
2523 assert(toSpill[0] == m_result);
2524 SSATmp* str = m_irf.gen(AssertNonNull, m_marker, m_strTestResult)->dst();
2525 toSpill[0] = str;
2527 auto exitTrace = m_ht.getExitTrace(nextOff, toSpill);
2529 TracePusher tp(m_tb, exitTrace, m_marker, exitTrace->back(),
2530 exitTrace->back()->skipHeader());
2531 gen(IncStat, cns(Stats::TC_SetMStrGuess_Miss), cns(1), cns(false));
2532 gen(DecRef, m_result);
2533 m_tb.add(str->inst());
2535 gen(CheckType, Type::Nullptr, exitTrace, m_strTestResult);
2536 gen(IncStat, cns(Stats::TC_SetMStrGuess_Hit), cns(1), cns(false));
2540 bool HhbcTranslator::VectorTranslator::needFirstRatchet() const {
2541 if (m_ni.inputs[m_mii.valCount()]->valueType() == KindOfArray) {
2542 switch (m_ni.immVecM[0]) {
2543 case MEC: case MEL: case MET: case MEI: return false;
2544 case MPC: case MPL: case MPT: case MW: return true;
2545 default: not_reached();
2548 return true;
2551 bool HhbcTranslator::VectorTranslator::needFinalRatchet() const {
2552 return m_mii.finalGet();
2555 // Ratchet operations occur after each intermediate operation, except
2556 // possibly the first and last (see need{First,Final}Ratchet()). No actual
2557 // ratchet occurs after the final operation, but this means that both tvRef
2558 // and tvRef2 can contain references just after the final operation. Here we
2559 // pretend that a ratchet occurs after the final operation, i.e. a "logical"
2560 // ratchet. The reason for counting logical ratchets as part of the total is
2561 // the following case, in which the logical count is 0:
2563 // (base is array)
2564 // BaseL
2565 // IssetElemL
2566 // no logical ratchet
2568 // Following are a few more examples to make the algorithm clear:
2570 // (base is array) (base is object) (base is object)
2571 // BaseL BaseL BaseL
2572 // ElemL ElemL CGetPropL
2573 // no ratchet ratchet logical ratchet
2574 // ElemL PropL
2575 // ratchet ratchet
2576 // ElemL CGetElemL
2577 // ratchet logical ratchet
2578 // IssetElemL
2579 // logical ratchet
2581 // (base is array)
2582 // BaseL
2583 // ElemL
2584 // no ratchet
2585 // ElemL
2586 // ratchet
2587 // ElemL
2588 // logical ratchet
2589 // SetElemL
2590 // no ratchet
2591 unsigned HhbcTranslator::VectorTranslator::nLogicalRatchets() const {
2592 // If we've proven elsewhere that we don't need an MInstrState struct, we
2593 // know this translation won't need any ratchets
2594 if (!m_needMIS) return 0;
2596 unsigned ratchets = m_ni.immVecM.size();
2597 if (!needFirstRatchet()) --ratchets;
2598 if (!needFinalRatchet()) --ratchets;
2599 return ratchets;
2602 int HhbcTranslator::VectorTranslator::ratchetInd() const {
2603 return needFirstRatchet() ? int(m_mInd) : int(m_mInd) - 1;