2 +----------------------------------------------------------------------+
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
{
36 jit::flat_set
<const IRInstruction
*>* visited_labels
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
49 always_assert(ptr
->isA(TBottom
));
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
)) {
70 auto const dsts
= sinst
->dsts();
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) {
78 visited_labels
= &label_set
.value();
80 visited_labels
->insert(sinst
);
83 sinst
->block()->forEachSrc(
84 dstIdx
, [&](const IRInstruction
* /*jmp*/, const SSATmp
* thePtr
) {
85 ret
= ret
| pointee(thePtr
, visited_labels
);
90 auto specific
= [&] () -> folly::Optional
<AliasClass
> {
91 if (typeNR
<= TBottom
) return AEmpty
;
93 if (typeNR
<= TMemToFrameGen
) {
94 if (sinst
->is(LdLocAddr
)) {
96 AFrame
{ sinst
->src(0), sinst
->extra
<LdLocAddr
>()->locId
}
102 if (typeNR
<= TMemToStkGen
) {
103 if (sinst
->is(LdStkAddr
)) {
105 AStack
{ sinst
->src(0), sinst
->extra
<LdStkAddr
>()->offset
, 1 }
111 if (typeNR
<= TMemToPropGen
) {
112 if (sinst
->is(LdPropAddr
, LdInitPropAddr
)) {
116 safe_cast
<uint32_t>(sinst
->extra
<ByteOffsetData
>()->offsetBytes
)
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
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() };
145 if (key
->hasConstVal(TStr
)) {
146 assertx(!base
->isA(TVec
));
147 return AElemS
{ base
, key
->strVal() };
152 if (typeNR
<= TMemToElemGen
) {
153 if (sinst
->is(LdPackedArrayDataElemAddr
)) return elem();
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
164 if (sinst
->is(ElemArrayU
, ElemVecU
, ElemDictU
, ElemKeysetU
)) {
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
));
176 sinst
->src(sinst
->numSrcs() - 1)->isA(TMIPropSPtr
| TNullptr
)
178 return sinst
->src(sinst
->numSrcs() - 2);
180 assertx(sinst
->srcs().back()->isA(TPtrToMISGen
));
181 return sinst
->srcs().back();
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
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
));
195 sinst
->src(sinst
->numSrcs() - 1)->isA(TMIPropSPtr
| TNullptr
)
197 return sinst
->src(sinst
->numSrcs() - 2);
199 assertx(sinst
->srcs().back()->isA(TPtrToMISGen
));
200 return sinst
->srcs().back();
203 return AElemAny
| pointee(src
, visited_labels
);
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
;
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
);
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
) {
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
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 +
281 AliasClass
actrec_func(SSATmp
* base
, IRSPRelOffset offset
) {
282 return AStack
{ base
, offset
+ int32_t{kActRecFuncCellOff
}, 1 };
285 InlineExitEffects
inline_exit_effects(SSATmp
* 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())};
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;
310 auto fpInst
= fp
->inst();
311 if (UNLIKELY(fpInst
->is(DefLabel
))) {
312 fpInst
= resolveFpDefLabel(fp
);
315 if (fpInst
->is(DefFP
)) return fpInst
->marker().func();
316 if (fpInst
->is(DefInlineFP
)) return fpInst
->extra
<DefInlineFP
>()->target
;
317 always_assert(false);
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
};
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
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
367 * For kills, locations on the eval stack below the re-entry depth should all
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
381 GeneralEffects
may_reenter(const IRInstruction
& inst
, GeneralEffects x
) {
382 auto const may_reenter_is_ok
=
383 (inst
.taken() && inst
.taken()->isCatch()) ||
394 MemoSetInstanceCache
,
397 MemoSetInstanceValue
);
400 "instruction {} claimed may_reenter, but it isn't allowed to say that",
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
416 auto const new_kills
= [&] {
417 if (inst
.marker().fp() == nullptr) return AEmpty
;
419 auto const killed_stack
= stack_below(
421 -inst
.marker().spOff() - 1
423 auto const kills_union
= x
.kills
.precise_union(killed_stack
);
424 return kills_union
? *kills_union
: killed_stack
;
427 return GeneralEffects
{
428 x
.loads
| AHeapAny
| backtrace_locals(inst
),
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
,
444 return GeneralEffects
{ loads
, stores
, AEmpty
, kill
};
447 GeneralEffects
may_load_store_move(AliasClass loads
,
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
,
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
;
474 locals
| AHeapAny
| iterMem
,
475 locals
| AHeapAny
| iterMem
,
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
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
;
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
];
508 stores
= stores
| AClsRefClsSlot
{ inst
.src(1), slot
.id
} |
509 AClsRefTSSlot
{ inst
.src(1), slot
.id
};
511 loads
= loads
| AClsRefClsSlot
{ inst
.src(1), slot
.id
} |
512 AClsRefTSSlot
{ inst
.src(1), slot
.id
};
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
;
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
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
);
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
;
555 assertx(inst
.is(PropDX
));
556 if (RuntimeOption::EvalPromoteEmptyObject
) loads
|= AMIStatePropS
;
557 stores
|= AMIStatePropS
;
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
)) {
580 return AMIStatePropS
;
583 return may_load_store_kill(
584 AHeapAny
| propSLoads
| all_pointees(inst
),
585 AHeapAny
| all_pointees(inst
),
590 //////////////////////////////////////////////////////////////////////
592 MemEffects
memory_effects_impl(const IRInstruction
& inst
) {
595 //////////////////////////////////////////////////////////////////////
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
604 stack_below(inst
.src(0), inst
.extra
<ReqBindJmp
>()->irSPOff
- 1)
609 stack_below(inst
.src(0), inst
.extra
<ReqRetranslate
>()->irSPOff
- 1)
611 case ReqRetranslateOpt
:
614 stack_below(inst
.src(0), inst
.extra
<ReqRetranslateOpt
>()->offset
- 1)
619 *stack_below(inst
.src(1),
620 inst
.extra
<JmpSwitchDest
>()->spOffBCFromIRSP
- 1).
621 precise_union(AMIStateAny
)
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).
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(
646 *AStackAny
.precise_union(AFrameAny
)->precise_union(AMIStateAny
)
649 // The suspend hooks can load anything (re-entering the VM), but can't write
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
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
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
));
696 auto const stack_kills
= stack_below(
698 inst
.extra
<EndCatch
>()->offset
- 1
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
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
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
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()}
737 AliasClass frame
= func
->numLocals()
738 ? AFrame
{inst
.dst(), AliasIdSet::IdRange(0, func
->numLocals())}
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
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(
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
)
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()
787 return may_load_store_kill(AEmpty
, AEmpty
, callee
);
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
795 return may_load_store(arStack
, arStack
);
799 return interp_one_effects(inst
);
803 stack_below(inst
.src(1), -inst
.marker().spOff() - 1) | AMIStateAny
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
:
817 case VerifyParamFailHard
:
818 return may_load_store(AUnknown
, AHeapAny
);
819 // VerifyParamFail might coerce the parameter to the desired type rather than
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
:
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
840 case VerifyRetFail
: {
841 auto func
= inst
.marker().func();
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
);
854 case VerifyPropFailHard
:
856 return may_load_store(AHeapAny
, AHeapAny
);
860 auto const extra
= inst
.extra
<CallUnpack
>();
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.
867 extra
->spOffset
+ extra
->numParams
+ kNumActRecCells
+
871 backtrace_locals(inst
),
873 actrec_func(inst
.src(0), extra
->spOffset
+ extra
->numParams
)
879 auto const extra
= inst
.extra
<ContEnter
>();
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
),
886 backtrace_locals(inst
),
887 // Callee. Stored inside the generator object, not used by ContEnter.
894 auto const extra
= inst
.extra
<Call
>();
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.
901 extra
->spOffset
+ extra
->numParams
+ kNumActRecCells
+
905 backtrace_locals(inst
),
907 actrec_func(inst
.src(0), extra
->spOffset
+ extra
->numParams
)
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
)) {
925 return may_load_store_kill(
932 // Resumable suspension takes everything from the frame and moves it into the
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();
945 ? AFrame
{ inst
.src(0), AliasIdSet::IdRange(0, nlocals
)}
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
;
954 return may_load_store_move(
961 // AGWH construction updates the AsyncGenerator object.
963 return may_load_store(AHeapAny
, AHeapAny
);
967 auto const extra
= inst
.extra
<CreateAAWH
>();
968 auto const frame
= AFrame
{
971 AliasIdSet::IdRange
{ extra
->first
, extra
->first
+ extra
->count
}
974 return may_reenter(inst
, may_load_store(frame
, AHeapAny
));
979 auto const extra
= inst
.extra
<CountWHNotDone
>();
980 auto const frame
= AFrame
{
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
);
1003 return UnknownEffects
{};
1005 //////////////////////////////////////////////////////////////////////
1006 // Iterator instructions
1011 return iter_effects(
1014 AFrame
{ inst
.src(1), inst
.extra
<IterData
>()->valId
}
1017 return iter_effects(
1020 AFrame
{ inst
.src(0), inst
.extra
<IterData
>()->valId
}
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
);
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
);
1040 return may_reenter(inst
, may_load_store(AHeapAny
, AHeapAny
));
1042 //////////////////////////////////////////////////////////////////////
1043 // Instructions that explicitly manipulate locals
1047 AFrame
{ inst
.src(0), inst
.extra
<StLoc
>()->locId
},
1053 auto const extra
= inst
.extra
<StLocRange
>();
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) };
1063 return PureLoad
{ AFrame
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
} };
1066 case LdLocPseudoMain
:
1067 // Note: LdLocPseudoMain is both a guard and a load, so it must not be a
1069 return may_load_store(
1070 AFrame
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
},
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
1084 AClsRefClsSlot
{ inst
.src(0), inst
.extra
<LdClsRefCls
>()->slot
}
1089 AClsRefTSSlot
{ inst
.src(0), inst
.extra
<LdClsRefTS
>()->slot
}
1094 AClsRefClsSlot
{ inst
.src(0), inst
.extra
<StClsRefCls
>()->slot
},
1100 AClsRefTSSlot
{ inst
.src(0), inst
.extra
<StClsRefTS
>()->slot
},
1105 return may_load_store_kill(
1107 AClsRefClsSlot
{ inst
.src(0), inst
.extra
<KillClsRefCls
>()->slot
}
1111 return may_load_store_kill(
1113 AClsRefTSSlot
{ inst
.src(0), inst
.extra
<KillClsRefTS
>()->slot
}
1116 //////////////////////////////////////////////////////////////////////
1117 // Pointer-based loads and stores
1120 return PureLoad
{ pointee(inst
.src(0)) };
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
1129 inst
.src(0)->type() <= TPtrToRMembCell
1136 inst
.src(0)->type() <= TPtrToRMembCell
1142 return PureLoad
{ AMIStateBase
};
1145 return PureStore
{ AMIStateBase
, inst
.src(0) };
1148 return PureStore
{ AMIStatePropS
, nullptr };
1150 case FinishMemberOp
:
1151 return may_load_store_kill(AEmpty
, AEmpty
, AMIStateAny
);
1155 auto const mem
= pointee(inst
.src(0));
1156 return may_load_store(mem
, mem
);
1159 return may_load_store(pointee(inst
.src(0)), AEmpty
);
1165 return may_load_store(pointee(inst
.src(0)), AEmpty
);
1167 case CheckRDSInitialized
:
1168 return may_load_store(
1169 ARds
{ inst
.extra
<CheckRDSInitialized
>()->handle
},
1172 case MarkRDSInitialized
:
1173 return may_load_store(
1175 ARds
{ inst
.extra
<MarkRDSInitialized
>()->handle
}
1179 return may_load_store(
1181 AHeapAny
| ARds
{ inst
.extra
<InitProps
>()->cls
->propHandle() }
1185 return may_load_store(
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
1198 return may_load_store(ARef
{ inst
.src(0) }, AEmpty
);
1200 return PureLoad
{ ARef
{ inst
.src(0) } };
1202 return PureStore
{ ARef
{ inst
.src(0) }, inst
.src(1) };
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
:
1220 AElemI
{ inst
.src(0), inst
.extra
<InitPackedLayoutArray
>()->index
},
1225 case LdPackedElem
: {
1226 auto const base
= inst
.src(0);
1227 auto const key
= inst
.src(1);
1229 key
->hasConstVal() ? AElemI
{ base
, key
->intVal() } : AElemIAny
1233 case InitPackedLayoutArrayLoop
:
1235 auto const extra
= inst
.extra
<InitPackedLayoutArrayLoop
>();
1236 auto const stack_in
= AStack
{
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
{
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
:
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
{
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
);
1274 auto const extra
= inst
.extra
<NewStructData
>();
1275 auto const stack_in
= AStack
{
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
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
1302 auto const extra
= inst
.extra
<MemoCacheStaticData
>();
1303 auto const loc
= [&] () -> AliasClass
{
1304 if (inst
.src(0)->isA(TFramePtr
)) {
1308 AliasIdSet::IdRange
{
1310 extra
->keys
.first
+ extra
->keys
.count
1315 assertx(inst
.src(0)->isA(TStkPtr
));
1316 assertx(extra
->stackOffset
);
1319 *extra
->stackOffset
,
1320 static_cast<int32_t>(extra
->keys
.count
)
1324 auto effects
= may_load_store(loc
, AEmpty
);
1325 if (inst
.op() == MemoSetStaticCache
|| inst
.op() == MemoSetLSBCache
) {
1326 effects
= may_reenter(inst
, 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
)) {
1344 AliasIdSet::IdRange
{
1346 extra
->keys
.first
+ extra
->keys
.count
1351 assertx(inst
.src(0)->isA(TStkPtr
));
1352 assertx(extra
->stackOffset
);
1355 *extra
->stackOffset
,
1356 static_cast<int32_t>(extra
->keys
.count
)
1359 auto effects
= may_load_store(loc
, AEmpty
);
1360 if (inst
.op() == MemoSetInstanceCache
) effects
= may_reenter(inst
, effects
);
1364 case MixedArrayGetK
:
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
)) {
1372 key
->hasConstVal() ? AElemI
{ base
, key
->intVal() } : AElemIAny
1375 if (key
->isA(TStr
)) {
1377 key
->hasConstVal() ? AElemS
{ base
, key
->strVal() } : AElemSAny
1380 return PureLoad
{ AElemAny
};
1383 case ElemMixedArrayK
:
1386 return IrrelevantEffects
{};
1389 auto const base
= inst
.src(0);
1390 return may_load_store(AElemI
{ base
, 0 }, AEmpty
);
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) },
1399 return may_load_store(AElemIAny
, AEmpty
);
1405 return may_load_store(AElemAny
, AEmpty
);
1409 return may_load_store(AEmpty
, AEmpty
);
1411 case ProfileMixedArrayOffset
:
1412 case CheckMixedArrayOffset
:
1414 case ProfileDictOffset
:
1415 case ProfileKeysetOffset
:
1416 case CheckDictOffset
:
1417 case CheckKeysetOffset
:
1418 return may_load_store(AHeapAny
, AEmpty
);
1422 return may_load_store(AElemAny
, AEmpty
);
1425 return may_load_store(AElemAny
| ARefAny
, AEmpty
);
1429 return may_load_store(AEmpty
, AEmpty
);
1435 case KeysetGetQuiet
:
1437 case KeysetEmptyElem
:
1440 case AKExistsKeyset
:
1441 return may_load_store(AElemAny
, AEmpty
);
1451 return may_load_store(AElemAny
, AEmpty
);
1455 return may_load_store(
1456 RuntimeOption::EvalHackArrDVArrs
? AElemAny
: AEmpty
,
1460 return may_load_store(AHeapAny
, AHeapAny
);
1462 //////////////////////////////////////////////////////////////////////
1463 // Member instructions
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.
1481 return may_load_store_kill(
1482 AHeapAny
| all_pointees(inst
),
1495 return minstr_final_with_prop_state(inst
);
1498 * SetRange behaves like a simpler version of SetElem.
1502 return may_load_store_kill(
1503 AHeapAny
| all_pointees(inst
),
1504 AHeapAny
| pointee(inst
.src(0)),
1508 case SetNewElemArray
:
1510 case SetNewElemKeyset
:
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
),
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).
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.
1552 return may_load_store(AHeapAny
, AEmpty
/* Note */);
1556 return may_reenter(inst
, may_load_store(AHeapAny
, AEmpty
/* Note */));
1558 case LdInitPropAddr
:
1559 return may_load_store(
1562 safe_cast
<uint32_t>(inst
.extra
<LdInitPropAddr
>()->offsetBytes
)
1567 return may_load_store(
1568 ARds
{ inst
.extra
<LdInitRDSAddr
>()->handle
},
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.
1580 case NewColFromArray
:
1582 case NewInstanceRaw
:
1586 case AllocPackedArray
:
1594 case Box
: // conditional allocation
1595 return IrrelevantEffects
{};
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.
1610 AStack
{ inst
.src(0), inst
.extra
<LdStk
>()->offset
, 1 }
1615 AStack
{ inst
.src(0), inst
.extra
<StStk
>()->offset
, 1 },
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
);
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
),
1637 return may_load_store(
1638 AStack
{ inst
.src(0), inst
.extra
<CheckStk
>()->offset
, 1 },
1642 // The following may re-enter, and also deal with a stack slot.
1645 auto const stk
= AStack
{
1646 inst
.src(0), inst
.extra
<CoerceStk
>()->offset
, 1
1648 return may_load_store(stk
, stk
);
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
);
1662 actrec_ctx(inst
.src(0), inst
.extra
<LdARCtx
>()->offset
)
1666 actrec_func(inst
.src(0), inst
.extra
<LdARFuncPtr
>()->offset
)
1670 return may_load_store(
1671 actrec(inst
.src(0), inst
.extra
<LdARIsDynamic
>()->offset
),
1675 case DbgAssertARFunc
:
1676 return may_load_store(
1677 actrec_func(inst
.src(0), inst
.extra
<DbgAssertARFunc
>()->offset
),
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
{
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
1717 case EqArrayDataPtr
:
1726 case HintMBaseInner
:
1732 case LdPackedArrayDataElemAddr
:
1765 case CheckFuncMMNonMagic
:
1766 case CheckSmashableClass
:
1791 case LdMIPropStateAddr
:
1796 case LdSubClsCnsClsName
:
1798 case CheckSubClsCns
:
1799 case LdClsCnsVecLen
:
1800 case ProfileSubClsCns
:
1802 case FuncSupportsAsyncEagerReturn
:
1803 case IsFunReifiedGenericsMatched
:
1804 case IsFuncDynCallable
:
1805 case IsClsDynConstructible
:
1809 case LdSmashableFunc
:
1813 case ExitPlaceholder
:
1817 case InstanceOfIfaceVtable
:
1818 case CheckARMagicFlag
:
1819 case LdARNumArgsAndFlags
:
1820 case StARNumArgsAndFlags
:
1821 case LdARReifiedGenerics
:
1822 case KillARReifiedGenerics
:
1829 case ProfileInstanceCheck
:
1831 case LookupSPropSlot
:
1833 case MangleReifiedName
:
1834 return IrrelevantEffects
{};
1840 safe_cast
<uint32_t>(inst
.extra
<StClosureArg
>()->offsetBytes
)
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.
1869 case CheckSurpriseFlags
:
1873 case StArResumeAddr
:
1875 case ZeroErrorLevel
:
1876 case RestoreErrorLevel
:
1880 case ContArUpdateIdx
:
1883 case IncProfCounter
:
1886 case ContStartedCheck
:
1890 case CountArrayFast
:
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
:
1910 case DbgAssertRefCount
:
1933 case LdSSwitchDestFast
:
1938 case ConvStrToArr
: // decrefs src, but src is a string
1942 case EagerSyncVMRegs
:
1944 case LdUnwinderValue
:
1955 case LdContResumeAddr
:
1956 case LdClsCachedSafe
:
1958 case UnwindCheckSideExit
:
1962 case LdClsMethodCacheCls
:
1963 case LdClsMethodCacheFunc
:
1964 case LdClsMethodFCacheFunc
:
1966 case LdClsTypeCnsClsName
:
1967 case FwdCtxStaticCall
:
1968 case ProfileArrayKind
:
1969 case ProfileSwitchDest
:
1970 case LdFuncNumParams
:
1979 case LdSwitchDblIndex
:
1980 case LdSwitchStrIndex
:
1989 case StrictlyIntegerConv
:
1991 return may_load_store(AEmpty
, AEmpty
);
1993 // Some that touch memory we might care about later, but currently don't:
1996 case ConvCellToBool
:
1998 case CountCollection
:
2000 case CheckPackedArrayDataBounds
:
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).
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
);
2037 return may_load_store(AHeapAny
, AHeapAny
);
2039 case GetMemoKeyScalar
:
2040 return IrrelevantEffects
{};
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
);
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
2069 case RaiseArrayIndexNotice
:
2070 case RaiseArrayKeyNotice
:
2071 case RaiseUninitLoc
:
2072 case RaiseUndefProp
:
2073 case RaiseMissingArg
:
2074 case RaiseTooManyArg
:
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
:
2091 case Count
: // re-enters on CountableClass
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
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
2138 case LookupFuncCached
: // autoload
2139 case StringGet
: // raise_notice
2140 case OrdStrIdx
: // raise_notice
2141 case ArrayAdd
: // decrefs source
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
2167 case LdSSwitchDestSlow
:
2170 case CoerceStrToInt
:
2171 case CoerceStrToDbl
:
2172 case CoerceCellToDbl
:
2173 case CoerceCellToInt
:
2174 case CoerceCellToBool
:
2182 case ConvShapeToDict
:
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
:
2197 case SetOpCellVerify
:
2199 case PropTypeRedefineCheck
: // Can raise and autoload
2200 case HandleRequestSurprise
:
2201 return may_load_store(AHeapAny
, AHeapAny
);
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
:
2218 case ConvShapeToArr
:
2219 case ConvKeysetToArr
:
2220 return may_load_store(AElemAny
, AEmpty
);
2223 case ConvArrToNonDVArr
:
2225 case ConvShapeToVec
:
2226 case ConvKeysetToVec
:
2228 case ConvKeysetToDict
:
2231 case ConvDictToVArr
:
2232 case ConvShapeToVArr
:
2233 case ConvKeysetToVArr
:
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
2261 return GeneralEffects
{
2262 AEmpty
, AEmpty
, AEmpty
,
2263 AStack
{ inst
.src(0), inst
.extra
<DbgTrashStk
>()->offset
, 1 }
2266 return GeneralEffects
{
2267 AEmpty
, AEmpty
, AEmpty
,
2268 actrec(inst
.src(0), inst
.extra
<DbgTrashFrame
>()->offset
)
2271 return GeneralEffects
{
2272 AEmpty
, AEmpty
, AEmpty
,
2273 pointee(inst
.src(0))
2275 case DbgTrashRetVal
:
2276 return IrrelevantEffects
{};
2278 //////////////////////////////////////////////////////////////////////
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
) {
2294 fp
->type() <= TFramePtr
,
2295 "Non frame pointer in memory effects"
2299 auto check_obj
= [&] (SSATmp
* obj
) {
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
);
2315 [&] (GeneralEffects x
) {
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
);
2355 always_assert(x
.callee
<= x
.stack
); },
2356 [&] (InlineEnterEffects x
){ check(x
.inlStack
);
2359 [&] (InlineExitEffects x
){ check(x
.inlStack
);
2361 check(x
.inlMeta
); },
2362 [&] (ReturnEffects x
) { check(x
.kills
); }
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(
2383 SuspendHookCreateCont
,
2387 if (is_special
|| !inst
.mayRaiseError()) return inner
;
2392 "Instruction {} has effects {}, but has been marked as MayRaiseError "
2393 "and must use a UnknownEffects, GeneralEffects, or CallEffects type.",
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
>(
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(); }
2417 assertx(check_effects(inst
, ret
));
2421 AliasClass
pointee(const SSATmp
* tmp
) {
2422 return pointee(tmp
, nullptr);
2425 //////////////////////////////////////////////////////////////////////
2427 MemEffects
canonicalize(MemEffects me
) {
2428 using R
= MemEffects
;
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
>(
2491 [&] (GeneralEffects x
) {
2492 return sformat("mlsmk({} ; {} ; {} ; {})",
2499 [&] (ExitEffects x
) {
2500 return sformat("exit({} ; {})", show(x
.live
), show(x
.kills
));
2502 [&] (InlineEnterEffects x
) {
2503 return sformat("inline_enter({} ; {} ; {})",
2509 [&] (InlineExitEffects x
) {
2510 return sformat("inline_exit({} ; {} ; {})",
2516 [&] (CallEffects x
) {
2517 return sformat("call({} ; {} ; {} ; {})",
2524 [&] (PureSpillFrame x
) {
2525 return sformat("stFrame({} ; {} ; {})",
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
) {
2544 inst
->extra
<DefInlineFP
>()->spOffset
+ int32_t{kNumActRecCells
} - 1,
2545 int32_t{kNumActRecCells
}
2549 //////////////////////////////////////////////////////////////////////