Various llvm backend bugfixes
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-minstr.cpp
blob07518fe8383f902e4a76c49406497bb892a90250
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
17 #include <sstream>
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 {
31 namespace {
33 //////////////////////////////////////////////////////////////////////
35 const StaticString s_PackedArray("PackedArray");
37 //////////////////////////////////////////////////////////////////////
39 bool wantPropSpecializedWarnings() {
40 return !RuntimeOption::RepoAuthoritative ||
41 !RuntimeOption::EvalDisableSomeRepoAuthNotices;
44 //////////////////////////////////////////////////////////////////////
46 enum class SimpleOp {
47 None,
48 Array,
49 ProfiledArray,
50 PackedArray,
51 String,
52 Vector, // c_Vector* or c_ImmVector*
53 Map, // c_Map*
54 Pair, // c_Pair*
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).
64 struct MTS {
65 explicit MTS(HTS& hts)
66 : hts(hts)
67 , ni(*hts.currentNormalizedInstruction)
68 , irb(*hts.irb)
69 , unit(hts.unit)
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; }
77 HTS& hts;
78 const NormalizedInstruction& ni;
79 IRBuilder& irb;
80 IRUnit& unit;
81 MInstrInfo mii;
82 BCMarker marker;
84 hphp_hash_map<unsigned,unsigned> stackInputs;
86 unsigned mInd;
87 unsigned iInd;
89 bool needMIS{true};
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
97 * progress.
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
104 * runtime.
106 * Don't change base directly; use setBase, to update base.type
107 * automatically.
109 struct {
110 SSATmp* value{nullptr};
111 Type type{Type::Bottom};
112 } base;
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
137 * operation */
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
146 // thing everywhere.
147 Block* makeEmptyCatch(MTS& env) {
148 HTS& hts = 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);
156 return 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
175 // appropriate.
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) {
185 case SimpleOp::None:
186 return false;
188 case SimpleOp::Array:
189 case SimpleOp::ProfiledArray:
190 case SimpleOp::String:
191 env.irb.constrainValue(env.base.value, DataTypeSpecific);
192 return true;
194 case SimpleOp::PackedArray:
195 constrainBase(
196 env,
197 TypeConstraint(DataTypeSpecialized).setWantArrayKind()
199 return true;
201 case SimpleOp::Vector:
202 case SimpleOp::Map:
203 case SimpleOp::Pair:
204 always_assert(env.base.type < Type::Obj &&
205 env.base.type.isSpecialized());
206 constrainBase(env, TypeConstraint(env.base.type.getClass()));
207 return true;
210 not_reached();
211 return false;
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,
228 Srcs... srcs) {
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&,
236 Srcs... srcs) {
237 return gen(env, opc, taken, srcs...);
241 template<class ExtraData, class... Srcs>
242 SSATmp* genStk(MTS& env,
243 Opcode opc,
244 Block* taken,
245 const ExtraData& extra,
246 Srcs... srcs) {
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
260 * StkPtr. */
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");
282 if (env.needMIS) {
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,
290 PropInfo propInfo) {
291 if (convertToType(propInfo.repoAuthType).not(Type::Uninit)) {
292 return false;
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
297 // get and set.
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());
306 unsigned mi = 0;
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)) {
315 return true;
317 ++ii;
318 } else {
319 return true;
323 return false;
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.
347 return;
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,
358 curClass(env));
360 if (baseType.maybeBoxed() && !baseType.isBoxed()) {
361 // We don't need to bother with weird base types.
362 return;
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
368 // no-op here.
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) {
405 env.needMIS = false;
406 if (simpleCollectionGet || simpleCollectionIsset) {
407 constrainBase(env, TypeConstraint(baseType.getClass()));
408 } else {
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));
427 ++iInd;
429 for (int mInd = 0; mInd < env.ni.immVecM.size(); ++mInd) {
430 auto mcode = env.ni.immVecM[mInd];
431 shape << separator;
432 if (mcode == MW) {
433 shape << "MW";
434 } else if (mcodeIsElem(mcode)) {
435 shape << "ME:" << rttStr(iInd);
436 } else if (mcodeIsProp(mcode)) {
437 shape << "MP:" << rttStr(iInd);
438 } else {
439 not_reached();
441 if (mcode != MW) ++iInd;
442 separator = " ";
444 shape << '>';
445 gen(env,
446 IncStatGrouped,
447 cns(env, makeStaticString("vector instructions")),
448 cns(env, makeStaticString(shape.str())),
449 cns(env, 1));
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));
459 switch (l.space) {
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);
474 case Location::This:
475 // If we don't have a current class context, this instruction will be
476 // unreachable.
477 if (!curClass(env)) PUNT(Unreachable-LdThis);
478 return ldThis(env);
480 default: not_reached();
484 void setBase(MTS& env,
485 SSATmp* tmp,
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);
504 return 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));
525 spillStack(env);
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);
552 return base->type();
553 }();
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.
579 if (readInst) {
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,
592 DataTypeGeneric);
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;
600 } else if (isMap) {
601 if (mcodeIsElem(env.ni.immVecM[0])) {
602 auto const key = getInput(env, env.mii.valCount() + 1,
603 DataTypeGeneric);
604 if (key->isA(Type::Int) || key->isA(Type::Str)) {
605 return SimpleOp::Map;
612 return SimpleOp::None;
615 //////////////////////////////////////////////////////////////////////
616 // Base ops
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) {
631 gen(env,
632 RaiseUninitLoc,
633 makeEmptyCatch(env),
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;
643 gen(
644 env,
645 StLoc,
646 LocalId(baseDL.location.offset),
647 fp(env),
648 base
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
660 * side-exit here.
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);
679 return;
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) {
687 setBase(
688 env,
689 ldLocAddr(env, baseDL.location.offset),
690 env.irb.localType(
691 baseDL.location.offset,
692 DataTypeSpecific
693 ).ptr(Ptr::Frame)
695 } else {
696 assert(baseDL.location.space == Location::Stack);
697 // Make sure the stack is clean before getting a pointer to one of its
698 // elements.
699 spillStack(env);
700 assert(env.stackInputs.count(env.iInd));
701 auto const sinfo = getStackValue(sp(env), env.stackInputs[env.iInd]);
702 setBase(
703 env,
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.
721 PUNT(emitBaseN);
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);
728 setBase(
729 env,
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.
746 setBase(
747 env,
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 //////////////////////////////////////////////////////////////////////
764 // Intermediate ops
766 PropInfo getCurrentPropertyOffset(MTS& env, const Class*& knownCls) {
767 auto const baseType = env.base.type.derefIfPtr();
768 if (!knownCls) {
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.
795 always_assert_flog(
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.
805 knownCls = nullptr;
806 return PropInfo{};
808 return info;
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,
820 SSATmp* baseAsObj,
821 SSATmp* propAddr,
822 PropInfo propInfo,
823 bool doWarn,
824 bool doDefine) {
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;
838 return env.irb.cond(
840 [&] (Block* taken) {
841 gen(env, CheckInitMem, taken, propAddr, cns(env, 0));
843 [&] { // Next: Property isn't Uninit. Do nothing.
844 return propAddr;
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
853 // probably a bug...
854 gen(env, RaiseUndefProp, makeEmptyCatch(env), baseAsObj, key);
856 if (doDefine) {
857 gen(
858 env,
859 StProp,
860 baseAsObj,
861 cns(env, propInfo.offset),
862 cns(env, Type::InitNull)
864 return propAddr;
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();
878 SCOPE_EXIT {
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(
892 env,
893 LdPropAddr,
894 convertToType(propInfo.repoAuthType).ptr(Ptr::Prop),
895 env.base.value,
896 cns(env, propInfo.offset)
898 setBase(
899 env,
900 checkInitProp(env, env.base.value, propAddr, propInfo, doWarn, doDefine)
902 return;
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(
917 [&] (Block* taken) {
918 gen(env, CheckTypeMem, Type::Obj, taken, env.base.value);
920 [&] {
921 // Next: Base is an object. Load property and check for uninit.
922 auto const obj = gen(
923 env,
924 LdMem,
925 env.base.type.deref() & Type::Obj,
926 env.base.value,
927 cns(env, 0)
929 auto const propAddr = gen(
930 env,
931 LdPropAddr,
932 convertToType(propInfo.repoAuthType).ptr(Ptr::Prop),
933 obj,
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);
940 if (doWarn) {
941 gen(env, WarnNonObjProp, makeMRCatch(env));
943 if (doDefine) {
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
959 * here.
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...
964 always_assert_flog(
965 false,
966 "Static analysis error: we would've needed to generate "
967 "stdClass-promotion code in the JIT, which is unexpected."
970 return initNull;
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());
983 return;
986 auto const key = getKey(env);
987 if (mia & MIA_define) {
988 setBase(
989 env,
990 gen(env,
991 PropDX,
992 makeMRCatch(env),
993 MInstrAttrData { mia },
994 env.base.value,
995 key,
996 misPtr(env))
998 } else {
999 setBase(
1000 env,
1001 gen(env,
1002 PropX,
1003 makeMRCatch(env),
1004 MInstrAttrData { mia },
1005 env.base.value,
1006 key,
1007 misPtr(env))
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);
1019 } else {
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))) {
1036 setBase(
1037 env,
1038 gen(env,
1039 warn ? ElemArrayW : ElemArray,
1040 makeMRCatch(env),
1041 env.base.value,
1042 key)
1044 return;
1047 assert(!(define && unset));
1048 if (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) {
1053 gen(
1054 env,
1055 RaiseError,
1056 makeMRCatch(env),
1057 cns(env, makeStaticString(Strings::OP_NOT_SUPPORTED_STRING))
1059 setBase(env, uninit);
1060 return;
1062 if (baseType.not(Type::Arr | Type::Obj)) {
1063 setBase(env, uninit);
1064 return;
1068 if (define || unset) {
1069 setBase(
1070 env,
1071 genStk(env,
1072 define ? ElemDX : ElemUX,
1073 makeMRCatch(env),
1074 MInstrAttrData { mia },
1075 env.base.value,
1076 key,
1077 misPtr(env))
1079 return;
1081 setBase(
1082 env,
1083 gen(env,
1084 ElemX,
1085 makeMRCatch(env),
1086 MInstrAttrData { mia },
1087 env.base.value,
1088 key,
1089 misPtr(env))
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: {
1098 emitElem(env);
1099 ++env.iInd;
1100 break;
1102 case MPC: case MPL: case MPT:
1103 emitProp(env);
1104 ++env.iInd;
1105 break;
1106 case MW:
1107 assert(env.mii.newElem());
1108 emitNewElem(env);
1109 break;
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;
1120 return true;
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.
1132 return true;
1134 if (mcodeIsProp(env.ni.immVecM[0])) return false;
1135 return true;
1137 return true;
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:
1150 // (base is array)
1151 // BaseL
1152 // IssetElemL
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
1161 // ElemL PropL
1162 // ratchet ratchet
1163 // ElemL CGetElemL
1164 // ratchet logical ratchet
1165 // IssetElemL
1166 // 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.
1171 // no ratchet
1172 // ElemL
1173 // ratchet
1174 // ElemL
1175 // logical ratchet
1176 // SetElemL
1177 // no ratchet
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;
1186 return 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))) {
1195 return;
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(
1210 env,
1211 LdMem,
1212 Type::Gen,
1213 env.misBase,
1214 cns(env, MISOFF(tvRef))
1216 gen(env, StMem, env.misBase, cns(env, MISOFF(tvRef2)), tvRef);
1218 // Reset 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)) {
1234 emitMTrace(env);
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);
1242 emitBaseOp(env);
1243 ++env.iInd;
1245 checkMIState(env);
1246 if (env.needMIS) {
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
1256 // operation.
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
1271 // here.
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;
1277 switch (l.space) {
1278 case Location::Stack:
1279 assert(stackIdx >= 0);
1280 env.stackInputs[i] = stackIdx--;
1281 break;
1283 default:
1284 break;
1287 assert(stackIdx == (stackRhs ? 0 : -1));
1289 if (stackRhs) {
1290 // If this instruction does have an RHS, it will be input 0 at
1291 // stack offset 0.
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);
1308 return unboxed;
1311 if (key->isConst() &&
1312 packedArrayBoundsCheckUnnecessary(base->type(), key->intVal())) {
1313 return doLdElem();
1316 return env.irb.cond(
1318 [&] (Block* taken) {
1319 gen(env, CheckPackedArrayBounds, taken, base, key);
1321 [&] { // Next:
1322 return doLdElem();
1324 [&] { // Taken:
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);
1343 return;
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);
1357 setBase(
1358 env,
1359 gen(env, CheckType, typePackedArr, exit, env.base.value)
1361 env.irb.constrainValue(
1362 env.base.value,
1363 TypeConstraint(DataTypeSpecialized).setWantArrayKind()
1365 env.result = emitPackedArrayGet(env, env.base.value, key);
1366 return;
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) {
1401 PUNT(emitPairGet);
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);
1407 } else {
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);
1424 [&] { // Next:
1425 return gen(env, IsPackedArrayElemNull, env.base.value, key);
1427 [&] { // Taken:
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.
1445 if (setRef) {
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.
1452 env.result = value;
1453 return;
1456 auto const newArr = gen(
1457 env,
1458 ArraySet,
1459 makeMRCatch(env),
1460 env.base.value,
1461 key,
1462 value
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);
1473 } else {
1474 not_reached();
1477 env.result = value;
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);
1488 env.irb.ifThen(
1489 [&](Block* taken) {
1490 gen(env, VectorHasImmCopy, taken, env.base.value);
1492 [&] {
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);
1507 env.result = value;
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);
1524 return;
1527 auto const ty = env.base.type.deref();
1528 auto const cellPtr = ty.maybeBoxed() ? gen(env, UnboxPtr, env.base.value)
1529 : env.base.value;
1530 env.result = gen(env, LdMem, ty.unbox(), cellPtr, cns(env, 0));
1531 gen(env, IncRef, env.result);
1532 return;
1535 auto const key = getKey(env);
1536 env.result = gen(
1537 env,
1538 CGetProp,
1539 makeMRCatch(env),
1540 env.base.value,
1541 key,
1542 misPtr(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)
1576 : 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);
1582 env.result = value;
1583 return;
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);
1590 env.result = 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));
1613 env.result = box;
1616 void emitUnsetProp(MTS& env) {
1617 auto const key = getKey(env);
1618 if (env.base.type.strip().not(Type::Obj)) {
1619 // Noop
1620 constrainBase(env, DataTypeSpecific);
1621 return;
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);
1632 break;
1633 case SimpleOp::PackedArray:
1634 env.result = emitPackedArrayGet(env, env.base.value, key);
1635 break;
1636 case SimpleOp::ProfiledArray:
1637 emitProfiledArrayGet(env, key);
1638 break;
1639 case SimpleOp::String:
1640 emitStringGet(env, key);
1641 break;
1642 case SimpleOp::Vector:
1643 emitVectorGet(env, key);
1644 break;
1645 case SimpleOp::Pair:
1646 emitPairGet(env, key);
1647 break;
1648 case SimpleOp::Map:
1649 env.result = gen(env, MapGet, makeMRCatch(env), env.base.value, key);
1650 break;
1651 case SimpleOp::None:
1652 env.result = gen(env, CGetElem, makeMRCatch(env), env.base.value,
1653 key, misPtr(env));
1654 break;
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,
1669 getKey(env));
1670 break;
1671 case SimpleOp::PackedArray:
1672 emitPackedArrayIsset(env);
1673 break;
1674 case SimpleOp::String:
1675 env.result = gen(env, StringIsset, env.base.value, getKey(env));
1676 break;
1677 case SimpleOp::Vector:
1678 env.result = gen(env, VectorIsset, env.base.value, getKey(env));
1679 break;
1680 case SimpleOp::Pair:
1681 env.result = gen(env, PairIsset, env.base.value, getKey(env));
1682 break;
1683 case SimpleOp::Map:
1684 env.result = gen(env, MapIsset, env.base.value, getKey(env));
1685 break;
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));
1692 break;
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);
1707 } else {
1708 gen(env, SetNewElem, makeCatchSet(env), env.base.value, value);
1710 env.result = 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);
1720 } else {
1721 genStk(env, SetWithRefNewElem, makeMRCatch(env),
1722 NoExtraData{},
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);
1736 break;
1737 case SimpleOp::PackedArray:
1738 case SimpleOp::String:
1739 always_assert(false && "Bad SimpleOp in emitSetElem");
1740 break;
1741 case SimpleOp::Vector:
1742 emitVectorSet(env, key, value);
1743 break;
1744 case SimpleOp::Map:
1745 gen(env, MapSet, makeMRCatch(env), env.base.value, key, value);
1746 env.result = value;
1747 break;
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.
1756 env.result = 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);
1762 } else {
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.
1766 env.result = value;
1767 env.strTestResult = result;
1769 break;
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);
1779 emitSetElem(env);
1780 assert(env.strTestResult == nullptr);
1781 } else {
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),
1793 misPtr(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));
1807 env.result = box;
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) {
1816 gen(env,
1817 RaiseError,
1818 makeMRCatch(env),
1819 cns(env, makeStaticString(Strings::CANT_UNSET_STRING)));
1820 return;
1822 if (baseType.not(Type::Arr | Type::Obj)) {
1823 // Noop
1824 return;
1827 genStk(env, UnsetElem, makeMRCatch(env), NoExtraData{}, env.base.value, key);
1830 void emitNotSuppNewElem(MTS& env) {
1831 PUNT(NotSuppNewElem);
1834 void emitVGetNewElem(MTS& env) {
1835 SPUNT(__func__);
1838 void emitSetOpNewElem(MTS& env) {
1839 SPUNT(__func__);
1842 void emitIncDecNewElem(MTS& env) {
1843 SPUNT(__func__);
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));
1850 env.result = box;
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,
1860 MINSTRS
1861 # undef MII
1863 elemOps[env.mii.instr()](env);
1864 break;
1866 case MPC: case MPL: case MPT:
1867 static MemFun propOps[] = {
1868 # define MII(instr, ...) &emit##instr##Prop,
1869 MINSTRS
1870 # undef MII
1872 propOps[env.mii.instr()](env);
1873 break;
1875 case MW:
1876 assert(env.mii.getAttr(MW) & MIA_final);
1877 static MemFun newOps[] = {
1878 # define MII(instr, attrs, bS, iS, vC, fN) \
1879 &emit##fN,
1880 MINSTRS
1881 # undef MII
1883 newOps[env.mii.instr()](env);
1884 break;
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
1919 // yet.
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(
1929 env,
1930 SpillStack,
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.
1943 env.irb.ifThen(
1944 [&] (Block* taken) {
1945 gen(env, CheckNullptr, taken, env.strTestResult);
1947 [&] {
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);
1952 push(env, str);
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.
1974 unsigned nStack =
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: {
1980 ++nStack;
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);
1986 gen(env,
1987 DecRefStack,
1988 StackOffset(env.stackInputs[i]),
1989 Type::Gen,
1990 catchSp);
1993 break;
1995 case Location::Local:
1996 case Location::Litstr:
1997 case Location::Litint:
1998 case Location::This: {
1999 // Do nothing.
2000 break;
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
2012 // function.
2013 if (env.result) {
2014 push(env, env.result);
2015 } else {
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(
2029 DecRefMem,
2030 env.marker,
2031 Type::Gen,
2032 env.misBase,
2033 cns(env, refOffs[env.failedSetBlock ? 1 - i : i])
2035 env.irb.add(inst);
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);
2047 return;
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 //////////////////////////////////////////////////////////////////////