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