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 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/mixed-array-defs.h"
21 #include "hphp/runtime/base/strings.h"
22 #include "hphp/runtime/vm/member-operations.h"
23 #include "hphp/runtime/vm/jit/frame-state.h"
24 #include "hphp/runtime/vm/jit/hhbc-translator.h"
25 #include "hphp/runtime/vm/jit/ir-instruction.h"
26 #include "hphp/runtime/vm/jit/ir.h"
27 #include "hphp/runtime/vm/jit/normalized-instruction.h"
28 #include "hphp/runtime/vm/jit/mc-generator.h"
29 #include "hphp/runtime/vm/repo.h"
30 #include "hphp/runtime/vm/repo-global-data.h"
31 #include "hphp/runtime/vm/jit/target-profile.h"
33 // These files do ugly things with macros so include them last
34 #include "hphp/util/assert-throw.h"
35 #include "hphp/runtime/vm/jit/minstr-translator-internal.h"
37 namespace HPHP
{ namespace JIT
{
41 using HPHP::JIT::Location
;
43 static bool wantPropSpecializedWarnings() {
44 return !RuntimeOption::RepoAuthoritative
||
45 !RuntimeOption::EvalDisableSomeRepoAuthNotices
;
48 bool MInstrEffects::supported(Opcode op
) {
49 return opcodeHasFlags(op
, MInstrProp
| MInstrElem
);
51 bool MInstrEffects::supported(const IRInstruction
* inst
) {
52 return supported(inst
->op());
55 void MInstrEffects::get(const IRInstruction
* inst
, LocalStateHook
& hook
) {
56 // If the base for this instruction is a local address, the helper call might
57 // have side effects on the local's value
58 SSATmp
* base
= inst
->src(minstrBaseIdx(inst
));
59 IRInstruction
* locInstr
= base
->inst();
60 if (locInstr
->op() == LdLocAddr
) {
61 UNUSED Type baseType
= locInstr
->dst()->type();
62 assert(baseType
.equals(base
->type()));
63 assert(baseType
.isPtr() || baseType
.isKnownDataType());
64 int loc
= locInstr
->extra
<LdLocAddr
>()->locId
;
66 MInstrEffects
effects(inst
);
67 if (effects
.baseTypeChanged
|| effects
.baseValChanged
) {
68 hook
.setLocalType(loc
, effects
.baseType
.derefIfPtr());
73 MInstrEffects::MInstrEffects(const IRInstruction
* inst
) {
74 init(inst
->op(), inst
->src(minstrBaseIdx(inst
))->type());
77 MInstrEffects::MInstrEffects(Opcode op
, Type base
) {
81 MInstrEffects::MInstrEffects(Opcode op
, SSATmp
* base
) {
82 assert(base
!= nullptr);
83 init(op
, base
->type());
86 MInstrEffects::MInstrEffects(Opcode opc
, const std::vector
<SSATmp
*>& srcs
) {
87 init(opc
, srcs
[minstrBaseIdx(opc
)]->type());
91 Opcode
canonicalOp(Opcode op
) {
92 if (op
== ElemUX
|| op
== ElemUXStk
||
93 op
== UnsetElem
|| op
== UnsetElemStk
) {
96 if (op
== SetWithRefElem
||
97 op
== SetWithRefElemStk
||
98 op
== SetWithRefNewElem
||
99 op
== SetWithRefNewElemStk
) {
100 return SetWithRefElem
;
102 return opcodeHasFlags(op
, MInstrProp
) ? SetProp
103 : opcodeHasFlags(op
, MInstrElem
) || op
== ArraySet
? SetElem
104 : bad_value
<Opcode
>();
107 void getBaseType(Opcode rawOp
, bool predict
,
108 Type
& baseType
, bool& baseValChanged
) {
109 always_assert(baseType
.notBoxed());
110 auto const op
= canonicalOp(rawOp
);
112 // Deal with possible promotion to stdClass or array
113 if ((op
== SetElem
|| op
== SetProp
|| op
== SetWithRefElem
) &&
114 baseType
.maybe(Type::Null
| Type::Bool
| Type::Str
)) {
115 auto newBase
= op
== SetProp
? Type::Obj
: Type::Arr
;
118 /* If the output type will be used as a prediction and not as fact, we
119 * can be optimistic here. Assume no promotion for string bases and
120 * promotion in other cases. */
121 baseType
= baseType
<= Type::Str
? Type::Str
: newBase
;
122 } else if (baseType
<= Type::Str
&&
123 (rawOp
== SetElem
|| rawOp
== SetElemStk
)) {
124 /* If the base is known to be a string and the operation is exactly
125 * SetElem, we're guaranteed that either the base will end as a
126 * CountedStr or the instruction will throw an exception and side
128 baseType
= Type::CountedStr
;
129 } else if (baseType
<= Type::Str
&&
130 (rawOp
== SetNewElem
|| rawOp
== SetNewElemStk
)) {
131 /* If the string base is empty, it will be promoted to an
132 * array. Otherwise the base will be left alone and we'll fatal. */
133 baseType
= Type::Arr
;
135 /* Regardless of whether or not promotion happens, we know the base
136 * cannot be Null after the operation. If the base was a subtype of Null
137 * this will give newBase. */
138 baseType
= (baseType
- Type::Null
) | newBase
;
141 baseValChanged
= true;
144 if ((op
== SetElem
|| op
== UnsetElem
|| op
== SetWithRefElem
) &&
145 baseType
.maybe(Type::Arr
| Type::Str
)) {
146 /* Modifying an array or string element, even when COW doesn't kick in,
147 * produces a new SSATmp for the base. StaticArr/StaticStr may be promoted
148 * to CountedArr/CountedStr. */
149 baseValChanged
= true;
150 if (baseType
.maybe(Type::StaticArr
)) baseType
|= Type::CountedArr
;
151 if (baseType
.maybe(Type::StaticStr
)) baseType
|= Type::CountedStr
;
156 void MInstrEffects::init(const Opcode rawOp
, const Type origBase
) {
158 always_assert(baseType
.isPtr() ^ baseType
.notPtr());
159 auto const basePtr
= baseType
.isPtr();
160 baseType
= baseType
.derefIfPtr();
162 // Only certain types of bases are supported now but this list may expand in
164 assert_not_implemented(basePtr
||
165 baseType
.subtypeOfAny(Type::Obj
, Type::Arr
));
167 baseTypeChanged
= baseValChanged
= false;
169 // Process the inner and outer types separately and then recombine them,
170 // since the minstr operations all operate on the inner cell of boxed
171 // bases. We treat the new inner type as a prediction because it will be
172 // verified the next time we load from the box.
173 auto inner
= (baseType
& Type::BoxedCell
).innerType();
174 auto outer
= baseType
& Type::Cell
;
175 getBaseType(rawOp
, false, outer
, baseValChanged
);
176 getBaseType(rawOp
, true, inner
, baseValChanged
);
178 baseType
= inner
.box() | outer
;
179 baseType
= basePtr
? baseType
.ptr() : baseType
;
181 baseTypeChanged
= baseType
!= origBase
;
183 /* Boxed bases may have their inner value changed but the value of the box
184 * will never change. */
185 baseValChanged
= !origBase
.isBoxed() && (baseValChanged
|| baseTypeChanged
);
188 // minstrBaseIdx returns the src index for inst's base operand.
189 int minstrBaseIdx(Opcode opc
) {
190 return opc
== SetNewElem
|| opc
== SetNewElemStk
? 0
191 : opc
== SetNewElemArray
|| opc
== SetNewElemArrayStk
? 0
192 : opc
== BindNewElem
|| opc
== BindNewElemStk
? 0
193 : opc
== ArraySet
? 1
194 : opc
== SetOpElem
|| opc
== SetOpElemStk
? 0
195 : opc
== IncDecElem
|| opc
== IncDecElemStk
? 0
196 : opcodeHasFlags(opc
, MInstrProp
) ? 2
197 : opcodeHasFlags(opc
, MInstrElem
) ? 1
200 int minstrBaseIdx(const IRInstruction
* inst
) {
201 return minstrBaseIdx(inst
->op());
204 HhbcTranslator::MInstrTranslator::MInstrTranslator(
205 const NormalizedInstruction
& ni
,
210 , m_unit(m_ht
.m_unit
)
211 , m_mii(getMInstrInfo(ni
.mInstrOp()))
212 , m_marker(ht
.makeMarker(ht
.bcOff()))
213 , m_iInd(m_mii
.valCount())
218 , m_strTestResult(nullptr)
219 , m_failedSetBlock(nullptr)
223 template<typename
... Srcs
>
224 SSATmp
* HhbcTranslator::MInstrTranslator::genStk(Opcode opc
, Block
* taken
,
226 assert(opcodeHasFlags(opc
, HasStackVersion
));
227 assert(!opcodeHasFlags(opc
, ModifiesStack
));
229 // We're going to make decisions based on the type of the base.
230 constrainBase(DataTypeSpecific
);
232 std::vector
<SSATmp
*> srcVec({srcs
...});
233 SSATmp
* base
= srcVec
[minstrBaseIdx(opc
)];
235 /* If the base is a pointer to a stack cell and the operation might change
236 * its type and/or value, use the version of the opcode that returns a new
238 if (base
->inst()->op() == LdStackAddr
) {
239 MInstrEffects
effects(opc
, srcVec
);
240 if (effects
.baseTypeChanged
|| effects
.baseValChanged
) {
241 opc
= getStackModifyingOpcode(opc
);
245 return gen(opc
, taken
, srcs
...);
248 void HhbcTranslator::MInstrTranslator::emit() {
249 // Assign stack slots to our stack inputs
251 // Emit the base and every intermediate op
253 // Emit the final operation
255 // Cleanup: decref inputs and scratch values
259 // Returns a pointer to the base of the current MInstrState struct, or
260 // a null pointer if it's not needed.
261 SSATmp
* HhbcTranslator::MInstrTranslator::genMisPtr() {
263 return gen(LdMIStateAddr
, m_misBase
, cns(kReservedRSPSpillSpace
));
265 return cns(Type::cns(nullptr, Type::PtrToUninit
));
271 bool mightCallMagicPropMethod(MInstrAttr mia
, const Class
* cls
,
273 auto const objAttr
= (mia
& Define
) ? ObjectData::UseSet
: ObjectData::UseGet
;
274 auto const clsHasMagicMethod
= !cls
|| (cls
->getODAttrs() & objAttr
);
276 return clsHasMagicMethod
&&
277 convertToType(propInfo
.repoAuthType
).maybe(Type::Uninit
);
281 mInstrHasUnknownOffsets(const NormalizedInstruction
& ni
, Class
* context
) {
282 const MInstrInfo
& mii
= getMInstrInfo(ni
.mInstrOp());
284 unsigned ii
= mii
.valCount() + 1;
285 for (; mi
< ni
.immVecM
.size(); ++mi
) {
286 MemberCode mc
= ni
.immVecM
[mi
];
287 if (mcodeIsProp(mc
)) {
288 const Class
* cls
= nullptr;
289 auto propInfo
= getPropertyOffset(ni
, context
, cls
, mii
, mi
, ii
);
290 if (propInfo
.offset
== -1 ||
291 mightCallMagicPropMethod(mii
.getAttr(mc
), cls
, propInfo
)) {
304 // Inspect the instruction we're about to translate and determine if
305 // it can be executed without using an MInstrState struct.
306 void HhbcTranslator::MInstrTranslator::checkMIState() {
307 if (m_ni
.immVec
.locationCode() == LNL
|| m_ni
.immVec
.locationCode() == LNC
) {
308 // We're definitely going to punt in emitBaseN, so we might not
309 // have guarded the base's type.
313 // DataTypeGeneric is used here because if we can't prove anything useful
314 // about the operation, this function doesn't care what the type is.
315 auto baseVal
= getBase(DataTypeGeneric
);
316 Type baseType
= baseVal
->type();
317 const bool baseArr
= baseType
<= Type::Arr
;
318 const bool isCGetM
= m_ni
.mInstrOp() == OpCGetM
;
319 const bool isSetM
= m_ni
.mInstrOp() == OpSetM
;
320 const bool isIssetM
= m_ni
.mInstrOp() == OpIssetM
;
321 const bool isUnsetM
= m_ni
.mInstrOp() == OpUnsetM
;
322 const bool isSingle
= m_ni
.immVecM
.size() == 1;
323 const bool unknownOffsets
= mInstrHasUnknownOffsets(m_ni
, contextClass());
325 if (baseType
.maybeBoxed() && !baseType
.isBoxed()) {
326 // We don't need to bother with weird base types.
329 baseType
= baseType
.unbox();
332 // CGetM or SetM with no unknown property offsets
333 const bool simpleProp
= !unknownOffsets
&& (isCGetM
|| isSetM
);
335 // SetM with only one vector element, for props and elems
336 const bool singleSet
= isSingle
&& isSetM
;
338 // Element access with one element in the vector
339 const bool singleElem
= isSingle
&& mcodeIsElem(m_ni
.immVecM
[0]);
341 // IssetM with one vector array element and an Arr base
342 const bool simpleArrayIsset
= isIssetM
&& singleElem
&& baseArr
;
344 // IssetM with one vector array element and a collection type
345 const bool simpleCollectionIsset
= isIssetM
&& singleElem
&&
346 baseType
< Type::Obj
&& isOptimizableCollectionClass(baseType
.getClass());
348 // UnsetM on an array with one vector element
349 const bool simpleArrayUnset
= isUnsetM
&& singleElem
&&
350 baseType
<= Type::Arr
;
352 // CGetM on an array with a base that won't use MInstrState. Str
353 // will use tvScratch and Obj will fatal or use tvRef.
354 const bool simpleArrayGet
= isCGetM
&& singleElem
&&
355 baseType
.not(Type::Str
| Type::Obj
);
356 const bool simpleCollectionGet
= isCGetM
&& singleElem
&&
357 baseType
< Type::Obj
&& isOptimizableCollectionClass(baseType
.getClass());
358 const bool simpleStringOp
= (isCGetM
|| isIssetM
) && isSingle
&&
359 isSimpleBase() && mcodeMaybeArrayIntKey(m_ni
.immVecM
[0]) &&
360 baseType
<= Type::Str
;
362 if (simpleProp
|| singleSet
||
363 simpleArrayGet
|| simpleCollectionGet
||
364 simpleArrayUnset
|| simpleCollectionIsset
||
365 simpleArrayIsset
|| simpleStringOp
) {
367 if (simpleCollectionGet
|| simpleCollectionIsset
) {
368 constrainBase(TypeConstraint(baseType
.getClass()), baseVal
);
370 constrainBase(DataTypeSpecific
, baseVal
);
375 void HhbcTranslator::MInstrTranslator::emitMPre() {
378 if (HPHP::Trace::moduleEnabled(HPHP::Trace::minstr
, 1)) {
383 m_misBase
= gen(DefMIStateBase
);
384 SSATmp
* uninit
= cns(Type::Uninit
);
386 if (nLogicalRatchets() > 0) {
387 gen(StMem
, m_misBase
, cns(MISOFF(tvRef
)), uninit
);
388 gen(StMem
, m_misBase
, cns(MISOFF(tvRef2
)), uninit
);
392 // The base location is input 0 or 1, and the location code is stored
393 // separately from m_ni.immVecM, so input indices (iInd) and member indices
394 // (mInd) commonly differ. Additionally, W members have no corresponding
395 // inputs, so it is necessary to track the two indices separately.
399 // Iterate over all but the last member, which is consumed by a final
401 for (m_mInd
= 0; m_mInd
< m_ni
.immVecM
.size() - 1; ++m_mInd
) {
402 emitIntermediateOp();
407 void HhbcTranslator::MInstrTranslator::emitMTrace() {
408 auto rttStr
= [this](int i
) {
409 return Type(m_ni
.inputs
[i
]->rtt
).unbox().toString();
411 std::ostringstream shape
;
412 int iInd
= m_mii
.valCount();
413 const char* separator
= "";
415 shape
<< opcodeToName(m_ni
.mInstrOp()) << " <";
416 auto baseLoc
= m_ni
.immVec
.locationCode();
417 shape
<< folly::format("{}:{} ", locationCodeString(baseLoc
), rttStr(iInd
));
420 for (int mInd
= 0; mInd
< m_ni
.immVecM
.size(); ++mInd
) {
421 auto mcode
= m_ni
.immVecM
[mInd
];
425 } else if (mcodeIsElem(mcode
)) {
426 shape
<< "ME:" << rttStr(iInd
);
427 } else if (mcodeIsProp(mcode
)) {
428 shape
<< "MP:" << rttStr(iInd
);
432 if (mcode
!= MW
) ++iInd
;
437 cns(makeStaticString("vector instructions")),
438 cns(makeStaticString(shape
.str())),
442 // Build a map from (stack) input index to stack index.
443 void HhbcTranslator::MInstrTranslator::numberStackInputs() {
444 // Stack inputs are pushed in the order they appear in the vector
445 // from left to right, so earlier elements in the vector are at
446 // higher offsets in the stack. m_mii.valCount() tells us how many
447 // rvals the instruction takes on the stack; they're pushed after
448 // any vector elements and we want to ignore them here.
449 bool stackRhs
= m_mii
.valCount() &&
450 m_ni
.inputs
[0]->location
.space
== Location::Stack
;
451 int stackIdx
= (int)stackRhs
+ m_ni
.immVec
.numStackValues() - 1;
452 for (unsigned i
= m_mii
.valCount(); i
< m_ni
.inputs
.size(); ++i
) {
453 const Location
& l
= m_ni
.inputs
[i
]->location
;
455 case Location::Stack
:
456 assert(stackIdx
>= 0);
457 m_stackInputs
[i
] = stackIdx
--;
464 assert(stackIdx
== (stackRhs
? 0 : -1));
467 // If this instruction does have an RHS, it will be input 0 at
469 assert(m_mii
.valCount() == 1);
470 m_stackInputs
[0] = 0;
474 SSATmp
* HhbcTranslator::MInstrTranslator::getBase(TypeConstraint tc
) {
475 assert(m_iInd
== m_mii
.valCount());
476 return getInput(m_iInd
, tc
);
479 SSATmp
* HhbcTranslator::MInstrTranslator::getKey() {
480 SSATmp
* key
= getInput(m_iInd
, DataTypeSpecific
);
481 auto keyType
= key
->type();
482 assert(keyType
.isBoxed() || keyType
.notBoxed());
483 if (keyType
.isBoxed()) {
484 key
= gen(LdRef
, Type::Cell
, key
);
489 SSATmp
* HhbcTranslator::MInstrTranslator::getValue() {
490 // If an instruction takes an rhs, it's always input 0.
491 assert(m_mii
.valCount() == 1);
492 const int kValIdx
= 0;
493 return getInput(kValIdx
, DataTypeSpecific
);
496 SSATmp
* HhbcTranslator::MInstrTranslator::getValAddr() {
497 assert(m_mii
.valCount() == 1);
498 const DynLocation
& dl
= *m_ni
.inputs
[0];
499 const Location
& l
= dl
.location
;
500 if (l
.space
== Location::Local
) {
501 assert(!m_stackInputs
.count(0));
502 return m_ht
.ldLocAddr(l
.offset
, DataTypeSpecific
);
504 assert(l
.space
== Location::Stack
);
505 assert(m_stackInputs
.count(0));
507 return m_ht
.ldStackAddr(m_stackInputs
[0], DataTypeSpecific
);
511 void HhbcTranslator::MInstrTranslator::constrainBase(TypeConstraint tc
,
513 if (!value
) value
= m_base
;
515 // Member operations only care about the inner type of the base if it's
516 // boxed, so this handles the logic of using the inner constraint when
518 auto baseType
= value
->type().derefIfPtr();
520 if (baseType
.maybeBoxed()) {
521 tc
.innerCat
= tc
.category
;
522 tc
.category
= DataTypeCountness
;
524 m_irb
.constrainValue(value
, tc
);
527 SSATmp
* HhbcTranslator::MInstrTranslator::getInput(unsigned i
,
529 const DynLocation
& dl
= *m_ni
.inputs
[i
];
530 const Location
& l
= dl
.location
;
532 assert(!!m_stackInputs
.count(i
) == (l
.space
== Location::Stack
));
534 case Location::Stack
:
535 return m_ht
.top(Type::StackElem
, m_stackInputs
[i
], tc
);
537 case Location::Local
:
538 // N.B. Exit block for LdGbl is nullptr because we always InterpOne
539 // member instructions in pseudomains
540 return m_ht
.ldLoc(l
.offset
, nullptr, tc
);
542 case Location::Litstr
:
543 return cns(m_ht
.lookupStringId(l
.offset
));
545 case Location::Litint
:
546 return cns(l
.offset
);
549 // If we don't have a current class context, this instruction will be
551 if (!m_ht
.curClass()) PUNT(Unreachable
-LdThis
);
553 return gen(LdThis
, m_irb
.fp());
555 default: not_reached();
559 void HhbcTranslator::MInstrTranslator::emitBaseLCR() {
560 const MInstrAttr
& mia
= m_mii
.getAttr(m_ni
.immVec
.locationCode());
561 const DynLocation
& base
= *m_ni
.inputs
[m_iInd
];
562 // We use DataTypeGeneric here because we might not care about the type. If
563 // we do, it's constrained further.
564 auto baseType
= getBase(DataTypeGeneric
)->type();
566 if (base
.location
.isLocal()) {
567 // Check for Uninit and warn/promote to InitNull as appropriate
568 if (baseType
<= Type::Uninit
) {
569 if (mia
& MIA_warn
) {
570 gen(RaiseUninitLoc
, makeEmptyCatch(),
571 cns(m_ht
.curFunc()->localVarName(base
.location
.offset
)));
573 if (mia
& MIA_define
) {
574 // We care whether or not the local is Uninit, and
575 // CountnessInit will tell us that.
576 m_irb
.constrainLocal(base
.location
.offset
, DataTypeSpecific
,
577 "emitBaseLCR: Uninit base local");
580 LocalId(base
.location
.offset
),
584 baseType
= Type::InitNull
;
589 // If the base is a box with a type that's changed, we need to bail out of
590 // the tracelet and retranslate. Doing an exit here is a little sketchy since
591 // we may have already emitted instructions with memory effects to initialize
592 // the MInstrState. These particular stores are harmless though, and the
593 // worst outcome here is that we'll end up doing the stores twice, once for
594 // this instruction and once at the beginning of the retranslation.
595 Block
* failedRef
= baseType
.isBoxed() ? m_ht
.makeExit() : nullptr;
596 if ((baseType
.subtypeOfAny(Type::Obj
, Type::BoxedObj
) &&
597 mcodeIsProp(m_ni
.immVecM
[0])) ||
598 simpleCollectionOp() != SimpleOp::None
) {
599 // In these cases we can pass the base by value, after unboxing if needed.
600 m_base
= m_ht
.unbox(getBase(DataTypeSpecific
), failedRef
);
602 // Register that we care about the specific type of the base, and might
603 // care about its specialized type.
604 constrainBase(DataTypeSpecific
);
605 constrainCollectionOpBase();
607 // Everything else is passed by pointer. We don't have to worry about
608 // unboxing here, since all the generic helpers understand boxed bases.
609 if (baseType
.isBoxed()) {
610 SSATmp
* box
= getBase(DataTypeSpecific
);
611 Type innerType
= baseType
.innerType();
612 assert(box
->isA(Type::BoxedCell
));
613 assert(innerType
.isKnownDataType() ||
614 innerType
== Type::Cell
|| innerType
== Type::InitCell
);
616 // Guard that the inner type hasn't changed. Even though we don't use the
617 // unboxed value, some emit* methods change their behavior based on the
619 auto inner
= gen(LdRef
, innerType
, failedRef
, box
);
621 // TODO(t2598894): We do this for consistency with the old guard
622 // relaxation code, but may change it in the future.
623 m_irb
.constrainValue(inner
, DataTypeSpecific
);
626 if (base
.location
.space
== Location::Local
) {
627 m_base
= m_ht
.ldLocAddr(base
.location
.offset
, DataTypeSpecific
);
629 assert(base
.location
.space
== Location::Stack
);
630 // Make sure the stack is clean before getting a pointer to one of its
633 assert(m_stackInputs
.count(m_iInd
));
634 m_base
= m_ht
.ldStackAddr(m_stackInputs
[m_iInd
], DataTypeSpecific
);
636 assert(m_base
->type().isPtr());
639 // TODO(t2598894): We do this for consistency with the old guard relaxation
640 // code, but may change it in the future.
641 constrainBase(DataTypeSpecific
);
644 // Is the current instruction a 1-element simple collection (includes Array),
646 HhbcTranslator::MInstrTranslator::SimpleOp
647 HhbcTranslator::MInstrTranslator::simpleCollectionOp() {
648 // DataTypeGeneric is used in here to avoid constraining the base in case we
649 // end up not caring about the type. Consumers of the return value must
650 // constrain the base as appropriate.
652 SSATmp
* base
= getInput(m_mii
.valCount(), DataTypeGeneric
);
653 auto baseType
= base
->type().unbox();
654 HPHP::Op op
= m_ni
.mInstrOp();
655 bool readInst
= (op
== OpCGetM
|| op
== OpIssetM
);
656 if ((op
== OpSetM
|| readInst
) &&
660 if (baseType
<= Type::Arr
) {
661 auto const isPacked
= (baseType
.hasArrayKind() &&
662 baseType
.getArrayKind() == ArrayData::kPackedKind
);
663 if (mcodeIsElem(m_ni
.immVecM
[0])) {
664 SSATmp
* key
= getInput(m_mii
.valCount() + 1, DataTypeGeneric
);
665 if (key
->isA(Type::Int
) || key
->isA(Type::Str
)) {
666 if (readInst
&& key
->isA(Type::Int
)) {
667 return isPacked
? SimpleOp::PackedArray
668 : SimpleOp::ProfiledArray
;
670 return SimpleOp::Array
;
673 } else if (baseType
<= Type::Str
&&
674 mcodeMaybeArrayIntKey(m_ni
.immVecM
[0])) {
675 SSATmp
* key
= getInput(m_mii
.valCount() + 1, DataTypeGeneric
);
676 if (key
->isA(Type::Int
)) {
677 // Don't bother with SetM on strings, because profile data
678 // shows it basically never happens.
680 return SimpleOp::String
;
683 } else if (baseType
.strictSubtypeOf(Type::Obj
)) {
684 const Class
* klass
= baseType
.getClass();
685 auto const isVector
= klass
== c_Vector::classof();
686 auto const isPair
= klass
== c_Pair::classof();
687 auto const isMap
= klass
== c_Map::classof();
688 // TODO: Add Frozen collections
690 if (isVector
|| isPair
) {
691 if (mcodeMaybeVectorKey(m_ni
.immVecM
[0])) {
692 SSATmp
* key
= getInput(m_mii
.valCount() + 1, DataTypeGeneric
);
693 if (key
->isA(Type::Int
)) {
694 // We don't specialize setting pair elements.
695 if (isPair
&& op
== OpSetM
) return SimpleOp::None
;
697 return isVector
? SimpleOp::Vector
: SimpleOp::Pair
;
701 if (mcodeIsElem(m_ni
.immVecM
[0])) {
702 SSATmp
* key
= getInput(m_mii
.valCount() + 1, DataTypeGeneric
);
703 if (key
->isA(Type::Int
) || key
->isA(Type::Str
)) {
704 return SimpleOp::Map
;
711 return SimpleOp::None
;
714 void HhbcTranslator::MInstrTranslator::constrainCollectionOpBase() {
715 auto type
= simpleCollectionOp();
720 case SimpleOp::Array
:
721 case SimpleOp::ProfiledArray
:
722 case SimpleOp::String
:
723 m_irb
.constrainValue(m_base
, DataTypeSpecific
);
726 case SimpleOp::PackedArray
:
727 constrainBase(TypeConstraint(DataTypeSpecialized
).setWantArrayKind());
730 case SimpleOp::Vector
:
732 case SimpleOp::Pair
: {
733 SSATmp
* base
= getInput(m_mii
.valCount(), DataTypeGeneric
);
734 auto baseType
= base
->type().unbox();
735 constrainBase(TypeConstraint(baseType
.getClass()));
743 // "Simple" bases are stack cells and locals.
744 bool HhbcTranslator::MInstrTranslator::isSimpleBase() {
745 LocationCode loc
= m_ni
.immVec
.locationCode();
746 return loc
== LL
|| loc
== LC
|| loc
== LR
;
749 bool HhbcTranslator::MInstrTranslator::isSingleMember() {
750 return m_ni
.immVecM
.size() == 1;
753 void HhbcTranslator::MInstrTranslator::emitBaseH() {
754 m_base
= gen(LdThis
, m_irb
.fp());
757 void HhbcTranslator::MInstrTranslator::emitBaseN() {
758 // If this is ever implemented, the check at the beginning of
759 // checkMIState must be removed/adjusted as appropriate.
763 template <bool warn
, bool define
>
764 static inline TypedValue
* baseGImpl(TypedValue key
,
767 StringData
* name
= prepareKey(key
);
768 SCOPE_EXIT
{ decRefStr(name
); };
769 VarEnv
* varEnv
= g_context
->m_globalVarEnv
;
770 assert(varEnv
!= NULL
);
771 base
= varEnv
->lookup(name
);
774 raise_notice(Strings::UNDEFINED_VARIABLE
, name
->data());
779 varEnv
->set(name
, &tv
);
780 base
= varEnv
->lookup(name
);
782 return const_cast<TypedValue
*>(init_null_variant
.asTypedValue());
785 if (base
->m_type
== KindOfRef
) {
786 base
= base
->m_data
.pref
->tv();
791 namespace MInstrHelpers
{
792 TypedValue
* baseG(TypedValue key
, MInstrState
* mis
) {
793 return baseGImpl
<false, false>(key
, mis
);
796 TypedValue
* baseGW(TypedValue key
, MInstrState
* mis
) {
797 return baseGImpl
<true, false>(key
, mis
);
800 TypedValue
* baseGD(TypedValue key
, MInstrState
* mis
) {
801 return baseGImpl
<false, true>(key
, mis
);
804 TypedValue
* baseGWD(TypedValue key
, MInstrState
* mis
) {
805 return baseGImpl
<true, true>(key
, mis
);
809 void HhbcTranslator::MInstrTranslator::emitBaseG() {
810 const MInstrAttr
& mia
= m_mii
.getAttr(m_ni
.immVec
.locationCode());
811 typedef TypedValue
* (*OpFunc
)(TypedValue
, MInstrState
*);
812 using namespace MInstrHelpers
;
813 static const OpFunc opFuncs
[] = {baseG
, baseGW
, baseGD
, baseGWD
};
814 OpFunc opFunc
= opFuncs
[mia
& MIA_base
];
815 SSATmp
* gblName
= getBase(DataTypeSpecific
);
816 if (!gblName
->isA(Type::Str
)) PUNT(BaseG
-non
-string
-name
);
820 cns(reinterpret_cast<TCA
>(opFunc
)),
825 void HhbcTranslator::MInstrTranslator::emitBaseS() {
826 const int kClassIdx
= m_ni
.inputs
.size() - 1;
827 SSATmp
* key
= getKey();
828 SSATmp
* clsRef
= getInput(kClassIdx
, DataTypeGeneric
/* will be a Cls */);
831 * Note, the base may be a pointer to a boxed type after this. We
832 * don't unbox here, because we never are going to generate a
833 * special translation unless we know it's not boxed, and the C++
834 * helpers for generic dims currently always conditionally unbox.
836 m_base
= m_ht
.ldClsPropAddr(makeCatch(), clsRef
, key
, true);
839 void HhbcTranslator::MInstrTranslator::emitBaseOp() {
840 LocationCode lCode
= m_ni
.immVec
.locationCode();
842 case LL
: case LC
: case LR
: emitBaseLCR(); break;
843 case LH
: emitBaseH(); break;
844 case LGL
: case LGC
: emitBaseG(); break;
845 case LNL
: case LNC
: emitBaseN(); break;
846 case LSL
: case LSC
: emitBaseS(); break;
847 default: not_reached();
851 void HhbcTranslator::MInstrTranslator::emitIntermediateOp() {
852 switch (m_ni
.immVecM
[m_mInd
]) {
853 case MEC
: case MEL
: case MET
: case MEI
: {
858 case MPC
: case MPL
: case MPT
:
863 assert(m_mii
.newElem());
866 default: not_reached();
870 PropInfo
HhbcTranslator::MInstrTranslator::getCurrentPropertyOffset(
871 const Class
*& knownCls
873 auto info
= getPropertyOffset(m_ni
, contextClass(), knownCls
,
874 m_mii
, m_mInd
, m_iInd
);
875 if (info
.offset
== -1) return info
;
877 auto baseType
= m_base
->type().derefIfPtr();
879 baseType
< Type::Obj
,
880 "Got property offset for base {} which isn't an object",
884 auto baseCls
= baseType
.getClass();
886 // baseCls and knownCls may differ due to a number of factors but they must
887 // always be related to each other somehow.
889 baseCls
->classof(knownCls
) || knownCls
->classof(baseCls
),
890 "Class mismatch between baseType({}) and knownCls({})",
891 baseCls
->name()->data(), knownCls
->name()->data()
894 if (m_irb
.constrainValue(m_base
, TypeConstraint(baseCls
).setWeak())) {
895 // We can't use this specialized class without making a guard more
896 // expensive, so don't do it.
903 void HhbcTranslator::MInstrTranslator::emitProp() {
904 const Class
* knownCls
= nullptr;
905 const auto propInfo
= getCurrentPropertyOffset(knownCls
);
906 auto mia
= m_mii
.getAttr(m_ni
.immVecM
[m_mInd
]);
907 if (propInfo
.offset
== -1 || (mia
& Unset
) ||
908 mightCallMagicPropMethod(mia
, knownCls
, propInfo
)) {
911 emitPropSpecialized(mia
, propInfo
);
915 template <MInstrAttr attrs
, bool isObj
>
916 static inline TypedValue
* propImpl(Class
* ctx
, TypedValue
* base
,
917 TypedValue key
, MInstrState
* mis
) {
918 return Prop
<WDU(attrs
), isObj
>(
919 mis
->tvScratch
, mis
->tvRef
, ctx
, base
, key
);
922 #define HELPER_TABLE(m) \
923 /* name attrs isObj */ \
924 m(propC, None, false) \
925 m(propCD, Define, false) \
926 m(propCDO, Define, true) \
927 m(propCO, None, true) \
928 m(propCU, Unset, false) \
929 m(propCUO, Unset, true) \
930 m(propCW, Warn, false) \
931 m(propCWD, WarnDefine, false) \
932 m(propCWDO, WarnDefine, true) \
933 m(propCWO, Warn, true)
935 #define PROP(nm, ...) \
936 TypedValue* nm(Class* ctx, TypedValue* base, TypedValue key, \
937 MInstrState* mis) { \
938 return propImpl<__VA_ARGS__>(ctx, base, key, mis); \
940 namespace MInstrHelpers
{
945 void HhbcTranslator::MInstrTranslator::emitPropGeneric() {
946 MemberCode mCode
= m_ni
.immVecM
[m_mInd
];
947 MInstrAttr mia
= MInstrAttr(m_mii
.getAttr(mCode
) & MIA_intermediate_prop
);
949 if ((mia
& Unset
) && m_base
->type().strip().not(Type::Obj
)) {
950 constrainBase(DataTypeSpecific
);
951 m_base
= m_irb
.genPtrToInitNull();
955 typedef TypedValue
* (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, MInstrState
*);
956 SSATmp
* key
= getKey();
957 BUILD_OPTAB(mia
, m_base
->isA(Type::Obj
));
959 m_base
= genStk(PropDX
, makeCatch(), cns((TCA
)opFunc
), CTX(),
960 m_base
, key
, genMisPtr());
962 m_base
= gen(PropX
, makeCatch(),
963 cns((TCA
)opFunc
), CTX(), m_base
, key
, genMisPtr());
969 * Helper for emitPropSpecialized to check if a property is Uninit. It
970 * returns a pointer to the property's address, or init_null_variant
971 * if the property was Uninit and doDefine is false.
973 * We can omit the uninit check for properties that we know may not be
974 * uninit due to the frontend's type inference.
976 SSATmp
* HhbcTranslator::MInstrTranslator::checkInitProp(
982 SSATmp
* key
= getKey();
983 assert(key
->isA(Type::StaticStr
));
984 assert(baseAsObj
->isA(Type::Obj
));
985 assert(propAddr
->type().isPtr());
987 auto const needsCheck
=
988 Type::Uninit
<= convertToType(propInfo
.repoAuthType
) &&
989 // The m_mInd check is to avoid initializing a property to
990 // InitNull right before it's going to be set to something else.
991 (doWarn
|| (doDefine
&& m_mInd
< m_ni
.immVecM
.size() - 1));
993 if (!needsCheck
) return propAddr
;
998 gen(CheckInitMem
, taken
, propAddr
, cns(0));
1000 [&] { // Next: Property isn't Uninit. Do nothing.
1003 [&] { // Taken: Property is Uninit. Raise a warning and return
1004 // a pointer to InitNull, either in the object or
1005 // init_null_variant.
1006 m_irb
.hint(Block::Hint::Unlikely
);
1007 if (doWarn
&& wantPropSpecializedWarnings()) {
1008 gen(RaiseUndefProp
, m_ht
.makeCatch(), baseAsObj
, key
);
1014 cns(propInfo
.offset
),
1019 return m_irb
.genPtrToInitNull();
1023 Class
* HhbcTranslator::MInstrTranslator::contextClass() const {
1024 return m_ht
.curFunc()->cls();
1027 void HhbcTranslator::MInstrTranslator::emitPropSpecialized(const MInstrAttr mia
,
1028 PropInfo propInfo
) {
1029 assert(!(mia
& MIA_warn
) || !(mia
& MIA_unset
));
1030 const bool doWarn
= mia
& MIA_warn
;
1031 const bool doDefine
= mia
& MIA_define
|| mia
& MIA_unset
;
1033 SSATmp
* initNull
= m_irb
.genPtrToInitNull();
1036 * Type-inference from hphpc only tells us that this is either an object of a
1037 * given class type or null. If it's not an object, it has to be a null type
1038 * based on type inference. (It could be KindOfRef with an object inside,
1039 * except that this isn't inferred for object properties so we're fine not
1040 * checking KindOfRef in that case.)
1042 * On the other hand, if m_base->isA(Type::Obj), we're operating on the base
1043 * which was already guarded by tracelet guards (and may have been KindOfRef,
1044 * but the Base* op already handled this). So we only need to do a type
1045 * check against null here in the intermediate cases.
1047 if (m_base
->isA(Type::Obj
)) {
1048 SSATmp
* propAddr
= gen(LdPropAddr
, m_base
, cns(propInfo
.offset
));
1049 m_base
= checkInitProp(m_base
, propAddr
, propInfo
, doWarn
, doDefine
);
1051 m_base
= m_irb
.cond(
1053 [&] (Block
* taken
) {
1054 return gen(LdMem
, Type::Obj
, taken
, m_base
, cns(0));
1056 [&] (SSATmp
* baseAsObj
) {
1057 // Next: Base is an object. Load property address and check for uninit
1058 return checkInitProp(baseAsObj
,
1059 gen(LdPropAddr
, baseAsObj
,
1060 cns(propInfo
.offset
)),
1065 [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull.
1066 m_irb
.hint(Block::Hint::Unlikely
);
1068 gen(WarnNonObjProp
, makeCatch());
1074 * This case logically is supposed to do a stdClass promotion. It
1075 * should ideally not be possible (since we have a known class type),
1076 * except that the static compiler doesn't correctly infer object
1077 * class types in some edge cases involving stdClass promotion.
1079 * This is impossible to handle "correctly" if we're in the middle of
1080 * a multi-dim property expression, because things further along may
1081 * also have type inference telling them that object properties are
1082 * at a given slot, but the object could actually be a stdClass
1083 * instead of the knownCls type if we were to promote here.
1085 * So, we throw a fatal error, which is what hphpc's generated C++
1086 * would do in this case too.
1089 * #1789661 (this can cause bugs if bytecode.cpp promotes)
1090 * #1124706 (we want to get rid of stdClass promotion in general)
1092 gen(ThrowNonObjProp
, makeCatch());
1098 // At this point m_base is either a pointer to init_null_variant or
1099 // a property in the object that we've verified isn't uninit.
1100 assert(m_base
->type().isPtr());
1103 template <KeyType keyType
, bool warn
, bool define
, bool reffy
,
1105 static inline TypedValue
* elemImpl(TypedValue
* base
, key_type
<keyType
> key
,
1108 return ElemU
<keyType
>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
1111 return ElemD
<warn
, reffy
, keyType
>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
1113 // We won't really modify the TypedValue in the non-D case, so
1114 // this const_cast is safe.
1115 return const_cast<TypedValue
*>(
1116 Elem
<warn
, keyType
>(mis
->tvScratch
, mis
->tvRef
, base
, key
)
1120 #define HELPER_TABLE(m) \
1121 /* name keyType attrs */ \
1122 m(elemC, KeyType::Any, None) \
1123 m(elemCD, KeyType::Any, Define) \
1124 m(elemCDR, KeyType::Any, DefineReffy) \
1125 m(elemCU, KeyType::Any, Unset) \
1126 m(elemCW, KeyType::Any, Warn) \
1127 m(elemCWD, KeyType::Any, WarnDefine) \
1128 m(elemCWDR, KeyType::Any, WarnDefineReffy) \
1129 m(elemI, KeyType::Int, None) \
1130 m(elemID, KeyType::Int, Define) \
1131 m(elemIDR, KeyType::Int, DefineReffy) \
1132 m(elemIU, KeyType::Int, Unset) \
1133 m(elemIW, KeyType::Int, Warn) \
1134 m(elemIWD, KeyType::Int, WarnDefine) \
1135 m(elemIWDR, KeyType::Int, WarnDefineReffy) \
1136 m(elemS, KeyType::Str, None) \
1137 m(elemSD, KeyType::Str, Define) \
1138 m(elemSDR, KeyType::Str, DefineReffy) \
1139 m(elemSU, KeyType::Str, Unset) \
1140 m(elemSW, KeyType::Str, Warn) \
1141 m(elemSWD, KeyType::Str, WarnDefine) \
1142 m(elemSWDR, KeyType::Str, WarnDefineReffy)
1144 #define ELEM(nm, keyType, attrs) \
1145 TypedValue* nm(TypedValue* base, key_type<keyType> key, MInstrState* mis) { \
1146 return elemImpl<keyType, WDRU(attrs)>(base, key, mis); \
1148 namespace MInstrHelpers
{
1153 void HhbcTranslator::MInstrTranslator::emitElem() {
1154 MemberCode mCode
= m_ni
.immVecM
[m_mInd
];
1155 MInstrAttr mia
= MInstrAttr(m_mii
.getAttr(mCode
) & MIA_intermediate
);
1156 SSATmp
* key
= getKey();
1158 // Fast path for the common/easy case
1159 const bool warn
= mia
& Warn
;
1160 const bool unset
= mia
& Unset
;
1161 const bool define
= mia
& Define
;
1162 if (m_base
->isA(Type::PtrToArr
) &&
1163 !unset
&& !define
&&
1164 (key
->isA(Type::Int
) || key
->isA(Type::Str
))) {
1165 constrainBase(DataTypeSpecific
);
1166 emitElemArray(key
, warn
);
1170 assert(!(define
&& unset
));
1172 SSATmp
* uninit
= m_irb
.genPtrToUninit();
1173 Type baseType
= m_base
->type().strip();
1174 constrainBase(DataTypeSpecific
);
1175 if (baseType
<= Type::Str
) {
1176 m_ht
.exceptionBarrier();
1180 cns(makeStaticString(Strings::OP_NOT_SUPPORTED_STRING
))
1185 if (baseType
.not(Type::Arr
| Type::Obj
)) {
1191 typedef TypedValue
* (*OpFunc
)(TypedValue
*, TypedValue
, MInstrState
*);
1192 BUILD_OPTAB(getKeyType(key
), mia
);
1193 if (define
|| unset
) {
1194 m_base
= genStk(define
? ElemDX
: ElemUX
, makeCatch(),
1195 cns((TCA
)opFunc
), m_base
, key
, genMisPtr());
1197 m_base
= gen(ElemX
, makeCatch(),
1198 cns((TCA
)opFunc
), m_base
, key
, genMisPtr());
1205 static const TypedValue
* elemArrayNotFound(int64_t k
) {
1207 raise_notice("Undefined index: %" PRId64
, k
);
1209 return null_variant
.asTypedValue();
1214 static const TypedValue
* elemArrayNotFound(const StringData
* k
) {
1216 raise_notice("Undefined index: %s", k
->data());
1218 return null_variant
.asTypedValue();
1221 static inline const TypedValue
* checkedGet(ArrayData
* a
, StringData
* key
) {
1223 return UNLIKELY(key
->isStrictlyInteger(i
)) ? a
->nvGet(i
) : a
->nvGet(key
);
1226 static inline const TypedValue
* checkedGet(ArrayData
* a
, int64_t key
) {
1230 template<KeyType keyType
, bool checkForInt
, bool warn
>
1231 static inline const TypedValue
* elemArrayImpl(TypedValue
* a
,
1232 key_type
<keyType
> key
) {
1233 assert(a
->m_type
== KindOfArray
);
1234 auto const ad
= a
->m_data
.parr
;
1235 auto const ret
= checkForInt
? checkedGet(ad
, key
)
1237 return ret
? ret
: elemArrayNotFound
<warn
>(key
);
1240 #define HELPER_TABLE(m) \
1241 /* name keyType checkForInt warn */ \
1242 m(elemArrayS, KeyType::Str, false, false) \
1243 m(elemArraySi, KeyType::Str, true, false) \
1244 m(elemArrayI, KeyType::Int, false, false) \
1245 m(elemArraySW, KeyType::Str, false, true) \
1246 m(elemArraySiW, KeyType::Str, true, true) \
1247 m(elemArrayIW, KeyType::Int, false, true)
1249 #define ELEM(nm, keyType, checkForInt, warn) \
1250 const TypedValue* nm(TypedValue* a, key_type<keyType> key) { \
1251 return elemArrayImpl<keyType, checkForInt, warn>( \
1254 namespace MInstrHelpers
{
1259 void HhbcTranslator::MInstrTranslator::emitElemArray(SSATmp
* key
, bool warn
) {
1262 m_ht
.checkStrictlyInteger(key
, keyType
, checkForInt
);
1264 typedef TypedValue
* (*OpFunc
)(ArrayData
*, TypedValue
*);
1265 BUILD_OPTAB(keyType
, checkForInt
, warn
);
1266 m_base
= gen(ElemArray
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
);
1270 void HhbcTranslator::MInstrTranslator::emitNewElem() {
1274 void HhbcTranslator::MInstrTranslator::emitRatchetRefs() {
1275 if (ratchetInd() < 0 || ratchetInd() >= int(nLogicalRatchets())) {
1279 m_base
= m_irb
.cond(
1281 [&] (Block
* taken
) {
1282 gen(CheckInitMem
, taken
, m_misBase
, cns(MISOFF(tvRef
)));
1284 [&] { // Next: tvRef isn't Uninit. Ratchet the refs
1285 // Clean up tvRef2 before overwriting it.
1286 if (ratchetInd() > 0) {
1287 gen(DecRefMem
, Type::Gen
, m_misBase
, cns(MISOFF(tvRef2
)));
1289 // Copy tvRef to tvRef2. Use mmx at some point
1290 SSATmp
* tvRef
= gen(LdMem
, Type::Gen
, m_misBase
, cns(MISOFF(tvRef
)));
1291 gen(StMem
, m_misBase
, cns(MISOFF(tvRef2
)), tvRef
);
1294 gen(StMem
, m_misBase
, cns(MISOFF(tvRef
)), cns(Type::Uninit
));
1296 // Adjust base pointer.
1297 assert(m_base
->type().isPtr());
1298 return gen(LdMIStateAddr
, m_misBase
, cns(MISOFF(tvRef2
)));
1300 [&] { // Taken: tvRef is Uninit. Do nothing.
1305 void HhbcTranslator::MInstrTranslator::emitFinalMOp() {
1306 typedef void (HhbcTranslator::MInstrTranslator::*MemFun
)();
1308 switch (m_ni
.immVecM
[m_mInd
]) {
1309 case MEC
: case MEL
: case MET
: case MEI
:
1310 static MemFun elemOps
[] = {
1311 # define MII(instr, ...) &HhbcTranslator::MInstrTranslator::emit##instr##Elem,
1315 (this->*elemOps
[m_mii
.instr()])();
1318 case MPC
: case MPL
: case MPT
:
1319 static MemFun propOps
[] = {
1320 # define MII(instr, ...) &HhbcTranslator::MInstrTranslator::emit##instr##Prop,
1324 (this->*propOps
[m_mii
.instr()])();
1328 assert(m_mii
.getAttr(MW
) & MIA_final
);
1329 static MemFun newOps
[] = {
1330 # define MII(instr, attrs, bS, iS, vC, fN) \
1331 &HhbcTranslator::MInstrTranslator::emit##fN,
1335 (this->*newOps
[m_mii
.instr()])();
1338 default: not_reached();
1342 template <KeyType keyType
, bool isObj
>
1343 static inline TypedValue
cGetPropImpl(Class
* ctx
, TypedValue
* base
,
1344 key_type
<keyType
> key
, MInstrState
* mis
) {
1346 TypedValue
* result
= Prop
<true, false, false, isObj
, keyType
>(
1347 scratch
, mis
->tvRef
, ctx
, base
, key
);
1349 if (result
->m_type
== KindOfRef
) {
1350 result
= result
->m_data
.pref
->tv();
1352 tvRefcountedIncRef(result
);
1356 #define HELPER_TABLE(m) \
1357 /* name keyType isObj */ \
1358 m(cGetPropC, KeyType::Any, false) \
1359 m(cGetPropCO, KeyType::Any, true) \
1360 m(cGetPropS, KeyType::Str, false) \
1361 m(cGetPropSO, KeyType::Str, true)
1363 #define PROP(nm, kt, isObj) \
1364 TypedValue nm(Class* ctx, TypedValue* base, key_type<kt> key, \
1365 MInstrState* mis) { \
1366 return cGetPropImpl<kt, isObj>(ctx, base, key, mis); \
1368 namespace MInstrHelpers
{
1373 void HhbcTranslator::MInstrTranslator::emitCGetProp() {
1374 const Class
* knownCls
= nullptr;
1375 const auto propInfo
= getCurrentPropertyOffset(knownCls
);
1377 if (propInfo
.offset
!= -1 &&
1378 !mightCallMagicPropMethod(None
, knownCls
, propInfo
)) {
1379 emitPropSpecialized(MIA_warn
, propInfo
);
1381 if (!RuntimeOption::RepoAuthoritative
) {
1382 SSATmp
* cellPtr
= gen(UnboxPtr
, m_base
);
1383 m_result
= gen(LdMem
, Type::Cell
, cellPtr
, cns(0));
1384 gen(IncRef
, m_result
);
1388 auto const ty
= convertToType(propInfo
.repoAuthType
);
1389 auto const cellPtr
= ty
.maybeBoxed() ? gen(UnboxPtr
, m_base
) : m_base
;
1390 m_result
= gen(LdMem
, ty
.unbox(), cellPtr
, cns(0));
1391 gen(IncRef
, m_result
);
1395 typedef TypedValue (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, MInstrState
*);
1396 SSATmp
* key
= getKey();
1397 auto keyType
= getKeyTypeNoInt(key
);
1398 BUILD_OPTAB(keyType
, m_base
->isA(Type::Obj
));
1399 m_result
= gen(CGetProp
, makeCatch(),
1400 cns((TCA
)opFunc
), CTX(), m_base
, key
, genMisPtr());
1404 template <KeyType keyType
, bool isObj
>
1405 static inline RefData
* vGetPropImpl(Class
* ctx
, TypedValue
* base
,
1406 key_type
<keyType
> key
, MInstrState
* mis
) {
1407 TypedValue
* result
= Prop
<false, true, false, isObj
, keyType
>(
1408 mis
->tvScratch
, mis
->tvRef
, ctx
, base
, key
);
1410 if (result
->m_type
!= KindOfRef
) {
1413 RefData
* ref
= result
->m_data
.pref
;
1418 #define HELPER_TABLE(m) \
1419 /* name keyType isObj */\
1420 m(vGetPropC, KeyType::Any, false) \
1421 m(vGetPropCO, KeyType::Any, true) \
1422 m(vGetPropS, KeyType::Str, false) \
1423 m(vGetPropSO, KeyType::Str, true)
1425 #define PROP(nm, kt, isObj) \
1426 RefData* nm(Class* ctx, TypedValue* base, key_type<kt> key, \
1427 MInstrState* mis) { \
1428 return vGetPropImpl<kt, isObj>(ctx, base, key, mis); \
1430 namespace MInstrHelpers
{
1435 void HhbcTranslator::MInstrTranslator::emitVGetProp() {
1436 SSATmp
* key
= getKey();
1437 typedef RefData
* (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, MInstrState
*);
1438 BUILD_OPTAB(getKeyTypeNoInt(key
), m_base
->isA(Type::Obj
));
1439 m_result
= genStk(VGetProp
, makeCatch(), cns((TCA
)opFunc
), CTX(),
1440 m_base
, key
, genMisPtr());
1444 template <bool useEmpty
, bool isObj
>
1445 static inline bool issetEmptyPropImpl(Class
* ctx
, TypedValue
* base
,
1447 return HPHP::IssetEmptyProp
<useEmpty
, isObj
>(ctx
, base
, key
);
1450 #define HELPER_TABLE(m) \
1451 /* name useEmpty isObj */ \
1452 m(issetPropC, false, false) \
1453 m(issetPropCE, true, false) \
1454 m(issetPropCEO, true, true) \
1455 m(issetPropCO, false, true)
1457 #define ISSET(nm, ...) \
1458 /* This returns int64_t to ensure all 64 bits of rax are valid */ \
1459 uint64_t nm(Class* ctx, TypedValue* base, TypedValue key) { \
1460 return issetEmptyPropImpl<__VA_ARGS__>(ctx, base, key); \
1462 namespace MInstrHelpers
{
1467 void HhbcTranslator::MInstrTranslator::emitIssetEmptyProp(bool isEmpty
) {
1468 SSATmp
* key
= getKey();
1469 typedef uint64_t (*OpFunc
)(Class
*, TypedValue
*, TypedValue
);
1470 BUILD_OPTAB(isEmpty
, m_base
->isA(Type::Obj
));
1471 m_result
= gen(isEmpty
? EmptyProp
: IssetProp
, makeCatch(),
1472 cns((TCA
)opFunc
), CTX(), m_base
, key
);
1476 void HhbcTranslator::MInstrTranslator::emitIssetProp() {
1477 emitIssetEmptyProp(false);
1480 void HhbcTranslator::MInstrTranslator::emitEmptyProp() {
1481 emitIssetEmptyProp(true);
1484 template <bool isObj
>
1485 static inline void setPropImpl(Class
* ctx
, TypedValue
* base
,
1486 TypedValue key
, Cell val
) {
1487 HPHP::SetProp
<false, isObj
>(ctx
, base
, key
, &val
);
1490 #define HELPER_TABLE(m) \
1492 m(setPropC, false) \
1495 #define PROP(nm, ...) \
1496 void nm(Class* ctx, TypedValue* base, TypedValue key, Cell val) { \
1497 setPropImpl<__VA_ARGS__>(ctx, base, key, val); \
1499 namespace MInstrHelpers
{
1504 void HhbcTranslator::MInstrTranslator::emitSetProp() {
1505 SSATmp
* value
= getValue();
1507 /* If we know the class for the current base, emit a direct property set. */
1508 const Class
* knownCls
= nullptr;
1509 const auto propInfo
= getCurrentPropertyOffset(knownCls
);
1511 if (propInfo
.offset
!= -1 &&
1512 !mightCallMagicPropMethod(Define
, knownCls
, propInfo
)) {
1513 emitPropSpecialized(MIA_define
, propInfo
);
1515 auto const propTy
= convertToType(propInfo
.repoAuthType
);
1516 auto const cellTy
= propTy
.maybeBoxed() ? propTy
.unbox() : propTy
;
1517 auto const cellPtr
= propTy
.maybeBoxed() ? gen(UnboxPtr
, m_base
) : m_base
;
1518 auto const oldVal
= gen(LdMem
, cellTy
, cellPtr
, cns(0));
1521 gen(StMem
, cellPtr
, cns(0), value
);
1522 gen(DecRef
, oldVal
);
1527 // Emit the appropriate helper call.
1528 typedef void (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, Cell
);
1529 SSATmp
* key
= getKey();
1530 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1531 genStk(SetProp
, makeCatchSet(), cns((TCA
)opFunc
), CTX(),
1532 m_base
, key
, value
);
1537 template <bool isObj
>
1538 static inline TypedValue
setOpPropImpl(Class
* ctx
, TypedValue
* base
,
1540 Cell val
, MInstrState
* mis
, SetOpOp op
) {
1541 TypedValue
* result
= HPHP::SetOpProp
<isObj
>(
1542 mis
->tvScratch
, mis
->tvRef
, ctx
, op
, base
, key
, &val
);
1545 cellDup(*tvToCell(result
), ret
);
1549 #define HELPER_TABLE(m) \
1551 m(setOpPropC, false) \
1552 m(setOpPropCO, true)
1554 #define SETOP(nm, ...) \
1555 TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
1556 Cell val, MInstrState* mis, SetOpOp op) { \
1557 return setOpPropImpl<__VA_ARGS__>(ctx, base, key, val, mis, op); \
1559 namespace MInstrHelpers
{
1564 void HhbcTranslator::MInstrTranslator::emitSetOpProp() {
1565 SetOpOp op
= SetOpOp(m_ni
.imm
[0].u_OA
);
1566 SSATmp
* key
= getKey();
1567 SSATmp
* value
= getValue();
1568 typedef TypedValue (*OpFunc
)(TypedValue
*, TypedValue
,
1569 Cell
, MInstrState
*, SetOpOp
);
1570 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1571 m_result
= genStk(SetOpProp
, makeCatch(), cns((TCA
)opFunc
),
1572 CTX(), m_base
, key
, value
, genMisPtr(), cns(op
));
1576 template <bool isObj
>
1577 static inline TypedValue
incDecPropImpl(Class
* ctx
, TypedValue
* base
,
1579 MInstrState
* mis
, IncDecOp op
) {
1581 result
.m_type
= KindOfUninit
;
1582 HPHP::IncDecProp
<true, isObj
>(
1583 mis
->tvScratch
, mis
->tvRef
, ctx
, op
, base
, key
, result
);
1584 assert(result
.m_type
!= KindOfRef
);
1589 #define HELPER_TABLE(m) \
1591 m(incDecPropC, false) \
1592 m(incDecPropCO, true)
1594 #define INCDEC(nm, ...) \
1595 TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
1596 MInstrState* mis, IncDecOp op) { \
1597 return incDecPropImpl<__VA_ARGS__>(ctx, base, key, mis, op); \
1599 namespace MInstrHelpers
{
1600 HELPER_TABLE(INCDEC
)
1604 void HhbcTranslator::MInstrTranslator::emitIncDecProp() {
1605 IncDecOp op
= static_cast<IncDecOp
>(m_ni
.imm
[0].u_OA
);
1606 SSATmp
* key
= getKey();
1607 typedef TypedValue (*OpFunc
)(TypedValue
*, TypedValue
,
1608 MInstrState
*, IncDecOp
);
1609 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1610 m_result
= genStk(IncDecProp
, makeCatch(), cns((TCA
)opFunc
),
1611 CTX(), m_base
, key
, genMisPtr(), cns(op
));
1615 template <bool isObj
>
1616 static inline void bindPropImpl(Class
* ctx
, TypedValue
* base
, TypedValue key
,
1617 RefData
* val
, MInstrState
* mis
) {
1618 TypedValue
* prop
= Prop
<false, true, false, isObj
>(
1619 mis
->tvScratch
, mis
->tvRef
, ctx
, base
, key
);
1620 if (!(prop
== &mis
->tvScratch
&& prop
->m_type
== KindOfUninit
)) {
1621 tvBindRef(val
, prop
);
1625 #define HELPER_TABLE(m) \
1627 m(bindPropC, false) \
1630 #define PROP(nm, ...) \
1631 void nm(Class* ctx, TypedValue* base, TypedValue key, \
1632 RefData* val, MInstrState* mis) { \
1633 bindPropImpl<__VA_ARGS__>(ctx, base, key, val, mis); \
1635 namespace MInstrHelpers
{
1640 void HhbcTranslator::MInstrTranslator::emitBindProp() {
1641 SSATmp
* key
= getKey();
1642 SSATmp
* box
= getValue();
1643 typedef void (*OpFunc
)(Class
*, TypedValue
*, TypedValue
, RefData
*,
1645 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1646 genStk(BindProp
, makeCatch(), cns((TCA
)opFunc
), CTX(),
1647 m_base
, key
, box
, genMisPtr());
1652 template <bool isObj
>
1653 static inline void unsetPropImpl(Class
* ctx
, TypedValue
* base
,
1655 HPHP::UnsetProp
<isObj
>(ctx
, base
, key
);
1658 #define HELPER_TABLE(m) \
1660 m(unsetPropC, false) \
1661 m(unsetPropCO, true)
1663 #define PROP(nm, ...) \
1664 static void nm(Class* ctx, TypedValue* base, TypedValue key) { \
1665 unsetPropImpl<__VA_ARGS__>(ctx, base, key); \
1667 namespace MInstrHelpers
{
1672 void HhbcTranslator::MInstrTranslator::emitUnsetProp() {
1673 SSATmp
* key
= getKey();
1675 if (m_base
->type().strip().not(Type::Obj
)) {
1677 constrainBase(DataTypeSpecific
);
1681 typedef void (*OpFunc
)(Class
*, TypedValue
*, TypedValue
);
1682 BUILD_OPTAB(m_base
->isA(Type::Obj
));
1683 gen(UnsetProp
, makeCatch(), cns((TCA
)opFunc
), CTX(), m_base
, key
);
1687 // Keep these error handlers in sync with ArrayData::getNotFound();
1689 static TypedValue
arrayGetNotFound(int64_t k
) {
1690 raise_notice("Undefined index: %" PRId64
, k
);
1697 static TypedValue
arrayGetNotFound(const StringData
* k
) {
1698 raise_notice("Undefined index: %s", k
->data());
1704 SSATmp
* HhbcTranslator::MInstrTranslator::emitPackedArrayGet(SSATmp
* base
,
1708 // We can call emitPackedArrayGet on arrays for which we do not statically
1709 // know that they are packed, if we have profile information for the array.
1710 assert(base
->isA(Type::Arr
) &&
1711 (profiled
|| base
->type().getArrayKind() == ArrayData::kPackedKind
));
1715 [&] (Block
* taken
) {
1716 gen(CheckPackedArrayBounds
, taken
, base
, key
);
1719 auto res
= gen(LdPackedArrayElem
, base
, key
);
1720 auto unboxed
= m_ht
.unbox(res
, nullptr);
1721 gen(IncRef
, unboxed
);
1725 m_irb
.hint(Block::Hint::Unlikely
);
1726 gen(RaiseArrayIndexNotice
, makeCatch(), key
);
1727 return cns(Type::InitNull
);
1732 template<KeyType keyType
, bool checkForInt
>
1733 static inline TypedValue
arrayGetImpl(ArrayData
* a
, key_type
<keyType
> key
) {
1734 auto ret
= checkForInt
? checkedGet(a
, key
) : a
->nvGet(key
);
1736 ret
= tvToCell(ret
);
1737 tvRefcountedIncRef(ret
);
1740 return arrayGetNotFound(key
);
1743 #define HELPER_TABLE(m) \
1744 /* name keyType checkForInt */\
1745 m(arrayGetS, KeyType::Str, false) \
1746 m(arrayGetSi, KeyType::Str, true) \
1747 m(arrayGetI, KeyType::Int, false)
1749 #define ELEM(nm, keyType, checkForInt) \
1750 TypedValue nm(ArrayData* a, key_type<keyType> key) { \
1751 return arrayGetImpl<keyType, checkForInt>(a, key); \
1753 namespace MInstrHelpers
{
1758 SSATmp
* HhbcTranslator::MInstrTranslator::emitArrayGet(SSATmp
* key
) {
1761 m_ht
.checkStrictlyInteger(key
, keyType
, checkForInt
);
1763 typedef TypedValue (*OpFunc
)(ArrayData
*, TypedValue
*);
1764 BUILD_OPTAB(keyType
, checkForInt
);
1765 assert(m_base
->isA(Type::Arr
));
1766 return gen(ArrayGet
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
);
1770 const StaticString
s_PackedArray("PackedArray");
1772 void HhbcTranslator::MInstrTranslator::emitProfiledArrayGet(SSATmp
* key
) {
1773 TargetProfile
<NonPackedArrayProfile
> prof(m_ht
.m_context
,
1774 m_irb
.state().marker(),
1775 s_PackedArray
.get());
1776 if (prof
.profiling()) {
1777 gen(ProfileArray
, RDSHandleData
{ prof
.handle() }, m_base
);
1778 m_result
= emitArrayGet(key
);
1782 m_ht
.emitIncStat(Stats::ArrayGet_Total
, 1, false);
1783 if (prof
.optimizing()) {
1784 m_ht
.emitIncStat(Stats::ArrayGet_Opt
, 1, false);
1785 auto const data
= prof
.data(NonPackedArrayProfile::reduce
);
1786 // NonPackedArrayProfile data counts how many times a non-packed
1787 // array was observed.
1788 if (data
.count
== 0) {
1789 m_ht
.emitIncStat(Stats::ArrayGet_Mono
, 1, false);
1790 m_result
= m_irb
.cond(
1792 [&] (Block
* taken
) {
1793 return gen(CheckType
, Type::Arr
.specialize(ArrayData::kPackedKind
),
1796 [&] (SSATmp
* base
) { // Next
1797 m_ht
.emitIncStat(Stats::ArrayGet_Packed
, 1, false);
1798 m_irb
.constrainValue(
1799 base
, TypeConstraint(DataTypeSpecialized
).setWantArrayKind());
1800 return emitPackedArrayGet(base
, key
, true);
1803 m_irb
.hint(Block::Hint::Unlikely
);
1804 m_ht
.emitIncStat(Stats::ArrayGet_Mixed
, 1, false);
1805 return emitArrayGet(key
);
1811 m_result
= emitArrayGet(key
);
1814 namespace MInstrHelpers
{
1815 StringData
* stringGetI(StringData
* str
, uint64_t x
) {
1816 if (LIKELY(x
< str
->size())) {
1817 return str
->getChar(x
);
1819 if (RuntimeOption::EnableHipHopSyntax
) {
1820 raise_warning("Out of bounds");
1822 return makeStaticString("");
1826 void HhbcTranslator::MInstrTranslator::emitStringGet(SSATmp
* key
) {
1827 assert(key
->isA(Type::Int
));
1828 m_result
= gen(StringGet
, makeCatch(),
1829 cns((TCA
)MInstrHelpers::stringGetI
), m_base
, key
);
1832 void HhbcTranslator::MInstrTranslator::emitVectorGet(SSATmp
* key
) {
1833 assert(key
->isA(Type::Int
));
1834 if (key
->isConst() && key
->intVal() < 0) {
1835 PUNT(emitVectorGet
);
1837 SSATmp
* size
= gen(LdVectorSize
, m_base
);
1838 gen(CheckBounds
, makeCatch(), key
, size
);
1839 SSATmp
* base
= gen(LdVectorBase
, m_base
);
1840 static_assert(sizeof(TypedValue
) == 16,
1841 "TypedValue size expected to be 16 bytes");
1842 auto idx
= gen(Shl
, key
, cns(4));
1843 m_result
= gen(LdElem
, base
, idx
);
1844 gen(IncRef
, m_result
);
1847 void HhbcTranslator::MInstrTranslator::emitPairGet(SSATmp
* key
) {
1848 assert(key
->isA(Type::Int
));
1849 static_assert(sizeof(TypedValue
) == 16,
1850 "TypedValue size expected to be 16 bytes");
1851 if (key
->isConst()) {
1852 auto idx
= key
->intVal();
1853 if (idx
< 0 || idx
> 1) {
1856 // no reason to check bounds
1857 SSATmp
* base
= gen(LdPairBase
, m_base
);
1858 auto index
= cns(key
->intVal() << 4);
1859 m_result
= gen(LdElem
, base
, index
);
1861 gen(CheckBounds
, makeCatch(), key
, cns(1));
1862 SSATmp
* base
= gen(LdPairBase
, m_base
);
1863 auto idx
= gen(Shl
, key
, cns(4));
1864 m_result
= gen(LdElem
, base
, idx
);
1866 gen(IncRef
, m_result
);
1869 template<KeyType keyType
>
1870 static inline TypedValue
mapGetImpl(c_Map
* map
, key_type
<keyType
> key
) {
1871 TypedValue
* ret
= map
->at(key
);
1872 tvRefcountedIncRef(ret
);
1876 #define HELPER_TABLE(m) \
1877 /* name keyType */ \
1878 m(mapGetS, KeyType::Str) \
1879 m(mapGetI, KeyType::Int)
1881 #define ELEM(nm, keyType) \
1882 TypedValue nm(c_Map* map, key_type<keyType> key) { \
1883 return mapGetImpl<keyType>(map, key); \
1885 namespace MInstrHelpers
{
1890 void HhbcTranslator::MInstrTranslator::emitMapGet(SSATmp
* key
) {
1891 assert(key
->isA(Type::Int
) || key
->isA(Type::Str
));
1892 KeyType keyType
= key
->isA(Type::Int
) ? KeyType::Int
: KeyType::Str
;
1894 typedef TypedValue (*OpFunc
)(c_Map
*, TypedValue
*);
1895 BUILD_OPTAB(keyType
);
1896 m_result
= gen(MapGet
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
);
1900 template <KeyType keyType
>
1901 static inline TypedValue
cGetElemImpl(TypedValue
* base
, key_type
<keyType
> key
,
1904 auto result
= Elem
<true, keyType
>(scratch
, mis
->tvRef
, base
, key
);
1905 result
= tvToCell(result
);
1906 tvRefcountedIncRef(result
);
1910 #define HELPER_TABLE(m) \
1912 m(cGetElemC, KeyType::Any) \
1913 m(cGetElemI, KeyType::Int) \
1914 m(cGetElemS, KeyType::Str)
1916 #define ELEM(nm, kt) \
1917 TypedValue nm(TypedValue* base, key_type<kt> key, MInstrState* mis) { \
1918 return cGetElemImpl<kt>(base, key, mis); \
1920 namespace MInstrHelpers
{
1925 void HhbcTranslator::MInstrTranslator::emitCGetElem() {
1926 SSATmp
* key
= getKey();
1928 SimpleOp simpleOpType
= simpleCollectionOp();
1929 switch (simpleOpType
) {
1930 case SimpleOp::Array
:
1931 m_result
= emitArrayGet(key
);
1933 case SimpleOp::PackedArray
:
1934 m_result
= emitPackedArrayGet(m_base
, key
);
1936 case SimpleOp::ProfiledArray
:
1937 emitProfiledArrayGet(key
);
1939 case SimpleOp::String
:
1942 case SimpleOp::Vector
:
1945 case SimpleOp::Pair
:
1951 case SimpleOp::None
:
1952 typedef TypedValue (*OpFunc
)(TypedValue
*, TypedValue
, MInstrState
*);
1953 BUILD_OPTAB(getKeyType(key
));
1954 m_result
= gen(CGetElem
, makeCatch(), cns((TCA
)opFunc
),
1955 m_base
, key
, genMisPtr());
1961 template <KeyType keyType
>
1962 static inline RefData
* vGetElemImpl(TypedValue
* base
, key_type
<keyType
> key
,
1964 TypedValue
* result
= HPHP::ElemD
<false, true, keyType
>(
1965 mis
->tvScratch
, mis
->tvRef
, base
, key
);
1967 if (result
->m_type
!= KindOfRef
) {
1970 RefData
* ref
= result
->m_data
.pref
;
1975 #define HELPER_TABLE(m) \
1976 /* name keyType */ \
1977 m(vGetElemC, KeyType::Any) \
1978 m(vGetElemI, KeyType::Int) \
1979 m(vGetElemS, KeyType::Str)
1981 #define ELEM(nm, kt) \
1982 RefData* nm(TypedValue* base, key_type<kt> key, MInstrState* mis) { \
1983 return vGetElemImpl<kt>(base, key, mis); \
1985 namespace MInstrHelpers
{
1990 void HhbcTranslator::MInstrTranslator::emitVGetElem() {
1991 SSATmp
* key
= getKey();
1992 typedef RefData
* (*OpFunc
)(TypedValue
*, TypedValue
, MInstrState
*);
1993 BUILD_OPTAB(getKeyType(key
));
1994 m_result
= genStk(VGetElem
, makeCatch(), cns((TCA
)opFunc
),
1995 m_base
, key
, genMisPtr());
1999 template <KeyType keyType
, bool isEmpty
>
2000 static inline bool issetEmptyElemImpl(TypedValue
* base
, key_type
<keyType
> key
,
2002 // mis == nullptr if we proved that it won't be used. mis->tvScratch and
2003 // mis->tvRef are ok because those params are passed by
2005 return HPHP::IssetEmptyElem
<isEmpty
, keyType
>(
2006 mis
->tvScratch
, mis
->tvRef
, base
, key
);
2009 #define HELPER_TABLE(m) \
2010 /* name keyType isEmpty */\
2011 m(issetElemC, KeyType::Any, false) \
2012 m(issetElemCE, KeyType::Any, true) \
2013 m(issetElemI, KeyType::Int, false) \
2014 m(issetElemIE, KeyType::Int, true) \
2015 m(issetElemS, KeyType::Str, false) \
2016 m(issetElemSE, KeyType::Str, true)
2018 #define ISSET(nm, kt, isEmpty) \
2019 uint64_t nm(TypedValue* base, key_type<kt> key, MInstrState* mis) { \
2020 return issetEmptyElemImpl<kt, isEmpty>(base, key, mis); \
2022 namespace MInstrHelpers
{
2027 void HhbcTranslator::MInstrTranslator::emitIssetEmptyElem(bool isEmpty
) {
2028 SSATmp
* key
= getKey();
2030 typedef uint64_t (*OpFunc
)(TypedValue
*, TypedValue
, MInstrState
*);
2031 BUILD_OPTAB(getKeyType(key
), isEmpty
);
2032 m_result
= gen(isEmpty
? EmptyElem
: IssetElem
, makeCatch(),
2033 cns((TCA
)opFunc
), m_base
, key
, genMisPtr());
2037 void HhbcTranslator::MInstrTranslator::emitPackedArrayIsset() {
2038 assert(m_base
->type().getArrayKind() == ArrayData::kPackedKind
);
2039 SSATmp
* key
= getKey();
2040 m_result
= m_irb
.cond(
2042 [&] (Block
* taken
) {
2043 gen(CheckPackedArrayBounds
, taken
, m_base
, key
);
2046 return gen(CheckPackedArrayElemNull
, m_base
, key
);
2053 template<KeyType keyType
, bool checkForInt
>
2054 static inline uint64_t arrayIssetImpl(ArrayData
* a
, key_type
<keyType
> key
) {
2055 auto const value
= checkForInt
? checkedGet(a
, key
) : a
->nvGet(key
);
2056 if (!value
) return 0;
2057 return !tvAsCVarRef(value
).isNull();
2060 #define HELPER_TABLE(m) \
2061 /* name keyType checkForInt */\
2062 m(arrayIssetS, KeyType::Str, false) \
2063 m(arrayIssetSi, KeyType::Str, true) \
2064 m(arrayIssetI, KeyType::Int, false)
2066 #define ISSET(nm, keyType, checkForInt) \
2067 uint64_t nm(ArrayData* a, key_type<keyType> key) { \
2068 return arrayIssetImpl<keyType, checkForInt>(a, key); \
2070 namespace MInstrHelpers
{
2075 void HhbcTranslator::MInstrTranslator::emitArrayIsset() {
2076 SSATmp
* key
= getKey();
2079 m_ht
.checkStrictlyInteger(key
, keyType
, checkForInt
);
2081 typedef uint64_t (*OpFunc
)(ArrayData
*, TypedValue
*);
2082 BUILD_OPTAB(keyType
, checkForInt
);
2083 assert(m_base
->isA(Type::Arr
));
2084 m_result
= gen(ArrayIsset
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
);
2088 void HhbcTranslator::MInstrTranslator::emitStringIsset() {
2089 auto key
= getKey();
2090 m_result
= gen(StringIsset
, m_base
, key
);
2093 namespace MInstrHelpers
{
2094 uint64_t vectorIsset(c_Vector
* vec
, int64_t index
) {
2095 auto result
= vec
->get(index
);
2096 return result
? !cellIsNull(result
) : false;
2100 void HhbcTranslator::MInstrTranslator::emitVectorIsset() {
2101 SSATmp
* key
= getKey();
2102 assert(key
->isA(Type::Int
));
2103 m_result
= gen(VectorIsset
,
2104 cns((TCA
)MInstrHelpers::vectorIsset
),
2109 namespace MInstrHelpers
{
2110 uint64_t pairIsset(c_Pair
* pair
, int64_t index
) {
2111 auto result
= pair
->get(index
);
2112 return result
? !cellIsNull(result
) : false;
2116 void HhbcTranslator::MInstrTranslator::emitPairIsset() {
2117 SSATmp
* key
= getKey();
2118 assert(key
->isA(Type::Int
));
2119 m_result
= gen(PairIsset
,
2120 cns((TCA
)MInstrHelpers::pairIsset
),
2125 template<KeyType keyType
>
2126 static inline uint64_t mapIssetImpl(c_Map
* map
, key_type
<keyType
> key
) {
2127 auto result
= map
->get(key
);
2128 return result
? !cellIsNull(result
) : false;
2131 #define HELPER_TABLE(m) \
2132 /* name keyType */ \
2133 m(mapIssetS, KeyType::Str) \
2134 m(mapIssetI, KeyType::Int)
2136 #define ELEM(nm, keyType) \
2137 uint64_t nm(c_Map* map, key_type<keyType> key) { \
2138 return mapIssetImpl<keyType>(map, key); \
2140 namespace MInstrHelpers
{
2145 void HhbcTranslator::MInstrTranslator::emitMapIsset() {
2146 SSATmp
* key
= getKey();
2147 assert(key
->isA(Type::Int
) || key
->isA(Type::Str
));
2148 KeyType keyType
= key
->isA(Type::Int
) ? KeyType::Int
: KeyType::Str
;
2150 typedef TypedValue (*OpFunc
)(c_Map
*, TypedValue
*);
2151 BUILD_OPTAB(keyType
);
2152 m_result
= gen(MapIsset
, cns((TCA
)opFunc
), m_base
, key
);
2156 void HhbcTranslator::MInstrTranslator::emitIssetElem() {
2157 SimpleOp simpleOpType
= simpleCollectionOp();
2158 switch (simpleOpType
) {
2159 case SimpleOp::Array
:
2160 case SimpleOp::ProfiledArray
:
2163 case SimpleOp::PackedArray
:
2164 emitPackedArrayIsset();
2166 case SimpleOp::String
:
2169 case SimpleOp::Vector
:
2172 case SimpleOp::Pair
:
2178 case SimpleOp::None
:
2179 emitIssetEmptyElem(false);
2184 void HhbcTranslator::MInstrTranslator::emitEmptyElem() {
2185 emitIssetEmptyElem(true);
2188 static inline ArrayData
* uncheckedSet(ArrayData
* a
,
2192 return g_array_funcs
.setStr
[a
->kind()](a
, key
, value
, copy
);
2195 static inline ArrayData
* uncheckedSet(ArrayData
* a
,
2199 return g_array_funcs
.setInt
[a
->kind()](a
, key
, value
, copy
);
2202 static inline ArrayData
* checkedSet(ArrayData
* a
,
2207 return UNLIKELY(key
->isStrictlyInteger(i
))
2208 ? uncheckedSet(a
, i
, value
, copy
)
2209 : uncheckedSet(a
, key
, value
, copy
);
2212 static inline ArrayData
* checkedSet(ArrayData
* a
,
2219 template<KeyType keyType
, bool checkForInt
, bool setRef
>
2220 static inline typename ShuffleReturn
<setRef
>::return_type
arraySetImpl(
2221 ArrayData
* a
, key_type
<keyType
> key
,
2222 Cell value
, RefData
* ref
) {
2223 static_assert(keyType
!= KeyType::Any
,
2224 "KeyType::Any is not supported in arraySetMImpl");
2225 assert(cellIsPlausible(value
));
2226 const bool copy
= a
->hasMultipleRefs();
2227 ArrayData
* ret
= checkForInt
? checkedSet(a
, key
, value
, copy
)
2228 : uncheckedSet(a
, key
, value
, copy
);
2230 return arrayRefShuffle
<setRef
>(a
, ret
, setRef
? ref
->tv() : nullptr);
2233 #define HELPER_TABLE(m) \
2234 /* name keyType checkForInt setRef */ \
2235 m(arraySetS, KeyType::Str, false, false) \
2236 m(arraySetSi, KeyType::Str, true, false) \
2237 m(arraySetI, KeyType::Int, false, false) \
2238 m(arraySetSR, KeyType::Str, false, true) \
2239 m(arraySetSiR, KeyType::Str, true, true) \
2240 m(arraySetIR, KeyType::Int, false, true)
2242 #define ELEM(nm, keyType, checkForInt, setRef) \
2243 typename ShuffleReturn<setRef>::return_type \
2244 nm(ArrayData* a, key_type<keyType> key, Cell value, RefData* ref) { \
2245 return arraySetImpl<keyType, checkForInt, setRef>( \
2246 a, key, value, ref); \
2248 namespace MInstrHelpers
{
2253 void HhbcTranslator::MInstrTranslator::emitArraySet(SSATmp
* key
,
2255 assert(m_iInd
== m_mii
.valCount() + 1);
2256 const int baseStkIdx
= m_mii
.valCount();
2257 assert(key
->type().notBoxed());
2258 assert(value
->type().notBoxed());
2261 m_ht
.checkStrictlyInteger(key
, keyType
, checkForInt
);
2262 const DynLocation
& base
= *m_ni
.inputs
[m_mii
.valCount()];
2263 bool setRef
= base
.rtt
.isBoxed();
2264 typedef ArrayData
* (*OpFunc
)(ArrayData
*, TypedValue
*, TypedValue
, RefData
*);
2265 BUILD_OPTAB(keyType
, checkForInt
, setRef
);
2267 // No catch trace below because the helper can't throw. It may reenter to
2268 // call destructors so it has a sync point in nativecalls.cpp, but exceptions
2269 // are swallowed at destructor boundaries right now: #2182869.
2271 assert(base
.location
.space
== Location::Local
||
2272 base
.location
.space
== Location::Stack
);
2273 SSATmp
* box
= getInput(baseStkIdx
, DataTypeSpecific
);
2274 gen(ArraySetRef
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
, value
, box
);
2275 // Unlike the non-ref case, we don't need to do anything to the stack
2276 // because any load of the box will be guarded.
2281 SSATmp
* newArr
= gen(ArraySet
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
,
2284 // Update the base's value with the new array
2285 if (base
.location
.space
== Location::Local
) {
2286 // We know it's not boxed (setRef above handles that), and
2287 // newArr has already been incref'd in the helper.
2288 gen(StLoc
, LocalId(base
.location
.offset
), m_irb
.fp(), newArr
);
2289 } else if (base
.location
.space
== Location::Stack
) {
2290 MInstrEffects
effects(newArr
->inst());
2291 assert(effects
.baseValChanged
);
2292 assert(effects
.baseType
<= Type::Arr
);
2293 m_ht
.extendStack(baseStkIdx
, Type::Gen
);
2294 m_ht
.replace(baseStkIdx
, newArr
);
2303 namespace MInstrHelpers
{
2304 void setWithRefElemC(TypedValue
* base
, TypedValue key
, TypedValue
* val
,
2306 base
= HPHP::ElemD
<false, false>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
2307 if (base
!= &mis
->tvScratch
) {
2310 assert(base
->m_type
== KindOfUninit
);
2314 void setWithRefNewElem(TypedValue
* base
, TypedValue
* val
,
2316 base
= NewElem(mis
->tvScratch
, mis
->tvRef
, base
);
2317 if (base
!= &mis
->tvScratch
) {
2320 assert(base
->m_type
== KindOfUninit
);
2325 void HhbcTranslator::MInstrTranslator::emitSetWithRefLElem() {
2326 SSATmp
* key
= getKey();
2327 SSATmp
* locAddr
= getValAddr();
2328 if (m_base
->type().strip() <= Type::Arr
&&
2329 !locAddr
->type().deref().maybeBoxed()) {
2330 constrainBase(DataTypeSpecific
);
2332 assert(m_strTestResult
== nullptr);
2334 genStk(SetWithRefElem
, makeCatch(),
2335 cns((TCA
)MInstrHelpers::setWithRefElemC
),
2336 m_base
, key
, locAddr
, genMisPtr());
2341 void HhbcTranslator::MInstrTranslator::emitSetWithRefLProp() {
2345 void HhbcTranslator::MInstrTranslator::emitSetWithRefRElem() {
2346 emitSetWithRefLElem();
2349 void HhbcTranslator::MInstrTranslator::emitSetWithRefRProp() {
2350 emitSetWithRefLProp();
2353 void HhbcTranslator::MInstrTranslator::emitSetWithRefNewElem() {
2354 if (m_base
->type().strip() <= Type::Arr
&&
2355 getValue()->type().notBoxed()) {
2356 constrainBase(DataTypeSpecific
);
2359 genStk(SetWithRefNewElem
, makeCatch(),
2360 cns((TCA
)MInstrHelpers::setWithRefNewElem
),
2361 m_base
, getValAddr(), genMisPtr());
2366 void HhbcTranslator::MInstrTranslator::emitVectorSet(
2367 SSATmp
* key
, SSATmp
* value
) {
2368 assert(key
->isA(Type::Int
));
2369 if (key
->isConst() && key
->intVal() < 0) {
2370 PUNT(emitVectorSet
); // will throw
2372 SSATmp
* size
= gen(LdVectorSize
, m_base
);
2373 gen(CheckBounds
, makeCatch(), key
, size
);
2375 m_irb
.ifThen([&](Block
* taken
) {
2376 gen(VectorHasImmCopy
, taken
, m_base
);
2379 m_irb
.hint(Block::Hint::Unlikely
);
2380 gen(VectorDoCow
, m_base
);
2384 SSATmp
* vecBase
= gen(LdVectorBase
, m_base
);
2386 static_assert(sizeof(TypedValue
) == 16,
2387 "TypedValue size expected to be 16 bytes");
2388 auto idx
= gen(Shl
, key
, cns(4));
2389 oldVal
= gen(LdElem
, vecBase
, idx
);
2390 gen(StElem
, vecBase
, idx
, value
);
2391 gen(DecRef
, oldVal
);
2396 template<KeyType keyType
>
2397 static inline void mapSetImpl(c_Map
* map
, key_type
<keyType
> key
, Cell value
) {
2398 map
->set(key
, &value
);
2401 #define HELPER_TABLE(m) \
2402 /* name keyType */ \
2403 m(mapSetS, KeyType::Str) \
2404 m(mapSetI, KeyType::Int)
2406 #define ELEM(nm, keyType) \
2407 void nm(c_Map* map, key_type<keyType> key, Cell value) { \
2408 mapSetImpl<keyType>(map, key, value); \
2410 namespace MInstrHelpers
{
2415 void HhbcTranslator::MInstrTranslator::emitMapSet(
2416 SSATmp
* key
, SSATmp
* value
) {
2417 assert(key
->isA(Type::Int
) || key
->isA(Type::Str
));
2418 KeyType keyType
= key
->isA(Type::Int
) ? KeyType::Int
: KeyType::Str
;
2420 typedef TypedValue (*OpFunc
)(c_Map
*, TypedValue
*, TypedValue
*);
2421 BUILD_OPTAB(keyType
);
2422 gen(MapSet
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
, value
);
2427 template <KeyType keyType
>
2428 static inline StringData
* setElemImpl(TypedValue
* base
, key_type
<keyType
> key
,
2430 return HPHP::SetElem
<false, keyType
>(base
, key
, &val
);
2433 #define HELPER_TABLE(m) \
2434 /* name keyType */ \
2435 m(setElemC, KeyType::Any) \
2436 m(setElemI, KeyType::Int) \
2437 m(setElemS, KeyType::Str)
2439 #define ELEM(nm, kt) \
2440 StringData* nm(TypedValue* base, key_type<kt> key, Cell val) { \
2441 return setElemImpl<kt>(base, key, val); \
2443 namespace MInstrHelpers
{
2448 void HhbcTranslator::MInstrTranslator::emitSetElem() {
2449 SSATmp
* value
= getValue();
2450 SSATmp
* key
= getKey();
2452 SimpleOp simpleOpType
= simpleCollectionOp();
2453 assert(simpleOpType
!= SimpleOp::PackedArray
);
2454 switch (simpleOpType
) {
2455 case SimpleOp::Array
:
2456 case SimpleOp::ProfiledArray
:
2457 emitArraySet(key
, value
);
2459 case SimpleOp::PackedArray
:
2462 case SimpleOp::String
:
2465 case SimpleOp::Vector
:
2466 emitVectorSet(key
, value
);
2469 emitMapSet(key
, value
);
2471 case SimpleOp::Pair
:
2472 case SimpleOp::None
:
2473 // Emit the appropriate helper call.
2474 typedef StringData
* (*OpFunc
)(TypedValue
*, TypedValue
, Cell
);
2475 BUILD_OPTAB(getKeyType(key
));
2476 constrainBase(DataTypeSpecific
);
2477 SSATmp
* result
= genStk(SetElem
, makeCatchSet(), cns((TCA
)opFunc
),
2478 m_base
, key
, value
);
2479 auto t
= result
->type();
2480 if (t
== Type::Nullptr
) {
2481 // Base is not a string. Result is always value.
2483 } else if (t
== Type::CountedStr
) {
2484 // Base is a string. Stack result is a new string so we're responsible for
2485 // decreffing value.
2489 assert(t
.equals(Type::CountedStr
| Type::Nullptr
));
2490 // Base might be a string. Assume the result is value, then inform
2491 // emitMPost that it needs to test the actual result.
2493 m_strTestResult
= result
;
2500 void HhbcTranslator::MInstrTranslator::emitSetOpElem() {
2501 SetOpOp op
= static_cast<SetOpOp
>(m_ni
.imm
[0].u_OA
);
2502 m_result
= genStk(SetOpElem
, makeCatch(),
2503 m_base
, getKey(), getValue(), genMisPtr(), cns(op
));
2506 void HhbcTranslator::MInstrTranslator::emitIncDecElem() {
2507 IncDecOp op
= static_cast<IncDecOp
>(m_ni
.imm
[0].u_OA
);
2508 m_result
= genStk(IncDecElem
, makeCatch(),
2509 m_base
, getKey(), genMisPtr(), cns(op
));
2512 namespace MInstrHelpers
{
2513 void bindElemC(TypedValue
* base
, TypedValue key
, RefData
* val
,
2515 base
= HPHP::ElemD
<false, true>(mis
->tvScratch
, mis
->tvRef
, base
, key
);
2516 if (!(base
== &mis
->tvScratch
&& base
->m_type
== KindOfUninit
)) {
2517 tvBindRef(val
, base
);
2522 void HhbcTranslator::MInstrTranslator::emitBindElem() {
2523 SSATmp
* key
= getKey();
2524 SSATmp
* box
= getValue();
2525 genStk(BindElem
, makeCatch(), cns((TCA
)MInstrHelpers::bindElemC
),
2526 m_base
, key
, box
, genMisPtr());
2530 template <KeyType keyType
>
2531 static inline void unsetElemImpl(TypedValue
* base
, key_type
<keyType
> key
) {
2532 HPHP::UnsetElem
<keyType
>(base
, key
);
2535 #define HELPER_TABLE(m) \
2536 /* name keyType */ \
2537 m(unsetElemC, KeyType::Any) \
2538 m(unsetElemI, KeyType::Int) \
2539 m(unsetElemS, KeyType::Str)
2541 #define ELEM(nm, kt) \
2542 void nm(TypedValue* base, key_type<kt> key) { \
2543 unsetElemImpl<kt>(base, key); \
2545 namespace MInstrHelpers
{
2550 void HhbcTranslator::MInstrTranslator::emitUnsetElem() {
2551 SSATmp
* key
= getKey();
2553 Type baseType
= m_base
->type().strip();
2554 constrainBase(DataTypeSpecific
);
2555 if (baseType
<= Type::Str
) {
2556 gen(RaiseError
, makeCatch(),
2557 cns(makeStaticString(Strings::CANT_UNSET_STRING
)));
2560 if (baseType
.not(Type::Arr
| Type::Obj
)) {
2565 typedef void (*OpFunc
)(TypedValue
*, TypedValue
);
2566 BUILD_OPTAB(getKeyType(key
));
2567 genStk(UnsetElem
, makeCatch(), cns((TCA
)opFunc
), m_base
, key
);
2571 void HhbcTranslator::MInstrTranslator::emitNotSuppNewElem() {
2572 PUNT(NotSuppNewElem
);
2575 void HhbcTranslator::MInstrTranslator::emitVGetNewElem() {
2579 void HhbcTranslator::MInstrTranslator::emitSetNewElem() {
2580 SSATmp
* value
= getValue();
2581 if (m_base
->type() <= Type::PtrToArr
) {
2582 constrainBase(DataTypeSpecific
);
2583 gen(SetNewElemArray
, makeCatchSet(), m_base
, value
);
2585 gen(SetNewElem
, makeCatchSet(), m_base
, value
);
2590 void HhbcTranslator::MInstrTranslator::emitSetOpNewElem() {
2594 void HhbcTranslator::MInstrTranslator::emitIncDecNewElem() {
2598 void HhbcTranslator::MInstrTranslator::emitBindNewElem() {
2599 SSATmp
* box
= getValue();
2600 genStk(BindNewElem
, makeCatch(), m_base
, box
, genMisPtr());
2604 void HhbcTranslator::MInstrTranslator::emitMPost() {
2605 SSATmp
* catchSp
= nullptr;
2606 if (m_failedSetBlock
) {
2607 auto* endCatch
= &m_failedSetBlock
->back();
2608 assert(endCatch
->is(EndCatch
, TryEndCatch
));
2609 catchSp
= endCatch
->src(1);
2610 assert(catchSp
->isA(Type::StkPtr
));
2613 // Decref stack inputs. If we're translating a SetM or BindM, then input 0 is
2614 // both our input and output so leave its refcount alone. If m_failedSetBlock
2615 // is non-null, the final helper call may throw an InvalidSetMException. We
2616 // need to add instructions to m_failedSetBlock to finish the vector
2617 // instruction in case this happens, so any DecRefs emitted here are also
2618 // added to m_failedSetBlock.
2620 (m_ni
.mInstrOp() == OpSetM
|| m_ni
.mInstrOp() == OpBindM
) ? 1 : 0;
2621 for (unsigned i
= nStack
; i
< m_ni
.inputs
.size(); ++i
) {
2622 const DynLocation
& input
= *m_ni
.inputs
[i
];
2623 switch (input
.location
.space
) {
2624 case Location::Stack
: {
2626 auto input
= getInput(i
, DataTypeSpecific
);
2627 if (input
->isA(Type::Gen
)) {
2629 if (m_failedSetBlock
) {
2630 BlockPusher
bp(m_irb
, m_marker
, m_failedSetBlock
);
2631 gen(DecRefStack
, StackOffset(m_stackInputs
[i
]), Type::Gen
, catchSp
);
2636 case Location::Local
:
2637 case Location::Litstr
:
2638 case Location::Litint
:
2639 case Location::This
: {
2644 default: not_reached();
2648 // Pop off all stack inputs
2649 m_ht
.discard(nStack
);
2651 // Push result, if one was produced. If we have a predicted result use that
2652 // instead of the real result; its validity will be guarded later in this
2655 m_ht
.push(m_result
);
2657 assert(m_ni
.mInstrOp() == OpUnsetM
||
2658 m_ni
.mInstrOp() == OpSetWithRefLM
||
2659 m_ni
.mInstrOp() == OpSetWithRefRM
);
2662 // Clean up tvRef(2): during exception handling any objects required only
2663 // during vector expansion need to be DecRef'd. There may be either one
2664 // or two such scratch objects, in the case of a Set the first of which will
2665 // always be tvRef2, in all other cases if only one scratch value is present
2666 // it will be stored in tvRef.
2667 static const size_t refOffs
[] = { MISOFF(tvRef
), MISOFF(tvRef2
) };
2668 for (unsigned i
= 0; i
< std::min(nLogicalRatchets(), 2U); ++i
) {
2669 IRInstruction
* inst
= m_unit
.gen(DecRefMem
, m_marker
, Type::Gen
, m_misBase
,
2670 cns(refOffs
[m_failedSetBlock
? 1 - i
: i
]));
2672 prependToTraces(inst
);
2675 emitSideExits(catchSp
, nStack
);
2678 void HhbcTranslator::MInstrTranslator::emitSideExits(SSATmp
* catchSp
,
2680 auto const nextOff
= m_ht
.nextBcOff();
2681 auto const op
= m_ni
.mInstrOp();
2682 const bool isSetWithRef
= op
== OpSetWithRefLM
|| op
== OpSetWithRefRM
;
2684 if (m_failedSetBlock
) {
2685 assert(bool(m_result
) ^ isSetWithRef
);
2686 // We need to emit code to clean up and side exit if the TryEndCatch falls
2687 // through because of an InvalidSetMException. If we're translating a
2688 // SetWithRef* bytecode we don't have to do anything special to the stack
2689 // since they have no stack output. Otherwise we need to pop our input
2690 // value and push the value from the exception to the stack (done with a
2691 // DecRefStack followed by a SpillStack).
2693 std::vector
<SSATmp
*> args
{
2694 catchSp
, // sp from the previous SpillStack
2695 cns(nStack
), // cells popped since the last SpillStack
2698 BlockPusher
bp(m_irb
, m_marker
, m_failedSetBlock
);
2699 if (!isSetWithRef
) {
2700 gen(DecRefStack
, StackOffset(0), Type::Cell
, catchSp
);
2701 args
.push_back(m_ht
.gen(LdUnwinderValue
, Type::Cell
));
2704 SSATmp
* sp
= gen(SpillStack
, std::make_pair(args
.size(), &args
[0]));
2705 gen(DeleteUnwinderException
);
2706 gen(SyncABIRegs
, m_irb
.fp(), sp
);
2707 gen(ReqBindJmp
, ReqBindJmpData(nextOff
));
2710 if (m_strTestResult
) {
2711 assert(!isSetWithRef
);
2712 // We expected SetElem's base to not be a Str but might be wrong. Make an
2713 // exit trace to side exit to the next instruction, replacing our guess
2714 // with the correct stack output.
2716 auto toSpill
= m_ht
.peekSpillValues();
2717 assert(toSpill
.size());
2718 assert(toSpill
.back() == m_result
);
2719 SSATmp
* str
= m_unit
.gen(AssertNonNull
, m_marker
, m_strTestResult
)->dst();
2720 toSpill
.back() = str
;
2722 auto exit
= m_ht
.makeExit(nextOff
, toSpill
);
2724 BlockPusher
tp(m_irb
, m_marker
, exit
, exit
->skipHeader());
2725 gen(IncStat
, cns(Stats::TC_SetMStrGuess_Miss
), cns(1), cns(false));
2726 gen(DecRef
, m_result
);
2727 m_irb
.add(str
->inst());
2729 gen(CheckNullptr
, exit
, m_strTestResult
);
2730 gen(IncStat
, cns(Stats::TC_SetMStrGuess_Hit
), cns(1), cns(false));
2734 Block
* HhbcTranslator::MInstrTranslator::makeEmptyCatch() {
2735 return m_ht
.makeCatch();
2738 Block
* HhbcTranslator::MInstrTranslator::makeCatch() {
2739 auto b
= makeEmptyCatch();
2740 m_failedVec
.push_back(b
);
2744 Block
* HhbcTranslator::MInstrTranslator::makeCatchSet() {
2745 assert(!m_failedSetBlock
);
2746 m_failedSetBlock
= makeCatch();
2748 // This catch trace will be modified in emitMPost to end with a side
2749 // exit, and TryEndCatch will fall through to that side exit if an
2750 // InvalidSetMException is thrown.
2751 m_failedSetBlock
->back().setOpcode(TryEndCatch
);
2752 return m_failedSetBlock
;
2755 void HhbcTranslator::MInstrTranslator::prependToTraces(IRInstruction
* inst
) {
2756 for (auto b
: m_failedVec
) {
2757 b
->prepend(m_unit
.cloneInstruction(inst
));
2761 bool HhbcTranslator::MInstrTranslator::needFirstRatchet() const {
2762 if (m_ni
.inputs
[m_mii
.valCount()]->rtt
.unbox() <= Type::Arr
) {
2763 switch (m_ni
.immVecM
[0]) {
2764 case MEC
: case MEL
: case MET
: case MEI
: return false;
2765 case MPC
: case MPL
: case MPT
: case MW
: return true;
2766 default: not_reached();
2772 bool HhbcTranslator::MInstrTranslator::needFinalRatchet() const {
2773 return m_mii
.finalGet();
2776 // Ratchet operations occur after each intermediate operation, except
2777 // possibly the first and last (see need{First,Final}Ratchet()). No actual
2778 // ratchet occurs after the final operation, but this means that both tvRef
2779 // and tvRef2 can contain references just after the final operation. Here we
2780 // pretend that a ratchet occurs after the final operation, i.e. a "logical"
2781 // ratchet. The reason for counting logical ratchets as part of the total is
2782 // the following case, in which the logical count is 0:
2787 // no logical ratchet
2789 // Following are a few more examples to make the algorithm clear:
2791 // (base is array) (base is object) (base is object)
2792 // BaseL BaseL BaseL
2793 // ElemL ElemL CGetPropL
2794 // no ratchet ratchet logical ratchet
2798 // ratchet logical ratchet
2812 unsigned HhbcTranslator::MInstrTranslator::nLogicalRatchets() const {
2813 // If we've proven elsewhere that we don't need an MInstrState struct, we
2814 // know this translation won't need any ratchets
2815 if (!m_needMIS
) return 0;
2817 unsigned ratchets
= m_ni
.immVecM
.size();
2818 if (!needFirstRatchet()) --ratchets
;
2819 if (!needFinalRatchet()) --ratchets
;
2823 int HhbcTranslator::MInstrTranslator::ratchetInd() const {
2824 return needFirstRatchet() ? int(m_mInd
) : int(m_mInd
) - 1;