2 +----------------------------------------------------------------------+
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
{
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());
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
) {
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
);
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
) {
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
);
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
) {
131 bool basePtr
, baseBoxed
;
132 stripBase(baseType
, basePtr
, baseBoxed
);
133 // Only certain types of bases are supported now but this list may expand in
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
;
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
166 baseType
= Type::CountedStr
;
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
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
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
) {
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
:
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;
247 return opcodeHasFlags(opc
, VectorProp
) ? 4
248 : opcodeHasFlags(opc
, VectorElem
) ? 3
252 int vectorValIdx(const IRInstruction
* inst
) {
253 return vectorValIdx(inst
->op());
256 HhbcTranslator::VectorTranslator::VectorTranslator(
257 const NormalizedInstruction
& ni
,
262 , m_irf(m_ht
.m_irFactory
)
263 , m_mii(getMInstrInfo(ni
.mInstrOp()))
264 , m_marker(ht
.makeMarker(ht
.bcOff()))
269 , m_strTestResult(nullptr)
270 , m_failedSetTrace(nullptr)
274 template<typename
... Srcs
>
275 SSATmp
* HhbcTranslator::VectorTranslator::genStk(Opcode opc
, IRTrace
* taken
,
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
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
298 // Emit the base and every intermediate op
300 // Emit the final operation
302 // Cleanup: decref inputs and scratch values
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() {
310 return gen(LdAddr
, m_misBase
, cns(kReservedRSPSpillSpace
));
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()) &&
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
||
374 void HhbcTranslator::VectorTranslator::emitMPre() {
377 if (HPHP::Trace::moduleEnabled(HPHP::Trace::minstr
, 1)) {
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();
399 // Iterate over all but the last member, which is consumed by a final
401 for (m_mInd
= 0; m_mInd
< m_ni
.immVecM
.size() - 1; ++m_mInd
) {
402 emitIntermediateOp();
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
));
420 for (int mInd
= 0; mInd
< m_ni
.immVecM
.size(); ++mInd
) {
421 auto mcode
= m_ni
.immVecM
[mInd
];
425 } else if (mcodeMaybeArrayOrMapKey(mcode
)) {
426 shape
<< "ME:" << rttStr(iInd
);
427 } else if (mcodeMaybePropName(mcode
)) {
428 shape
<< "MP:" << rttStr(iInd
);
432 if (mcode
!= MW
) ++iInd
;
437 cns(StringData::GetStaticString("vector instructions")),
438 cns(StringData::GetStaticString(shape
.str())),
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
;
455 case Location::Stack
:
456 assert(stackIdx
>= 0);
457 m_stackInputs
[i
] = stackIdx
--;
464 assert(stackIdx
== (stackRhs
? 0 : -1));
467 // If this instruction does have an RHS, it will be input 0 at
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
);
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
);
504 assert(l
.space
== Location::Stack
);
505 assert(mapContains(m_stackInputs
, 0));
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
));
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
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
);
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
);
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
) {
569 LocalId(base
.location
.offset
),
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()));
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
);
604 assert(base
.location
.space
== Location::Stack
);
605 // Make sure the stack is clean before getting a pointer to one of its
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),
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
) &&
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() {
677 template <bool warn
, bool define
>
678 static inline TypedValue
* baseGImpl(TypedValue
*key
,
681 StringData
* name
= prepareKey(key
);
682 VarEnv
* varEnv
= g_vmContext
->m_globalVarEnv
;
683 assert(varEnv
!= NULL
);
684 base
= varEnv
->lookup(name
);
687 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
692 varEnv
->set(name
, &tv
);
693 base
= varEnv
->lookup(name
);
695 return const_cast<TypedValue
*>(init_null_variant
.asTypedValue());
699 if (base
->m_type
== KindOfRef
) {
700 base
= base
->m_data
.pref
->tv();
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();
731 getEmptyCatchTrace(),
732 cns(reinterpret_cast<TCA
>(opFunc
)),
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
,
747 void HhbcTranslator::VectorTranslator::emitBaseOp() {
748 LocationCode lCode
= m_ni
.immVec
.locationCode();
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
: {
766 case MPC
: case MPL
: case MPT
:
771 assert(m_mii
.newElem());
774 default: not_reached();
779 void HhbcTranslator::VectorTranslator::emitProp() {
780 const Class
* knownCls
= nullptr;
781 const auto propInfo
= getPropertyOffset(m_ni
, contextClass(),
784 auto mia
= m_mii
.getAttr(m_ni
.immVecM
[m_mInd
]);
785 if (propInfo
.offset
== -1 || (mia
& Unset
)) {
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
{
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();
831 typedef TypedValue
* (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, MInstrState
*);
832 SSATmp
* key
= getKey();
833 BUILD_OPTAB(mia
, m_base
->isA(Type::Obj
));
835 m_base
= genStk(PropDX
, getCatchTrace(), cns((TCA
)opFunc
), CTX(),
836 m_base
, key
, genMisPtr());
838 m_base
= gen(PropX
, getCatchTrace(),
839 cns((TCA
)opFunc
), CTX(), m_base
, key
, genMisPtr());
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(
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(),
873 gen(CheckInitMem
, taken
, propAddr
, cns(0));
875 [&] { // Next: Property isn't Uninit. Do nothing.
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
);
889 cns(propInfo
.offset
),
890 m_tb
.genDefInitNull()
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
,
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
);
927 SSATmp
* baseAsObj
= nullptr;
928 m_base
= m_tb
.cond(m_ht
.curFunc(),
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
935 return checkInitProp(baseAsObj
,
936 gen(LdPropAddr
, baseAsObj
,
937 cns(propInfo
.offset
)),
942 [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull.
943 m_tb
.hint(Block::Hint::Unlikely
);
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.
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
);
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
,
983 static inline TypedValue
* elemImpl(TypedValue
* base
, TypedValue keyVal
,
985 TypedValue
* key
= keyPtr
<keyType
>(keyVal
);
987 return ElemU
<keyType
>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
989 return ElemD
<warn
, reffy
, keyType
>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
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) \
1021 TypedValue* nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1022 return elemImpl<keyType, WDRU(attrs)>(base, key, mis); \
1024 namespace VectorHelpers
{
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
));
1038 SSATmp
* uninit
= m_tb
.genPtrToUninit();
1039 Type baseType
= m_base
->type().strip();
1040 if (baseType
.subtypeOf(Type::Str
)) {
1041 m_ht
.exceptionBarrier();
1044 cns(StringData::GetStaticString(Strings::OP_NOT_SUPPORTED_STRING
))
1049 if (baseType
.not(Type::Arr
| Type::Obj
)) {
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());
1061 m_base
= gen(ElemX
, getCatchTrace(),
1062 cns((TCA
)opFunc
), m_base
, key
, genMisPtr());
1067 void HhbcTranslator::VectorTranslator::emitNewElem() {
1071 void HhbcTranslator::VectorTranslator::emitRatchetRefs() {
1072 if (ratchetInd() < 0 || ratchetInd() >= int(nLogicalRatchets())) {
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
);
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.
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,
1114 (this->*elemOps
[m_mii
.instr()])();
1117 case MPC
: case MPL
: case MPT
:
1118 static MemFun propOps
[] = {
1119 # define MII(instr, ...) &HhbcTranslator::VectorTranslator::emit##instr##Prop,
1123 (this->*propOps
[m_mii
.instr()])();
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,
1134 (this->*newOps
[m_mii
.instr()])();
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
);
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
);
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, ...) \
1165 TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
1166 MInstrState* mis) { \
1167 return cGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1169 namespace VectorHelpers
{
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
);
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());
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
) {
1206 RefData
* ref
= result
->m_data
.pref
;
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, ...) \
1220 RefData* nm(Class* ctx, TypedValue* base, TypedValue key, \
1221 MInstrState* mis) { \
1222 return vGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
1224 namespace VectorHelpers
{
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());
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
{
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
);
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) \
1286 m(setPropC, false) \
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
{
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
;
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
);
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
);
1334 tvReadCell(result
, &ret
);
1338 #define HELPER_TABLE(m) \
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
{
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());
1362 genStk(SetOpProp
, getCatchTrace(), cns((TCA
)opFunc
),
1363 m_base
, key
, value
, genMisPtr(), cns(op
));
1367 template <bool isObj
>
1368 static inline TypedValue
incDecPropImpl(TypedValue
* base
, TypedValue keyVal
,
1369 MInstrState
* mis
, IncDecOp op
) {
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
);
1379 #define HELPER_TABLE(m) \
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
)
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());
1402 genStk(IncDecProp
, getCatchTrace(), cns((TCA
)opFunc
),
1403 m_base
, key
, genMisPtr(), cns(op
));
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) \
1419 m(bindPropC, false) \
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
{
1432 void HhbcTranslator::VectorTranslator::emitBindProp() {
1433 SSATmp
* key
= getKey();
1434 SSATmp
* box
= getValue();
1435 typedef void (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, RefData
*,
1437 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1438 genStk(BindProp
, getCatchTrace(), cns((TCA
)opFunc
), CTX(),
1439 m_base
, key
, box
, genMisPtr());
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) \
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
{
1464 void HhbcTranslator::VectorTranslator::emitUnsetProp() {
1465 SSATmp
* key
= getKey();
1467 if (m_base
->type().strip().not(Type::Obj
)) {
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
);
1478 // Keep these error handlers in sync with ArrayData::getNotFound();
1480 static TypedValue
arrayGetNotFound(int64_t k
) {
1481 raise_notice("Undefined index: %" PRId64
, k
);
1488 static TypedValue
arrayGetNotFound(const StringData
* k
) {
1489 raise_notice("Undefined index: %s", k
->data());
1495 static inline TypedValue
* checkedGet(ArrayData
* a
, StringData
* key
) {
1497 return UNLIKELY(key
->isStrictlyInteger(i
)) ? a
->nvGet(i
) : a
->nvGet(key
);
1500 static inline TypedValue
* checkedGet(ArrayData
* a
, int64_t key
) {
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
)
1510 ret
= tvToCell(ret
);
1511 tvRefcountedIncRef(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) \
1525 TypedValue nm(ArrayData* a, TypedValue* key) { \
1526 return arrayGetImpl<keyType, checkForInt>(a, keyAsRaw<keyType>(key)); \
1528 namespace VectorHelpers
{
1533 void HhbcTranslator::VectorTranslator::emitArrayGet(SSATmp
* key
) {
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
);
1545 namespace VectorHelpers
{
1546 TypedValue
vectorGet(c_Vector
* vec
, int64_t key
) {
1547 TypedValue
* ret
= vec
->at(key
);
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
);
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
);
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) \
1585 TypedValue nm(c_Map* map, TypedValue* key) { \
1586 return mapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1588 namespace VectorHelpers
{
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
);
1605 template<KeyType keyType
>
1606 static inline TypedValue
stableMapGetImpl(
1607 c_StableMap
* map
, typename KeyTypeTraits
<keyType
>::rawType key
) {
1608 TypedValue
* ret
= map
->at(key
);
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) \
1619 TypedValue nm(c_StableMap* map, TypedValue* key) { \
1620 return stableMapGetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1622 namespace VectorHelpers
{
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
);
1639 template <KeyType keyType
>
1640 static inline TypedValue
cGetElemImpl(TypedValue
* base
, TypedValue keyVal
,
1642 TypedValue
* key
= keyPtr
<keyType
>(keyVal
);
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
);
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, ...) \
1661 TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1662 return cGetElemImpl<__VA_ARGS__>(base, key, mis); \
1664 namespace VectorHelpers
{
1669 void HhbcTranslator::VectorTranslator::emitCGetElem() {
1670 SSATmp
* key
= getKey();
1672 SimpleOp simpleOpType
= isSimpleCollectionOp();
1673 switch (simpleOpType
) {
1674 case SimpleOp::Array
:
1677 case SimpleOp::Vector
:
1680 case SimpleOp::Pair
:
1686 case SimpleOp::StableMap
:
1687 emitStableMapGet(key
);
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());
1699 template <KeyType keyType
>
1700 static inline RefData
* vGetElemImpl(TypedValue
* base
, TypedValue keyVal
,
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
) {
1709 RefData
* ref
= result
->m_data
.pref
;
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
{
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());
1738 template <KeyType keyType
, bool isEmpty
>
1739 static inline bool issetEmptyElemImpl(TypedValue
* base
, TypedValue keyVal
,
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
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, ...) \
1760 uint64_t nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
1761 return issetEmptyElemImpl<__VA_ARGS__>(base, key, mis); \
1763 namespace VectorHelpers
{
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());
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
)
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
{
1802 void HhbcTranslator::VectorTranslator::emitArrayIsset() {
1803 SSATmp
* key
= getKey();
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
);
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
),
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
),
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) \
1859 uint64_t nm(c_Map* map, TypedValue* key) { \
1860 return mapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1862 namespace VectorHelpers
{
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
);
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) \
1891 uint64_t nm(c_StableMap* map, TypedValue* key) { \
1892 return stableMapIssetImpl<keyType>(map, keyAsRaw<keyType>(key)); \
1894 namespace VectorHelpers
{
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
);
1910 void HhbcTranslator::VectorTranslator::emitIssetElem() {
1911 SimpleOp simpleOpType
= isSimpleCollectionOp();
1912 switch (simpleOpType
) {
1913 case SimpleOp::Array
:
1916 case SimpleOp::Vector
:
1919 case SimpleOp::Pair
:
1925 case SimpleOp::StableMap
:
1926 emitStableMapIsset();
1929 emitIssetEmptyElem(false);
1934 void HhbcTranslator::VectorTranslator::emitEmptyElem() {
1935 emitIssetEmptyElem(true);
1938 static inline ArrayData
* checkedSet(ArrayData
* a
, StringData
* key
,
1939 CVarRef value
, bool copy
) {
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
) {
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) \
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
{
1984 void HhbcTranslator::VectorTranslator::emitArraySet(SSATmp
* key
,
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());
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.
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.
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
);
2031 namespace VectorHelpers
{
2032 void setWithRefElemC(TypedValue
* base
, TypedValue keyVal
, TypedValue
* val
,
2034 base
= HPHP::ElemD
<false, false>(mis
->tvScratch
, mis
->tvRef
, base
, &keyVal
);
2035 if (base
!= &mis
->tvScratch
) {
2038 assert(base
->m_type
== KindOfUninit
);
2042 void setWithRefNewElem(TypedValue
* base
, TypedValue
* val
,
2044 base
= NewElem(mis
->tvScratch
, mis
->tvRef
, base
);
2045 if (base
!= &mis
->tvScratch
) {
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()) {
2059 assert(m_strTestResult
== nullptr);
2061 genStk(SetWithRefElem
, getCatchTrace(),
2062 cns((TCA
)VectorHelpers::setWithRefElemC
),
2063 m_base
, key
, locAddr
, genMisPtr());
2068 void HhbcTranslator::VectorTranslator::emitSetWithRefLProp() {
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()) {
2085 genStk(SetWithRefNewElem
, getCatchTrace(),
2086 cns((TCA
)VectorHelpers::setWithRefNewElem
),
2087 m_base
, getValAddr(), genMisPtr());
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
);
2105 template<KeyType keyType
>
2106 static inline void mapSetImpl(
2108 typename KeyTypeTraits
<keyType
>::rawType key
,
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) \
2120 void nm(c_Map* map, TypedValue* key, Cell value) { \
2121 mapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
2123 namespace VectorHelpers
{
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
);
2141 template<KeyType keyType
>
2142 static inline void stableMapSetImpl(
2144 typename KeyTypeTraits
<keyType
>::rawType key
,
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) \
2156 void nm(c_StableMap* map, TypedValue* key, Cell value) { \
2157 stableMapSetImpl<keyType>(map, keyAsRaw<keyType>(key), value); \
2159 namespace VectorHelpers
{
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
);
2177 template <KeyType keyType
>
2178 static inline StringData
* setElemImpl(TypedValue
* base
, TypedValue keyVal
,
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, ...) \
2192 StringData* nm(TypedValue* base, TypedValue key, Cell val) { \
2193 return setElemImpl<__VA_ARGS__>(base, key, val); \
2195 namespace VectorHelpers
{
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
);
2209 case SimpleOp::Vector
:
2210 emitVectorSet(key
, value
);
2213 emitMapSet(key
, value
);
2215 case SimpleOp::StableMap
:
2216 emitStableMapSet(key
, value
);
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.
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.
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.
2239 m_strTestResult
= result
;
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
);
2253 tvReadCell(result
, &ret
);
2257 #define OPELEM_TABLE(m, nm, 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
{
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
);
2282 genStk(SetOpElem
, getCatchTrace(), cns((TCA
)opFunc
),
2283 m_base
, key
, getValue(), genMisPtr());
2287 template <IncDecOp op
>
2288 static inline TypedValue
incDecElemImpl(TypedValue
* base
, TypedValue keyVal
,
2291 HPHP::IncDecElem
<true>(
2292 mis
->tvScratch
, mis
->tvRef
, op
, base
, &keyVal
, result
);
2293 assert(result
.m_type
!= KindOfRef
);
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
{
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
);
2316 m_result
= genStk(IncDecElem
, getCatchTrace(), cns((TCA
)opFunc
),
2317 m_base
, key
, genMisPtr());
2321 namespace VectorHelpers
{
2322 void bindElemC(TypedValue
* base
, TypedValue keyVal
, RefData
* val
,
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());
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, ...) \
2353 void nm(TypedValue* base, TypedValue key) { \
2354 unsetElemImpl<__VA_ARGS__>(base, key); \
2356 namespace VectorHelpers
{
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();
2368 cns(StringData::GetStaticString(Strings::CANT_UNSET_STRING
)));
2371 if (baseType
.not(Type::Arr
| Type::Obj
)) {
2376 typedef void (*OpFunc
)(TypedValue
*, TypedValue
);
2377 BUILD_OPTAB_HOT(getKeyTypeIS(key
));
2378 genStk(UnsetElem
, getCatchTrace(), cns((TCA
)opFunc
), m_base
, key
);
2382 void HhbcTranslator::VectorTranslator::emitNotSuppNewElem() {
2386 void HhbcTranslator::VectorTranslator::emitVGetNewElem() {
2390 void HhbcTranslator::VectorTranslator::emitSetNewElem() {
2391 SSATmp
* value
= getValue();
2392 gen(SetNewElem
, getCatchSetTrace(), m_base
, value
);
2396 void HhbcTranslator::VectorTranslator::emitSetOpNewElem() {
2400 void HhbcTranslator::VectorTranslator::emitIncDecNewElem() {
2404 void HhbcTranslator::VectorTranslator::emitBindNewElem() {
2405 SSATmp
* box
= getValue();
2406 genStk(BindNewElem
, getCatchTrace(), m_base
, box
, genMisPtr());
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.
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
: {
2430 auto input
= getInput(i
);
2431 if (input
->isA(Type::Gen
)) {
2433 if (m_failedSetTrace
) {
2434 TracePusher
tp(m_tb
, m_failedSetTrace
, m_marker
);
2435 gen(DecRefStack
, StackOffset(m_stackInputs
[i
]), Type::Cell
, catchSp
);
2440 case Location::Local
:
2441 case Location::Litstr
:
2442 case Location::Litint
:
2443 case Location::This
: {
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
2459 m_ht
.push(m_result
);
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
]));
2476 prependToTraces(inst
);
2479 emitSideExits(catchSp
, nStack
);
2482 void HhbcTranslator::VectorTranslator::emitSideExits(SSATmp
* catchSp
,
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
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();
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();
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:
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
2577 // ratchet logical 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
;
2602 int HhbcTranslator::VectorTranslator::ratchetInd() const {
2603 return needFirstRatchet() ? int(m_mInd
) : int(m_mInd
) - 1;