Rename ArrayOffset -> ArrayAccess
[hiphop-php.git] / hphp / runtime / vm / jit / memory-effects.cpp
blobe0b286db1145bf0247ec1175467fb5f24ab334f4
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(), (uint32_t)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)) {
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 AliasClass stack = func->numLocals()
734 ? AStack{inst.dst(), FPRelOffset{-1}, func->numLocals()}
735 : AEmpty;
736 AliasClass frame = func->numLocals()
737 ? AFrame{inst.dst(), AliasIdSet::IdRange(0, func->numLocals())}
738 : AEmpty;
739 AliasClass clsrefs = func->numClsRefSlots() ? AClsRefSlotAny : AEmpty;
741 * Notice that the stack positions and frame locals described here are
742 * exactly the set of alias locations that are about to be overlapping
743 * inside the inlined frame. Likewise, ClsRefSlots from the caller and the
744 * callee are about to be jumbled.
746 return InlineEnterEffects{ stack, frame | clsrefs, inline_fp_frame(&inst) };
750 * BeginInlining is similar to DefInlineFP, however, it must always be the
751 * first instruction in the inlined call and has no effect serving only as
752 * a marker to memory effects that the stack cells within the inlined call
753 * are now dead.
755 * Unlike DefInlineFP it does not load the SpillFrame, which we hope to push
756 * off the main trace or elide entirely.
758 case BeginInlining: {
760 * SP relative offset of the first non-frame cell within the inlined call.
762 auto inlineStackOff = inst.extra<BeginInlining>()->offset;
763 return may_load_store_kill(
764 AEmpty,
765 AEmpty,
767 * This prevents stack slots from the caller from being sunk into the
768 * callee. Note that some of these stack slots overlap with the frame
769 * locals of the callee-- those slots are inacessible in the inlined
770 * call as frame and stack locations may not alias.
772 stack_below(inst.src(0), inlineStackOff)
776 case InlineSuspend:
777 case InlineReturn: {
778 return inline_exit_effects(inst.src(0));
781 case InlineReturnNoFrame: {
782 auto const callee = AliasClass(AStack {
783 inst.extra<InlineReturnNoFrame>()->offset,
784 std::numeric_limits<int32_t>::max()
785 }) | AMIStateAny;
786 return may_load_store_kill(AEmpty, AEmpty, callee);
789 case SyncReturnBC: {
790 auto const spOffset = inst.extra<SyncReturnBC>()->spOffset;
791 auto const arStack = actrec(inst.src(0), spOffset);
792 // This instruction doesn't actually load but SpillFrame cannot be pushed
793 // past it
794 return may_load_store(arStack, arStack);
797 case InterpOne:
798 return interp_one_effects(inst);
799 case InterpOneCF:
800 return ExitEffects {
801 AUnknown,
802 stack_below(inst.src(1), -inst.marker().spOff() - 1) | AMIStateAny
805 case NativeImpl:
806 return UnknownEffects {};
808 // NB: on the failure path, these C++ helpers do a fixup and read frame
809 // locals before they throw. They can also invoke the user error handler and
810 // go do whatever they want to non-frame locations.
812 // TODO(#5372569): if we combine dv inits into the same regions we could
813 // possibly avoid storing KindOfUninits if we modify this.
814 case VerifyParamCallable:
815 case VerifyParamCls:
816 case VerifyParamFailHard:
817 return may_load_store(AUnknown, AHeapAny);
818 // VerifyParamFail might coerce the parameter to the desired type rather than
819 // throwing.
820 case VerifyParamFail: {
821 auto const localId = inst.src(0)->intVal();
822 assertx(localId >= 0);
823 auto const stores =
824 AHeapAny | AFrame{inst.marker().fp(), safe_cast<uint32_t>(localId)};
825 return may_load_store(AUnknown, stores);
827 case VerifyReifiedLocalType: {
828 auto const extra = inst.extra<ParamData>();
829 assertx(extra->paramId >= 0);
830 auto const stores =
831 AHeapAny | AFrame{inst.marker().fp(),safe_cast<uint32_t>(extra->paramId)};
832 return may_load_store(AUnknown, stores);
834 // However the following ones can't read locals from our frame on the way
835 // out, except as a side effect of raising a warning.
836 case VerifyRetCallable:
837 case VerifyRetCls:
838 case VerifyReifiedReturnType:
839 return may_load_store(AHeapAny, AHeapAny);
841 case VerifyRetFail:
842 case VerifyRetFailHard:
843 return may_load_store(AHeapAny | AStackAny, AHeapAny);
845 case VerifyPropCls:
846 case VerifyPropFail:
847 case VerifyPropFailHard:
848 case VerifyProp:
849 return may_load_store(AHeapAny, AHeapAny);
851 case CallUnpack:
853 auto const extra = inst.extra<CallUnpack>();
854 return CallEffects {
855 // Kills. Everything on the stack below the incoming parameters.
856 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
857 // Stack. The act-rec, incoming parameters, and everything below.
858 stack_below(
859 inst.src(0),
860 extra->spOffset + extra->numParams + kNumActRecCells +
861 extra->numOut - 1
863 // Locals.
864 backtrace_locals(inst),
865 // Callee.
866 actrec_func(inst.src(0), extra->spOffset + extra->numParams)
870 case ContEnter:
872 auto const extra = inst.extra<ContEnter>();
873 return CallEffects {
874 // Kills. Everything on the stack below the sent value.
875 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
876 // Stack. The value being sent, and everything below.
877 stack_below(inst.src(0), extra->spOffset),
878 // Locals.
879 backtrace_locals(inst),
880 // Callee. Stored inside the generator object, not used by ContEnter.
881 AEmpty
885 case Call:
887 auto const extra = inst.extra<Call>();
888 return CallEffects {
889 // Kills. Everything on the stack below the incoming parameters.
890 stack_below(inst.src(0), extra->spOffset - 1) | AMIStateAny,
891 // Stack. The act-rec, incoming parameters, and everything below.
892 stack_below(
893 inst.src(0),
894 extra->spOffset + extra->numParams + kNumActRecCells +
895 extra->numOut - 1
897 // Locals.
898 backtrace_locals(inst),
899 // Callee.
900 actrec_func(inst.src(0), extra->spOffset + extra->numParams)
904 case CallBuiltin:
906 auto const stk = [&] () -> AliasClass {
907 AliasClass ret = AEmpty;
908 for (auto i = uint32_t{2}; i < inst.numSrcs(); ++i) {
909 if (inst.src(i)->type() <= TPtrToGen) {
910 auto const cls = pointee(inst.src(i));
911 if (cls.maybe(AStackAny)) {
912 ret = ret | cls;
916 return ret;
917 }();
918 auto const callee = inst.extra<CallBuiltin>()->callee;
919 if (callee->isFoldable()) {
920 return may_load_store_kill(stk | AHeapAny, AHeapAny, AMIStateAny);
921 } else {
922 return may_load_store_kill(
923 stk | AHeapAny | ARdsAny,
924 AHeapAny | ARdsAny,
925 AMIStateAny
930 // Resumable suspension takes everything from the frame and moves it into the
931 // heap.
932 case CreateGen:
933 case CreateAGen:
934 case CreateAFWH:
935 case CreateAFWHNoVV: {
936 auto const fp = canonical(inst.src(0));
937 auto fpInst = fp->inst();
938 if (fpInst->is(DefLabel)) fpInst = resolveFpDefLabel(fp);
939 auto const frame = [&] () -> AliasClass {
940 if (fpInst->is(DefFP)) return AFrameAny;
941 assertx(fpInst->is(DefInlineFP));
942 auto const nlocals = fpInst->extra<DefInlineFP>()->target->numLocals();
943 return nlocals
944 ? AFrame { fp, AliasIdSet::IdRange(0, nlocals)}
945 : AEmpty;
946 }();
947 auto const clsrefs = [&] () -> AliasClass {
948 if (fpInst->is(DefFP)) return AClsRefSlotAny;
949 assertx(fpInst->is(DefInlineFP));
950 auto const nrefs = fpInst->extra<DefInlineFP>()->target->numClsRefSlots();
951 return nrefs ? AClsRefSlotAny : AEmpty;
952 }();
953 return may_load_store_move(
954 frame | clsrefs,
955 AHeapAny,
956 frame | clsrefs
960 // AGWH construction updates the AsyncGenerator object.
961 case CreateAGWH:
962 return may_load_store(AHeapAny, AHeapAny);
964 case CreateAAWH:
966 auto const extra = inst.extra<CreateAAWH>();
967 auto const frame = AFrame {
968 inst.src(0),
969 AliasIdSet {
970 AliasIdSet::IdRange{ extra->first, extra->first + extra->count }
973 return may_reenter(inst, may_load_store(frame, AHeapAny));
976 case CountWHNotDone:
978 auto const extra = inst.extra<CountWHNotDone>();
979 auto const frame = AFrame {
980 inst.src(0),
981 AliasIdSet {
982 AliasIdSet::IdRange{ extra->first, extra->first + extra->count }
985 return may_load_store(frame, AEmpty);
988 // This re-enters to call extension-defined instance constructors.
989 case ConstructInstance:
990 return may_reenter(inst, may_load_store(AHeapAny, AHeapAny));
992 // Closures don't ever throw or reenter on construction
993 case ConstructClosure:
994 return IrrelevantEffects{};
996 case CheckStackOverflow:
997 case CheckSurpriseFlagsEnter:
998 case CheckSurpriseAndStack:
999 return may_load_store(AEmpty, AEmpty);
1001 case InitExtraArgs:
1002 return UnknownEffects {};
1004 //////////////////////////////////////////////////////////////////////
1005 // Iterator instructions
1007 case IterInit:
1008 case LIterInit:
1009 case LIterNext:
1010 return iter_effects(
1011 inst,
1012 inst.src(1),
1013 AFrame { inst.src(1), inst.extra<IterData>()->valId }
1015 case IterNext:
1016 return iter_effects(
1017 inst,
1018 inst.src(0),
1019 AFrame { inst.src(0), inst.extra<IterData>()->valId }
1022 case IterInitK:
1023 case LIterInitK:
1024 case LIterNextK:
1026 AliasClass key = AFrame { inst.src(1), inst.extra<IterData>()->keyId };
1027 AliasClass val = AFrame { inst.src(1), inst.extra<IterData>()->valId };
1028 return iter_effects(inst, inst.src(1), key | val);
1031 case IterNextK:
1033 AliasClass key = AFrame { inst.src(0), inst.extra<IterData>()->keyId };
1034 AliasClass val = AFrame { inst.src(0), inst.extra<IterData>()->valId };
1035 return iter_effects(inst, inst.src(0), key | val);
1038 case IterFree:
1039 return may_reenter(inst, may_load_store(AHeapAny, AHeapAny));
1041 //////////////////////////////////////////////////////////////////////
1042 // Instructions that explicitly manipulate locals
1044 case StLoc:
1045 return PureStore {
1046 AFrame { inst.src(0), inst.extra<StLoc>()->locId },
1047 inst.src(1),
1048 nullptr
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), nullptr };
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), nullptr };
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),
1096 nullptr
1099 case StClsRefTS:
1100 return PureStore {
1101 AClsRefTSSlot { inst.src(0), inst.extra<StClsRefTS>()->slot },
1102 inst.src(1),
1103 nullptr
1106 case KillClsRefCls:
1107 return may_load_store_kill(
1108 AEmpty, AEmpty,
1109 AClsRefClsSlot { inst.src(0), inst.extra<KillClsRefCls>()->slot }
1112 case KillClsRefTS:
1113 return may_load_store_kill(
1114 AEmpty, AEmpty,
1115 AClsRefTSSlot { inst.src(0), inst.extra<KillClsRefTS>()->slot }
1118 //////////////////////////////////////////////////////////////////////
1119 // Pointer-based loads and stores
1121 case LdMem:
1122 return PureLoad { pointee(inst.src(0)) };
1123 case StMem:
1124 return PureStore { pointee(inst.src(0)), inst.src(1), inst.src(0) };
1126 // TODO(#5962341): These take non-constant offset arguments, and are
1127 // currently only used for collections and class property inits, so we aren't
1128 // hooked up yet.
1129 case StElem:
1130 return PureStore {
1131 inst.src(0)->type() <= TPtrToRMembCell
1132 ? AHeapAny
1133 : AUnknown,
1134 inst.src(2),
1135 inst.src(0)
1137 case LdElem:
1138 return PureLoad {
1139 inst.src(0)->type() <= TPtrToRMembCell
1140 ? AHeapAny
1141 : AUnknown
1144 case LdMBase:
1145 return PureLoad { AMIStateBase };
1147 case StMBase:
1148 return PureStore { AMIStateBase, inst.src(0), nullptr };
1150 case StMIPropState:
1151 return PureStore { AMIStatePropS, nullptr, nullptr };
1153 case FinishMemberOp:
1154 return may_load_store_kill(AEmpty, AEmpty, AMIStateAny);
1156 case BoxPtr:
1158 auto const mem = pointee(inst.src(0));
1159 return may_load_store(mem, mem);
1161 case UnboxPtr:
1162 return may_load_store(pointee(inst.src(0)), AEmpty);
1164 case IsNTypeMem:
1165 case IsTypeMem:
1166 case CheckTypeMem:
1167 case CheckInitMem:
1168 return may_load_store(pointee(inst.src(0)), AEmpty);
1170 case CheckRDSInitialized:
1171 return may_load_store(
1172 ARds { inst.extra<CheckRDSInitialized>()->handle },
1173 AEmpty
1175 case MarkRDSInitialized:
1176 return may_load_store(
1177 AEmpty,
1178 ARds { inst.extra<MarkRDSInitialized>()->handle }
1181 case InitProps:
1182 return may_load_store(
1183 AHeapAny,
1184 AHeapAny | ARds { inst.extra<InitProps>()->cls->propHandle() }
1187 case InitSProps:
1188 return may_load_store(
1189 AHeapAny,
1190 AHeapAny | ARds { inst.extra<InitSProps>()->cls->sPropInitHandle() }
1193 case LdClsFromClsMeth:
1194 case LdFuncFromClsMeth:
1195 return may_load_store(AHeapAny, AEmpty);
1197 //////////////////////////////////////////////////////////////////////
1198 // Object/Ref loads/stores
1200 case CheckRefInner:
1201 return may_load_store(ARef { inst.src(0) }, AEmpty);
1202 case LdRef:
1203 return PureLoad { ARef { inst.src(0) } };
1204 case StRef:
1205 return PureStore { ARef { inst.src(0) }, inst.src(1), inst.src(0) };
1207 case InitObjProps:
1208 return may_load_store(AEmpty, APropAny);
1210 case InitObjMemoSlots:
1211 // Writes to memo slots, but these are not modeled.
1212 return IrrelevantEffects {};
1214 // Loads $obj->trace, stores $obj->file and $obj->line.
1215 case InitThrowableFileAndLine:
1216 return may_load_store(AHeapAny, APropAny);
1218 //////////////////////////////////////////////////////////////////////
1219 // Array loads and stores
1221 case InitPackedLayoutArray:
1222 return PureStore {
1223 AElemI { inst.src(0), inst.extra<InitPackedLayoutArray>()->index },
1224 inst.src(1),
1225 inst.src(0)
1228 case LdVecElem:
1229 case LdPackedElem: {
1230 auto const base = inst.src(0);
1231 auto const key = inst.src(1);
1232 return PureLoad {
1233 key->hasConstVal() ? AElemI { base, key->intVal() } : AElemIAny
1237 case InitPackedLayoutArrayLoop:
1239 auto const extra = inst.extra<InitPackedLayoutArrayLoop>();
1240 auto const stack_in = AStack {
1241 inst.src(1),
1242 extra->offset + static_cast<int32_t>(extra->size) - 1,
1243 static_cast<int32_t>(extra->size)
1245 return may_load_store_move(stack_in, AElemIAny, stack_in);
1248 case NewKeysetArray:
1250 // NewKeysetArray is reading elements from the stack, but writes to a
1251 // completely new array, so we can treat the store set as empty.
1252 auto const extra = inst.extra<NewKeysetArray>();
1253 auto const stack_in = AStack {
1254 inst.src(0),
1255 extra->offset + static_cast<int32_t>(extra->size) - 1,
1256 static_cast<int32_t>(extra->size)
1258 return may_load_store_move(stack_in, AEmpty, stack_in);
1261 case NewStructArray:
1262 case NewStructDArray:
1263 case NewStructDict:
1265 // NewStructArray is reading elements from the stack, but writes to a
1266 // completely new array, so we can treat the store set as empty.
1267 auto const extra = inst.extra<NewStructData>();
1268 auto const stack_in = AStack {
1269 inst.src(0),
1270 extra->offset + static_cast<int32_t>(extra->numKeys) - 1,
1271 static_cast<int32_t>(extra->numKeys)
1273 return may_load_store_move(stack_in, AEmpty, stack_in);
1276 case NewRecord:
1278 auto const extra = inst.extra<NewStructData>();
1279 auto const stack_in = AStack {
1280 inst.src(1),
1281 extra->offset + static_cast<int32_t>(extra->numKeys) - 1,
1282 static_cast<int32_t>(extra->numKeys)
1284 return may_load_store_move(stack_in, AEmpty, stack_in);
1286 case MemoGetStaticValue:
1287 case MemoGetLSBValue:
1288 case MemoGetInstanceValue:
1289 // Only reads the memo value (which isn't modeled here).
1290 return may_load_store(AEmpty, AEmpty);
1292 case MemoSetStaticValue:
1293 case MemoSetLSBValue:
1294 case MemoSetInstanceValue:
1295 // Writes to the memo value (which isn't modeled), but can re-enter to run
1296 // a destructor.
1297 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
1299 case MemoGetStaticCache:
1300 case MemoGetLSBCache:
1301 case MemoSetStaticCache:
1302 case MemoSetLSBCache: {
1303 // Reads some (non-zero) set of locals for keys, and reads/writes from the
1304 // memo cache (which isn't modeled). The set can re-enter to run a
1305 // destructor.
1306 auto const extra = inst.extra<MemoCacheStaticData>();
1307 auto const loc = [&] () -> AliasClass {
1308 if (inst.src(0)->isA(TFramePtr)) {
1309 return AFrame {
1310 inst.src(0),
1311 AliasIdSet{
1312 AliasIdSet::IdRange{
1313 extra->keys.first,
1314 extra->keys.first + extra->keys.count
1319 assertx(inst.src(0)->isA(TStkPtr));
1320 assertx(extra->stackOffset);
1321 return AStack {
1322 inst.src(0),
1323 *extra->stackOffset,
1324 static_cast<int32_t>(extra->keys.count)
1326 }();
1328 auto effects = may_load_store(loc, AEmpty);
1329 if (inst.op() == MemoSetStaticCache || inst.op() == MemoSetLSBCache) {
1330 effects = may_reenter(inst, effects);
1332 return effects;
1335 case MemoGetInstanceCache:
1336 case MemoSetInstanceCache: {
1337 // Reads some set of locals for keys, and reads/writes from the memo cache
1338 // (which isn't modeled). The set can re-enter to run a destructor.
1339 auto const extra = inst.extra<MemoCacheInstanceData>();
1340 auto const loc = [&]() -> AliasClass {
1341 // Unlike MemoGet/SetStaticCache, we can have an empty key range here.
1342 if (extra->keys.count == 0) return AEmpty;
1344 if (inst.src(0)->isA(TFramePtr)) {
1345 return AFrame {
1346 inst.src(0),
1347 AliasIdSet{
1348 AliasIdSet::IdRange{
1349 extra->keys.first,
1350 extra->keys.first + extra->keys.count
1355 assertx(inst.src(0)->isA(TStkPtr));
1356 assertx(extra->stackOffset);
1357 return AStack {
1358 inst.src(0),
1359 *extra->stackOffset,
1360 static_cast<int32_t>(extra->keys.count)
1362 }();
1363 auto effects = may_load_store(loc, AEmpty);
1364 if (inst.op() == MemoSetInstanceCache) effects = may_reenter(inst, effects);
1365 return effects;
1368 case MixedArrayGetK:
1369 case DictGetK:
1370 case KeysetGetK: {
1371 auto const base = inst.src(0);
1372 auto const key = inst.src(1);
1373 always_assert(key->isA(TInt | TStr));
1374 if (key->isA(TInt)) {
1375 return PureLoad {
1376 key->hasConstVal() ? AElemI { base, key->intVal() } : AElemIAny
1379 if (key->isA(TStr)) {
1380 return PureLoad {
1381 key->hasConstVal() ? AElemS { base, key->strVal() } : AElemSAny
1384 return PureLoad { AElemAny };
1387 case ElemMixedArrayK:
1388 case ElemDictK:
1389 case ElemKeysetK:
1390 return IrrelevantEffects {};
1392 case VecFirst: {
1393 auto const base = inst.src(0);
1394 return may_load_store(AElemI { base, 0 }, AEmpty);
1396 case VecLast: {
1397 auto const base = inst.src(0);
1398 if (base->hasConstVal(TArr)) {
1399 return may_load_store(
1400 AElemI { base, static_cast<int64_t>(base->arrVal()->size() - 1) },
1401 AEmpty);
1403 return may_load_store(AElemIAny, AEmpty);
1405 case DictFirst:
1406 case DictLast:
1407 case KeysetFirst:
1408 case KeysetLast:
1409 return may_load_store(AElemAny, AEmpty);
1411 case DictFirstKey:
1412 case DictLastKey:
1413 return may_load_store(AEmpty, AEmpty);
1415 case ProfileMixedArrayAccess:
1416 case CheckMixedArrayOffset:
1417 case CheckArrayCOW:
1418 case ProfileDictAccess:
1419 case ProfileKeysetAccess:
1420 case CheckDictOffset:
1421 case CheckKeysetOffset:
1422 return may_load_store(AHeapAny, AEmpty);
1424 case ArrayIsset:
1425 case AKExistsArr:
1426 return may_load_store(AElemAny, AEmpty);
1428 case ArrayIdx:
1429 return may_load_store(AElemAny | ARefAny, AEmpty);
1431 case SameArr:
1432 case NSameArr:
1433 return may_load_store(AEmpty, AEmpty);
1435 case DictGetQuiet:
1436 case DictIsset:
1437 case DictEmptyElem:
1438 case DictIdx:
1439 case KeysetGetQuiet:
1440 case KeysetIsset:
1441 case KeysetEmptyElem:
1442 case KeysetIdx:
1443 case AKExistsDict:
1444 case AKExistsKeyset:
1445 return may_load_store(AElemAny, AEmpty);
1447 case SameVec:
1448 case NSameVec:
1449 case SameDict:
1450 case NSameDict:
1451 case EqKeyset:
1452 case NeqKeyset:
1453 case SameKeyset:
1454 case NSameKeyset:
1455 return may_load_store(AElemAny, AEmpty);
1457 case SameShape:
1458 case NSameShape:
1459 return may_load_store(
1460 RuntimeOption::EvalHackArrDVArrs ? AElemAny : AEmpty,
1461 AEmpty);
1463 case AKExistsObj:
1464 return may_load_store(AHeapAny, AHeapAny);
1466 //////////////////////////////////////////////////////////////////////
1467 // Member instructions
1469 case CheckMBase:
1470 return may_load_store(pointee(inst.src(0)), AEmpty);
1473 * Various minstr opcodes that take a PtrToGen in src 0, which may or may not
1474 * point to a frame local or the evaluation stack. Some may read or write to
1475 * that pointer while some only read. They can all re-enter the VM and access
1476 * arbitrary heap locations.
1478 case CGetElem:
1479 case EmptyElem:
1480 case IssetElem:
1481 case CGetProp:
1482 case CGetPropQ:
1483 case EmptyProp:
1484 case IssetProp:
1485 return may_load_store_kill(
1486 AHeapAny | all_pointees(inst),
1487 AHeapAny,
1488 AMIStatePropS
1491 case IncDecElem:
1492 case IncDecProp:
1493 case SetElem:
1494 case SetNewElem:
1495 case SetOpElem:
1496 case SetOpProp:
1497 case SetProp:
1498 return minstr_final_with_prop_state(inst);
1501 * SetRange behaves like a simpler version of SetElem.
1503 case SetRange:
1504 case SetRangeRev:
1505 return may_load_store_kill(
1506 AHeapAny | all_pointees(inst),
1507 AHeapAny | pointee(inst.src(0)),
1508 AMIStatePropS
1511 case SetNewElemArray:
1512 case SetNewElemVec:
1513 case SetNewElemKeyset:
1514 case UnsetElem:
1515 case ElemArrayD:
1516 case ElemArrayU:
1517 case ElemVecD:
1518 case ElemVecU:
1519 case ElemDictD:
1520 case ElemDictU:
1521 case ElemKeysetU:
1522 case UnsetProp:
1523 // Right now we generally can't limit any of these better than general
1524 // re-entry rules, since they can raise warnings and re-enter.
1525 return may_load_store_kill(
1526 AHeapAny | all_pointees(inst),
1527 AHeapAny | all_pointees(inst),
1528 AMIStatePropS
1531 case ReservePackedArrayDataNewElem:
1532 return may_load_store(AHeapAny, AHeapAny);
1535 * Intermediate minstr operations. In addition to a base pointer like the
1536 * operations above, these may take a pointer to MInstrState::tvRef, which
1537 * they may store to (but not read from).
1539 case ElemX:
1540 case ElemDX:
1541 case ElemUX:
1542 case PropX:
1543 case PropDX:
1544 case PropQ:
1545 return minstr_with_tvref(inst);
1548 * Collection accessors can read from their inner array buffer, but stores
1549 * COW and behave as if they only affect collection memory locations. We
1550 * don't track those, so it's returning AEmpty for now.
1552 case MapIsset:
1553 case PairIsset:
1554 case VectorIsset:
1555 return may_load_store(AHeapAny, AEmpty /* Note */);
1556 case MapGet:
1557 case MapSet:
1558 case VectorSet:
1559 return may_reenter(inst, may_load_store(AHeapAny, AEmpty /* Note */));
1561 case LdInitPropAddr:
1562 return may_load_store(
1563 AProp {
1564 inst.src(0),
1565 safe_cast<uint32_t>(inst.extra<LdInitPropAddr>()->offsetBytes)
1567 AEmpty
1569 case LdInitRDSAddr:
1570 return may_load_store(
1571 ARds { inst.extra<LdInitRDSAddr>()->handle },
1572 AEmpty
1575 //////////////////////////////////////////////////////////////////////
1576 // Instructions that allocate new objects, without reading any other memory
1577 // at all, so any effects they have on some types of memory locations we
1578 // track are isolated from anything else we care about.
1580 case NewArray:
1581 case NewClsMeth:
1582 case NewCol:
1583 case NewColFromArray:
1584 case NewPair:
1585 case NewInstanceRaw:
1586 case NewMixedArray:
1587 case NewDArray:
1588 case NewDictArray:
1589 case AllocPackedArray:
1590 case AllocVArray:
1591 case AllocVecArray:
1592 case ConvBoolToArr:
1593 case ConvDblToStr:
1594 case ConvDblToArr:
1595 case ConvIntToArr:
1596 case ConvIntToStr:
1597 case Box: // conditional allocation
1598 return IrrelevantEffects {};
1600 case AllocObj:
1601 // AllocObj re-enters to call constructors, but if it weren't for that we
1602 // could ignore its loads and stores since it's a new object.
1603 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
1604 case AllocObjReified:
1605 // Similar to AllocObj but also stores the reification
1606 return may_reenter(inst, may_load_store(AEmpty, AHeapAny));
1608 //////////////////////////////////////////////////////////////////////
1609 // Instructions that explicitly manipulate the stack.
1611 case LdStk:
1612 return PureLoad {
1613 AStack { inst.src(0), inst.extra<LdStk>()->offset, 1 }
1616 case StStk:
1617 return PureStore {
1618 AStack { inst.src(0), inst.extra<StStk>()->offset, 1 },
1619 inst.src(1),
1620 nullptr
1623 case StOutValue:
1624 // Technically these writes affect the caller's stack, but there is no way
1625 // to actually observe them from within the callee. They can also only
1626 // occur once on any exit path from a function.
1627 return may_load_store(AEmpty, AEmpty);
1629 case SpillFrame:
1631 auto const spOffset = inst.extra<SpillFrame>()->spOffset;
1632 return PureSpillFrame {
1633 actrec(inst.src(0), spOffset),
1634 actrec_ctx(inst.src(0), spOffset),
1635 actrec_func(inst.src(0), spOffset),
1636 inst.src(1)
1640 case CheckStk:
1641 return may_load_store(
1642 AStack { inst.src(0), inst.extra<CheckStk>()->offset, 1 },
1643 AEmpty
1646 case LdARCtx:
1647 return PureLoad {
1648 actrec_ctx(inst.src(0), inst.extra<LdARCtx>()->offset)
1650 case LdARFuncPtr:
1651 return PureLoad {
1652 actrec_func(inst.src(0), inst.extra<LdARFuncPtr>()->offset)
1655 case DbgAssertARFunc:
1656 return may_load_store(
1657 actrec_func(inst.src(0), inst.extra<DbgAssertARFunc>()->offset),
1658 AEmpty
1661 case Unreachable:
1662 // Unreachable code kills every memory location.
1663 return may_load_store_kill(AEmpty, AEmpty, AUnknown);
1665 case ResolveTypeStruct: {
1666 auto const extra = inst.extra<ResolveTypeStructData>();
1667 auto const stack_in = AStack {
1668 inst.src(0),
1669 extra->offset + static_cast<int32_t>(extra->size) - 1,
1670 static_cast<int32_t>(extra->size)
1672 return may_load_store(AliasClass(stack_in)|AHeapAny, AHeapAny);
1675 //////////////////////////////////////////////////////////////////////
1676 // Instructions that never read or write memory locations tracked by this
1677 // module.
1679 case AbsDbl:
1680 case AddDbl:
1681 case AddInt:
1682 case AddIntO:
1683 case AndInt:
1684 case AssertType:
1685 case AssertLoc:
1686 case AssertStk:
1687 case AssertMBase:
1688 case AssertARFunc:
1689 case FuncGuard:
1690 case DefFP:
1691 case DefSP:
1692 case EndGuards:
1693 case EqBool:
1694 case EqCls:
1695 case EqFunc:
1696 case EqStrPtr:
1697 case EqArrayDataPtr:
1698 case EqDbl:
1699 case EqInt:
1700 case GteBool:
1701 case GteInt:
1702 case GtBool:
1703 case GtInt:
1704 case HintLocInner:
1705 case HintStkInner:
1706 case HintMBaseInner:
1707 case Jmp:
1708 case JmpNZero:
1709 case JmpZero:
1710 case LdPropAddr:
1711 case LdStkAddr:
1712 case LdPackedArrayDataElemAddr:
1713 case LteBool:
1714 case LteDbl:
1715 case LteInt:
1716 case LtBool:
1717 case LtInt:
1718 case GtDbl:
1719 case GteDbl:
1720 case LtDbl:
1721 case DivDbl:
1722 case DivInt:
1723 case MulDbl:
1724 case MulInt:
1725 case MulIntO:
1726 case NeqBool:
1727 case NeqDbl:
1728 case NeqInt:
1729 case SameObj:
1730 case NSameObj:
1731 case EqRes:
1732 case NeqRes:
1733 case CmpBool:
1734 case CmpInt:
1735 case CmpDbl:
1736 case SubDbl:
1737 case SubInt:
1738 case SubIntO:
1739 case XorBool:
1740 case XorInt:
1741 case OrInt:
1742 case AssertNonNull:
1743 case CheckNonNull:
1744 case CheckNullptr:
1745 case CheckFuncMMNonMagic:
1746 case CheckSmashableClass:
1747 case Ceil:
1748 case Floor:
1749 case DefLabel:
1750 case CheckInit:
1751 case Nop:
1752 case Mod:
1753 case Conjure:
1754 case ConjureUse:
1755 case EndBlock:
1756 case ConvBoolToInt:
1757 case ConvBoolToDbl:
1758 case DefConst:
1759 case LdLocAddr:
1760 case Sqrt:
1761 case Shl:
1762 case Shr:
1763 case Lshr:
1764 case IsNType:
1765 case IsType:
1766 case Mov:
1767 case ConvClsToCctx:
1768 case ConvDblToBool:
1769 case ConvDblToInt:
1770 case DblAsBits:
1771 case LdMIPropStateAddr:
1772 case LdMIStateAddr:
1773 case LdPairBase:
1774 case LdClsCns:
1775 case LdSubClsCns:
1776 case LdSubClsCnsClsName:
1777 case LdTypeCns:
1778 case CheckSubClsCns:
1779 case LdClsCnsVecLen:
1780 case ProfileSubClsCns:
1781 case CheckCtxThis:
1782 case FuncSupportsAsyncEagerReturn:
1783 case IsFunReifiedGenericsMatched:
1784 case IsFuncDynCallable:
1785 case IsClsDynConstructible:
1786 case LdFuncMFunc:
1787 case LdFuncRxLevel:
1788 case LdSmashable:
1789 case LdSmashableFunc:
1790 case IsReifiedName:
1791 case LdARNumParams:
1792 case LdRDSAddr:
1793 case ExitPlaceholder:
1794 case CheckRange:
1795 case ProfileType:
1796 case LdIfaceMethod:
1797 case InstanceOfIfaceVtable:
1798 case CheckARMagicFlag:
1799 case LdARNumArgsAndFlags:
1800 case StARNumArgsAndFlags:
1801 case LdARReifiedGenerics:
1802 case KillARReifiedGenerics:
1803 case LdTVAux:
1804 case LdARInvName:
1805 case StARInvName:
1806 case MethodExists:
1807 case GetTime:
1808 case GetTimeNs:
1809 case ProfileInstanceCheck:
1810 case Select:
1811 case LookupSPropSlot:
1812 case ConvPtrToLval:
1813 case MangleReifiedName:
1814 return IrrelevantEffects {};
1816 case StClosureArg:
1817 return PureStore {
1818 AProp {
1819 inst.src(0),
1820 safe_cast<uint32_t>(inst.extra<StClosureArg>()->offsetBytes)
1822 inst.src(1),
1823 inst.src(0)
1826 //////////////////////////////////////////////////////////////////////
1827 // Instructions that technically do some things w/ memory, but not in any way
1828 // we currently care about. They however don't return IrrelevantEffects
1829 // because we assume (in refcount-opts) that IrrelevantEffects instructions
1830 // can't even inspect Countable reference count fields, and several of these
1831 // can. All GeneralEffects instructions are assumed to possibly do so.
1833 case DecRefNZ:
1834 case AFWHBlockOn:
1835 case IncRef:
1836 case LdClosureCtx:
1837 case StClosureCtx:
1838 case StContArKey:
1839 case StContArValue:
1840 case LdRetVal:
1841 case ConvStrToInt:
1842 case ConvResToInt:
1843 case OrdStr:
1844 case ChrInt:
1845 case CreateSSWH:
1846 case NewLikeArray:
1847 case CheckRefs:
1848 case LdClsCctx:
1849 case BeginCatch:
1850 case CheckSurpriseFlags:
1851 case CheckType:
1852 case CheckVArray:
1853 case CheckDArray:
1854 case StArResumeAddr:
1855 case StContArState:
1856 case ZeroErrorLevel:
1857 case RestoreErrorLevel:
1858 case CheckCold:
1859 case ContArIncIdx:
1860 case ContArIncKey:
1861 case ContArUpdateIdx:
1862 case ContValid:
1863 case ContStarted:
1864 case IncProfCounter:
1865 case IncStat:
1866 case ContPreNext:
1867 case ContStartedCheck:
1868 case ConvArrToBool:
1869 case ConvArrToDbl:
1870 case CountArray:
1871 case CountArrayFast:
1872 case CountVec:
1873 case CountDict:
1874 case CountShape:
1875 case CountKeyset:
1876 case InstanceOf:
1877 case InstanceOfBitmask:
1878 case NInstanceOfBitmask:
1879 case InstanceOfIface:
1880 case InterfaceSupportsArr:
1881 case InterfaceSupportsVec:
1882 case InterfaceSupportsDict:
1883 case InterfaceSupportsKeyset:
1884 case InterfaceSupportsDbl:
1885 case InterfaceSupportsInt:
1886 case InterfaceSupportsStr:
1887 case IsWaitHandle:
1888 case IsCol:
1889 case IsDVArray:
1890 case HasToString:
1891 case DbgAssertRefCount:
1892 case GtStr:
1893 case GteStr:
1894 case LtStr:
1895 case LteStr:
1896 case EqStr:
1897 case NeqStr:
1898 case SameStr:
1899 case NSameStr:
1900 case CmpStr:
1901 case GtStrInt:
1902 case GteStrInt:
1903 case LtStrInt:
1904 case LteStrInt:
1905 case EqStrInt:
1906 case NeqStrInt:
1907 case CmpStrInt:
1908 case GtRes:
1909 case GteRes:
1910 case LtRes:
1911 case LteRes:
1912 case CmpRes:
1913 case LdBindAddr:
1914 case LdSSwitchDestFast:
1915 case RBTraceEntry:
1916 case RBTraceMsg:
1917 case ConvIntToBool:
1918 case ConvIntToDbl:
1919 case ConvStrToArr: // decrefs src, but src is a string
1920 case ConvStrToBool:
1921 case ConvStrToDbl:
1922 case ConvResToDbl:
1923 case EagerSyncVMRegs:
1924 case ExtendsClass:
1925 case LdUnwinderValue:
1926 case LdCtx:
1927 case LdCctx:
1928 case LdClosure:
1929 case LdClsName:
1930 case LdAFWHActRec:
1931 case LdClsCtx:
1932 case LdContActRec:
1933 case LdContArKey:
1934 case LdContArValue:
1935 case LdContField:
1936 case LdContResumeAddr:
1937 case LdClsCachedSafe:
1938 case LdClsInitData:
1939 case UnwindCheckSideExit:
1940 case LdCns:
1941 case LdFuncVecLen:
1942 case LdClsMethod:
1943 case LdClsMethodCacheCls:
1944 case LdClsMethodCacheFunc:
1945 case LdClsMethodFCacheFunc:
1946 case LdClsTypeCns:
1947 case LdClsTypeCnsClsName:
1948 case FwdCtxStaticCall:
1949 case ProfileArrayKind:
1950 case ProfileSwitchDest:
1951 case LdFuncNumParams:
1952 case LdFuncName:
1953 case LdGblAddr:
1954 case LdGblAddrDef:
1955 case LdObjClass:
1956 case LdObjInvoke:
1957 case LdObjMethod:
1958 case LdStrLen:
1959 case StringIsset:
1960 case LdSwitchDblIndex:
1961 case LdSwitchStrIndex:
1962 case LdVectorBase:
1963 case LdWHResult:
1964 case LdWHState:
1965 case LdWHNotDone:
1966 case LookupClsRDS:
1967 case DbgTraceCall:
1968 case InitCtx:
1969 case PackMagicArgs:
1970 case StrictlyIntegerConv:
1971 case DbgAssertFunc:
1972 case ProfileMethod:
1973 return may_load_store(AEmpty, AEmpty);
1975 // Some that touch memory we might care about later, but currently don't:
1976 case ColIsEmpty:
1977 case ColIsNEmpty:
1978 case ConvCellToBool:
1979 case ConvObjToBool:
1980 case CountCollection:
1981 case LdVectorSize:
1982 case CheckPackedArrayDataBounds:
1983 case LdColVec:
1984 case LdColDict:
1985 case EnterFrame:
1986 return may_load_store(AEmpty, AEmpty);
1988 //////////////////////////////////////////////////////////////////////
1989 // Instructions that can re-enter the VM and touch most heap things. They
1990 // also may generally write to the eval stack below an offset (see
1991 // alias-class.h above AStack for more).
1993 case DecRef:
1995 auto const src = inst.src(0);
1996 // It could decref the inner ref.
1997 auto affected = src->isA(TBoxedCell) ? ARef { src } :
1998 src->type().maybe(TBoxedCell) ? ARefAny : AEmpty;
1999 if (src->type().maybe(TKeyset | TBoxedKeyset)) {
2000 // TKeyset can't re-enter, but it will decref any contained
2001 // strings. Without this, an incref of a string contained in
2002 // a Keyset could be sunk past the decref of the Keyset.
2003 affected |= AHeapAny;
2005 // Need to add affected to the `store' set. See comments about
2006 // `GeneralEffects' in memory-effects.h.
2007 auto const effect = may_load_store(affected, affected);
2008 if (src->type().maybe(TArr | TVec | TDict | TObj | TRes |
2009 TBoxedArr | TBoxedVec | TBoxedDict |
2010 TBoxedObj | TBoxedRes)) {
2011 // Could re-enter to run a destructor. Keysets are exempt because they
2012 // can only contain strings or integers.
2013 return may_reenter(inst, effect);
2015 return effect;
2018 case GetMemoKey:
2019 return may_load_store(AHeapAny, AHeapAny);
2021 case GetMemoKeyScalar:
2022 return IrrelevantEffects{};
2024 case LdArrFuncCtx:
2025 case LdFunc: // these all can autoload
2027 AliasClass effects =
2028 actrec(inst.src(1), inst.extra<IRSPRelOffsetData>()->offset);
2029 return may_load_store(effects, effects);
2032 case LookupClsMethod: // autoload, and it writes part of the new actrec
2034 AliasClass effects =
2035 actrec(inst.src(2), inst.extra<LookupClsMethod>()->calleeAROffset);
2036 return may_load_store(effects, effects);
2039 case LdClsPropAddrOrNull: // may run 86{s,p}init, which can autoload
2040 case LdClsPropAddrOrRaise: // raises errors, and 86{s,p}init
2041 case BaseG:
2042 case Clone:
2043 case ThrowArrayIndexException:
2044 case ThrowArrayKeyException:
2045 case RaiseUninitLoc:
2046 case RaiseUndefProp:
2047 case RaiseMissingArg:
2048 case RaiseTooManyArg:
2049 case RaiseError:
2050 case RaiseErrorOnInvalidIsAsExpressionType:
2051 case RaiseNotice:
2052 case RaiseWarning:
2053 case RaiseHackArrCompatNotice:
2054 case RaiseHackArrParamNotice:
2055 case RaiseHackArrPropNotice:
2056 case RaiseForbiddenDynCall:
2057 case RaiseForbiddenDynConstruct:
2058 case RaiseRxCallViolation:
2059 case RaiseStrToClassNotice:
2060 case CheckClsReifiedGenericMismatch:
2061 case CheckFunReifiedGenericMismatch:
2062 case ConvCellToStr:
2063 case ConvObjToStr:
2064 case Count: // re-enters on CountableClass
2065 case GtObj:
2066 case GteObj:
2067 case LtObj:
2068 case LteObj:
2069 case EqObj:
2070 case NeqObj:
2071 case CmpObj:
2072 case GtArr:
2073 case GteArr:
2074 case LtArr:
2075 case LteArr:
2076 case EqArr:
2077 case NeqArr:
2078 case CmpArr:
2079 case GtShape:
2080 case GteShape:
2081 case LtShape:
2082 case LteShape:
2083 case EqShape:
2084 case NeqShape:
2085 case CmpShape:
2086 case GtVec:
2087 case GteVec:
2088 case LtVec:
2089 case LteVec:
2090 case EqVec:
2091 case NeqVec:
2092 case CmpVec:
2093 case EqDict:
2094 case NeqDict:
2095 case ConvCellToArr: // decrefs src, may read obj props
2096 case ConvCellToObj: // decrefs src
2097 case ConvObjToArr: // decrefs src
2098 case ConvObjToVArr: // can invoke PHP
2099 case ConvObjToDArr: // can invoke PHP
2100 case OODeclExists:
2101 case DefCls: // autoload
2102 case LdCls: // autoload
2103 case LdClsCached: // autoload
2104 case LdFuncCached: // autoload
2105 case LdRecCached: // autoload
2106 case LdSwitchObjIndex: // decrefs arg
2107 case InitClsCns: // autoload
2108 case LookupClsMethodCache: // autoload
2109 case LookupClsMethodFCache: // autoload
2110 case LookupCnsE:
2111 case LookupFuncCached: // autoload
2112 case StringGet: // raise_notice
2113 case OrdStrIdx: // raise_notice
2114 case ArrayAdd: // decrefs source
2115 case AddElemIntKey:
2116 case AddElemStrKey:
2117 case AddNewElem: // can re-enter
2118 case AddNewElemKeyset: // can re-enter
2119 case DictAddElemIntKey: // decrefs value
2120 case DictAddElemStrKey: // decrefs value
2121 case ArrayGet: // kVPackedKind warnings
2122 case ArraySet: // kVPackedKind warnings
2123 case ArraySetRef: // kVPackedKind warnings
2124 case DictGet:
2125 case KeysetGet:
2126 case VecSet:
2127 case VecSetRef:
2128 case DictSet:
2129 case DictSetRef:
2130 case ElemArrayX:
2131 case ElemDictX:
2132 case ElemKeysetX:
2133 case LdClsCtor:
2134 case ConcatStrStr:
2135 case PrintStr:
2136 case PrintBool:
2137 case PrintInt:
2138 case ConcatIntStr:
2139 case ConcatStrInt:
2140 case LdSSwitchDestSlow:
2141 case ConvObjToDbl:
2142 case ConvObjToInt:
2143 case ConvCellToInt:
2144 case ConvResToStr:
2145 case ConcatStr3:
2146 case ConcatStr4:
2147 case ConvCellToDbl:
2148 case ConvArrToVec:
2149 case ConvArrToDict:
2150 case ConvShapeToDict:
2151 case ConvObjToVec:
2152 case ConvObjToDict:
2153 case ConvObjToKeyset:
2154 case ThrowOutOfBounds:
2155 case ThrowInvalidArrayKey:
2156 case ThrowInvalidOperation:
2157 case ThrowArithmeticError:
2158 case ThrowDivisionByZeroError:
2159 case ThrowDivisionByZeroException:
2160 case ThrowHasThisNeedStatic:
2161 case ThrowLateInitPropError:
2162 case ThrowMissingThis:
2163 case ThrowParameterWrongType:
2164 case ThrowParamRefMismatch:
2165 case ThrowParamRefMismatchRange:
2166 case SetOpCell:
2167 case SetOpCellVerify:
2168 case ThrowAsTypeStructException:
2169 case PropTypeRedefineCheck: // Can raise and autoload
2170 case HandleRequestSurprise:
2171 return may_load_store(AHeapAny, AHeapAny);
2173 case AddNewElemVec:
2174 case IsTypeStruct:
2175 case LdReifiedGeneric:
2176 case RecordReifiedGenericsAndGetName:
2177 case RecordReifiedGenericsAndGetTSList:
2178 return may_load_store(AElemAny, AEmpty);
2180 case ConvArrToKeyset: // Decrefs input values
2181 case ConvVecToKeyset:
2182 case ConvDictToKeyset:
2183 case ConvShapeToKeyset:
2184 case ConvDictToDArr: // These 4 may raise Hack array compat notices
2185 case ConvShapeToDArr:
2186 case ConvKeysetToDArr:
2187 case ConvDictToArr:
2188 case ConvShapeToArr:
2189 case ConvKeysetToArr:
2190 return may_load_store(AElemAny, AEmpty);
2192 case ConvVecToArr:
2193 case ConvArrToNonDVArr:
2194 case ConvDictToVec:
2195 case ConvShapeToVec:
2196 case ConvKeysetToVec:
2197 case ConvVecToDict:
2198 case ConvKeysetToDict:
2199 case ConvArrToVArr:
2200 case ConvVecToVArr:
2201 case ConvDictToVArr:
2202 case ConvShapeToVArr:
2203 case ConvKeysetToVArr:
2204 case ConvArrToDArr:
2205 case ConvVecToDArr:
2206 return may_load_store(AElemAny, AEmpty);
2208 case ReleaseVVAndSkip: // can decref ExtraArgs or VarEnv and Locals
2209 return may_reenter(inst,
2210 may_load_store(AHeapAny|AFrameAny, AHeapAny|AFrameAny));
2212 // debug_backtrace() traverses stack and WaitHandles on the heap.
2213 case DebugBacktrace:
2214 case DebugBacktraceFast:
2215 return may_load_store(AHeapAny|AFrameAny|AStackAny, AHeapAny);
2217 // This instruction doesn't touch memory we track, except that it may
2218 // re-enter to construct php Exception objects. During this re-entry anything
2219 // can happen (e.g. a surprise flag check could cause a php signal handler to
2220 // run arbitrary code).
2221 case AFWHPrepareChild:
2222 return may_reenter(inst, may_load_store(AEmpty, AEmpty));
2224 //////////////////////////////////////////////////////////////////////
2225 // The following instructions are used for debugging memory optimizations.
2226 // We can't ignore them, because they can prevent future optimizations;
2227 // eg t1 = LdStk<N>; DbgTrashStk<N>; StStk<N> t1
2228 // If we ignore the DbgTrashStk it looks like the StStk is redundant
2230 case DbgTrashStk:
2231 return GeneralEffects {
2232 AEmpty, AEmpty, AEmpty,
2233 AStack { inst.src(0), inst.extra<DbgTrashStk>()->offset, 1 }
2235 case DbgTrashFrame:
2236 return GeneralEffects {
2237 AEmpty, AEmpty, AEmpty,
2238 actrec(inst.src(0), inst.extra<DbgTrashFrame>()->offset)
2240 case DbgTrashMem:
2241 return GeneralEffects {
2242 AEmpty, AEmpty, AEmpty,
2243 pointee(inst.src(0))
2245 case DbgTrashRetVal:
2246 return IrrelevantEffects {};
2248 //////////////////////////////////////////////////////////////////////
2252 not_reached();
2255 //////////////////////////////////////////////////////////////////////
2257 DEBUG_ONLY bool check_effects(const IRInstruction& inst, MemEffects me) {
2258 SCOPE_ASSERT_DETAIL("Memory Effects") {
2259 return folly::sformat(" inst: {}\n effects: {}\n", inst, show(me));
2262 auto check_fp = [&] (FPRelOffset base) {
2263 always_assert_flog(
2264 base.offset <= 0,
2265 "frame offset is above base frame"
2269 auto check_obj = [&] (SSATmp* obj) {
2270 always_assert_flog(
2271 obj->type() <= TObj,
2272 "Non obj pointer in memory effects"
2276 auto check = [&] (AliasClass a) {
2277 if (auto const fr = a.frame()) check_fp(fr->base);
2278 if (auto const sl = a.clsRefClsSlot()) check_fp(sl->base);
2279 if (auto const sl = a.clsRefTSSlot()) check_fp(sl->base);
2280 if (auto const pr = a.prop()) check_obj(pr->obj);
2283 match<void>(
2285 [&] (GeneralEffects x) {
2286 check(x.loads);
2287 check(x.stores);
2288 check(x.moves);
2289 check(x.kills);
2291 // Locations may-moved always should also count as may-loads.
2292 always_assert(x.moves <= x.loads);
2294 if (inst.mayRaiseError()) {
2295 // Any instruction that can raise an error can run a user error handler
2296 // and have arbitrary effects on the heap.
2297 always_assert(AHeapAny <= x.loads);
2298 always_assert(AHeapAny <= x.stores);
2300 * They also ought to kill /something/ on the stack, because of
2301 * possible re-entry. It's not incorrect to leave things out of the
2302 * kills set, but this assertion is here because we shouldn't do it on
2303 * purpose, so this is here until we have a reason not to assert it.
2305 * The mayRaiseError instructions should all be going through
2306 * may_reenter right now, which will kill the stack below the re-entry
2307 * depth---unless the marker for `inst' doesn't have an fp set.
2309 always_assert(inst.marker().fp() == nullptr ||
2310 AStackAny.maybe(x.kills));
2313 [&] (PureLoad x) { check(x.src); },
2314 [&] (PureStore x) { check(x.dst); },
2315 [&] (PureSpillFrame x) { check(x.stk); check(x.ctx); check(x.callee);
2316 always_assert(x.ctx <= x.stk);
2317 always_assert(x.callee <= x.stk); },
2318 [&] (ExitEffects x) { check(x.live); check(x.kills); },
2319 [&] (IrrelevantEffects) {},
2320 [&] (UnknownEffects) {},
2321 [&] (CallEffects x) { check(x.kills);
2322 check(x.stack);
2323 check(x.locals);
2324 check(x.callee);
2325 always_assert(x.callee <= x.stack); },
2326 [&] (InlineEnterEffects x){ check(x.inlStack);
2327 check(x.inlFrame);
2328 check(x.actrec); },
2329 [&] (InlineExitEffects x){ check(x.inlStack);
2330 check(x.inlFrame);
2331 check(x.inlMeta); },
2332 [&] (ReturnEffects x) { check(x.kills); }
2335 return true;
2338 //////////////////////////////////////////////////////////////////////
2342 MemEffects memory_effects(const IRInstruction& inst) {
2343 auto const inner = memory_effects_impl(inst);
2344 auto const ret = [&] () -> MemEffects {
2345 // These instructions have special handling because they occur as functions
2346 // suspend or return. Their ability to reenter is handled in
2347 // memory_effects_impl, and comments in that function explain why.
2348 auto const is_special = inst.is(
2349 ReturnHook,
2350 SuspendHookAwaitEF,
2351 SuspendHookAwaitEG,
2352 SuspendHookAwaitR,
2353 SuspendHookCreateCont,
2354 SuspendHookYield
2357 if (is_special || !inst.mayRaiseError()) return inner;
2359 auto fail = [&] {
2360 always_assert_flog(
2361 false,
2362 "Instruction {} has effects {}, but has been marked as MayRaiseError "
2363 "and must use a UnknownEffects, GeneralEffects, or CallEffects type.",
2364 inst,
2365 show(inner)
2367 return may_load_store(AUnknown, AUnknown);
2370 // Calls are implicitly MayRaise, all other instructions must use the
2371 // GeneralEffects or UnknownEffects class of memory effects
2372 return match<MemEffects>(
2373 inner,
2374 [&] (GeneralEffects x) { return may_reenter(inst, x); },
2375 [&] (CallEffects x) { return x; },
2376 [&] (UnknownEffects x) { return x; },
2377 [&] (PureLoad) { return fail(); },
2378 [&] (PureStore) { return fail(); },
2379 [&] (PureSpillFrame) { return fail(); },
2380 [&] (ExitEffects) { return fail(); },
2381 [&] (InlineExitEffects) { return fail(); },
2382 [&] (InlineEnterEffects) { return fail(); },
2383 [&] (IrrelevantEffects) { return fail(); },
2384 [&] (ReturnEffects) { return fail(); }
2386 }();
2387 assertx(check_effects(inst, ret));
2388 return ret;
2391 AliasClass pointee(const SSATmp* tmp) {
2392 return pointee(tmp, nullptr);
2395 //////////////////////////////////////////////////////////////////////
2397 MemEffects canonicalize(MemEffects me) {
2398 using R = MemEffects;
2399 return match<R>(
2401 [&] (GeneralEffects x) -> R {
2402 return GeneralEffects {
2403 canonicalize(x.loads),
2404 canonicalize(x.stores),
2405 canonicalize(x.moves),
2406 canonicalize(x.kills)
2409 [&] (PureLoad x) -> R {
2410 return PureLoad { canonicalize(x.src) };
2412 [&] (PureStore x) -> R {
2413 return PureStore { canonicalize(x.dst), x.value, x.dep };
2415 [&] (PureSpillFrame x) -> R {
2416 return PureSpillFrame {
2417 canonicalize(x.stk),
2418 canonicalize(x.ctx),
2419 canonicalize(x.callee)
2422 [&] (ExitEffects x) -> R {
2423 return ExitEffects { canonicalize(x.live), canonicalize(x.kills) };
2425 [&] (InlineEnterEffects x) -> R {
2426 return InlineEnterEffects {
2427 canonicalize(x.inlStack),
2428 canonicalize(x.inlFrame),
2429 canonicalize(x.actrec),
2432 [&] (InlineExitEffects x) -> R {
2433 return InlineExitEffects {
2434 canonicalize(x.inlStack),
2435 canonicalize(x.inlFrame),
2436 canonicalize(x.inlMeta)
2439 [&] (CallEffects x) -> R {
2440 return CallEffects {
2441 canonicalize(x.kills),
2442 canonicalize(x.stack),
2443 canonicalize(x.locals),
2444 canonicalize(x.callee)
2447 [&] (ReturnEffects x) -> R {
2448 return ReturnEffects { canonicalize(x.kills) };
2450 [&] (IrrelevantEffects x) -> R { return x; },
2451 [&] (UnknownEffects x) -> R { return x; }
2455 //////////////////////////////////////////////////////////////////////
2457 std::string show(MemEffects effects) {
2458 using folly::sformat;
2459 return match<std::string>(
2460 effects,
2461 [&] (GeneralEffects x) {
2462 return sformat("mlsmk({} ; {} ; {} ; {})",
2463 show(x.loads),
2464 show(x.stores),
2465 show(x.moves),
2466 show(x.kills)
2469 [&] (ExitEffects x) {
2470 return sformat("exit({} ; {})", show(x.live), show(x.kills));
2472 [&] (InlineEnterEffects x) {
2473 return sformat("inline_enter({} ; {} ; {})",
2474 show(x.inlStack),
2475 show(x.inlFrame),
2476 show(x.actrec)
2479 [&] (InlineExitEffects x) {
2480 return sformat("inline_exit({} ; {} ; {})",
2481 show(x.inlStack),
2482 show(x.inlFrame),
2483 show(x.inlMeta)
2486 [&] (CallEffects x) {
2487 return sformat("call({} ; {} ; {} ; {})",
2488 show(x.kills),
2489 show(x.stack),
2490 show(x.locals),
2491 show(x.callee)
2494 [&] (PureSpillFrame x) {
2495 return sformat("stFrame({} ; {} ; {})",
2496 show(x.stk),
2497 show(x.ctx),
2498 show(x.callee)
2501 [&] (PureLoad x) { return sformat("ld({})", show(x.src)); },
2502 [&] (PureStore x) { return sformat("st({})", show(x.dst)); },
2503 [&] (ReturnEffects x) { return sformat("return({})", show(x.kills)); },
2504 [&] (IrrelevantEffects) { return "IrrelevantEffects"; },
2505 [&] (UnknownEffects) { return "UnknownEffects"; }
2509 //////////////////////////////////////////////////////////////////////
2511 AliasClass inline_fp_frame(const IRInstruction* inst) {
2512 return AStack {
2513 inst->src(0),
2514 inst->extra<DefInlineFP>()->spOffset + int32_t{kNumActRecCells} - 1,
2515 int32_t{kNumActRecCells}
2519 //////////////////////////////////////////////////////////////////////