2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
16 #include <type_traits>
19 #include "hphp/runtime/ext/ext_collections.h"
20 #include "hphp/runtime/vm/jit/minstr-effects.h"
21 #include "hphp/runtime/vm/jit/normalized-instruction.h"
22 #include "hphp/runtime/vm/jit/target-profile.h"
24 #include "hphp/runtime/vm/jit/irgen-sprop-global.h"
25 #include "hphp/runtime/vm/jit/irgen-exit.h"
27 #include "hphp/runtime/vm/jit/irgen-internal.h"
29 namespace HPHP
{ namespace jit
{ namespace irgen
{
33 //////////////////////////////////////////////////////////////////////
35 const StaticString
s_PackedArray("PackedArray");
37 //////////////////////////////////////////////////////////////////////
39 bool wantPropSpecializedWarnings() {
40 return !RuntimeOption::RepoAuthoritative
||
41 !RuntimeOption::EvalDisableSomeRepoAuthNotices
;
44 //////////////////////////////////////////////////////////////////////
52 Vector
, // c_Vector* or c_ImmVector*
58 * Minstr Translation State. Member instructions are complex enough that we
59 * need our own state environment while processing one.
61 * This is implicitly convertible to HTS so you can use ht-internal functions
62 * on it. Effectively MTS <: HTS (except the dot operator).
65 explicit MTS(HTS
& hts
)
67 , ni(*hts
.currentNormalizedInstruction
)
70 , mii(getMInstrInfo(ni
.mInstrOp()))
71 , marker(makeMarker(hts
, bcOff(hts
)))
72 , iInd(mii
.valCount())
74 /* implicit */ operator HTS
&() { return hts
; }
75 /* implicit */ operator const HTS
&() const { return hts
; }
78 const NormalizedInstruction
& ni
;
84 hphp_hash_map
<unsigned,unsigned> stackInputs
;
91 // The base for any accesses to the current MInstrState.
92 SSATmp
* misBase
{nullptr};
95 * The value of the base for the next member operation. Starts as the base
96 * for the whole instruction and is updated as the translator makes
99 * We have a separate type in case we have more information about the type
100 * than base.value->type() has (this may be the case with pointers to locals
101 * or stack slots right now, for example). If base.value is not nullptr,
102 * base.value->type() is always a supertype of base.type, and base.type is
103 * always large enough to accommodate the type the base ends up having at
106 * Don't change base directly; use setBase, to update base.type
110 SSATmp
* value
{nullptr};
111 Type type
{Type::Bottom
};
114 /* Value computed before we do anything to allow better translations for
115 * common, simple operations. */
116 SimpleOp simpleOp
{SimpleOp::None
};
118 /* The result of the vector instruction. nullptr if the current instruction
119 * doesn't produce a result. */
120 SSATmp
* result
{nullptr};
122 /* If set, contains a value of type CountedStr|Nullptr. If a runtime test
123 * determines that the value is not Nullptr, we incorrectly predicted the
124 * output type of the instruction and must side exit. */
125 SSATmp
* strTestResult
{nullptr};
127 /* If set, contains the catch target for the final set operation of this
128 * instruction. The operations that set this member may need to return an
129 * unexpected type, in which case they'll throw an InvalidSetMException. To
130 * handle this, emitMPost adds code to the catch trace to fish the correct
131 * value out of the exception and side exit. */
132 Block
* failedSetBlock
{nullptr};
134 /* Contains a list of all catch blocks created in building the vector.
135 * Each block must be appended with cleanup tasks (generally just DecRef)
136 * to be performed if an exception occurs during the course of the vector
138 std::vector
<Block
*> failedVec
;
141 // TODO(#5710382): this did something different in the old code, so let's be
142 // explicit at first and audit everything.
143 Block
* makeCatch(MTS
&) = delete;
145 // TODO(#5710382): we should just use makeCatch for this so it means the same
147 Block
* makeEmptyCatch(MTS
& env
) {
149 using irgen::makeCatch
;
150 return makeCatch(hts
);
153 Block
* makeMRCatch(MTS
& env
) {
154 auto b
= makeEmptyCatch(env
);
155 env
.failedVec
.push_back(b
);
159 Block
* makeCatchSet(MTS
& env
) {
160 assert(!env
.failedSetBlock
);
161 env
.failedSetBlock
= makeMRCatch(env
);
163 // This catch trace will be modified in emitMPost to end with a side
164 // exit, and TryEndCatch will fall through to that side exit if an
165 // InvalidSetMException is thrown.
166 env
.failedSetBlock
->back().setOpcode(TryEndCatch
);
167 return env
.failedSetBlock
;
170 //////////////////////////////////////////////////////////////////////
172 void constrainBase(MTS
& env
, TypeConstraint tc
) {
173 // Member operations only care about the inner type of the base if it's
174 // boxed, so this handles the logic of using the inner constraint when
176 if (env
.base
.type
.maybeBoxed()) {
177 //tc.innerCat = tc.category;
178 tc
.category
= DataTypeCountness
;
180 env
.irb
.constrainValue(env
.base
.value
, tc
);
183 bool constrainCollectionOpBase(MTS
& env
) {
184 switch (env
.simpleOp
) {
188 case SimpleOp::Array
:
189 case SimpleOp::ProfiledArray
:
190 case SimpleOp::String
:
191 env
.irb
.constrainValue(env
.base
.value
, DataTypeSpecific
);
194 case SimpleOp::PackedArray
:
197 TypeConstraint(DataTypeSpecialized
).setWantArrayKind()
201 case SimpleOp::Vector
:
204 always_assert(env
.base
.type
< Type::Obj
&&
205 env
.base
.type
.isSpecialized());
206 constrainBase(env
, TypeConstraint(env
.base
.type
.getClass()));
214 void specializeBaseIfPossible(MTS
& env
, Type baseType
) {
215 if (constrainCollectionOpBase(env
)) return;
216 if (baseType
>= Type::Obj
) return;
217 if (!baseType
.isSpecialized()) return;
218 constrainBase(env
, TypeConstraint(baseType
.getClass()));
221 //////////////////////////////////////////////////////////////////////
223 struct NoExtraData
{};
225 template<class E
> struct genStkImpl
{
226 template<class... Srcs
>
227 static SSATmp
* go(MTS
& env
, Opcode opc
, Block
* taken
, const E
& extra
,
229 return gen(env
, opc
, taken
, extra
, srcs
...);
233 template<> struct genStkImpl
<NoExtraData
> {
234 template<class... Srcs
>
235 static SSATmp
* go(MTS
& env
, Opcode opc
, Block
* taken
, const NoExtraData
&,
237 return gen(env
, opc
, taken
, srcs
...);
241 template<class ExtraData
, class... Srcs
>
242 SSATmp
* genStk(MTS
& env
,
245 const ExtraData
& extra
,
247 static_assert(!std::is_same
<ExtraData
,SSATmp
*>::value
,
248 "Pass NoExtraData{} if you don't need extra data in genStk");
249 assert(opcodeHasFlags(opc
, HasStackVersion
));
250 assert(!opcodeHasFlags(opc
, ModifiesStack
));
252 // We're going to make decisions based on the type of the base.
253 constrainBase(env
, DataTypeSpecific
);
255 auto srcVec
= std::vector
<SSATmp
*> { srcs
... };
256 auto const base
= srcVec
[minstrBaseIdx(opc
)];
258 /* If the base is a pointer to a stack cell and the operation might change
259 * its type and/or value, use the version of the opcode that returns a new
261 if (base
->inst()->op() == LdStackAddr
) {
262 auto const prev
= getStackValue(
263 base
->inst()->src(0),
264 base
->inst()->extra
<LdStackAddr
>()->offset
266 MInstrEffects
effects(opc
, prev
.knownType
.ptr(Ptr::Stk
));
267 if (effects
.baseTypeChanged
|| effects
.baseValChanged
) {
268 return genStkImpl
<ExtraData
>::go(env
,
269 getStackModifyingOpcode(opc
), taken
, extra
, srcs
..., sp(env
));
273 return genStkImpl
<ExtraData
>::go(env
, opc
, taken
, extra
, srcs
...);
276 //////////////////////////////////////////////////////////////////////
278 // Returns a pointer to the base of the current MInstrState struct, or a null
279 // pointer if it's not needed.
280 SSATmp
* misPtr(MTS
& env
) {
281 assert(env
.base
.value
&& "misPtr called before emitBaseOp");
283 return gen(env
, LdMIStateAddr
, env
.misBase
,
284 cns(env
, RDS::kVmMInstrStateOff
));
286 return cns(env
, Type::cns(nullptr, Type::PtrToMISUninit
));
289 bool mightCallMagicPropMethod(MInstrAttr mia
, const Class
* cls
,
291 if (convertToType(propInfo
.repoAuthType
).not(Type::Uninit
)) {
294 if (!cls
) return true;
295 bool const no_override_magic
=
296 // NB: this function can't yet be used for unset or isset contexts. Just
298 (mia
& MIA_define
) ? cls
->attrs() & AttrNoOverrideMagicSet
299 : cls
->attrs() & AttrNoOverrideMagicGet
;
300 return !no_override_magic
;
303 bool mInstrHasUnknownOffsets(const NormalizedInstruction
& ni
,
304 const Class
* context
) {
305 const MInstrInfo
& mii
= getMInstrInfo(ni
.mInstrOp());
307 unsigned ii
= mii
.valCount() + 1;
308 for (; mi
< ni
.immVecM
.size(); ++mi
) {
309 MemberCode mc
= ni
.immVecM
[mi
];
310 if (mcodeIsProp(mc
)) {
311 const Class
* cls
= nullptr;
312 auto propInfo
= getPropertyOffset(ni
, context
, cls
, mii
, mi
, ii
);
313 if (propInfo
.offset
== -1 ||
314 mightCallMagicPropMethod(mii
.getAttr(mc
), cls
, propInfo
)) {
326 // "Simple" bases are stack cells and locals.
327 bool isSimpleBase(MTS
& env
) {
328 auto const loc
= env
.ni
.immVec
.locationCode();
329 return loc
== LL
|| loc
== LC
|| loc
== LR
;
332 bool isSingleMember(MTS
& env
) { return env
.ni
.immVecM
.size() == 1; }
334 bool isOptimizableCollectionClass(const Class
* klass
) {
335 return klass
== c_Vector::classof() ||
336 klass
== c_Map::classof() ||
337 klass
== c_Pair::classof();
340 // Inspect the instruction we're about to translate and determine if it can be
341 // executed without using an MInstrState struct.
342 void checkMIState(MTS
& env
) {
343 if (env
.ni
.immVec
.locationCode() == LNL
||
344 env
.ni
.immVec
.locationCode() == LNC
) {
345 // We're definitely going to punt in emitBaseN, so we might not
346 // have guarded the base's type.
350 Type baseType
= env
.base
.type
.derefIfPtr();
351 const bool baseArr
= baseType
<= Type::Arr
;
352 const bool isCGetM
= env
.ni
.mInstrOp() == Op::CGetM
;
353 const bool isSetM
= env
.ni
.mInstrOp() == Op::SetM
;
354 const bool isIssetM
= env
.ni
.mInstrOp() == Op::IssetM
;
355 const bool isUnsetM
= env
.ni
.mInstrOp() == Op::UnsetM
;
356 const bool isSingle
= env
.ni
.immVecM
.size() == 1;
357 const bool unknownOffsets
= mInstrHasUnknownOffsets(env
.ni
,
360 if (baseType
.maybeBoxed() && !baseType
.isBoxed()) {
361 // We don't need to bother with weird base types.
365 // Type::unbox() is a little dangerous since it can be more specific than
366 // what LdRef actually returns, but in all cases where the base value comes
367 // from a LdRef, m_base will be the dest of that LdRef and unbox() will be a
369 baseType
= baseType
.unbox();
371 // CGetM or SetM with no unknown property offsets
372 const bool simpleProp
= !unknownOffsets
&& (isCGetM
|| isSetM
);
374 // SetM with only one vector element, for props and elems
375 const bool singleSet
= isSingle
&& isSetM
;
377 // Element access with one element in the vector
378 const bool singleElem
= isSingle
&& mcodeIsElem(env
.ni
.immVecM
[0]);
380 // IssetM with one vector array element and an Arr base
381 const bool simpleArrayIsset
= isIssetM
&& singleElem
&& baseArr
;
383 // IssetM with one vector array element and a collection type
384 const bool simpleCollectionIsset
= isIssetM
&& singleElem
&&
385 baseType
< Type::Obj
&& isOptimizableCollectionClass(baseType
.getClass());
387 // UnsetM on an array with one vector element
388 const bool simpleArrayUnset
= isUnsetM
&& singleElem
&&
389 baseType
<= Type::Arr
;
391 // CGetM on an array with a base that won't use MInstrState. Str
392 // will use tvScratch and Obj will fatal or use tvRef.
393 const bool simpleArrayGet
= isCGetM
&& singleElem
&&
394 baseType
.not(Type::Str
| Type::Obj
);
395 const bool simpleCollectionGet
= isCGetM
&& singleElem
&&
396 baseType
< Type::Obj
&& isOptimizableCollectionClass(baseType
.getClass());
397 const bool simpleStringOp
= (isCGetM
|| isIssetM
) && isSingle
&&
398 isSimpleBase(env
) && mcodeMaybeArrayIntKey(env
.ni
.immVecM
[0]) &&
399 baseType
<= Type::Str
;
401 if (simpleProp
|| singleSet
||
402 simpleArrayGet
|| simpleCollectionGet
||
403 simpleArrayUnset
|| simpleCollectionIsset
||
404 simpleArrayIsset
|| simpleStringOp
) {
406 if (simpleCollectionGet
|| simpleCollectionIsset
) {
407 constrainBase(env
, TypeConstraint(baseType
.getClass()));
409 constrainBase(env
, DataTypeSpecific
);
414 //////////////////////////////////////////////////////////////////////
416 void emitMTrace(MTS
& env
) {
417 auto rttStr
= [&](int i
) {
418 return Type(env
.ni
.inputs
[i
]->rtt
).unbox().toString();
420 std::ostringstream shape
;
421 int iInd
= env
.mii
.valCount();
422 const char* separator
= "";
424 shape
<< opcodeToName(env
.ni
.mInstrOp()) << " <";
425 auto baseLoc
= env
.ni
.immVec
.locationCode();
426 shape
<< folly::format("{}:{} ", locationCodeString(baseLoc
), rttStr(iInd
));
429 for (int mInd
= 0; mInd
< env
.ni
.immVecM
.size(); ++mInd
) {
430 auto mcode
= env
.ni
.immVecM
[mInd
];
434 } else if (mcodeIsElem(mcode
)) {
435 shape
<< "ME:" << rttStr(iInd
);
436 } else if (mcodeIsProp(mcode
)) {
437 shape
<< "MP:" << rttStr(iInd
);
441 if (mcode
!= MW
) ++iInd
;
447 cns(env
, makeStaticString("vector instructions")),
448 cns(env
, makeStaticString(shape
.str())),
452 //////////////////////////////////////////////////////////////////////
454 SSATmp
* getInput(MTS
& env
, unsigned i
, TypeConstraint tc
) {
455 auto const& dl
= *env
.ni
.inputs
[i
];
456 auto const& l
= dl
.location
;
458 assert(!!env
.stackInputs
.count(i
) == (l
.space
== Location::Stack
));
460 case Location::Stack
:
461 return top(env
, Type::StackElem
, env
.stackInputs
[i
], tc
);
463 case Location::Local
:
464 // N.B. Exit block for LdLocPseudoMain is nullptr because we always
465 // InterpOne member instructions in pseudomains
466 return ldLoc(env
, l
.offset
, nullptr, tc
);
468 case Location::Litstr
:
469 return cns(env
, curUnit(env
)->lookupLitstrId(l
.offset
));
471 case Location::Litint
:
472 return cns(env
, l
.offset
);
475 // If we don't have a current class context, this instruction will be
477 if (!curClass(env
)) PUNT(Unreachable
-LdThis
);
480 default: not_reached();
484 void setBase(MTS
& env
,
486 folly::Optional
<Type
> baseType
= folly::none
) {
487 env
.base
.value
= tmp
;
488 env
.base
.type
= baseType
? *baseType
: env
.base
.value
->type();
489 always_assert(env
.base
.type
<= env
.base
.value
->type());
492 SSATmp
* getBase(MTS
& env
, TypeConstraint tc
) {
493 assert(env
.iInd
== env
.mii
.valCount());
494 return getInput(env
, env
.iInd
, tc
);
497 SSATmp
* getKey(MTS
& env
) {
498 auto key
= getInput(env
, env
.iInd
, DataTypeSpecific
);
499 auto const keyType
= key
->type();
500 assert(keyType
.isBoxed() || keyType
.notBoxed());
501 if (keyType
.isBoxed()) {
502 key
= gen(env
, LdRef
, Type::InitCell
, key
);
507 SSATmp
* getValue(MTS
& env
) {
508 // If an instruction takes an rhs, it's always input 0.
509 assert(env
.mii
.valCount() == 1);
510 const int kValIdx
= 0;
511 return getInput(env
, kValIdx
, DataTypeSpecific
);
514 SSATmp
* getValAddr(MTS
& env
) {
515 assert(env
.mii
.valCount() == 1);
516 auto const& dl
= *env
.ni
.inputs
[0];
517 auto const& l
= dl
.location
;
518 if (l
.space
== Location::Local
) {
519 assert(!env
.stackInputs
.count(0));
520 return ldLocAddr(env
, l
.offset
);
523 assert(l
.space
== Location::Stack
);
524 assert(env
.stackInputs
.count(0));
526 return ldStackAddr(env
, env
.stackInputs
[0]);
529 //////////////////////////////////////////////////////////////////////
531 // Compute whether the current instruction a 1-element simple collection
532 // (includes Array) operation.
533 SimpleOp
computeSimpleCollectionOp(MTS
& env
) {
534 // DataTypeGeneric is used in here to avoid constraining the base in case we
535 // end up not caring about the type. Consumers of the return value must
536 // constrain the base as appropriate.
537 auto const base
= getBase(env
, DataTypeGeneric
); // XXX: gens unneeded instrs
538 if (!base
->type().isBoxed() && base
->type().maybeBoxed()) {
539 // We might be doing a Base NL or something similar. Either way we can't
540 // do a simple op if we have a mixed boxed/unboxed type.
541 return SimpleOp::None
;
544 auto const baseType
= [&] {
545 auto const& baseDL
= *env
.ni
.inputs
[env
.iInd
];
546 // Before we do any simpleCollectionOp on a local base, we will always emit
547 // the appropriate CheckRefInner guard to allow us to use a predicted inner
548 // type. So when calculating the SimpleOp assume that type.
549 if (base
->type().maybeBoxed() && baseDL
.location
.isLocal()) {
550 return env
.irb
.predictedInnerType(baseDL
.location
.offset
);
555 auto const op
= env
.ni
.mInstrOp();
556 bool const readInst
= (op
== OpCGetM
|| op
== OpIssetM
);
557 if ((op
== OpSetM
|| readInst
) && isSimpleBase(env
) && isSingleMember(env
)) {
558 if (baseType
<= Type::Arr
) {
559 auto const isPacked
=
560 baseType
.isSpecialized() &&
561 baseType
.hasArrayKind() &&
562 baseType
.getArrayKind() == ArrayData::kPackedKind
;
563 if (mcodeIsElem(env
.ni
.immVecM
[0])) {
564 SSATmp
* key
= getInput(env
, env
.mii
.valCount() + 1, DataTypeGeneric
);
565 if (key
->isA(Type::Int
) || key
->isA(Type::Str
)) {
566 if (readInst
&& key
->isA(Type::Int
)) {
567 return isPacked
? SimpleOp::PackedArray
568 : SimpleOp::ProfiledArray
;
570 return SimpleOp::Array
;
573 } else if (baseType
<= Type::Str
&&
574 mcodeMaybeArrayIntKey(env
.ni
.immVecM
[0])) {
575 auto const key
= getInput(env
, env
.mii
.valCount() + 1, DataTypeGeneric
);
576 if (key
->isA(Type::Int
)) {
577 // Don't bother with SetM on strings, because profile data
578 // shows it basically never happens.
580 return SimpleOp::String
;
583 } else if (baseType
.strictSubtypeOf(Type::Obj
)) {
584 const Class
* klass
= baseType
.getClass();
585 auto const isVector
= klass
== c_Vector::classof();
586 auto const isPair
= klass
== c_Pair::classof();
587 auto const isMap
= klass
== c_Map::classof();
589 if (isVector
|| isPair
) {
590 if (mcodeMaybeVectorKey(env
.ni
.immVecM
[0])) {
591 auto const key
= getInput(env
, env
.mii
.valCount() + 1,
593 if (key
->isA(Type::Int
)) {
594 // We don't specialize setting pair elements.
595 if (isPair
&& op
== OpSetM
) return SimpleOp::None
;
597 return isVector
? SimpleOp::Vector
: SimpleOp::Pair
;
601 if (mcodeIsElem(env
.ni
.immVecM
[0])) {
602 auto const key
= getInput(env
, env
.mii
.valCount() + 1,
604 if (key
->isA(Type::Int
) || key
->isA(Type::Str
)) {
605 return SimpleOp::Map
;
612 return SimpleOp::None
;
615 //////////////////////////////////////////////////////////////////////
618 void emitBaseLCR(MTS
& env
) {
619 auto const& mia
= env
.mii
.getAttr(env
.ni
.immVec
.locationCode());
620 auto const& baseDL
= *env
.ni
.inputs
[env
.iInd
];
621 // We use DataTypeGeneric here because we might not care about the type. If
622 // we do, it's constrained further.
623 auto base
= getBase(env
, DataTypeGeneric
);
624 auto baseType
= base
->type();
625 assert(baseType
.isBoxed() || baseType
.notBoxed());
627 if (baseDL
.location
.isLocal()) {
628 // Check for Uninit and warn/promote to InitNull as appropriate
629 if (baseType
<= Type::Uninit
) {
630 if (mia
& MIA_warn
) {
634 cns(env
, curFunc(env
)->localVarName(baseDL
.location
.offset
)));
636 if (mia
& MIA_define
) {
637 // We care whether or not the local is Uninit, and
638 // CountnessInit will tell us that.
639 env
.irb
.constrainLocal(baseDL
.location
.offset
, DataTypeSpecific
,
640 "emitBaseLCR: Uninit base local");
641 base
= cns(env
, Type::InitNull
);
642 baseType
= Type::InitNull
;
646 LocalId(baseDL
.location
.offset
),
655 * If the base is boxed, and from a local, we can do a better translation
656 * using the inner type after guarding. If we're going to do a generic
657 * translation that uses a pointer to the local we still want this
658 * LdRef---some of the translations will be smarter if they know the inner
659 * type. This is the first code emitted for the minstr so it's ok to
662 Block
* failedRef
= baseType
.isBoxed() ? makeExit(env
) : nullptr;
663 if (baseType
.isBoxed() && baseDL
.location
.isLocal()) {
664 auto const predTy
= env
.irb
.predictedInnerType(baseDL
.location
.offset
);
665 gen(env
, CheckRefInner
, predTy
, failedRef
, base
);
666 base
= gen(env
, LdRef
, predTy
, base
);
667 baseType
= base
->type();
670 // Check for common cases where we can pass the base by value, we unboxed
671 // above if it was needed.
672 if ((baseType
.subtypeOfAny(Type::Obj
) && mcodeIsProp(env
.ni
.immVecM
[0])) ||
673 env
.simpleOp
!= SimpleOp::None
) {
674 // Register that we care about the specific type of the base, though, and
675 // might care about its specialized type.
676 setBase(env
, base
, baseType
);
677 constrainBase(env
, DataTypeSpecific
);
678 specializeBaseIfPossible(env
, baseType
);
682 // Everything else is passed by pointer. We don't have to worry about
683 // unboxing, since all the generic helpers understand boxed bases. They still
684 // may rely on the CheckRefInner guard above, though; the various emit*
685 // functions may do smarter things based on the guarded type.
686 if (baseDL
.location
.space
== Location::Local
) {
689 ldLocAddr(env
, baseDL
.location
.offset
),
691 baseDL
.location
.offset
,
696 assert(baseDL
.location
.space
== Location::Stack
);
697 // Make sure the stack is clean before getting a pointer to one of its
700 assert(env
.stackInputs
.count(env
.iInd
));
701 auto const sinfo
= getStackValue(sp(env
), env
.stackInputs
[env
.iInd
]);
704 ldStackAddr(env
, env
.stackInputs
[env
.iInd
]),
705 sinfo
.knownType
.ptr(Ptr::Stk
)
708 assert(env
.base
.value
->type().isPtr());
709 assert(env
.base
.type
.isPtr());
711 // TODO(t2598894): We do this for consistency with the old guard relaxation
712 // code, but may change it in the future.
713 constrainBase(env
, DataTypeSpecific
);
716 void emitBaseH(MTS
& env
) { setBase(env
, getBase(env
, DataTypeSpecific
)); }
718 void emitBaseN(MTS
& env
) {
719 // If this is ever implemented, the check at the beginning of
720 // checkMIState must be removed/adjusted as appropriate.
724 void emitBaseG(MTS
& env
) {
725 auto const& mia
= env
.mii
.getAttr(env
.ni
.immVec
.locationCode());
726 auto const gblName
= getBase(env
, DataTypeSpecific
);
727 if (!gblName
->isA(Type::Str
)) PUNT(BaseG
-non
-string
-name
);
730 gen(env
, BaseG
, MInstrAttrData
{ mia
}, makeEmptyCatch(env
), gblName
)
734 void emitBaseS(MTS
& env
) {
735 const int kClassIdx
= env
.ni
.inputs
.size() - 1;
736 auto const key
= getKey(env
);
737 auto const clsRef
= getInput(env
, kClassIdx
,
738 DataTypeGeneric
/* will be a Cls */);
741 * Note, the base may be a pointer to a boxed type after this. We don't
742 * unbox here, because we never are going to generate a special translation
743 * unless we know it's not boxed, and the C++ helpers for generic dims
744 * currently always conditionally unbox.
748 ldClsPropAddr(env
, makeEmptyCatch(env
), clsRef
, key
, true)
752 void emitBaseOp(MTS
& env
) {
753 switch (env
.ni
.immVec
.locationCode()) {
754 case LL
: case LC
: case LR
: emitBaseLCR(env
); break;
755 case LH
: emitBaseH(env
); break;
756 case LGL
: case LGC
: emitBaseG(env
); break;
757 case LNL
: case LNC
: emitBaseN(env
); break;
758 case LSL
: case LSC
: emitBaseS(env
); break;
759 default: not_reached();
763 //////////////////////////////////////////////////////////////////////
766 PropInfo
getCurrentPropertyOffset(MTS
& env
, const Class
*& knownCls
) {
767 auto const baseType
= env
.base
.type
.derefIfPtr();
769 if (baseType
< (Type::Obj
|Type::InitNull
) && baseType
.isSpecialized()) {
770 knownCls
= baseType
.getClass();
775 * TODO(#5616733): If we still don't have a knownCls, we can't do anything
776 * good. It's possible we still have the known information statically, and
777 * it might be in m_ni.inputs, but right now we can't really trust that
778 * because it's not very clear what it means. See task for more information.
780 if (!knownCls
) return PropInfo
{};
782 auto const info
= getPropertyOffset(env
.ni
, curClass(env
), knownCls
,
783 env
.mii
, env
.mInd
, env
.iInd
);
784 if (info
.offset
== -1) return info
;
786 auto const baseCls
= baseType
.getClass();
789 * baseCls and knownCls may differ due to a number of factors but they must
790 * always be related to each other somehow if they are both non-null.
792 * TODO(#5616733): stop using ni.inputs here. Just use the class we know
793 * from this translation, if there is one.
796 baseCls
->classof(knownCls
) || knownCls
->classof(baseCls
),
797 "Class mismatch between baseType({}) and knownCls({})",
798 baseCls
->name()->data(), knownCls
->name()->data()
801 if (env
.irb
.constrainValue(env
.base
.value
,
802 TypeConstraint(baseCls
).setWeak())) {
803 // We can't use this specialized class without making a guard more
804 // expensive, so don't do it.
812 * Helper for emitPropSpecialized to check if a property is Uninit. It returns
813 * a pointer to the property's address, or init_null_variant if the property
814 * was Uninit and doDefine is false.
816 * We can omit the uninit check for properties that we know may not be uninit
817 * due to the frontend's type inference.
819 SSATmp
* checkInitProp(MTS
& env
,
825 auto const key
= getKey(env
);
826 assert(key
->isA(Type::StaticStr
));
827 assert(baseAsObj
->isA(Type::Obj
));
828 assert(propAddr
->type().isPtr());
830 auto const needsCheck
=
831 Type::Uninit
<= propAddr
->type().deref() &&
832 // The m_mInd check is to avoid initializing a property to
833 // InitNull right before it's going to be set to something else.
834 (doWarn
|| (doDefine
&& env
.mInd
< env
.ni
.immVecM
.size() - 1));
836 if (!needsCheck
) return propAddr
;
841 gen(env
, CheckInitMem
, taken
, propAddr
, cns(env
, 0));
843 [&] { // Next: Property isn't Uninit. Do nothing.
846 [&] { // Taken: Property is Uninit. Raise a warning and return
847 // a pointer to InitNull, either in the object or
848 // init_null_variant.
849 env
.irb
.hint(Block::Hint::Unlikely
);
850 if (doWarn
&& wantPropSpecializedWarnings()) {
851 // TODO(#5710382): is the empty catch actually correct here? The
852 // pre-refactored code did a makeCatch from hhbc-translator, which was
854 gen(env
, RaiseUndefProp
, makeEmptyCatch(env
), baseAsObj
, key
);
861 cns(env
, propInfo
.offset
),
862 cns(env
, Type::InitNull
)
866 return env
.irb
.genPtrToInitNull();
871 void emitPropSpecialized(MTS
& env
, const MInstrAttr mia
, PropInfo propInfo
) {
872 assert(!(mia
& MIA_warn
) || !(mia
& MIA_unset
));
873 const bool doWarn
= mia
& MIA_warn
;
874 const bool doDefine
= mia
& MIA_define
|| mia
& MIA_unset
;
876 auto const initNull
= env
.irb
.genPtrToInitNull();
879 // After this function, m_base is either a pointer to init_null_variant or
880 // a property in the object that we've verified isn't uninit.
881 assert(env
.base
.type
.isPtr());
885 * Normal case, where the base is an object (and not a pointer to
886 * something)---just do a lea with the type information we got from static
887 * analysis. The caller of this function will use it to know whether it can
888 * avoid a generic incref, unbox, etc.
890 if (env
.base
.type
<= Type::Obj
) {
891 auto const propAddr
= gen(
894 convertToType(propInfo
.repoAuthType
).ptr(Ptr::Prop
),
896 cns(env
, propInfo
.offset
)
900 checkInitProp(env
, env
.base
.value
, propAddr
, propInfo
, doWarn
, doDefine
)
906 * We also support nullable objects for the base. This is a frequent result
907 * of static analysis on multi-dim property accesses ($foo->bar->baz), since
908 * hhbbc doesn't try to prove __construct must be run or that sort of thing
909 * (so every object-holding object property can also be null).
911 * After a null check, if it's actually an object we can just do LdPropAddr,
912 * otherwise we just give out a pointer to the init_null_variant (after
913 * raising the appropriate warnings).
915 auto const newBase
= env
.irb
.cond(
918 gen(env
, CheckTypeMem
, Type::Obj
, taken
, env
.base
.value
);
921 // Next: Base is an object. Load property and check for uninit.
922 auto const obj
= gen(
925 env
.base
.type
.deref() & Type::Obj
,
929 auto const propAddr
= gen(
932 convertToType(propInfo
.repoAuthType
).ptr(Ptr::Prop
),
934 cns(env
, propInfo
.offset
)
936 return checkInitProp(env
, obj
, propAddr
, propInfo
, doWarn
, doDefine
);
938 [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull.
939 env
.irb
.hint(Block::Hint::Unlikely
);
941 gen(env
, WarnNonObjProp
, makeMRCatch(env
));
945 * This case is where we're logically supposed to do stdClass
946 * promotion. However, it's impossible that we're going to be asked to
947 * do this with the way type inference works ahead of time right now:
949 * o In defining minstrs, the only way hhbbc will know that an object
950 * type is nullable and also specialized is if the only type it can
951 * be is ?Obj=stdClass. (This is because it does object property
952 * inference in a control flow insensitive way, so if null is
953 * possible stdClass must be added to the type, and there are no
954 * unions of multiple specialized object types.)
956 * o On the other hand, if the type was really ?Obj=stdClass, we
957 * wouldn't have gotten a known property offset for any properties,
958 * because stdClass has no declared properties, so we can't be
961 * We could punt, but it's better to assert for now because if we
962 * change this in hhbbc it will be on-purpose...
966 "Static analysis error: we would've needed to generate "
967 "stdClass-promotion code in the JIT, which is unexpected."
973 setBase(env
, newBase
);
976 void emitPropGeneric(MTS
& env
) {
977 auto const mCode
= env
.ni
.immVecM
[env
.mInd
];
978 auto const mia
= MInstrAttr(env
.mii
.getAttr(mCode
) & MIA_intermediate_prop
);
980 if ((mia
& MIA_unset
) && env
.base
.type
.strip().not(Type::Obj
)) {
981 constrainBase(env
, DataTypeSpecific
);
982 setBase(env
, env
.irb
.genPtrToInitNull());
986 auto const key
= getKey(env
);
987 if (mia
& MIA_define
) {
993 MInstrAttrData
{ mia
},
1004 MInstrAttrData
{ mia
},
1012 void emitProp(MTS
& env
) {
1013 const Class
* knownCls
= nullptr;
1014 const auto propInfo
= getCurrentPropertyOffset(env
, knownCls
);
1015 auto mia
= env
.mii
.getAttr(env
.ni
.immVecM
[env
.mInd
]);
1016 if (propInfo
.offset
== -1 || (mia
& MIA_unset
) ||
1017 mightCallMagicPropMethod(mia
, knownCls
, propInfo
)) {
1018 emitPropGeneric(env
);
1020 emitPropSpecialized(env
, mia
, propInfo
);
1024 void emitElem(MTS
& env
) {
1025 auto const mCode
= env
.ni
.immVecM
[env
.mInd
];
1026 auto const mia
= MInstrAttr(env
.mii
.getAttr(mCode
) & MIA_intermediate
);
1027 auto const key
= getKey(env
);
1029 // Fast path for the common/easy case
1030 const bool warn
= mia
& MIA_warn
;
1031 const bool unset
= mia
& MIA_unset
;
1032 const bool define
= mia
& MIA_define
;
1033 if (env
.base
.type
<= Type::PtrToArr
&&
1034 !unset
&& !define
&&
1035 (key
->isA(Type::Int
) || key
->isA(Type::Str
))) {
1039 warn
? ElemArrayW
: ElemArray
,
1047 assert(!(define
&& unset
));
1049 auto const uninit
= env
.irb
.genPtrToUninit();
1050 auto const baseType
= env
.base
.type
.strip();
1051 constrainBase(env
, DataTypeSpecific
);
1052 if (baseType
<= Type::Str
) {
1057 cns(env
, makeStaticString(Strings::OP_NOT_SUPPORTED_STRING
))
1059 setBase(env
, uninit
);
1062 if (baseType
.not(Type::Arr
| Type::Obj
)) {
1063 setBase(env
, uninit
);
1068 if (define
|| unset
) {
1072 define
? ElemDX
: ElemUX
,
1074 MInstrAttrData
{ mia
},
1086 MInstrAttrData
{ mia
},
1093 void emitNewElem(MTS
& env
) { PUNT(emitNewElem
); }
1095 void emitIntermediateOp(MTS
& env
) {
1096 switch (env
.ni
.immVecM
[env
.mInd
]) {
1097 case MEC
: case MEL
: case MET
: case MEI
: {
1102 case MPC
: case MPL
: case MPT
:
1107 assert(env
.mii
.newElem());
1110 default: not_reached();
1114 //////////////////////////////////////////////////////////////////////
1116 bool needFirstRatchet(const MTS
& env
) {
1117 auto const firstTy
= env
.ni
.inputs
[env
.mii
.valCount()]->rtt
.unbox();
1118 if (firstTy
<= Type::Arr
) {
1119 if (mcodeIsElem(env
.ni
.immVecM
[0])) return false;
1122 if (firstTy
< Type::Obj
&& firstTy
.isSpecialized()) {
1123 auto const klass
= firstTy
.getClass();
1124 auto const no_overrides
= AttrNoOverrideMagicGet
|
1125 AttrNoOverrideMagicSet
|
1126 AttrNoOverrideMagicIsset
|
1127 AttrNoOverrideMagicUnset
;
1128 if ((klass
->attrs() & no_overrides
) != no_overrides
) {
1129 // Note: we could also add a check here on whether the first property RAT
1130 // contains Uninit---if not we can still return false. See
1131 // mightCallMagicPropMethod.
1134 if (mcodeIsProp(env
.ni
.immVecM
[0])) return false;
1140 bool needFinalRatchet(const MTS
& env
) { return env
.mii
.finalGet(); }
1142 // Ratchet operations occur after each intermediate operation, except
1143 // possibly the first and last (see need{First,Final}Ratchet()). No actual
1144 // ratchet occurs after the final operation, but this means that both tvRef
1145 // and tvRef2 can contain references just after the final operation. Here we
1146 // pretend that a ratchet occurs after the final operation, i.e. a "logical"
1147 // ratchet. The reason for counting logical ratchets as part of the total is
1148 // the following case, in which the logical count is 0:
1153 // no logical ratchet
1155 // Following are a few more examples to make the algorithm clear:
1157 // (base is array) (base is object*) (base is object*)
1158 // BaseL BaseL BaseL
1159 // ElemL ElemL CGetPropL
1160 // no ratchet ratchet logical ratchet
1164 // ratchet logical ratchet
1168 // (base is array) * If the base is a known (specialized) object type,
1169 // BaseL we can also avoid the first rachet if we can
1170 // ElemL prove it can't possibly invoke magic methods.
1178 unsigned nLogicalRatchets(const MTS
& env
) {
1179 // If we've proven elsewhere that we don't need an MInstrState struct, we
1180 // know this translation won't need any ratchets
1181 if (!env
.needMIS
) return 0;
1183 unsigned ratchets
= env
.ni
.immVecM
.size();
1184 if (!needFirstRatchet(env
)) --ratchets
;
1185 if (!needFinalRatchet(env
)) --ratchets
;
1189 int ratchetInd(const MTS
& env
) {
1190 return needFirstRatchet(env
) ? int(env
.mInd
) : int(env
.mInd
) - 1;
1193 void emitRatchetRefs(MTS
& env
) {
1194 if (ratchetInd(env
) < 0 || ratchetInd(env
) >= int(nLogicalRatchets(env
))) {
1198 setBase(env
, env
.irb
.cond(
1200 [&] (Block
* taken
) {
1201 gen(env
, CheckInitMem
, taken
, env
.misBase
, cns(env
, MISOFF(tvRef
)));
1203 [&] { // Next: tvRef isn't Uninit. Ratchet the refs
1204 // Clean up tvRef2 before overwriting it.
1205 if (ratchetInd(env
) > 0) {
1206 gen(env
, DecRefMem
, Type::Gen
, env
.misBase
, cns(env
, MISOFF(tvRef2
)));
1208 // Copy tvRef to tvRef2.
1209 auto const tvRef
= gen(
1214 cns(env
, MISOFF(tvRef
))
1216 gen(env
, StMem
, env
.misBase
, cns(env
, MISOFF(tvRef2
)), tvRef
);
1219 gen(env
, StMem
, env
.misBase
, cns(env
, MISOFF(tvRef
)),
1220 cns(env
, Type::Uninit
));
1222 // Adjust base pointer.
1223 assert(env
.base
.type
.isPtr());
1224 return gen(env
, LdMIStateAddr
, env
.misBase
, cns(env
, MISOFF(tvRef2
)));
1226 [&] { // Taken: tvRef is Uninit. Do nothing.
1227 return env
.base
.value
;
1232 void emitMPre(MTS
& env
) {
1233 if (HPHP::Trace::moduleEnabled(HPHP::Trace::minstr
, 1)) {
1237 // The base location is input 0 or 1, and the location code is stored
1238 // separately from ni.immVecM, so input indices (iInd) and member indices
1239 // (mInd) commonly differ. Additionally, W members have no corresponding
1240 // inputs, so it is necessary to track the two indices separately.
1241 env
.simpleOp
= computeSimpleCollectionOp(env
);
1247 env
.misBase
= gen(env
, DefMIStateBase
);
1248 auto const uninit
= cns(env
, Type::Uninit
);
1249 if (nLogicalRatchets(env
) > 0) {
1250 gen(env
, StMem
, env
.misBase
, cns(env
, MISOFF(tvRef
)), uninit
);
1251 gen(env
, StMem
, env
.misBase
, cns(env
, MISOFF(tvRef2
)), uninit
);
1255 // Iterate over all but the last member, which is consumed by a final
1257 for (env
.mInd
= 0; env
.mInd
< env
.ni
.immVecM
.size() - 1; ++env
.mInd
) {
1258 emitIntermediateOp(env
);
1259 emitRatchetRefs(env
);
1263 //////////////////////////////////////////////////////////////////////
1265 // Build a map from (stack) input index to stack index.
1266 void numberStackInputs(MTS
& env
) {
1267 // Stack inputs are pushed in the order they appear in the vector from left
1268 // to right, so earlier elements in the vector are at higher offsets in the
1269 // stack. mii.valCount() tells us how many rvals the instruction takes on the
1270 // stack; they're pushed after any vector elements and we want to ignore them
1272 bool stackRhs
= env
.mii
.valCount() &&
1273 env
.ni
.inputs
[0]->location
.space
== Location::Stack
;
1274 int stackIdx
= (int)stackRhs
+ env
.ni
.immVec
.numStackValues() - 1;
1275 for (unsigned i
= env
.mii
.valCount(); i
< env
.ni
.inputs
.size(); ++i
) {
1276 const Location
& l
= env
.ni
.inputs
[i
]->location
;
1278 case Location::Stack
:
1279 assert(stackIdx
>= 0);
1280 env
.stackInputs
[i
] = stackIdx
--;
1287 assert(stackIdx
== (stackRhs
? 0 : -1));
1290 // If this instruction does have an RHS, it will be input 0 at
1292 assert(env
.mii
.valCount() == 1);
1293 env
.stackInputs
[0] = 0;
1297 //////////////////////////////////////////////////////////////////////
1298 // "Simple op" handlers.
1300 SSATmp
* emitPackedArrayGet(MTS
& env
, SSATmp
* base
, SSATmp
* key
) {
1301 assert(base
->isA(Type::Arr
) &&
1302 base
->type().getArrayKind() == ArrayData::kPackedKind
);
1304 auto doLdElem
= [&] {
1305 auto res
= gen(env
, LdPackedArrayElem
, base
, key
);
1306 auto unboxed
= unbox(env
, res
, nullptr);
1307 gen(env
, IncRef
, unboxed
);
1311 if (key
->isConst() &&
1312 packedArrayBoundsCheckUnnecessary(base
->type(), key
->intVal())) {
1316 return env
.irb
.cond(
1318 [&] (Block
* taken
) {
1319 gen(env
, CheckPackedArrayBounds
, taken
, base
, key
);
1325 env
.irb
.hint(Block::Hint::Unlikely
);
1326 gen(env
, RaiseArrayIndexNotice
, makeMRCatch(env
), key
);
1327 return cns(env
, Type::InitNull
);
1332 SSATmp
* emitArrayGet(MTS
& env
, SSATmp
* key
) {
1333 return gen(env
, ArrayGet
, makeMRCatch(env
), env
.base
.value
, key
);
1336 void emitProfiledArrayGet(MTS
& env
, SSATmp
* key
) {
1337 TargetProfile
<NonPackedArrayProfile
> prof(env
.hts
.context
,
1338 env
.irb
.nextMarker(),
1339 s_PackedArray
.get());
1340 if (prof
.profiling()) {
1341 gen(env
, ProfileArray
, RDSHandleData
{ prof
.handle() }, env
.base
.value
);
1342 env
.result
= emitArrayGet(env
, key
);
1346 if (prof
.optimizing()) {
1347 auto const data
= prof
.data(NonPackedArrayProfile::reduce
);
1348 // NonPackedArrayProfile data counts how many times a non-packed array was
1349 // observed. Zero means it was monomorphic (or never executed).
1350 auto const typePackedArr
= Type::Arr
.specialize(ArrayData::kPackedKind
);
1351 if (env
.base
.type
.maybe(typePackedArr
) &&
1352 (data
.count
== 0 || RuntimeOption::EvalJitPGOArrayGetStress
)) {
1353 // It's safe to side-exit still because we only do these profiled array
1354 // gets on the first element, with simple bases and single-element dims.
1355 // See computeSimpleCollectionOp.
1356 auto const exit
= makeExit(env
);
1359 gen(env
, CheckType
, typePackedArr
, exit
, env
.base
.value
)
1361 env
.irb
.constrainValue(
1363 TypeConstraint(DataTypeSpecialized
).setWantArrayKind()
1365 env
.result
= emitPackedArrayGet(env
, env
.base
.value
, key
);
1370 // Fall back to a generic array get.
1371 env
.result
= emitArrayGet(env
, key
);
1374 void emitStringGet(MTS
& env
, SSATmp
* key
) {
1375 assert(key
->isA(Type::Int
));
1376 env
.result
= gen(env
, StringGet
, makeMRCatch(env
), env
.base
.value
, key
);
1379 void emitVectorGet(MTS
& env
, SSATmp
* key
) {
1380 assert(key
->isA(Type::Int
));
1381 if (key
->isConst() && key
->intVal() < 0) {
1382 PUNT(emitVectorGet
);
1384 auto const size
= gen(env
, LdVectorSize
, env
.base
.value
);
1385 gen(env
, CheckBounds
, makeMRCatch(env
), key
, size
);
1386 auto const base
= gen(env
, LdVectorBase
, env
.base
.value
);
1387 static_assert(sizeof(TypedValue
) == 16,
1388 "TypedValue size expected to be 16 bytes");
1389 auto idx
= gen(env
, Shl
, key
, cns(env
, 4));
1390 env
.result
= gen(env
, LdElem
, base
, idx
);
1391 gen(env
, IncRef
, env
.result
);
1394 void emitPairGet(MTS
& env
, SSATmp
* key
) {
1395 assert(key
->isA(Type::Int
));
1396 static_assert(sizeof(TypedValue
) == 16,
1397 "TypedValue size expected to be 16 bytes");
1398 if (key
->isConst()) {
1399 auto idx
= key
->intVal();
1400 if (idx
< 0 || idx
> 1) {
1403 // no reason to check bounds
1404 auto const base
= gen(env
, LdPairBase
, env
.base
.value
);
1405 auto index
= cns(env
, key
->intVal() << 4);
1406 env
.result
= gen(env
, LdElem
, base
, index
);
1408 gen(env
, CheckBounds
, makeMRCatch(env
), key
, cns(env
, 1));
1409 auto const base
= gen(env
, LdPairBase
, env
.base
.value
);
1410 auto idx
= gen(env
, Shl
, key
, cns(env
, 4));
1411 env
.result
= gen(env
, LdElem
, base
, idx
);
1413 gen(env
, IncRef
, env
.result
);
1416 void emitPackedArrayIsset(MTS
& env
) {
1417 assert(env
.base
.type
.getArrayKind() == ArrayData::kPackedKind
);
1418 auto const key
= getKey(env
);
1419 env
.result
= env
.irb
.cond(
1421 [&] (Block
* taken
) {
1422 gen(env
, CheckPackedArrayBounds
, taken
, env
.base
.value
, key
);
1425 return gen(env
, IsPackedArrayElemNull
, env
.base
.value
, key
);
1428 return cns(env
, false);
1433 void emitArraySet(MTS
& env
, SSATmp
* key
, SSATmp
* value
) {
1434 assert(env
.iInd
== env
.mii
.valCount() + 1);
1435 const int baseStkIdx
= env
.mii
.valCount();
1436 assert(key
->type().notBoxed());
1437 assert(value
->type().notBoxed());
1439 auto const& base
= *env
.ni
.inputs
[env
.mii
.valCount()];
1440 bool const setRef
= base
.rtt
.isBoxed();
1442 // No catch trace below because the helper can't throw. It may reenter to
1443 // call destructors so it has a sync point in nativecalls.cpp, but exceptions
1444 // are swallowed at destructor boundaries right now: #2182869.
1446 assert(base
.location
.space
== Location::Local
||
1447 base
.location
.space
== Location::Stack
);
1448 auto const box
= getInput(env
, baseStkIdx
, DataTypeSpecific
);
1449 gen(env
, ArraySetRef
, makeMRCatch(env
), env
.base
.value
, key
, value
, box
);
1450 // Unlike the non-ref case, we don't need to do anything to the stack
1451 // because any load of the box will be guarded.
1456 auto const newArr
= gen(
1465 // Update the base's value with the new array
1466 if (base
.location
.space
== Location::Local
) {
1467 // We know it's not boxed (setRef above handles that), and
1468 // newArr has already been incref'd in the helper.
1469 gen(env
, StLoc
, LocalId(base
.location
.offset
), fp(env
), newArr
);
1470 } else if (base
.location
.space
== Location::Stack
) {
1471 extendStack(env
, baseStkIdx
, Type::Gen
);
1472 env
.irb
.evalStack().replace(baseStkIdx
, newArr
);
1480 void emitVectorSet(MTS
& env
, SSATmp
* key
, SSATmp
* value
) {
1481 assert(key
->isA(Type::Int
));
1482 if (key
->isConst() && key
->intVal() < 0) {
1483 PUNT(emitVectorSet
); // will throw
1485 auto const size
= gen(env
, LdVectorSize
, env
.base
.value
);
1486 gen(env
, CheckBounds
, makeMRCatch(env
), key
, size
);
1490 gen(env
, VectorHasImmCopy
, taken
, env
.base
.value
);
1493 env
.irb
.hint(Block::Hint::Unlikely
);
1494 gen(env
, VectorDoCow
, env
.base
.value
);
1498 gen(env
, IncRef
, value
);
1499 auto const vecBase
= gen(env
, LdVectorBase
, env
.base
.value
);
1500 static_assert(sizeof(TypedValue
) == 16,
1501 "TypedValue size expected to be 16 bytes");
1502 auto const idx
= gen(env
, Shl
, key
, cns(env
, 4));
1503 auto const oldVal
= gen(env
, LdElem
, vecBase
, idx
);
1504 gen(env
, StElem
, vecBase
, idx
, value
);
1505 gen(env
, DecRef
, oldVal
);
1510 //////////////////////////////////////////////////////////////////////
1512 void emitCGetProp(MTS
& env
) {
1513 const Class
* knownCls
= nullptr;
1514 const auto propInfo
= getCurrentPropertyOffset(env
, knownCls
);
1516 if (propInfo
.offset
!= -1 &&
1517 !mightCallMagicPropMethod(MIA_none
, knownCls
, propInfo
)) {
1518 emitPropSpecialized(env
, MIA_warn
, propInfo
);
1520 if (!RuntimeOption::RepoAuthoritative
) {
1521 auto const cellPtr
= gen(env
, UnboxPtr
, env
.base
.value
);
1522 env
.result
= gen(env
, LdMem
, Type::Cell
, cellPtr
, cns(env
, 0));
1523 gen(env
, IncRef
, env
.result
);
1527 auto const ty
= env
.base
.type
.deref();
1528 auto const cellPtr
= ty
.maybeBoxed() ? gen(env
, UnboxPtr
, env
.base
.value
)
1530 env
.result
= gen(env
, LdMem
, ty
.unbox(), cellPtr
, cns(env
, 0));
1531 gen(env
, IncRef
, env
.result
);
1535 auto const key
= getKey(env
);
1546 void emitVGetProp(MTS
& env
) {
1547 auto const key
= getKey(env
);
1548 env
.result
= genStk(env
, VGetProp
, makeMRCatch(env
), NoExtraData
{},
1549 env
.base
.value
, key
, misPtr(env
));
1552 void emitIssetProp(MTS
& env
) {
1553 auto const key
= getKey(env
);
1554 env
.result
= gen(env
, IssetProp
, makeMRCatch(env
), env
.base
.value
, key
);
1557 void emitEmptyProp(MTS
& env
) {
1558 auto const key
= getKey(env
);
1559 env
.result
= gen(env
, EmptyProp
, makeMRCatch(env
), env
.base
.value
, key
);
1562 void emitSetProp(MTS
& env
) {
1563 auto const value
= getValue(env
);
1565 /* If we know the class for the current base, emit a direct property set. */
1566 const Class
* knownCls
= nullptr;
1567 const auto propInfo
= getCurrentPropertyOffset(env
, knownCls
);
1569 if (propInfo
.offset
!= -1 &&
1570 !mightCallMagicPropMethod(MIA_define
, knownCls
, propInfo
)) {
1571 emitPropSpecialized(env
, MIA_define
, propInfo
);
1573 auto const propTy
= env
.base
.type
.deref();
1574 auto const cellTy
= propTy
.maybeBoxed() ? propTy
.unbox() : propTy
;
1575 auto const cellPtr
= propTy
.maybeBoxed() ? gen(env
, UnboxPtr
, env
.base
.value
)
1577 auto const oldVal
= gen(env
, LdMem
, cellTy
, cellPtr
, cns(env
, 0));
1579 gen(env
, IncRef
, value
);
1580 gen(env
, StMem
, cellPtr
, cns(env
, 0), value
);
1581 gen(env
, DecRef
, oldVal
);
1586 // Emit the appropriate helper call.
1587 auto const key
= getKey(env
);
1588 genStk(env
, SetProp
, makeCatchSet(env
), NoExtraData
{},
1589 env
.base
.value
, key
, value
);
1593 void emitSetOpProp(MTS
& env
) {
1594 SetOpOp op
= SetOpOp(env
.ni
.imm
[0].u_OA
);
1595 auto const key
= getKey(env
);
1596 auto const value
= getValue(env
);
1597 env
.result
= genStk(env
, SetOpProp
, makeMRCatch(env
), SetOpData
{ op
},
1598 env
.base
.value
, key
, value
, misPtr(env
));
1601 void emitIncDecProp(MTS
& env
) {
1602 IncDecOp op
= static_cast<IncDecOp
>(env
.ni
.imm
[0].u_OA
);
1603 auto const key
= getKey(env
);
1604 env
.result
= genStk(env
, IncDecProp
, makeMRCatch(env
), IncDecData
{ op
},
1605 env
.base
.value
, key
, misPtr(env
));
1608 void emitBindProp(MTS
& env
) {
1609 auto const key
= getKey(env
);
1610 auto const box
= getValue(env
);
1611 genStk(env
, BindProp
, makeMRCatch(env
), NoExtraData
{},
1612 env
.base
.value
, key
, box
, misPtr(env
));
1616 void emitUnsetProp(MTS
& env
) {
1617 auto const key
= getKey(env
);
1618 if (env
.base
.type
.strip().not(Type::Obj
)) {
1620 constrainBase(env
, DataTypeSpecific
);
1623 gen(env
, UnsetProp
, makeMRCatch(env
), env
.base
.value
, key
);
1626 void emitCGetElem(MTS
& env
) {
1627 auto const key
= getKey(env
);
1629 switch (env
.simpleOp
) {
1630 case SimpleOp::Array
:
1631 env
.result
= emitArrayGet(env
, key
);
1633 case SimpleOp::PackedArray
:
1634 env
.result
= emitPackedArrayGet(env
, env
.base
.value
, key
);
1636 case SimpleOp::ProfiledArray
:
1637 emitProfiledArrayGet(env
, key
);
1639 case SimpleOp::String
:
1640 emitStringGet(env
, key
);
1642 case SimpleOp::Vector
:
1643 emitVectorGet(env
, key
);
1645 case SimpleOp::Pair
:
1646 emitPairGet(env
, key
);
1649 env
.result
= gen(env
, MapGet
, makeMRCatch(env
), env
.base
.value
, key
);
1651 case SimpleOp::None
:
1652 env
.result
= gen(env
, CGetElem
, makeMRCatch(env
), env
.base
.value
,
1658 void emitVGetElem(MTS
& env
) {
1659 auto const key
= getKey(env
);
1660 env
.result
= genStk(env
, VGetElem
, makeMRCatch(env
), NoExtraData
{},
1661 env
.base
.value
, key
, misPtr(env
));
1664 void emitIssetElem(MTS
& env
) {
1665 switch (env
.simpleOp
) {
1666 case SimpleOp::Array
:
1667 case SimpleOp::ProfiledArray
:
1668 env
.result
= gen(env
, ArrayIsset
, makeMRCatch(env
), env
.base
.value
,
1671 case SimpleOp::PackedArray
:
1672 emitPackedArrayIsset(env
);
1674 case SimpleOp::String
:
1675 env
.result
= gen(env
, StringIsset
, env
.base
.value
, getKey(env
));
1677 case SimpleOp::Vector
:
1678 env
.result
= gen(env
, VectorIsset
, env
.base
.value
, getKey(env
));
1680 case SimpleOp::Pair
:
1681 env
.result
= gen(env
, PairIsset
, env
.base
.value
, getKey(env
));
1684 env
.result
= gen(env
, MapIsset
, env
.base
.value
, getKey(env
));
1686 case SimpleOp::None
:
1688 auto const key
= getKey(env
);
1689 env
.result
= gen(env
, IssetElem
, makeMRCatch(env
),
1690 env
.base
.value
, key
, misPtr(env
));
1696 void emitEmptyElem(MTS
& env
) {
1697 auto const key
= getKey(env
);
1698 env
.result
= gen(env
, EmptyElem
, makeMRCatch(env
),
1699 env
.base
.value
, key
, misPtr(env
));
1702 void emitSetNewElem(MTS
& env
) {
1703 auto const value
= getValue(env
);
1704 if (env
.base
.type
<= Type::PtrToArr
) {
1705 constrainBase(env
, DataTypeSpecific
);
1706 gen(env
, SetNewElemArray
, makeCatchSet(env
), env
.base
.value
, value
);
1708 gen(env
, SetNewElem
, makeCatchSet(env
), env
.base
.value
, value
);
1713 void emitSetWithRefLProp(MTS
& env
) { SPUNT(__func__
); }
1714 void emitSetWithRefRProp(MTS
& env
) { emitSetWithRefLProp(env
); }
1716 void emitSetWithRefNewElem(MTS
& env
) {
1717 if (env
.base
.type
.strip() <= Type::Arr
&& getValue(env
)->type().notBoxed()) {
1718 constrainBase(env
, DataTypeSpecific
);
1719 emitSetNewElem(env
);
1721 genStk(env
, SetWithRefNewElem
, makeMRCatch(env
),
1723 env
.base
.value
, getValAddr(env
), misPtr(env
));
1725 env
.result
= nullptr;
1728 void emitSetElem(MTS
& env
) {
1729 auto const value
= getValue(env
);
1730 auto const key
= getKey(env
);
1732 switch (env
.simpleOp
) {
1733 case SimpleOp::Array
:
1734 case SimpleOp::ProfiledArray
:
1735 emitArraySet(env
, key
, value
);
1737 case SimpleOp::PackedArray
:
1738 case SimpleOp::String
:
1739 always_assert(false && "Bad SimpleOp in emitSetElem");
1741 case SimpleOp::Vector
:
1742 emitVectorSet(env
, key
, value
);
1745 gen(env
, MapSet
, makeMRCatch(env
), env
.base
.value
, key
, value
);
1748 case SimpleOp::Pair
:
1749 case SimpleOp::None
:
1750 constrainBase(env
, DataTypeSpecific
);
1751 auto const result
= genStk(env
, SetElem
, makeCatchSet(env
), NoExtraData
{},
1752 env
.base
.value
, key
, value
);
1753 auto const t
= result
->type();
1754 if (t
== Type::Nullptr
) {
1755 // Base is not a string. Result is always value.
1757 } else if (t
== Type::CountedStr
) {
1758 // Base is a string. Stack result is a new string so we're responsible for
1759 // decreffing value.
1760 env
.result
= result
;
1761 gen(env
, DecRef
, value
);
1763 assert(t
.equals(Type::CountedStr
| Type::Nullptr
));
1764 // Base might be a string. Assume the result is value, then inform
1765 // emitMPost that it needs to test the actual result.
1767 env
.strTestResult
= result
;
1773 void emitSetWithRefLElem(MTS
& env
) {
1774 auto const key
= getKey(env
);
1775 auto const locAddr
= getValAddr(env
);
1776 if (env
.base
.type
.strip() <= Type::Arr
&&
1777 !locAddr
->type().deref().maybeBoxed()) {
1778 constrainBase(env
, DataTypeSpecific
);
1780 assert(env
.strTestResult
== nullptr);
1782 genStk(env
, SetWithRefElem
, makeMRCatch(env
), NoExtraData
{},
1783 env
.base
.value
, key
, locAddr
, misPtr(env
));
1785 env
.result
= nullptr;
1787 void emitSetWithRefRElem(MTS
& env
) { emitSetWithRefLElem(env
); }
1789 void emitSetOpElem(MTS
& env
) {
1790 auto const op
= static_cast<SetOpOp
>(env
.ni
.imm
[0].u_OA
);
1791 env
.result
= genStk(env
, SetOpElem
, makeMRCatch(env
), SetOpData
{op
},
1792 env
.base
.value
, getKey(env
), getValue(env
),
1796 void emitIncDecElem(MTS
& env
) {
1797 auto const op
= static_cast<IncDecOp
>(env
.ni
.imm
[0].u_OA
);
1798 env
.result
= genStk(env
, IncDecElem
, makeMRCatch(env
), IncDecData
{ op
},
1799 env
.base
.value
, getKey(env
), misPtr(env
));
1802 void emitBindElem(MTS
& env
) {
1803 auto const key
= getKey(env
);
1804 auto const box
= getValue(env
);
1805 genStk(env
, BindElem
, makeMRCatch(env
), NoExtraData
{},
1806 env
.base
.value
, key
, box
, misPtr(env
));
1810 void emitUnsetElem(MTS
& env
) {
1811 auto const key
= getKey(env
);
1813 auto const baseType
= env
.base
.type
.strip();
1814 constrainBase(env
, DataTypeSpecific
);
1815 if (baseType
<= Type::Str
) {
1819 cns(env
, makeStaticString(Strings::CANT_UNSET_STRING
)));
1822 if (baseType
.not(Type::Arr
| Type::Obj
)) {
1827 genStk(env
, UnsetElem
, makeMRCatch(env
), NoExtraData
{}, env
.base
.value
, key
);
1830 void emitNotSuppNewElem(MTS
& env
) {
1831 PUNT(NotSuppNewElem
);
1834 void emitVGetNewElem(MTS
& env
) {
1838 void emitSetOpNewElem(MTS
& env
) {
1842 void emitIncDecNewElem(MTS
& env
) {
1846 void emitBindNewElem(MTS
& env
) {
1847 auto const box
= getValue(env
);
1848 genStk(env
, BindNewElem
, makeMRCatch(env
), NoExtraData
{},
1849 env
.base
.value
, box
, misPtr(env
));
1853 void emitFinalMOp(MTS
& env
) {
1854 using MemFun
= void (*)(MTS
&);
1856 switch (env
.ni
.immVecM
[env
.mInd
]) {
1857 case MEC
: case MEL
: case MET
: case MEI
:
1858 static MemFun elemOps
[] = {
1859 # define MII(instr, ...) &emit##instr##Elem,
1863 elemOps
[env
.mii
.instr()](env
);
1866 case MPC
: case MPL
: case MPT
:
1867 static MemFun propOps
[] = {
1868 # define MII(instr, ...) &emit##instr##Prop,
1872 propOps
[env
.mii
.instr()](env
);
1876 assert(env
.mii
.getAttr(MW
) & MIA_final
);
1877 static MemFun newOps
[] = {
1878 # define MII(instr, attrs, bS, iS, vC, fN) \
1883 newOps
[env
.mii
.instr()](env
);
1886 default: not_reached();
1890 //////////////////////////////////////////////////////////////////////
1892 void prependToTraces(MTS
& env
, IRInstruction
* inst
) {
1893 for (auto b
: env
.failedVec
) {
1894 b
->prepend(env
.unit
.cloneInstruction(inst
));
1898 void emitSideExits(MTS
& env
, SSATmp
* catchSp
, int nStack
) {
1899 auto const nextOff
= nextBcOff(env
);
1900 auto const op
= env
.ni
.mInstrOp();
1901 const bool isSetWithRef
= op
== OpSetWithRefLM
|| op
== OpSetWithRefRM
;
1903 if (env
.failedSetBlock
) {
1904 assert(bool(env
.result
) ^ isSetWithRef
);
1905 // We need to emit code to clean up and side exit if the TryEndCatch falls
1906 // through because of an InvalidSetMException. If we're translating a
1907 // SetWithRef* bytecode we don't have to do anything special to the stack
1908 // since they have no stack output. Otherwise we need to pop our input
1909 // value and push the value from the exception to the stack (done with a
1910 // DecRefStack followed by a SpillStack).
1912 auto args
= std::vector
<SSATmp
*> {
1913 catchSp
, // sp from the previous SpillStack
1914 cns(env
, nStack
), // cells popped since the last SpillStack
1917 // Need to save FP, we're switching to our side exit block, but it hasn't
1918 // had a predecessor propagate state to it via FrameStateMgr::finishBlock
1920 auto const frame
= fp(env
);
1922 BlockPusher
bp(env
.irb
, env
.marker
, env
.failedSetBlock
);
1923 if (!isSetWithRef
) {
1924 gen(env
, DecRefStack
, StackOffset(0), Type::Cell
, catchSp
);
1925 args
.push_back(gen(env
, LdUnwinderValue
, Type::Cell
));
1928 auto const stack
= gen(
1931 std::make_pair(args
.size(), &args
[0])
1933 gen(env
, DeleteUnwinderException
);
1934 gen(env
, SyncABIRegs
, frame
, stack
);
1935 gen(env
, ReqBindJmp
, ReqBindJmpData(nextOff
));
1938 if (env
.strTestResult
) {
1939 assert(!isSetWithRef
);
1940 // We expected SetElem's base to not be a Str but might be wrong. Make an
1941 // exit trace to side exit to the next instruction, replacing our guess
1942 // with the correct stack output.
1944 [&] (Block
* taken
) {
1945 gen(env
, CheckNullptr
, taken
, env
.strTestResult
);
1948 env
.irb
.hint(Block::Hint::Unlikely
);
1949 auto const str
= gen(env
, AssertNonNull
, env
.strTestResult
);
1950 popC(env
); // we already pushed env.result
1951 gen(env
, DecRef
, env
.result
);
1953 gen(env
, Jmp
, makeExit(env
, nextBcOff(env
)));
1959 void emitMPost(MTS
& env
) {
1960 SSATmp
* catchSp
= nullptr;
1961 if (env
.failedSetBlock
) {
1962 auto const endCatch
= &env
.failedSetBlock
->back();
1963 assert(endCatch
->is(EndCatch
, TryEndCatch
));
1964 catchSp
= endCatch
->src(1);
1965 assert(catchSp
->isA(Type::StkPtr
));
1968 // Decref stack inputs. If we're translating a SetM or BindM, then input 0 is
1969 // both our input and output so leave its refcount alone. If m_failedSetBlock
1970 // is non-null, the final helper call may throw an InvalidSetMException. We
1971 // need to add instructions to m_failedSetBlock to finish the vector
1972 // instruction in case this happens, so any DecRefs emitted here are also
1973 // added to m_failedSetBlock.
1975 (env
.ni
.mInstrOp() == OpSetM
|| env
.ni
.mInstrOp() == OpBindM
) ? 1 : 0;
1976 for (unsigned i
= nStack
; i
< env
.ni
.inputs
.size(); ++i
) {
1977 auto const& input
= *env
.ni
.inputs
[i
];
1978 switch (input
.location
.space
) {
1979 case Location::Stack
: {
1981 auto input
= getInput(env
, i
, DataTypeSpecific
);
1982 if (input
->isA(Type::Gen
)) {
1983 gen(env
, DecRef
, input
);
1984 if (env
.failedSetBlock
) {
1985 BlockPauser
bp(env
.irb
, env
.marker
, env
.failedSetBlock
);
1988 StackOffset(env
.stackInputs
[i
]),
1995 case Location::Local
:
1996 case Location::Litstr
:
1997 case Location::Litint
:
1998 case Location::This
: {
2003 default: not_reached();
2007 // Pop off all stack inputs
2008 discard(env
, nStack
);
2010 // Push result, if one was produced. If we have a predicted result use that
2011 // instead of the real result; its validity will be guarded later in this
2014 push(env
, env
.result
);
2016 assert(env
.ni
.mInstrOp() == Op::UnsetM
||
2017 env
.ni
.mInstrOp() == Op::SetWithRefLM
||
2018 env
.ni
.mInstrOp() == Op::SetWithRefRM
);
2021 // Clean up tvRef(2): during exception handling any objects required only
2022 // during vector expansion need to be DecRef'd. There may be either one
2023 // or two such scratch objects, in the case of a Set the first of which will
2024 // always be tvRef2, in all other cases if only one scratch value is present
2025 // it will be stored in tvRef.
2026 static constexpr size_t refOffs
[] = { MISOFF(tvRef
), MISOFF(tvRef2
) };
2027 for (unsigned i
= 0; i
< std::min(nLogicalRatchets(env
), 2U); ++i
) {
2028 auto const inst
= env
.unit
.gen(
2033 cns(env
, refOffs
[env
.failedSetBlock
? 1 - i
: i
])
2036 prependToTraces(env
, inst
);
2039 emitSideExits(env
, catchSp
, nStack
);
2042 //////////////////////////////////////////////////////////////////////
2044 void implMInstr(HTS
& hts
) {
2045 if (curFunc(hts
)->isPseudoMain()) {
2046 interpOne(hts
, *hts
.currentNormalizedInstruction
);
2050 auto env
= MTS
{ hts
};
2051 numberStackInputs(env
); // Assign stack slots to our stack inputs
2052 emitMPre(env
); // Emit the base and every intermediate op
2053 emitFinalMOp(env
); // Emit the final operation
2054 emitMPost(env
); // Cleanup: decref inputs and scratch values
2057 //////////////////////////////////////////////////////////////////////
2061 void emitBindM(HTS
& env
, int) { implMInstr(env
); }
2062 void emitCGetM(HTS
& env
, int) { implMInstr(env
); }
2063 void emitEmptyM(HTS
& env
, int) { implMInstr(env
); }
2064 void emitFPassM(HTS
& env
, int32_t, int) { implMInstr(env
); }
2065 void emitIncDecM(HTS
& env
, IncDecOp
, int) { implMInstr(env
); }
2066 void emitIssetM(HTS
& env
, int) { implMInstr(env
); }
2067 void emitSetM(HTS
& env
, int) { implMInstr(env
); }
2068 void emitSetOpM(HTS
& env
, SetOpOp
, int) { implMInstr(env
); }
2069 void emitSetWithRefLM(HTS
& env
, int, int32_t) { implMInstr(env
); }
2070 void emitSetWithRefRM(HTS
& env
, int) { implMInstr(env
); }
2071 void emitUnsetM(HTS
& env
, int) { implMInstr(env
); }
2072 void emitVGetM(HTS
& env
, int) { implMInstr(env
); }
2074 //////////////////////////////////////////////////////////////////////