Avoid dynamic static checks when forwarding context
[hiphop-php.git] / hphp / runtime / vm / jit / memory-effects.cpp
bloba31c2be87d1fa3af0051bec870600c653cbb518f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 "hphp/runtime/vm/jit/memory-effects.h"
18 #include "hphp/util/match.h"
19 #include "hphp/util/safe-cast.h"
20 #include "hphp/util/assertions.h"
22 #include "hphp/runtime/vm/bytecode.h"
23 #include "hphp/runtime/vm/jit/analysis.h"
24 #include "hphp/runtime/vm/jit/dce.h"
25 #include "hphp/runtime/vm/jit/ir-instruction.h"
26 #include "hphp/runtime/vm/jit/ssa-tmp.h"
28 #include <folly/Optional.h>
30 namespace HPHP { namespace jit {
32 namespace {
34 AliasClass pointee(
35 const SSATmp* ptr,
36 jit::flat_set<const IRInstruction*>* visited_labels
37 ) {
38 auto const type = ptr->type();
39 always_assert(type <= TMemToGen);
40 auto const maybeRef = type.maybe(TMemToRefGen);
41 auto const typeNR = type - TMemToRefGen;
42 auto const canonPtr = canonical(ptr);
43 if (!canonPtr->isA(TMemToGen)) {
44 // This can happen when ptr is TBottom from a passthrough instruction with
45 // a src that isn't TBottom. The most common cause of this is something
46 // like "t5:Bottom = CheckType<Str> t2:Int". It means ptr isn't really a
47 // pointer, so return AEmpty to avoid unnecessarily pessimizing any
48 // optimizations.
49 always_assert(ptr->isA(TBottom));
50 return AEmpty;
53 auto const sinst = canonPtr->inst();
55 if (sinst->is(UnboxPtr)) {
56 return ARefAny | pointee(sinst->src(0), visited_labels);
59 if (sinst->is(LdRDSAddr, LdInitRDSAddr)) {
60 return ARds { sinst->extra<RDSHandleData>()->handle };
63 // For phis, union all incoming values, taking care to not recurse infinitely
64 // in the presence of loops.
65 if (sinst->is(DefLabel)) {
66 if (visited_labels && visited_labels->count(sinst)) {
67 return AEmpty;
70 auto const dsts = sinst->dsts();
71 auto const dstIdx =
72 std::find(dsts.begin(), dsts.end(), canonPtr) - dsts.begin();
73 always_assert(dstIdx >= 0 && dstIdx < sinst->numDsts());
75 folly::Optional<jit::flat_set<const IRInstruction*>> label_set;
76 if (visited_labels == nullptr) {
77 label_set.emplace();
78 visited_labels = &label_set.value();
80 visited_labels->insert(sinst);
82 auto ret = AEmpty;
83 sinst->block()->forEachSrc(
84 dstIdx, [&](const IRInstruction* /*jmp*/, const SSATmp* thePtr) {
85 ret = ret | pointee(thePtr, visited_labels);
86 });
87 return ret;
90 auto specific = [&] () -> folly::Optional<AliasClass> {
91 if (typeNR <= TBottom) return AEmpty;
93 if (typeNR <= TMemToFrameGen) {
94 if (sinst->is(LdLocAddr)) {
95 return AliasClass {
96 AFrame { sinst->src(0), sinst->extra<LdLocAddr>()->locId }
99 return AFrameAny;
102 if (typeNR <= TMemToStkGen) {
103 if (sinst->is(LdStkAddr)) {
104 return AliasClass {
105 AStack { sinst->src(0), sinst->extra<LdStkAddr>()->offset, 1 }
108 return AStackAny;
111 if (typeNR <= TMemToPropGen) {
112 if (sinst->is(LdPropAddr, LdInitPropAddr)) {
113 return AliasClass {
114 AProp {
115 sinst->src(0),
116 safe_cast<uint32_t>(sinst->extra<ByteOffsetData>()->offsetBytes)
120 return APropAny;
123 if (typeNR <= TMemToMISGen) {
124 if (sinst->is(LdMIStateAddr)) {
125 return mis_from_offset(sinst->src(0)->intVal());
127 if (ptr->hasConstVal() && ptr->rawVal() == 0) {
128 // nullptr tvRef pointer, representing an instruction that doesn't use
129 // it.
130 return AEmpty;
132 return AMIStateTV;
135 auto elem = [&] () -> AliasClass {
136 auto base = sinst->src(0);
137 auto key = sinst->src(1);
139 always_assert(base->isA(TArrLike));
141 if (key->isA(TInt)) {
142 if (key->hasConstVal()) return AElemI { base, key->intVal() };
143 return AElemIAny;
145 if (key->hasConstVal(TStr)) {
146 assertx(!base->isA(TVec));
147 return AElemS { base, key->strVal() };
149 return AElemAny;
152 if (typeNR <= TMemToElemGen) {
153 if (sinst->is(LdPackedArrayDataElemAddr)) return elem();
154 return AElemAny;
157 // The result of ElemArray{,W,U} is either the address of an array element,
158 // or &immutable_null_base.
159 if (typeNR <= TMemToMembGen) {
160 if (sinst->is(ElemArrayX, ElemDictX, ElemKeysetX)) return elem();
162 // Takes a PtrToGen as its first operand, so we can't easily grab an array
163 // base.
164 if (sinst->is(ElemArrayU, ElemVecU, ElemDictU, ElemKeysetU)) {
165 return AElemAny;
168 // These instructions can only get at tvRef when given it as a
169 // src. Otherwise they can only return pointers to properties or
170 // &immutable_null_base.
171 if (sinst->is(PropX, PropDX, PropQ)) {
172 auto const src = [&]{
173 if (sinst->is(PropDX)) {
174 assertx(sinst->src(sinst->numSrcs() - 2)->isA(TMemToMISGen));
175 assertx(
176 sinst->src(sinst->numSrcs() - 1)->isA(TMIPropSPtr | TNullptr)
178 return sinst->src(sinst->numSrcs() - 2);
179 } else {
180 assertx(sinst->srcs().back()->isA(TPtrToMISGen));
181 return sinst->srcs().back();
183 }();
184 return APropAny | pointee(src, visited_labels);
187 // Like the Prop* instructions, but for array elements. These could also
188 // return pointers to collection elements but those don't exist in
189 // AliasClass yet.
190 if (sinst->is(ElemX, ElemDX, ElemUX)) {
191 auto const src = [&]{
192 if (sinst->is(ElemDX)) {
193 assertx(sinst->src(sinst->numSrcs() - 2)->isA(TMemToMISGen));
194 assertx(
195 sinst->src(sinst->numSrcs() - 1)->isA(TMIPropSPtr | TNullptr)
197 return sinst->src(sinst->numSrcs() - 2);
198 } else {
199 assertx(sinst->srcs().back()->isA(TPtrToMISGen));
200 return sinst->srcs().back();
202 }();
203 return AElemAny | pointee(src, visited_labels);
206 return folly::none;
209 return folly::none;
210 }();
212 auto ret = maybeRef ? ARefAny : AEmpty;
213 if (specific) return *specific | ret;
216 * None of the above worked, so try to make the smallest union we can based
217 * on the pointer type.
219 if (typeNR.maybe(TMemToStkGen)) ret = ret | AStackAny;
220 if (typeNR.maybe(TMemToFrameGen)) ret = ret | AFrameAny;
221 if (typeNR.maybe(TMemToPropGen)) ret = ret | APropAny;
222 if (typeNR.maybe(TMemToElemGen)) ret = ret | AElemAny;
223 if (typeNR.maybe(TMemToMISGen)) ret = ret | AMIStateTV;
224 if (typeNR.maybe(TMemToClsInitGen)) ret = ret | AHeapAny;
225 if (typeNR.maybe(TMemToClsCnsGen)) ret = ret | AHeapAny;
226 if (typeNR.maybe(TMemToSPropGen)) ret = ret | ARdsAny;
227 return ret;
230 //////////////////////////////////////////////////////////////////////
232 AliasClass all_pointees(folly::Range<SSATmp**> srcs) {
233 auto ret = AliasClass{AEmpty};
234 for (auto const& src : srcs) {
235 if (src->isA(TMemToGen)) {
236 ret = ret | pointee(src);
239 return ret;
242 // Return an AliasClass containing all locations pointed to by any MemToGen
243 // sources to an instruction.
244 AliasClass all_pointees(const IRInstruction& inst) {
245 return all_pointees(inst.srcs());
248 // Return an AliasClass representing a range of the eval stack that contains
249 // everything below a logical depth.
250 template<typename Off>
251 AliasClass stack_below(SSATmp* base, Off offset) {
252 return AStack { base, offset, std::numeric_limits<int32_t>::max() };
255 //////////////////////////////////////////////////////////////////////
258 * Helper functions for alias classes representing an ActRec on the stack
259 * (whether pre-live or live).
262 // Return an AliasClass representing an entire ActRec at base + offset.
263 AliasClass actrec(SSATmp* base, IRSPRelOffset offset) {
264 return AStack {
265 base,
266 // The offset is the bottom of where the ActRec is stored, but AliasClass
267 // needs an offset to the highest cell.
268 offset + int32_t{kNumActRecCells} - 1,
269 int32_t{kNumActRecCells}
273 // Return an AliasClass representing just the context field of an ActRec at base
274 // + offset.
275 AliasClass actrec_ctx(SSATmp* base, IRSPRelOffset offset) {
276 return AStack { base, offset + int32_t{kActRecCtxCellOff}, 1 };
279 // Return AliasClass representing just the func field of an ActRec at base +
280 // offset.
281 AliasClass actrec_func(SSATmp* base, IRSPRelOffset offset) {
282 return AStack { base, offset + int32_t{kActRecFuncCellOff}, 1 };
285 InlineExitEffects inline_exit_effects(SSATmp* fp) {
286 fp = canonical(fp);
287 auto fpInst = fp->inst();
288 if (UNLIKELY(fpInst->is(DefLabel))) fpInst = resolveFpDefLabel(fp);
289 assertx(fpInst && fpInst->is(DefInlineFP));
290 auto const func = fpInst->extra<DefInlineFP>()->target;
291 auto const frame = [&] () -> AliasClass {
292 if (!func->numLocals()) return AEmpty;
293 return AFrame {fp, AliasIdSet::IdRange(0, func->numLocals())};
294 }();
295 auto const stack = stack_below(fp, FPRelOffset{2});
296 AliasClass clsref = func->numClsRefSlots() ? AClsRefSlotAny : AEmpty;
297 return InlineExitEffects{ stack, frame, clsref | AMIStateAny };
300 //////////////////////////////////////////////////////////////////////
302 // Determine an AliasClass representing any locals in the instruction's frame
303 // which might be accessed via debug_backtrace().
305 AliasClass backtrace_locals(const IRInstruction& inst) {
306 auto const func = [&]() -> const Func* {
307 auto fp = inst.marker().fp();
308 if (!fp) return nullptr;
309 fp = canonical(fp);
310 auto fpInst = fp->inst();
311 if (UNLIKELY(fpInst->is(DefLabel))) {
312 fpInst = resolveFpDefLabel(fp);
313 assertx(fpInst);
315 if (fpInst->is(DefFP)) return fpInst->marker().func();
316 if (fpInst->is(DefInlineFP)) return fpInst->extra<DefInlineFP>()->target;
317 always_assert(false);
318 }();
320 // Either there's no func or no frame-pointer. Either way, be conservative and
321 // assume anything can be read. This can happen in test code, for instance.
322 if (!func) return AFrameAny;
324 auto const add86meta = [&] (AliasClass ac) {
325 // The 86metadata variable can also exist in a VarEnv, but accessing that is
326 // considered a heap effect, so we can ignore it.
327 auto const local = func->lookupVarId(s_86metadata.get());
328 if (local == kInvalidId) return ac;
329 return ac | AFrame { inst.marker().fp(), local };
332 auto ac = AEmpty;
333 auto const numParams = func->numParams();
335 if (!RuntimeOption::EnableArgsInBacktraces) return add86meta(ac);
337 if (func->hasReifiedGenerics()) {
338 // First non param local contains reified generics
339 AliasIdSet reifiedgenerics{ AliasIdSet::IdRange{numParams, numParams + 1} };
340 ac |= AFrame { inst.marker().fp(), reifiedgenerics };
343 if (func->cls() && func->cls()->hasReifiedGenerics()) {
344 // There is no way to access the SSATmp for ObjectData of `this` here,
345 // so be very pessimistic
346 ac |= APropAny;
349 if (!numParams) return add86meta(ac);
351 AliasIdSet params{ AliasIdSet::IdRange{0, numParams} };
352 return add86meta(ac | AFrame { inst.marker().fp(), params });
355 /////////////////////////////////////////////////////////////////////
358 * Modify a GeneralEffects to take potential VM re-entry into account. This
359 * affects may-load, may-store, and kills information for the instruction. The
360 * GeneralEffects should already contain AHeapAny in both loads and stores if
361 * it affects those locations for reasons other than re-entry, but does not
362 * need to if it doesn't.
364 * For loads, we need to take into account any locals potentially accessed by
365 * debug_backtrace().
367 * For kills, locations on the eval stack below the re-entry depth should all
368 * be added.
370 * Important note: because of the `kills' set modifications, an instruction may
371 * not report that it can re-enter if it actually can't. The reason this can
372 * go wrong is that if the instruction was in an inlined function, if we've
373 * removed the DefInlineFP its spOff will not be meaningful (unless it's a
374 * DecRef instruction, which we explicitly adjust in dce.cpp). In this case
375 * the `kills' set will refer to the wrong stack locations. In general this
376 * means instructions that can re-enter must have catch traces---but a few
377 * other instructions are exceptions, either since they are not allowed in
378 * inlined functions or because they take the (possibly-inlined) FramePtr as a
379 * source.
381 GeneralEffects may_reenter(const IRInstruction& inst, GeneralEffects x) {
382 auto const may_reenter_is_ok =
383 (inst.taken() && inst.taken()->isCatch()) ||
384 inst.is(DecRef,
385 ReleaseVVAndSkip,
386 LIterInit,
387 LIterInitK,
388 LIterNext,
389 LIterNextK,
390 IterFree,
391 GenericRetDecRefs,
392 MemoSetStaticCache,
393 MemoSetLSBCache,
394 MemoSetInstanceCache,
395 MemoSetStaticValue,
396 MemoSetLSBValue,
397 MemoSetInstanceValue);
398 always_assert_flog(
399 may_reenter_is_ok,
400 "instruction {} claimed may_reenter, but it isn't allowed to say that",
401 inst
405 * We want to union `killed_stack' into whatever else the instruction already
406 * said it must kill, but if we end up with an unrepresentable AliasClass we
407 * can't return a set that's too big (the `kills' set is unlike the other
408 * AliasClasses in GeneralEffects in that means it kills /everything/ in the
409 * set, since it's must-information).
411 * If we can't represent the union, just take the stack, in part because we
412 * have some debugging asserts about this right now---but also nothing
413 * actually uses may_reenter with a non-AEmpty kills at the time of this
414 * writing anyway.
416 auto const new_kills = [&] {
417 if (inst.marker().fp() == nullptr) return AEmpty;
419 auto const killed_stack = stack_below(
420 inst.marker().fp(),
421 -inst.marker().spOff() - 1
423 auto const kills_union = x.kills.precise_union(killed_stack);
424 return kills_union ? *kills_union : killed_stack;
425 }();
427 return GeneralEffects {
428 x.loads | AHeapAny | backtrace_locals(inst),
429 x.stores | AHeapAny,
430 x.moves,
431 new_kills
435 //////////////////////////////////////////////////////////////////////
437 GeneralEffects may_load_store(AliasClass loads, AliasClass stores) {
438 return GeneralEffects { loads, stores, AEmpty, AEmpty };
441 GeneralEffects may_load_store_kill(AliasClass loads,
442 AliasClass stores,
443 AliasClass kill) {
444 return GeneralEffects { loads, stores, AEmpty, kill };
447 GeneralEffects may_load_store_move(AliasClass loads,
448 AliasClass stores,
449 AliasClass move) {
450 assertx(move <= loads);
451 return GeneralEffects { loads, stores, move, AEmpty };
454 //////////////////////////////////////////////////////////////////////
457 * Helper for iterator instructions. They all affect some locals, but are
458 * otherwise the same.
460 * N.B. Currently the memory for frame iterator slots is not part of the
461 * AliasClass lattice, since we never really manipulate them from the TC yet,
462 * so we don't report the effect these instructions have on it.
464 GeneralEffects iter_effects(const IRInstruction& inst,
465 SSATmp* fp,
466 AliasClass locals) {
467 auto const iterID = inst.extra<IterData>()->iterId;
468 AliasClass const iterPos = AIterPos { fp, iterID };
469 AliasClass const iterBase = AIterBase { fp, iterID };
470 auto const iterMem = iterPos | iterBase;
471 return may_reenter(
472 inst,
473 may_load_store_kill(
474 locals | AHeapAny | iterMem,
475 locals | AHeapAny | iterMem,
476 AMIStateAny
482 * Construct effects for InterpOne, using the information in its extra data.
484 * We always consider an InterpOne as potentially doing anything to the heap,
485 * potentially re-entering, potentially raising warnings in the current frame,
486 * potentially reading any locals, and potentially reading/writing any stack
487 * location that isn't below the bottom of the stack.
489 * The extra data for the InterpOne comes with some additional information
490 * about which local(s) it may modify, which is all we try to be more precise
491 * about right now.
493 GeneralEffects interp_one_effects(const IRInstruction& inst) {
494 auto const extra = inst.extra<InterpOne>();
495 auto loads = AHeapAny | AStackAny | AFrameAny | ARdsAny;
496 auto stores = AHeapAny | AStackAny | ARdsAny;
497 if (extra->smashesAllLocals) {
498 stores = stores | AFrameAny;
499 } else {
500 for (auto i = uint32_t{0}; i < extra->nChangedLocals; ++i) {
501 stores = stores | AFrame { inst.src(1), extra->changedLocals[i].id };
505 for (auto i = uint32_t{0}; i < extra->nChangedClsRefSlots; ++i) {
506 auto const& slot = extra->changedClsRefSlots[i];
507 if (slot.write) {
508 stores = stores | AClsRefClsSlot { inst.src(1), slot.id } |
509 AClsRefTSSlot { inst.src(1), slot.id };
510 } else {
511 loads = loads | AClsRefClsSlot { inst.src(1), slot.id } |
512 AClsRefTSSlot { inst.src(1), slot.id };
516 auto kills = AEmpty;
517 if (isMemberBaseOp(extra->opcode)) {
518 stores = stores | AMIStateAny;
519 kills = kills | AMIStateAny;
520 } else if (isMemberDimOp(extra->opcode) || isMemberFinalOp(extra->opcode)) {
521 stores = stores | AMIStateAny;
522 loads = loads | AMIStateAny;
523 } else {
524 kills = kills | AMIStateAny;
527 return may_load_store_kill(loads, stores, kills);
530 ////////////////////////////////////////////////////////////////////////////////
533 * Construct effects for member instructions that take &tvRef as their last
534 * argument.
536 * These instructions never load tvRef, but they might store to it.
538 MemEffects minstr_with_tvref(const IRInstruction& inst) {
539 auto loads = AHeapAny;
540 auto stores = AHeapAny | all_pointees(inst);
541 auto kills = AEmpty;
543 auto const srcs = inst.srcs();
544 if (inst.is(ElemDX, PropDX)) {
545 assertx(inst.src(inst.numSrcs() - 2)->isA(TMemToMISGen));
546 assertx(inst.src(inst.numSrcs() - 1)->isA(TMIPropSPtr | TNullptr));
547 loads |= all_pointees(srcs.subpiece(0, srcs.size() - 2));
549 auto const propPtr = inst.src(inst.numSrcs() - 1);
550 if (RuntimeOption::EvalCheckPropTypeHints <= 0 || propPtr->isA(TNullptr)) {
551 kills = AMIStatePropS;
552 } else if (inst.is(ElemDX)) {
553 loads |= AMIStatePropS;
554 } else {
555 assertx(inst.is(PropDX));
556 if (RuntimeOption::EvalPromoteEmptyObject) loads |= AMIStatePropS;
557 stores |= AMIStatePropS;
559 } else {
560 assertx(srcs.back()->isA(TMemToMISGen));
561 loads |= all_pointees(srcs.subpiece(0, srcs.size() - 1));
562 kills = AMIStatePropS;
565 return may_load_store_kill(loads, stores, kills);
568 //////////////////////////////////////////////////////////////////////
570 MemEffects minstr_final_with_prop_state(const IRInstruction& inst) {
571 auto const propSLoads = [&]{
572 auto const propPtr = inst.srcs().back();
573 assertx(propPtr->isA(TMIPropSPtr | TNullptr));
574 if (RuntimeOption::EvalCheckPropTypeHints <= 0) return AEmpty;
575 if (propPtr->isA(TNullptr)) return AEmpty;
576 if (!RuntimeOption::EvalPromoteEmptyObject &&
577 inst.is(IncDecProp, SetOpProp, SetProp, VGetProp)) {
578 return AEmpty;
580 return AMIStatePropS;
581 }();
583 return may_load_store_kill(
584 AHeapAny | propSLoads | all_pointees(inst),
585 AHeapAny | all_pointees(inst),
586 AMIStatePropS
590 //////////////////////////////////////////////////////////////////////
592 MemEffects memory_effects_impl(const IRInstruction& inst) {
593 switch (inst.op()) {
595 //////////////////////////////////////////////////////////////////////
596 // Region exits
598 // These exits don't leave the current php function, and could head to code
599 // that could read or write anything as far as we know (including frame
600 // locals).
601 case ReqBindJmp:
602 return ExitEffects {
603 AUnknown,
604 stack_below(inst.src(0), inst.extra<ReqBindJmp>()->irSPOff - 1)
606 case ReqRetranslate:
607 return ExitEffects {
608 AUnknown,
609 stack_below(inst.src(0), inst.extra<ReqRetranslate>()->irSPOff - 1)
611 case ReqRetranslateOpt:
612 return ExitEffects {
613 AUnknown,
614 stack_below(inst.src(0), inst.extra<ReqRetranslateOpt>()->offset - 1)
616 case JmpSwitchDest:
617 return ExitEffects {
618 AUnknown,
619 *stack_below(inst.src(1),
620 inst.extra<JmpSwitchDest>()->spOffBCFromIRSP - 1).
621 precise_union(AMIStateAny)
623 case JmpSSwitchDest:
624 return ExitEffects {
625 AUnknown,
626 *stack_below(inst.src(1),
627 inst.extra<JmpSSwitchDest>()->offset - 1).
628 precise_union(AMIStateAny)
631 //////////////////////////////////////////////////////////////////////
632 // Unusual instructions
635 * The ReturnHook sets up the ActRec so the unwinder knows everything is
636 * already released (i.e. it calls ar->setLocalsDecRefd()).
638 * The eval stack is also dead at this point (the return value is passed to
639 * ReturnHook as src(1), and the ReturnHook may not access the stack).
641 case ReturnHook:
642 // Note, this instruction can re-enter, but doesn't need the may_reenter()
643 // treatmeant because of the special kill semantics for locals and stack.
644 return may_load_store_kill(
645 AHeapAny, AHeapAny,
646 *AStackAny.precise_union(AFrameAny)->precise_union(AMIStateAny)
649 // The suspend hooks can load anything (re-entering the VM), but can't write
650 // to frame locals.
651 case SuspendHookAwaitEF:
652 case SuspendHookAwaitEG:
653 case SuspendHookAwaitR:
654 case SuspendHookCreateCont:
655 case SuspendHookYield:
656 // TODO: may-load here probably doesn't need to include AFrameAny normally.
657 return may_reenter(inst,
658 may_load_store_kill(AUnknown, AHeapAny, AMIStateAny));
661 * If we're returning from a function, it's ReturnEffects. The RetCtrl
662 * opcode also suspends resumables, which we model as having any possible
663 * effects.
665 case RetCtrl:
666 if (inst.extra<RetCtrl>()->suspendingResumed) {
667 // Suspending can go anywhere, and doesn't even kill locals.
668 return UnknownEffects {};
670 return ReturnEffects {
671 AStackAny | AFrameAny | AClsRefSlotAny | AMIStateAny
674 case AsyncFuncRet:
675 case AsyncFuncRetSlow:
676 return ReturnEffects { AStackAny | AMIStateAny };
678 case AsyncSwitchFast:
679 // Suspending can go anywhere, and doesn't even kill locals.
680 return UnknownEffects {};
682 case GenericRetDecRefs:
684 * The may-store information here is AUnknown: even though we know it
685 * doesn't really "store" to the frame locals, the values that used to be
686 * there are no longer available because they are DecRef'd, which we are
687 * required to report as may-store information to make it visible to
688 * reference count optimizations. It's conceptually the same as if it was
689 * storing an Uninit over each of the locals, but the stores of uninits
690 * would be dead so we're not actually doing that.
692 return may_reenter(inst,
693 may_load_store_kill(AUnknown, AUnknown, AMIStateAny));
695 case EndCatch: {
696 auto const stack_kills = stack_below(
697 inst.src(1),
698 inst.extra<EndCatch>()->offset - 1
700 return ExitEffects {
701 AUnknown,
702 stack_kills | AMIStateTempBase | AMIStateBase | AMIStatePropS
707 * DefInlineFP has some special treatment here.
709 * It's logically `publishing' a pointer to a pre-live ActRec, making it
710 * live. It doesn't actually load from this ActRec, but after it's done this
711 * the set of things that can load from it is large enough that the easiest
712 * way to model this is to consider it as a load on behalf of `publishing'
713 * the ActRec. Once it's published, it's a live activation record, and
714 * doesn't get written to as if it were a stack slot anymore (we've
715 * effectively converted AStack locations into a frame until the
716 * InlineReturn).
718 * Note: We may push the publishing of the inline frame below the start of
719 * the inline function so that we can avoid spilling the inline frame in the
720 * common case. Because of this we cannot add the stack positions within the
721 * inline function to the kill set here as they may be live having been stored
722 * on the main trace.
724 * TODO(#3634984): Additionally, DefInlineFP is marking may-load on all the
725 * locals of the outer frame. This is probably not necessary anymore, but we
726 * added it originally because a store sinking prototype needed to know it
727 * can't push StLocs past a DefInlineFP, because of reserved registers.
728 * Right now it's just here because we need to think about and test it before
729 * removing that set.
731 case DefInlineFP: {
732 auto const func = inst.extra<DefInlineFP>()->target;
733 auto const nslots = func->numSlotsInFrame() - func->numLocals();
734 AliasClass stack = func->numLocals()
735 ? AStack{inst.dst(), FPRelOffset{-nslots}, func->numLocals()}
736 : AEmpty;
737 AliasClass frame = func->numLocals()
738 ? AFrame{inst.dst(), AliasIdSet::IdRange(0, func->numLocals())}
739 : AEmpty;
740 AliasClass clsrefs = func->numClsRefSlots() ? AClsRefSlotAny : AEmpty;
742 * Notice that the stack positions and frame locals described here are
743 * exactly the set of alias locations that are about to be overlapping
744 * inside the inlined frame. Likewise, ClsRefSlots from the caller and the
745 * callee are about to be jumbled.
747 return InlineEnterEffects{ stack, frame | clsrefs, inline_fp_frame(&inst) };
751 * BeginInlining is similar to DefInlineFP, however, it must always be the
752 * first instruction in the inlined call and has no effect serving only as
753 * a marker to memory effects that the stack cells within the inlined call
754 * are now dead.
756 * Unlike DefInlineFP it does not load the SpillFrame, which we hope to push
757 * off the main trace or elide entirely.
759 case BeginInlining: {
761 * SP relative offset of the first non-frame cell within the inlined call.
763 auto inlineStackOff = inst.extra<BeginInlining>()->offset;
764 return may_load_store_kill(
765 AEmpty,
766 AEmpty,
768 * This prevents stack slots from the caller from being sunk into the
769 * callee. Note that some of these stack slots overlap with the frame
770 * locals of the callee-- those slots are inacessible in the inlined
771 * call as frame and stack locations may not alias.
773 stack_below(inst.src(0), inlineStackOff)
777 case InlineSuspend:
778 case InlineReturn: {
779 return inline_exit_effects(inst.src(0));
782 case InlineReturnNoFrame: {
783 auto const callee = AliasClass(AStack {
784 inst.extra<InlineReturnNoFrame>()->offset,
785 std::numeric_limits<int32_t>::max()
786 }) | AMIStateAny;
787 return may_load_store_kill(AEmpty, AEmpty, callee);
790 case SyncReturnBC: {
791 auto const spOffset = inst.extra<SyncReturnBC>()->spOffset;
792 auto const arStack = actrec(inst.src(0), spOffset);
793 // This instruction doesn't actually load but SpillFrame cannot be pushed
794 // past it
795 return may_load_store(arStack, arStack);
798 case InterpOne:
799 return interp_one_effects(inst);
800 case InterpOneCF:
801 return ExitEffects {
802 AUnknown,
803 stack_below(inst.src(1), -inst.marker().spOff() - 1) | AMIStateAny
806 case NativeImpl:
807 return UnknownEffects {};
809 // NB: on the failure path, these C++ helpers do a fixup and read frame
810 // locals before they throw. They can also invoke the user error handler and
811 // go do whatever they want to non-frame locations.
813 // TODO(#5372569): if we combine dv inits into the same regions we could
814 // possibly avoid storing KindOfUninits if we modify this.
815 case VerifyParamCallable:
816 case VerifyParamCls:
817 case VerifyParamFailHard:
818 return may_load_store(AUnknown, AHeapAny);
819 // VerifyParamFail might coerce the parameter to the desired type rather than
820 // throwing.
821 case VerifyParamFail: {
822 auto const localId = inst.src(0)->intVal();
823 auto const stores = AHeapAny | AFrame{inst.marker().fp(), localId};
824 return may_load_store(AUnknown, stores);
826 case VerifyReifiedLocalType: {
827 auto const extra = inst.extra<ParamData>();
828 auto const stores = AHeapAny | AFrame{inst.marker().fp(), extra->paramId};
829 return may_load_store(AUnknown, stores);
831 // However the following ones can't read locals from our frame on the way
832 // out, except as a side effect of raising a warning.
833 case VerifyRetCallable:
834 case VerifyRetCls:
835 case VerifyReifiedReturnType:
836 return may_load_store(AHeapAny, AHeapAny);
837 // In PHP 7 VerifyRetFail can coerce the return type in weak files-- even in
838 // a strict file we may still coerce int to float. This is not true of HH
839 // files.
840 case VerifyRetFail: {
841 auto func = inst.marker().func();
842 auto mayCoerce =
843 RuntimeOption::PHP7_ScalarTypes &&
844 !RuntimeOption::EnableHipHopSyntax &&
845 !func->unit()->isHHFile();
846 auto stores = mayCoerce ? AHeapAny | AStackAny : AHeapAny;
847 return may_load_store(AHeapAny | AStackAny, stores);
849 case VerifyRetFailHard:
850 return may_load_store(AHeapAny | AStackAny, AHeapAny);
852 case VerifyPropCls:
853 case VerifyPropFail:
854 case VerifyPropFailHard:
855 case VerifyProp:
856 return may_load_store(AHeapAny, AHeapAny);
858 case CallUnpack:
860 auto const extra = inst.extra<CallUnpack>();
861 return CallEffects {
862 // Kills. Everything on the stack below the incoming parameters.
863 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
864 // Stack. The act-rec, incoming parameters, and everything below.
865 stack_below(
866 inst.src(0),
867 extra->spOffset + extra->numParams + kNumActRecCells +
868 extra->numOut - 1
870 // Locals.
871 backtrace_locals(inst),
872 // Callee.
873 actrec_func(inst.src(0), extra->spOffset + extra->numParams)
877 case ContEnter:
879 auto const extra = inst.extra<ContEnter>();
880 return CallEffects {
881 // Kills. Everything on the stack below the sent value.
882 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
883 // Stack. The value being sent, and everything below.
884 stack_below(inst.src(0), extra->spOffset),
885 // Locals.
886 backtrace_locals(inst),
887 // Callee. Stored inside the generator object, not used by ContEnter.
888 AEmpty
892 case Call:
894 auto const extra = inst.extra<Call>();
895 return CallEffects {
896 // Kills. Everything on the stack below the incoming parameters.
897 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
898 // Stack. The act-rec, incoming parameters, and everything below.
899 stack_below(
900 inst.src(0),
901 extra->spOffset + extra->numParams + kNumActRecCells +
902 extra->numOut - 1
904 // Locals.
905 backtrace_locals(inst),
906 // Callee.
907 actrec_func(inst.src(0), extra->spOffset + extra->numParams)
911 case CallBuiltin:
913 auto const stk = [&] () -> AliasClass {
914 AliasClass ret = AEmpty;
915 for (auto i = uint32_t{2}; i < inst.numSrcs(); ++i) {
916 if (inst.src(i)->type() <= TPtrToGen) {
917 auto const cls = pointee(inst.src(i));
918 if (cls.maybe(AStackAny)) {
919 ret = ret | cls;
923 return ret;
924 }();
925 return may_load_store_kill(
926 stk | AHeapAny,
927 AEmpty,
928 AMIStateAny
932 // Resumable suspension takes everything from the frame and moves it into the
933 // heap.
934 case CreateGen:
935 case CreateAGen:
936 case CreateAFWH:
937 case CreateAFWHNoVV: {
938 auto fpInst = inst.src(0)->inst();
939 if (fpInst->is(DefLabel)) fpInst = resolveFpDefLabel(inst.src(0));
940 auto const frame = [&] () -> AliasClass {
941 if (fpInst->is(DefFP)) return AFrameAny;
942 assertx(fpInst->is(DefInlineFP));
943 auto const nlocals = fpInst->extra<DefInlineFP>()->target->numLocals();
944 return nlocals
945 ? AFrame { inst.src(0), AliasIdSet::IdRange(0, nlocals)}
946 : AEmpty;
947 }();
948 auto const clsrefs = [&] () -> AliasClass {
949 if (fpInst->is(DefFP)) return AClsRefSlotAny;
950 assertx(fpInst->is(DefInlineFP));
951 auto const nrefs = fpInst->extra<DefInlineFP>()->target->numClsRefSlots();
952 return nrefs ? AClsRefSlotAny : AEmpty;
953 }();
954 return may_load_store_move(
955 frame | clsrefs,
956 AHeapAny,
957 frame | clsrefs
961 // AGWH construction updates the AsyncGenerator object.
962 case CreateAGWH:
963 return may_load_store(AHeapAny, AHeapAny);
965 case CreateAAWH:
967 auto const extra = inst.extra<CreateAAWH>();
968 auto const frame = AFrame {
969 inst.src(0),
970 AliasIdSet {
971 AliasIdSet::IdRange{ extra->first, extra->first + extra->count }
974 return may_reenter(inst, may_load_store(frame, AHeapAny));
977 case CountWHNotDone:
979 auto const extra = inst.extra<CountWHNotDone>();
980 auto const frame = AFrame {
981 inst.src(0),
982 AliasIdSet {
983 AliasIdSet::IdRange{ extra->first, extra->first + extra->count }
986 return may_load_store(frame, AEmpty);
989 // This re-enters to call extension-defined instance constructors.
990 case ConstructInstance:
991 return may_reenter(inst, may_load_store(AHeapAny, AHeapAny));
993 // Closures don't ever throw or reenter on construction
994 case ConstructClosure:
995 return IrrelevantEffects{};
997 case CheckStackOverflow:
998 case CheckSurpriseFlagsEnter:
999 case CheckSurpriseAndStack:
1000 return may_load_store(AEmpty, AEmpty);
1002 case InitExtraArgs:
1003 return UnknownEffects {};
1005 //////////////////////////////////////////////////////////////////////
1006 // Iterator instructions
1008 case IterInit:
1009 case LIterInit:
1010 case LIterNext:
1011 return iter_effects(
1012 inst,
1013 inst.src(1),
1014 AFrame { inst.src(1), inst.extra<IterData>()->valId }
1016 case IterNext:
1017 return iter_effects(
1018 inst,
1019 inst.src(0),
1020 AFrame { inst.src(0), inst.extra<IterData>()->valId }
1023 case IterInitK:
1024 case LIterInitK:
1025 case LIterNextK:
1027 AliasClass key = AFrame { inst.src(1), inst.extra<IterData>()->keyId };
1028 AliasClass val = AFrame { inst.src(1), inst.extra<IterData>()->valId };
1029 return iter_effects(inst, inst.src(1), key | val);
1032 case IterNextK:
1034 AliasClass key = AFrame { inst.src(0), inst.extra<IterData>()->keyId };
1035 AliasClass val = AFrame { inst.src(0), inst.extra<IterData>()->valId };
1036 return iter_effects(inst, inst.src(0), key | val);
1039 case IterFree:
1040 return may_reenter(inst, may_load_store(AHeapAny, AHeapAny));
1042 //////////////////////////////////////////////////////////////////////
1043 // Instructions that explicitly manipulate locals
1045 case StLoc:
1046 return PureStore {
1047 AFrame { inst.src(0), inst.extra<StLoc>()->locId },
1048 inst.src(1)
1051 case StLocRange:
1053 auto const extra = inst.extra<StLocRange>();
1054 auto acls = AEmpty;
1056 for (auto locId = extra->start; locId < extra->end; ++locId) {
1057 acls = acls | AFrame { inst.src(0), locId };
1059 return PureStore { acls, inst.src(1) };
1062 case LdLoc:
1063 return PureLoad { AFrame { inst.src(0), inst.extra<LocalId>()->locId } };
1065 case CheckLoc:
1066 case LdLocPseudoMain:
1067 // Note: LdLocPseudoMain is both a guard and a load, so it must not be a
1068 // PureLoad.
1069 return may_load_store(
1070 AFrame { inst.src(0), inst.extra<LocalId>()->locId },
1071 AEmpty
1074 case StLocPseudoMain:
1075 // This can store to globals or locals, but we don't have globals supported
1076 // in AliasClass yet.
1077 return PureStore { AUnknown, inst.src(1) };
1079 //////////////////////////////////////////////////////////////////////
1080 // Instructions that manipulate class-ref slots
1082 case LdClsRefCls:
1083 return PureLoad {
1084 AClsRefClsSlot { inst.src(0), inst.extra<LdClsRefCls>()->slot }
1087 case LdClsRefTS:
1088 return PureLoad {
1089 AClsRefTSSlot { inst.src(0), inst.extra<LdClsRefTS>()->slot }
1092 case StClsRefCls:
1093 return PureStore {
1094 AClsRefClsSlot { inst.src(0), inst.extra<StClsRefCls>()->slot },
1095 inst.src(1)
1098 case StClsRefTS:
1099 return PureStore {
1100 AClsRefTSSlot { inst.src(0), inst.extra<StClsRefTS>()->slot },
1101 inst.src(1)
1104 case KillClsRefCls:
1105 return may_load_store_kill(
1106 AEmpty, AEmpty,
1107 AClsRefClsSlot { inst.src(0), inst.extra<KillClsRefCls>()->slot }
1110 case KillClsRefTS:
1111 return may_load_store_kill(
1112 AEmpty, AEmpty,
1113 AClsRefTSSlot { inst.src(0), inst.extra<KillClsRefTS>()->slot }
1116 //////////////////////////////////////////////////////////////////////
1117 // Pointer-based loads and stores
1119 case LdMem:
1120 return PureLoad { pointee(inst.src(0)) };
1121 case StMem:
1122 return PureStore { pointee(inst.src(0)), inst.src(1) };
1124 // TODO(#5962341): These take non-constant offset arguments, and are
1125 // currently only used for collections and class property inits, so we aren't
1126 // hooked up yet.
1127 case StElem:
1128 return PureStore {
1129 inst.src(0)->type() <= TPtrToRMembCell
1130 ? AHeapAny
1131 : AUnknown,
1132 inst.src(2)
1134 case LdElem:
1135 return PureLoad {
1136 inst.src(0)->type() <= TPtrToRMembCell
1137 ? AHeapAny
1138 : AUnknown
1141 case LdMBase:
1142 return PureLoad { AMIStateBase };
1144 case StMBase:
1145 return PureStore { AMIStateBase, inst.src(0) };
1147 case StMIPropState:
1148 return PureStore { AMIStatePropS, nullptr };
1150 case FinishMemberOp:
1151 return may_load_store_kill(AEmpty, AEmpty, AMIStateAny);
1153 case BoxPtr:
1155 auto const mem = pointee(inst.src(0));
1156 return may_load_store(mem, mem);
1158 case UnboxPtr:
1159 return may_load_store(pointee(inst.src(0)), AEmpty);
1161 case IsNTypeMem:
1162 case IsTypeMem:
1163 case CheckTypeMem:
1164 case CheckInitMem:
1165 return may_load_store(pointee(inst.src(0)), AEmpty);
1167 case CheckRDSInitialized:
1168 return may_load_store(
1169 ARds { inst.extra<CheckRDSInitialized>()->handle },
1170 AEmpty
1172 case MarkRDSInitialized:
1173 return may_load_store(
1174 AEmpty,
1175 ARds { inst.extra<MarkRDSInitialized>()->handle }
1178 case InitProps:
1179 return may_load_store(
1180 AHeapAny,
1181 AHeapAny | ARds { inst.extra<InitProps>()->cls->propHandle() }
1184 case InitSProps:
1185 return may_load_store(
1186 AHeapAny,
1187 AHeapAny | ARds { inst.extra<InitSProps>()->cls->sPropInitHandle() }
1190 case LdClsFromClsMeth:
1191 case LdFuncFromClsMeth:
1192 return may_load_store(AHeapAny, AEmpty);
1194 //////////////////////////////////////////////////////////////////////
1195 // Object/Ref loads/stores
1197 case CheckRefInner:
1198 return may_load_store(ARef { inst.src(0) }, AEmpty);
1199 case LdRef:
1200 return PureLoad { ARef { inst.src(0) } };
1201 case StRef:
1202 return PureStore { ARef { inst.src(0) }, inst.src(1) };
1204 case InitObjProps:
1205 return may_load_store(AEmpty, APropAny);
1207 case InitObjMemoSlots:
1208 // Writes to memo slots, but these are not modeled.
1209 return IrrelevantEffects {};
1211 // Loads $obj->trace, stores $obj->file and $obj->line.
1212 case InitThrowableFileAndLine:
1213 return may_load_store(AHeapAny, APropAny);
1215 //////////////////////////////////////////////////////////////////////
1216 // Array loads and stores
1218 case InitPackedLayoutArray:
1219 return PureStore {
1220 AElemI { inst.src(0), inst.extra<InitPackedLayoutArray>()->index },
1221 inst.src(1)
1224 case LdVecElem:
1225 case LdPackedElem: {
1226 auto const base = inst.src(0);
1227 auto const key = inst.src(1);
1228 return PureLoad {
1229 key->hasConstVal() ? AElemI { base, key->intVal() } : AElemIAny
1233 case InitPackedLayoutArrayLoop:
1235 auto const extra = inst.extra<InitPackedLayoutArrayLoop>();
1236 auto const stack_in = AStack {
1237 inst.src(1),
1238 extra->offset + static_cast<int32_t>(extra->size) - 1,
1239 static_cast<int32_t>(extra->size)
1241 return may_load_store_move(stack_in, AElemIAny, stack_in);
1244 case NewKeysetArray:
1246 // NewKeysetArray is reading elements from the stack, but writes to a
1247 // completely new array, so we can treat the store set as empty.
1248 auto const extra = inst.extra<NewKeysetArray>();
1249 auto const stack_in = AStack {
1250 inst.src(0),
1251 extra->offset + static_cast<int32_t>(extra->size) - 1,
1252 static_cast<int32_t>(extra->size)
1254 return may_load_store_move(stack_in, AEmpty, stack_in);
1257 case NewStructArray:
1258 case NewStructDArray:
1259 case NewStructDict:
1261 // NewStructArray is reading elements from the stack, but writes to a
1262 // completely new array, so we can treat the store set as empty.
1263 auto const extra = inst.extra<NewStructData>();
1264 auto const stack_in = AStack {
1265 inst.src(0),
1266 extra->offset + static_cast<int32_t>(extra->numKeys) - 1,
1267 static_cast<int32_t>(extra->numKeys)
1269 return may_load_store_move(stack_in, AEmpty, stack_in);
1272 case NewRecord:
1274 auto const extra = inst.extra<NewStructData>();
1275 auto const stack_in = AStack {
1276 inst.src(1),
1277 extra->offset + static_cast<int32_t>(extra->numKeys) - 1,
1278 static_cast<int32_t>(extra->numKeys)
1280 return may_load_store_move(stack_in, AEmpty, stack_in);
1282 case MemoGetStaticValue:
1283 case MemoGetLSBValue:
1284 case MemoGetInstanceValue:
1285 // Only reads the memo value (which isn't modeled here).
1286 return may_load_store(AEmpty, AEmpty);
1288 case MemoSetStaticValue:
1289 case MemoSetLSBValue:
1290 case MemoSetInstanceValue:
1291 // Writes to the memo value (which isn't modeled), but can re-enter to run
1292 // a destructor.
1293 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
1295 case MemoGetStaticCache:
1296 case MemoGetLSBCache:
1297 case MemoSetStaticCache:
1298 case MemoSetLSBCache: {
1299 // Reads some (non-zero) set of locals for keys, and reads/writes from the
1300 // memo cache (which isn't modeled). The set can re-enter to run a
1301 // destructor.
1302 auto const extra = inst.extra<MemoCacheStaticData>();
1303 auto const loc = [&] () -> AliasClass {
1304 if (inst.src(0)->isA(TFramePtr)) {
1305 return AFrame {
1306 inst.src(0),
1307 AliasIdSet{
1308 AliasIdSet::IdRange{
1309 extra->keys.first,
1310 extra->keys.first + extra->keys.count
1315 assertx(inst.src(0)->isA(TStkPtr));
1316 assertx(extra->stackOffset);
1317 return AStack {
1318 inst.src(0),
1319 *extra->stackOffset,
1320 static_cast<int32_t>(extra->keys.count)
1322 }();
1324 auto effects = may_load_store(loc, AEmpty);
1325 if (inst.op() == MemoSetStaticCache || inst.op() == MemoSetLSBCache) {
1326 effects = may_reenter(inst, effects);
1328 return effects;
1331 case MemoGetInstanceCache:
1332 case MemoSetInstanceCache: {
1333 // Reads some set of locals for keys, and reads/writes from the memo cache
1334 // (which isn't modeled). The set can re-enter to run a destructor.
1335 auto const extra = inst.extra<MemoCacheInstanceData>();
1336 auto const loc = [&]() -> AliasClass {
1337 // Unlike MemoGet/SetStaticCache, we can have an empty key range here.
1338 if (extra->keys.count == 0) return AEmpty;
1340 if (inst.src(0)->isA(TFramePtr)) {
1341 return AFrame {
1342 inst.src(0),
1343 AliasIdSet{
1344 AliasIdSet::IdRange{
1345 extra->keys.first,
1346 extra->keys.first + extra->keys.count
1351 assertx(inst.src(0)->isA(TStkPtr));
1352 assertx(extra->stackOffset);
1353 return AStack {
1354 inst.src(0),
1355 *extra->stackOffset,
1356 static_cast<int32_t>(extra->keys.count)
1358 }();
1359 auto effects = may_load_store(loc, AEmpty);
1360 if (inst.op() == MemoSetInstanceCache) effects = may_reenter(inst, effects);
1361 return effects;
1364 case MixedArrayGetK:
1365 case DictGetK:
1366 case KeysetGetK: {
1367 auto const base = inst.src(0);
1368 auto const key = inst.src(1);
1369 always_assert(key->isA(TInt | TStr));
1370 if (key->isA(TInt)) {
1371 return PureLoad {
1372 key->hasConstVal() ? AElemI { base, key->intVal() } : AElemIAny
1375 if (key->isA(TStr)) {
1376 return PureLoad {
1377 key->hasConstVal() ? AElemS { base, key->strVal() } : AElemSAny
1380 return PureLoad { AElemAny };
1383 case ElemMixedArrayK:
1384 case ElemDictK:
1385 case ElemKeysetK:
1386 return IrrelevantEffects {};
1388 case VecFirst: {
1389 auto const base = inst.src(0);
1390 return may_load_store(AElemI { base, 0 }, AEmpty);
1392 case VecLast: {
1393 auto const base = inst.src(0);
1394 if (base->hasConstVal(TArr)) {
1395 return may_load_store(
1396 AElemI { base, static_cast<int64_t>(base->arrVal()->size() - 1) },
1397 AEmpty);
1399 return may_load_store(AElemIAny, AEmpty);
1401 case DictFirst:
1402 case DictLast:
1403 case KeysetFirst:
1404 case KeysetLast:
1405 return may_load_store(AElemAny, AEmpty);
1407 case DictFirstKey:
1408 case DictLastKey:
1409 return may_load_store(AEmpty, AEmpty);
1411 case ProfileMixedArrayOffset:
1412 case CheckMixedArrayOffset:
1413 case CheckArrayCOW:
1414 case ProfileDictOffset:
1415 case ProfileKeysetOffset:
1416 case CheckDictOffset:
1417 case CheckKeysetOffset:
1418 return may_load_store(AHeapAny, AEmpty);
1420 case ArrayIsset:
1421 case AKExistsArr:
1422 return may_load_store(AElemAny, AEmpty);
1424 case ArrayIdx:
1425 return may_load_store(AElemAny | ARefAny, AEmpty);
1427 case SameArr:
1428 case NSameArr:
1429 return may_load_store(AEmpty, AEmpty);
1431 case DictGetQuiet:
1432 case DictIsset:
1433 case DictEmptyElem:
1434 case DictIdx:
1435 case KeysetGetQuiet:
1436 case KeysetIsset:
1437 case KeysetEmptyElem:
1438 case KeysetIdx:
1439 case AKExistsDict:
1440 case AKExistsKeyset:
1441 return may_load_store(AElemAny, AEmpty);
1443 case SameVec:
1444 case NSameVec:
1445 case SameDict:
1446 case NSameDict:
1447 case EqKeyset:
1448 case NeqKeyset:
1449 case SameKeyset:
1450 case NSameKeyset:
1451 return may_load_store(AElemAny, AEmpty);
1453 case SameShape:
1454 case NSameShape:
1455 return may_load_store(
1456 RuntimeOption::EvalHackArrDVArrs ? AElemAny : AEmpty,
1457 AEmpty);
1459 case AKExistsObj:
1460 return may_load_store(AHeapAny, AHeapAny);
1462 //////////////////////////////////////////////////////////////////////
1463 // Member instructions
1465 case CheckMBase:
1466 return may_load_store(pointee(inst.src(0)), AEmpty);
1469 * Various minstr opcodes that take a PtrToGen in src 0, which may or may not
1470 * point to a frame local or the evaluation stack. Some may read or write to
1471 * that pointer while some only read. They can all re-enter the VM and access
1472 * arbitrary heap locations.
1474 case CGetElem:
1475 case EmptyElem:
1476 case IssetElem:
1477 case CGetProp:
1478 case CGetPropQ:
1479 case EmptyProp:
1480 case IssetProp:
1481 return may_load_store_kill(
1482 AHeapAny | all_pointees(inst),
1483 AHeapAny,
1484 AMIStatePropS
1487 case IncDecElem:
1488 case IncDecProp:
1489 case SetElem:
1490 case SetNewElem:
1491 case SetOpElem:
1492 case SetOpProp:
1493 case SetProp:
1494 case VGetProp:
1495 return minstr_final_with_prop_state(inst);
1498 * SetRange behaves like a simpler version of SetElem.
1500 case SetRange:
1501 case SetRangeRev:
1502 return may_load_store_kill(
1503 AHeapAny | all_pointees(inst),
1504 AHeapAny | pointee(inst.src(0)),
1505 AMIStatePropS
1508 case SetNewElemArray:
1509 case SetNewElemVec:
1510 case SetNewElemKeyset:
1511 case UnsetElem:
1512 case ElemArrayD:
1513 case ElemArrayU:
1514 case ElemVecD:
1515 case ElemVecU:
1516 case ElemDictD:
1517 case ElemDictU:
1518 case ElemKeysetU:
1519 case UnsetProp:
1520 // Right now we generally can't limit any of these better than general
1521 // re-entry rules, since they can raise warnings and re-enter.
1522 return may_load_store_kill(
1523 AHeapAny | all_pointees(inst),
1524 AHeapAny | all_pointees(inst),
1525 AMIStatePropS
1528 case ReservePackedArrayDataNewElem:
1529 return may_load_store(AHeapAny, AHeapAny);
1532 * Intermediate minstr operations. In addition to a base pointer like the
1533 * operations above, these may take a pointer to MInstrState::tvRef, which
1534 * they may store to (but not read from).
1536 case ElemX:
1537 case ElemDX:
1538 case ElemUX:
1539 case PropX:
1540 case PropDX:
1541 case PropQ:
1542 return minstr_with_tvref(inst);
1545 * Collection accessors can read from their inner array buffer, but stores
1546 * COW and behave as if they only affect collection memory locations. We
1547 * don't track those, so it's returning AEmpty for now.
1549 case MapIsset:
1550 case PairIsset:
1551 case VectorIsset:
1552 return may_load_store(AHeapAny, AEmpty /* Note */);
1553 case MapGet:
1554 case MapSet:
1555 case VectorSet:
1556 return may_reenter(inst, may_load_store(AHeapAny, AEmpty /* Note */));
1558 case LdInitPropAddr:
1559 return may_load_store(
1560 AProp {
1561 inst.src(0),
1562 safe_cast<uint32_t>(inst.extra<LdInitPropAddr>()->offsetBytes)
1564 AEmpty
1566 case LdInitRDSAddr:
1567 return may_load_store(
1568 ARds { inst.extra<LdInitRDSAddr>()->handle },
1569 AEmpty
1572 //////////////////////////////////////////////////////////////////////
1573 // Instructions that allocate new objects, without reading any other memory
1574 // at all, so any effects they have on some types of memory locations we
1575 // track are isolated from anything else we care about.
1577 case NewArray:
1578 case NewClsMeth:
1579 case NewCol:
1580 case NewColFromArray:
1581 case NewPair:
1582 case NewInstanceRaw:
1583 case NewMixedArray:
1584 case NewDArray:
1585 case NewDictArray:
1586 case AllocPackedArray:
1587 case AllocVArray:
1588 case AllocVecArray:
1589 case ConvBoolToArr:
1590 case ConvDblToStr:
1591 case ConvDblToArr:
1592 case ConvIntToArr:
1593 case ConvIntToStr:
1594 case Box: // conditional allocation
1595 return IrrelevantEffects {};
1597 case AllocObj:
1598 // AllocObj re-enters to call constructors, but if it weren't for that we
1599 // could ignore its loads and stores since it's a new object.
1600 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
1601 case AllocObjReified:
1602 // Similar to AllocObj but also stores the reification
1603 return may_reenter(inst, may_load_store(AEmpty, AHeapAny));
1605 //////////////////////////////////////////////////////////////////////
1606 // Instructions that explicitly manipulate the stack.
1608 case LdStk:
1609 return PureLoad {
1610 AStack { inst.src(0), inst.extra<LdStk>()->offset, 1 }
1613 case StStk:
1614 return PureStore {
1615 AStack { inst.src(0), inst.extra<StStk>()->offset, 1 },
1616 inst.src(1)
1619 case StOutValue:
1620 // Technically these writes affect the caller's stack, but there is no way
1621 // to actually observe them from within the callee. They can also only
1622 // occur once on any exit path from a function.
1623 return may_load_store(AEmpty, AEmpty);
1625 case SpillFrame:
1627 auto const spOffset = inst.extra<SpillFrame>()->spOffset;
1628 return PureSpillFrame {
1629 actrec(inst.src(0), spOffset),
1630 actrec_ctx(inst.src(0), spOffset),
1631 actrec_func(inst.src(0), spOffset),
1632 inst.src(1)
1636 case CheckStk:
1637 return may_load_store(
1638 AStack { inst.src(0), inst.extra<CheckStk>()->offset, 1 },
1639 AEmpty
1642 // The following may re-enter, and also deal with a stack slot.
1643 case CoerceStk:
1645 auto const stk = AStack {
1646 inst.src(0), inst.extra<CoerceStk>()->offset, 1
1648 return may_load_store(stk, stk);
1651 case CoerceMem:
1653 auto aInst = inst.src(0)->inst();
1654 if (aInst->is(LdLocAddr)) {
1655 return may_load_store(AFrameAny, AFrameAny);
1657 return may_load_store(AUnknown, AUnknown);
1660 case LdARCtx:
1661 return PureLoad {
1662 actrec_ctx(inst.src(0), inst.extra<LdARCtx>()->offset)
1664 case LdARFuncPtr:
1665 return PureLoad {
1666 actrec_func(inst.src(0), inst.extra<LdARFuncPtr>()->offset)
1669 case LdARIsDynamic:
1670 return may_load_store(
1671 actrec(inst.src(0), inst.extra<LdARIsDynamic>()->offset),
1672 AEmpty
1675 case DbgAssertARFunc:
1676 return may_load_store(
1677 actrec_func(inst.src(0), inst.extra<DbgAssertARFunc>()->offset),
1678 AEmpty
1681 case Unreachable:
1682 // Unreachable code kills every memory location.
1683 return may_load_store_kill(AEmpty, AEmpty, AUnknown);
1685 case ResolveTypeStruct: {
1686 auto const extra = inst.extra<ResolveTypeStructData>();
1687 auto const stack_in = AStack {
1688 inst.src(0),
1689 extra->offset + static_cast<int32_t>(extra->size) - 1,
1690 static_cast<int32_t>(extra->size)
1692 return may_load_store(AliasClass(stack_in)|AHeapAny, AHeapAny);
1695 //////////////////////////////////////////////////////////////////////
1696 // Instructions that never read or write memory locations tracked by this
1697 // module.
1699 case AbsDbl:
1700 case AddDbl:
1701 case AddInt:
1702 case AddIntO:
1703 case AndInt:
1704 case AssertType:
1705 case AssertLoc:
1706 case AssertStk:
1707 case AssertMBase:
1708 case AssertARFunc:
1709 case FuncGuard:
1710 case DefFP:
1711 case DefSP:
1712 case EndGuards:
1713 case EqBool:
1714 case EqCls:
1715 case EqFunc:
1716 case EqStrPtr:
1717 case EqArrayDataPtr:
1718 case EqDbl:
1719 case EqInt:
1720 case GteBool:
1721 case GteInt:
1722 case GtBool:
1723 case GtInt:
1724 case HintLocInner:
1725 case HintStkInner:
1726 case HintMBaseInner:
1727 case Jmp:
1728 case JmpNZero:
1729 case JmpZero:
1730 case LdPropAddr:
1731 case LdStkAddr:
1732 case LdPackedArrayDataElemAddr:
1733 case LteBool:
1734 case LteDbl:
1735 case LteInt:
1736 case LtBool:
1737 case LtInt:
1738 case GtDbl:
1739 case GteDbl:
1740 case LtDbl:
1741 case DivDbl:
1742 case DivInt:
1743 case MulDbl:
1744 case MulInt:
1745 case MulIntO:
1746 case NeqBool:
1747 case NeqDbl:
1748 case NeqInt:
1749 case SameObj:
1750 case NSameObj:
1751 case EqRes:
1752 case NeqRes:
1753 case CmpBool:
1754 case CmpInt:
1755 case CmpDbl:
1756 case SubDbl:
1757 case SubInt:
1758 case SubIntO:
1759 case XorBool:
1760 case XorInt:
1761 case OrInt:
1762 case AssertNonNull:
1763 case CheckNonNull:
1764 case CheckNullptr:
1765 case CheckFuncMMNonMagic:
1766 case CheckSmashableClass:
1767 case Ceil:
1768 case Floor:
1769 case DefLabel:
1770 case CheckInit:
1771 case Nop:
1772 case Mod:
1773 case Conjure:
1774 case ConjureUse:
1775 case EndBlock:
1776 case ConvBoolToInt:
1777 case ConvBoolToDbl:
1778 case DefConst:
1779 case LdLocAddr:
1780 case Sqrt:
1781 case Shl:
1782 case Shr:
1783 case Lshr:
1784 case IsNType:
1785 case IsType:
1786 case Mov:
1787 case ConvClsToCctx:
1788 case ConvDblToBool:
1789 case ConvDblToInt:
1790 case DblAsBits:
1791 case LdMIPropStateAddr:
1792 case LdMIStateAddr:
1793 case LdPairBase:
1794 case LdClsCns:
1795 case LdSubClsCns:
1796 case LdSubClsCnsClsName:
1797 case LdTypeCns:
1798 case CheckSubClsCns:
1799 case LdClsCnsVecLen:
1800 case ProfileSubClsCns:
1801 case CheckCtxThis:
1802 case FuncSupportsAsyncEagerReturn:
1803 case IsFunReifiedGenericsMatched:
1804 case IsFuncDynCallable:
1805 case IsClsDynConstructible:
1806 case LdFuncMFunc:
1807 case LdFuncRxLevel:
1808 case LdSmashable:
1809 case LdSmashableFunc:
1810 case IsReifiedName:
1811 case LdARNumParams:
1812 case LdRDSAddr:
1813 case ExitPlaceholder:
1814 case CheckRange:
1815 case ProfileType:
1816 case LdIfaceMethod:
1817 case InstanceOfIfaceVtable:
1818 case CheckARMagicFlag:
1819 case LdARNumArgsAndFlags:
1820 case StARNumArgsAndFlags:
1821 case LdARReifiedGenerics:
1822 case KillARReifiedGenerics:
1823 case LdTVAux:
1824 case LdARInvName:
1825 case StARInvName:
1826 case MethodExists:
1827 case GetTime:
1828 case GetTimeNs:
1829 case ProfileInstanceCheck:
1830 case Select:
1831 case LookupSPropSlot:
1832 case ConvPtrToLval:
1833 case MangleReifiedName:
1834 return IrrelevantEffects {};
1836 case StClosureArg:
1837 return PureStore {
1838 AProp {
1839 inst.src(0),
1840 safe_cast<uint32_t>(inst.extra<StClosureArg>()->offsetBytes)
1842 inst.src(1)
1845 //////////////////////////////////////////////////////////////////////
1846 // Instructions that technically do some things w/ memory, but not in any way
1847 // we currently care about. They however don't return IrrelevantEffects
1848 // because we assume (in refcount-opts) that IrrelevantEffects instructions
1849 // can't even inspect Countable reference count fields, and several of these
1850 // can. All GeneralEffects instructions are assumed to possibly do so.
1852 case DecRefNZ:
1853 case AFWHBlockOn:
1854 case IncRef:
1855 case LdClosureCtx:
1856 case StClosureCtx:
1857 case StContArKey:
1858 case StContArValue:
1859 case LdRetVal:
1860 case ConvStrToInt:
1861 case ConvResToInt:
1862 case OrdStr:
1863 case ChrInt:
1864 case CreateSSWH:
1865 case NewLikeArray:
1866 case CheckRefs:
1867 case LdClsCctx:
1868 case BeginCatch:
1869 case CheckSurpriseFlags:
1870 case CheckType:
1871 case CheckVArray:
1872 case CheckDArray:
1873 case StArResumeAddr:
1874 case StContArState:
1875 case ZeroErrorLevel:
1876 case RestoreErrorLevel:
1877 case CheckCold:
1878 case ContArIncIdx:
1879 case ContArIncKey:
1880 case ContArUpdateIdx:
1881 case ContValid:
1882 case ContStarted:
1883 case IncProfCounter:
1884 case IncStat:
1885 case ContPreNext:
1886 case ContStartedCheck:
1887 case ConvArrToBool:
1888 case ConvArrToDbl:
1889 case CountArray:
1890 case CountArrayFast:
1891 case CountVec:
1892 case CountDict:
1893 case CountShape:
1894 case CountKeyset:
1895 case InstanceOf:
1896 case InstanceOfBitmask:
1897 case NInstanceOfBitmask:
1898 case InstanceOfIface:
1899 case InterfaceSupportsArr:
1900 case InterfaceSupportsVec:
1901 case InterfaceSupportsDict:
1902 case InterfaceSupportsKeyset:
1903 case InterfaceSupportsDbl:
1904 case InterfaceSupportsInt:
1905 case InterfaceSupportsStr:
1906 case IsWaitHandle:
1907 case IsCol:
1908 case IsDVArray:
1909 case HasToString:
1910 case DbgAssertRefCount:
1911 case GtStr:
1912 case GteStr:
1913 case LtStr:
1914 case LteStr:
1915 case EqStr:
1916 case NeqStr:
1917 case SameStr:
1918 case NSameStr:
1919 case CmpStr:
1920 case GtStrInt:
1921 case GteStrInt:
1922 case LtStrInt:
1923 case LteStrInt:
1924 case EqStrInt:
1925 case NeqStrInt:
1926 case CmpStrInt:
1927 case GtRes:
1928 case GteRes:
1929 case LtRes:
1930 case LteRes:
1931 case CmpRes:
1932 case LdBindAddr:
1933 case LdSSwitchDestFast:
1934 case RBTraceEntry:
1935 case RBTraceMsg:
1936 case ConvIntToBool:
1937 case ConvIntToDbl:
1938 case ConvStrToArr: // decrefs src, but src is a string
1939 case ConvStrToBool:
1940 case ConvStrToDbl:
1941 case ConvResToDbl:
1942 case EagerSyncVMRegs:
1943 case ExtendsClass:
1944 case LdUnwinderValue:
1945 case LdCtx:
1946 case LdCctx:
1947 case LdClosure:
1948 case LdClsName:
1949 case LdAFWHActRec:
1950 case LdClsCtx:
1951 case LdContActRec:
1952 case LdContArKey:
1953 case LdContArValue:
1954 case LdContField:
1955 case LdContResumeAddr:
1956 case LdClsCachedSafe:
1957 case LdClsInitData:
1958 case UnwindCheckSideExit:
1959 case LdCns:
1960 case LdFuncVecLen:
1961 case LdClsMethod:
1962 case LdClsMethodCacheCls:
1963 case LdClsMethodCacheFunc:
1964 case LdClsMethodFCacheFunc:
1965 case LdClsTypeCns:
1966 case LdClsTypeCnsClsName:
1967 case FwdCtxStaticCall:
1968 case ProfileArrayKind:
1969 case ProfileSwitchDest:
1970 case LdFuncNumParams:
1971 case LdFuncName:
1972 case LdGblAddr:
1973 case LdGblAddrDef:
1974 case LdObjClass:
1975 case LdObjInvoke:
1976 case LdObjMethod:
1977 case LdStrLen:
1978 case StringIsset:
1979 case LdSwitchDblIndex:
1980 case LdSwitchStrIndex:
1981 case LdVectorBase:
1982 case LdWHResult:
1983 case LdWHState:
1984 case LdWHNotDone:
1985 case LookupClsRDS:
1986 case DbgTraceCall:
1987 case InitCtx:
1988 case PackMagicArgs:
1989 case StrictlyIntegerConv:
1990 case DbgAssertFunc:
1991 return may_load_store(AEmpty, AEmpty);
1993 // Some that touch memory we might care about later, but currently don't:
1994 case ColIsEmpty:
1995 case ColIsNEmpty:
1996 case ConvCellToBool:
1997 case ConvObjToBool:
1998 case CountCollection:
1999 case LdVectorSize:
2000 case CheckPackedArrayDataBounds:
2001 case LdColVec:
2002 case LdColDict:
2003 case EnterFrame:
2004 return may_load_store(AEmpty, AEmpty);
2006 //////////////////////////////////////////////////////////////////////
2007 // Instructions that can re-enter the VM and touch most heap things. They
2008 // also may generally write to the eval stack below an offset (see
2009 // alias-class.h above AStack for more).
2011 case DecRef:
2013 auto const src = inst.src(0);
2014 // It could decref the inner ref.
2015 auto affected = src->isA(TBoxedCell) ? ARef { src } :
2016 src->type().maybe(TBoxedCell) ? ARefAny : AEmpty;
2017 if (src->type().maybe(TKeyset | TBoxedKeyset)) {
2018 // TKeyset can't re-enter, but it will decref any contained
2019 // strings. Without this, an incref of a string contained in
2020 // a Keyset could be sunk past the decref of the Keyset.
2021 affected |= AHeapAny;
2023 // Need to add affected to the `store' set. See comments about
2024 // `GeneralEffects' in memory-effects.h.
2025 auto const effect = may_load_store(affected, affected);
2026 if (src->type().maybe(TArr | TVec | TDict | TObj | TRes |
2027 TBoxedArr | TBoxedVec | TBoxedDict |
2028 TBoxedObj | TBoxedRes)) {
2029 // Could re-enter to run a destructor. Keysets are exempt because they
2030 // can only contain strings or integers.
2031 return may_reenter(inst, effect);
2033 return effect;
2036 case GetMemoKey:
2037 return may_load_store(AHeapAny, AHeapAny);
2039 case GetMemoKeyScalar:
2040 return IrrelevantEffects{};
2042 case LdArrFuncCtx:
2043 case LdFunc: // these all can autoload
2045 AliasClass effects =
2046 actrec(inst.src(1), inst.extra<IRSPRelOffsetData>()->offset);
2047 return may_load_store(effects, effects);
2050 case LookupClsMethod: // autoload, and it writes part of the new actrec
2052 AliasClass effects =
2053 actrec(inst.src(2), inst.extra<LookupClsMethod>()->calleeAROffset);
2054 return may_load_store(effects, effects);
2057 case ProfileFunc:
2058 case ProfileMethod:
2060 AliasClass effects =
2061 actrec(inst.src(0), inst.extra<ProfileCallTargetData>()->bcSPOff);
2062 return may_load_store(effects, AEmpty);
2065 case LdClsPropAddrOrNull: // may run 86{s,p}init, which can autoload
2066 case LdClsPropAddrOrRaise: // raises errors, and 86{s,p}init
2067 case BaseG:
2068 case Clone:
2069 case RaiseArrayIndexNotice:
2070 case RaiseArrayKeyNotice:
2071 case RaiseUninitLoc:
2072 case RaiseUndefProp:
2073 case RaiseMissingArg:
2074 case RaiseTooManyArg:
2075 case RaiseError:
2076 case RaiseNotice:
2077 case RaiseWarning:
2078 case RaiseMissingThis:
2079 case FatalMissingThis:
2080 case RaiseHackArrCompatNotice:
2081 case RaiseHackArrParamNotice:
2082 case RaiseHackArrPropNotice:
2083 case RaiseForbiddenDynCall:
2084 case RaiseForbiddenDynConstruct:
2085 case RaiseRxCallViolation:
2086 case RaiseStrToClassNotice:
2087 case CheckClsReifiedGenericMismatch:
2088 case CheckFunReifiedGenericMismatch:
2089 case ConvCellToStr:
2090 case ConvObjToStr:
2091 case Count: // re-enters on CountableClass
2092 case GtObj:
2093 case GteObj:
2094 case LtObj:
2095 case LteObj:
2096 case EqObj:
2097 case NeqObj:
2098 case CmpObj:
2099 case GtArr:
2100 case GteArr:
2101 case LtArr:
2102 case LteArr:
2103 case EqArr:
2104 case NeqArr:
2105 case CmpArr:
2106 case GtShape:
2107 case GteShape:
2108 case LtShape:
2109 case LteShape:
2110 case EqShape:
2111 case NeqShape:
2112 case CmpShape:
2113 case GtVec:
2114 case GteVec:
2115 case LtVec:
2116 case LteVec:
2117 case EqVec:
2118 case NeqVec:
2119 case CmpVec:
2120 case EqDict:
2121 case NeqDict:
2122 case ConvCellToArr: // decrefs src, may read obj props
2123 case ConvCellToObj: // decrefs src
2124 case ConvObjToArr: // decrefs src
2125 case ConvObjToVArr: // can invoke PHP
2126 case ConvObjToDArr: // can invoke PHP
2127 case OODeclExists:
2128 case DefCls: // autoload
2129 case LdCls: // autoload
2130 case LdClsCached: // autoload
2131 case LdFuncCached: // autoload
2132 case LdRecCached: // autoload
2133 case LdSwitchObjIndex: // decrefs arg
2134 case InitClsCns: // autoload
2135 case LookupClsMethodCache: // autoload
2136 case LookupClsMethodFCache: // autoload
2137 case LookupCnsE:
2138 case LookupFuncCached: // autoload
2139 case StringGet: // raise_notice
2140 case OrdStrIdx: // raise_notice
2141 case ArrayAdd: // decrefs source
2142 case AddElemIntKey:
2143 case AddElemStrKey:
2144 case AddNewElem: // can re-enter
2145 case AddNewElemKeyset: // can re-enter
2146 case DictAddElemIntKey: // decrefs value
2147 case DictAddElemStrKey: // decrefs value
2148 case ArrayGet: // kVPackedKind warnings
2149 case ArraySet: // kVPackedKind warnings
2150 case ArraySetRef: // kVPackedKind warnings
2151 case DictGet:
2152 case KeysetGet:
2153 case VecSet:
2154 case VecSetRef:
2155 case DictSet:
2156 case DictSetRef:
2157 case ElemArrayX:
2158 case ElemDictX:
2159 case ElemKeysetX:
2160 case LdClsCtor:
2161 case ConcatStrStr:
2162 case PrintStr:
2163 case PrintBool:
2164 case PrintInt:
2165 case ConcatIntStr:
2166 case ConcatStrInt:
2167 case LdSSwitchDestSlow:
2168 case ConvObjToDbl:
2169 case ConvObjToInt:
2170 case CoerceStrToInt:
2171 case CoerceStrToDbl:
2172 case CoerceCellToDbl:
2173 case CoerceCellToInt:
2174 case CoerceCellToBool:
2175 case ConvCellToInt:
2176 case ConvResToStr:
2177 case ConcatStr3:
2178 case ConcatStr4:
2179 case ConvCellToDbl:
2180 case ConvArrToVec:
2181 case ConvArrToDict:
2182 case ConvShapeToDict:
2183 case ConvObjToVec:
2184 case ConvObjToDict:
2185 case ConvObjToKeyset:
2186 case ThrowOutOfBounds:
2187 case ThrowInvalidArrayKey:
2188 case ThrowInvalidOperation:
2189 case ThrowArithmeticError:
2190 case ThrowDivisionByZeroError:
2191 case ThrowDivisionByZeroException:
2192 case ThrowHasThisNeedStatic:
2193 case ThrowLateInitPropError:
2194 case ThrowParamRefMismatch:
2195 case ThrowParamRefMismatchRange:
2196 case SetOpCell:
2197 case SetOpCellVerify:
2198 case AsTypeStruct:
2199 case PropTypeRedefineCheck: // Can raise and autoload
2200 case HandleRequestSurprise:
2201 return may_load_store(AHeapAny, AHeapAny);
2203 case AddNewElemVec:
2204 case IsTypeStruct:
2205 case LdReifiedGeneric:
2206 case RecordReifiedGenericsAndGetName:
2207 case RecordReifiedGenericsAndGetTSList:
2208 return may_load_store(AElemAny, AEmpty);
2210 case ConvArrToKeyset: // Decrefs input values
2211 case ConvVecToKeyset:
2212 case ConvDictToKeyset:
2213 case ConvShapeToKeyset:
2214 case ConvDictToDArr: // These 4 may raise Hack array compat notices
2215 case ConvShapeToDArr:
2216 case ConvKeysetToDArr:
2217 case ConvDictToArr:
2218 case ConvShapeToArr:
2219 case ConvKeysetToArr:
2220 return may_load_store(AElemAny, AEmpty);
2222 case ConvVecToArr:
2223 case ConvArrToNonDVArr:
2224 case ConvDictToVec:
2225 case ConvShapeToVec:
2226 case ConvKeysetToVec:
2227 case ConvVecToDict:
2228 case ConvKeysetToDict:
2229 case ConvArrToVArr:
2230 case ConvVecToVArr:
2231 case ConvDictToVArr:
2232 case ConvShapeToVArr:
2233 case ConvKeysetToVArr:
2234 case ConvArrToDArr:
2235 case ConvVecToDArr:
2236 return may_load_store(AElemAny, AEmpty);
2238 case ReleaseVVAndSkip: // can decref ExtraArgs or VarEnv and Locals
2239 return may_reenter(inst,
2240 may_load_store(AHeapAny|AFrameAny, AHeapAny|AFrameAny));
2242 // debug_backtrace() traverses stack and WaitHandles on the heap.
2243 case DebugBacktrace:
2244 case DebugBacktraceFast:
2245 return may_load_store(AHeapAny|AFrameAny|AStackAny, AHeapAny);
2247 // This instruction doesn't touch memory we track, except that it may
2248 // re-enter to construct php Exception objects. During this re-entry anything
2249 // can happen (e.g. a surprise flag check could cause a php signal handler to
2250 // run arbitrary code).
2251 case AFWHPrepareChild:
2252 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
2254 //////////////////////////////////////////////////////////////////////
2255 // The following instructions are used for debugging memory optimizations.
2256 // We can't ignore them, because they can prevent future optimizations;
2257 // eg t1 = LdStk<N>; DbgTrashStk<N>; StStk<N> t1
2258 // If we ignore the DbgTrashStk it looks like the StStk is redundant
2260 case DbgTrashStk:
2261 return GeneralEffects {
2262 AEmpty, AEmpty, AEmpty,
2263 AStack { inst.src(0), inst.extra<DbgTrashStk>()->offset, 1 }
2265 case DbgTrashFrame:
2266 return GeneralEffects {
2267 AEmpty, AEmpty, AEmpty,
2268 actrec(inst.src(0), inst.extra<DbgTrashFrame>()->offset)
2270 case DbgTrashMem:
2271 return GeneralEffects {
2272 AEmpty, AEmpty, AEmpty,
2273 pointee(inst.src(0))
2275 case DbgTrashRetVal:
2276 return IrrelevantEffects {};
2278 //////////////////////////////////////////////////////////////////////
2282 not_reached();
2285 //////////////////////////////////////////////////////////////////////
2287 DEBUG_ONLY bool check_effects(const IRInstruction& inst, MemEffects me) {
2288 SCOPE_ASSERT_DETAIL("Memory Effects") {
2289 return folly::sformat(" inst: {}\n effects: {}\n", inst, show(me));
2292 auto check_fp = [&] (SSATmp* fp) {
2293 always_assert_flog(
2294 fp->type() <= TFramePtr,
2295 "Non frame pointer in memory effects"
2299 auto check_obj = [&] (SSATmp* obj) {
2300 always_assert_flog(
2301 obj->type() <= TObj,
2302 "Non obj pointer in memory effects"
2306 auto check = [&] (AliasClass a) {
2307 if (auto const fr = a.frame()) check_fp(fr->fp);
2308 if (auto const sl = a.clsRefClsSlot()) check_fp(sl->fp);
2309 if (auto const sl = a.clsRefTSSlot()) check_fp(sl->fp);
2310 if (auto const pr = a.prop()) check_obj(pr->obj);
2313 match<void>(
2315 [&] (GeneralEffects x) {
2316 check(x.loads);
2317 check(x.stores);
2318 check(x.moves);
2319 check(x.kills);
2321 // Locations may-moved always should also count as may-loads.
2322 always_assert(x.moves <= x.loads);
2324 if (inst.mayRaiseError()) {
2325 // Any instruction that can raise an error can run a user error handler
2326 // and have arbitrary effects on the heap.
2327 always_assert(AHeapAny <= x.loads);
2328 always_assert(AHeapAny <= x.stores);
2330 * They also ought to kill /something/ on the stack, because of
2331 * possible re-entry. It's not incorrect to leave things out of the
2332 * kills set, but this assertion is here because we shouldn't do it on
2333 * purpose, so this is here until we have a reason not to assert it.
2335 * The mayRaiseError instructions should all be going through
2336 * may_reenter right now, which will kill the stack below the re-entry
2337 * depth---unless the marker for `inst' doesn't have an fp set.
2339 always_assert(inst.marker().fp() == nullptr ||
2340 AStackAny.maybe(x.kills));
2343 [&] (PureLoad x) { check(x.src); },
2344 [&] (PureStore x) { check(x.dst); },
2345 [&] (PureSpillFrame x) { check(x.stk); check(x.ctx); check(x.callee);
2346 always_assert(x.ctx <= x.stk);
2347 always_assert(x.callee <= x.stk); },
2348 [&] (ExitEffects x) { check(x.live); check(x.kills); },
2349 [&] (IrrelevantEffects) {},
2350 [&] (UnknownEffects) {},
2351 [&] (CallEffects x) { check(x.kills);
2352 check(x.stack);
2353 check(x.locals);
2354 check(x.callee);
2355 always_assert(x.callee <= x.stack); },
2356 [&] (InlineEnterEffects x){ check(x.inlStack);
2357 check(x.inlFrame);
2358 check(x.actrec); },
2359 [&] (InlineExitEffects x){ check(x.inlStack);
2360 check(x.inlFrame);
2361 check(x.inlMeta); },
2362 [&] (ReturnEffects x) { check(x.kills); }
2365 return true;
2368 //////////////////////////////////////////////////////////////////////
2372 MemEffects memory_effects(const IRInstruction& inst) {
2373 auto const inner = memory_effects_impl(inst);
2374 auto const ret = [&] () -> MemEffects {
2375 // These instructions have special handling because they occur as functions
2376 // suspend or return. Their ability to reenter is handled in
2377 // memory_effects_impl, and comments in that function explain why.
2378 auto const is_special = inst.is(
2379 ReturnHook,
2380 SuspendHookAwaitEF,
2381 SuspendHookAwaitEG,
2382 SuspendHookAwaitR,
2383 SuspendHookCreateCont,
2384 SuspendHookYield
2387 if (is_special || !inst.mayRaiseError()) return inner;
2389 auto fail = [&] {
2390 always_assert_flog(
2391 false,
2392 "Instruction {} has effects {}, but has been marked as MayRaiseError "
2393 "and must use a UnknownEffects, GeneralEffects, or CallEffects type.",
2394 inst,
2395 show(inner)
2397 return may_load_store(AUnknown, AUnknown);
2400 // Calls are implicitly MayRaise, all other instructions must use the
2401 // GeneralEffects or UnknownEffects class of memory effects
2402 return match<MemEffects>(
2403 inner,
2404 [&] (GeneralEffects x) { return may_reenter(inst, x); },
2405 [&] (CallEffects x) { return x; },
2406 [&] (UnknownEffects x) { return x; },
2407 [&] (PureLoad) { return fail(); },
2408 [&] (PureStore) { return fail(); },
2409 [&] (PureSpillFrame) { return fail(); },
2410 [&] (ExitEffects) { return fail(); },
2411 [&] (InlineExitEffects) { return fail(); },
2412 [&] (InlineEnterEffects) { return fail(); },
2413 [&] (IrrelevantEffects) { return fail(); },
2414 [&] (ReturnEffects) { return fail(); }
2416 }();
2417 assertx(check_effects(inst, ret));
2418 return ret;
2421 AliasClass pointee(const SSATmp* tmp) {
2422 return pointee(tmp, nullptr);
2425 //////////////////////////////////////////////////////////////////////
2427 MemEffects canonicalize(MemEffects me) {
2428 using R = MemEffects;
2429 return match<R>(
2431 [&] (GeneralEffects x) -> R {
2432 return GeneralEffects {
2433 canonicalize(x.loads),
2434 canonicalize(x.stores),
2435 canonicalize(x.moves),
2436 canonicalize(x.kills)
2439 [&] (PureLoad x) -> R {
2440 return PureLoad { canonicalize(x.src) };
2442 [&] (PureStore x) -> R {
2443 return PureStore { canonicalize(x.dst), x.value };
2445 [&] (PureSpillFrame x) -> R {
2446 return PureSpillFrame {
2447 canonicalize(x.stk),
2448 canonicalize(x.ctx),
2449 canonicalize(x.callee)
2452 [&] (ExitEffects x) -> R {
2453 return ExitEffects { canonicalize(x.live), canonicalize(x.kills) };
2455 [&] (InlineEnterEffects x) -> R {
2456 return InlineEnterEffects {
2457 canonicalize(x.inlStack),
2458 canonicalize(x.inlFrame),
2459 canonicalize(x.actrec),
2462 [&] (InlineExitEffects x) -> R {
2463 return InlineExitEffects {
2464 canonicalize(x.inlStack),
2465 canonicalize(x.inlFrame),
2466 canonicalize(x.inlMeta)
2469 [&] (CallEffects x) -> R {
2470 return CallEffects {
2471 canonicalize(x.kills),
2472 canonicalize(x.stack),
2473 canonicalize(x.locals),
2474 canonicalize(x.callee)
2477 [&] (ReturnEffects x) -> R {
2478 return ReturnEffects { canonicalize(x.kills) };
2480 [&] (IrrelevantEffects x) -> R { return x; },
2481 [&] (UnknownEffects x) -> R { return x; }
2485 //////////////////////////////////////////////////////////////////////
2487 std::string show(MemEffects effects) {
2488 using folly::sformat;
2489 return match<std::string>(
2490 effects,
2491 [&] (GeneralEffects x) {
2492 return sformat("mlsmk({} ; {} ; {} ; {})",
2493 show(x.loads),
2494 show(x.stores),
2495 show(x.moves),
2496 show(x.kills)
2499 [&] (ExitEffects x) {
2500 return sformat("exit({} ; {})", show(x.live), show(x.kills));
2502 [&] (InlineEnterEffects x) {
2503 return sformat("inline_enter({} ; {} ; {})",
2504 show(x.inlStack),
2505 show(x.inlFrame),
2506 show(x.actrec)
2509 [&] (InlineExitEffects x) {
2510 return sformat("inline_exit({} ; {} ; {})",
2511 show(x.inlStack),
2512 show(x.inlFrame),
2513 show(x.inlMeta)
2516 [&] (CallEffects x) {
2517 return sformat("call({} ; {} ; {} ; {})",
2518 show(x.kills),
2519 show(x.stack),
2520 show(x.locals),
2521 show(x.callee)
2524 [&] (PureSpillFrame x) {
2525 return sformat("stFrame({} ; {} ; {})",
2526 show(x.stk),
2527 show(x.ctx),
2528 show(x.callee)
2531 [&] (PureLoad x) { return sformat("ld({})", show(x.src)); },
2532 [&] (PureStore x) { return sformat("st({})", show(x.dst)); },
2533 [&] (ReturnEffects x) { return sformat("return({})", show(x.kills)); },
2534 [&] (IrrelevantEffects) { return "IrrelevantEffects"; },
2535 [&] (UnknownEffects) { return "UnknownEffects"; }
2539 //////////////////////////////////////////////////////////////////////
2541 AliasClass inline_fp_frame(const IRInstruction* inst) {
2542 return AStack {
2543 inst->src(0),
2544 inst->extra<DefInlineFP>()->spOffset + int32_t{kNumActRecCells} - 1,
2545 int32_t{kNumActRecCells}
2549 //////////////////////////////////////////////////////////////////////