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"
27 #include "hphp/runtime/vm/jit/type-array-elem.h"
29 #include <folly/Optional.h>
31 namespace HPHP
{ namespace jit
{
35 const StaticString
s_GLOBALS("GLOBALS");
37 uint32_t iterId(const IRInstruction
& inst
) {
38 return inst
.extra
<IterId
>()->iterId
;
43 jit::flat_set
<const IRInstruction
*>* visited_labels
45 auto const type
= ptr
->type();
46 always_assert(type
<= TMemToCell
);
47 auto const canonPtr
= canonical(ptr
);
48 if (!canonPtr
->isA(TMemToCell
)) {
49 // This can happen when ptr is TBottom from a passthrough instruction with
50 // a src that isn't TBottom. The most common cause of this is something
51 // like "t5:Bottom = CheckType<Str> t2:Int". It means ptr isn't really a
52 // pointer, so return AEmpty to avoid unnecessarily pessimizing any
54 always_assert(ptr
->isA(TBottom
));
58 auto const sinst
= canonPtr
->inst();
60 if (sinst
->is(LdRDSAddr
, LdInitRDSAddr
)) {
61 return ARds
{ sinst
->extra
<RDSHandleData
>()->handle
};
64 // For phis, union all incoming values, taking care to not recurse infinitely
65 // in the presence of loops.
66 if (sinst
->is(DefLabel
)) {
67 if (visited_labels
&& visited_labels
->count(sinst
)) {
71 auto const dsts
= sinst
->dsts();
73 std::find(dsts
.begin(), dsts
.end(), canonPtr
) - dsts
.begin();
74 always_assert(dstIdx
>= 0 && dstIdx
< sinst
->numDsts());
76 folly::Optional
<jit::flat_set
<const IRInstruction
*>> label_set
;
77 if (visited_labels
== nullptr) {
79 visited_labels
= &label_set
.value();
81 visited_labels
->insert(sinst
);
84 sinst
->block()->forEachSrc(
85 dstIdx
, [&](const IRInstruction
* /*jmp*/, const SSATmp
* thePtr
) {
86 ret
= ret
| pointee(thePtr
, visited_labels
);
91 auto specific
= [&] () -> folly::Optional
<AliasClass
> {
92 if (type
<= TBottom
) return AEmpty
;
94 if (type
<= TMemToFrameCell
) {
95 if (sinst
->is(LdLocAddr
)) {
97 ALocal
{ sinst
->src(0), sinst
->extra
<LdLocAddr
>()->locId
}
103 if (type
<= TMemToStkCell
) {
104 if (sinst
->is(LdStkAddr
)) {
106 AStack
{ sinst
->src(0), sinst
->extra
<LdStkAddr
>()->offset
, 1 }
112 if (type
<= TMemToPropCell
) {
113 if (sinst
->is(LdPropAddr
, LdInitPropAddr
)) {
117 safe_cast
<uint16_t>(sinst
->extra
<IndexData
>()->index
)
124 if (type
<= TMemToMISCell
) {
125 if (sinst
->is(LdMIStateAddr
)) {
126 return mis_from_offset(sinst
->src(0)->intVal());
128 if (ptr
->hasConstVal() && ptr
->rawVal() == 0) {
129 // nullptr tvRef pointer, representing an instruction that doesn't use
133 return AMIStateTempBase
;
136 auto elem
= [&] () -> AliasClass
{
137 auto base
= sinst
->src(0);
138 auto key
= sinst
->src(1);
140 always_assert(base
->isA(TArrLike
));
142 if (key
->isA(TInt
)) {
143 if (key
->hasConstVal()) return AElemI
{ base
, key
->intVal() };
146 if (key
->hasConstVal(TStr
)) {
147 assertx(!base
->isA(TVec
));
148 return AElemS
{ base
, key
->strVal() };
153 if (type
<= TMemToElemCell
) {
154 if (sinst
->is(LdPackedArrayDataElemAddr
)) return elem();
158 // The result of ElemArray{,W,U} is either the address of an array element,
159 // or &immutable_null_base.
160 if (type
<= TMemToMembCell
) {
161 // Takes a PtrToCell as its first operand, so we can't easily grab an
163 if (sinst
->is(ElemArrayU
, ElemVecU
, ElemDictU
, ElemKeysetU
)) {
167 // These instructions can only get at tvRef when given it as a
168 // src. Otherwise they can only return pointers to properties or
169 // &immutable_null_base.
170 if (sinst
->is(PropX
, PropDX
, PropQ
)) {
171 assertx(sinst
->srcs().back()->isA(TMemToMISCell
));
172 auto const src
= sinst
->srcs().back();
173 return APropAny
| pointee(src
, visited_labels
);
176 // Like the Prop* instructions, but for array elements. These ops could
177 // pointers to collection elements, which we don't have AliasClasses for.
178 if (sinst
->is(ElemDX
, ElemUX
)) {
188 if (specific
) return *specific
;
191 * None of the above worked, so try to make the smallest union we can based
192 * on the pointer type.
195 if (type
.maybe(TMemToStkCell
)) ret
= ret
| AStackAny
;
196 if (type
.maybe(TMemToFrameCell
)) ret
= ret
| ALocalAny
;
197 if (type
.maybe(TMemToPropCell
)) ret
= ret
| APropAny
;
198 if (type
.maybe(TMemToElemCell
)) ret
= ret
| AElemAny
;
199 if (type
.maybe(TMemToMISCell
)) ret
= ret
| AMIStateTempBase
;
200 if (type
.maybe(TMemToClsInitCell
)) ret
= ret
| AHeapAny
;
201 if (type
.maybe(TMemToClsCnsCell
)) ret
= ret
| AHeapAny
;
202 if (type
.maybe(TMemToSPropCell
)) ret
= ret
| ARdsAny
;
206 //////////////////////////////////////////////////////////////////////
208 AliasClass
all_pointees(folly::Range
<SSATmp
**> srcs
) {
209 auto ret
= AliasClass
{AEmpty
};
210 for (auto const& src
: srcs
) {
211 if (src
->isA(TMemToCell
)) {
212 ret
= ret
| pointee(src
);
218 // Return an AliasClass containing all locations pointed to by any MemToCell
219 // sources to an instruction.
220 AliasClass
all_pointees(const IRInstruction
& inst
) {
221 return all_pointees(inst
.srcs());
224 // Return an AliasClass representing a range of the eval stack that contains
225 // everything below a logical depth.
226 template<typename Off
>
227 AliasClass
stack_below(SSATmp
* base
, Off offset
) {
228 return AStack
{ base
, offset
, std::numeric_limits
<int32_t>::max() };
231 //////////////////////////////////////////////////////////////////////
233 // Return an AliasClass representing an entire ActRec at base + offset.
234 AliasClass
actrec(SSATmp
* base
, IRSPRelOffset offset
) {
237 // The offset is the bottom of where the ActRec is stored, but AliasClass
238 // needs an offset to the highest cell.
239 offset
+ int32_t{kNumActRecCells
} - 1,
240 int32_t{kNumActRecCells
}
245 * AliasClass that can be used to represent effects on liveFrame().
247 AliasClass
livefp(SSATmp
* fp
) {
248 return AFBasePtr
| AActRec
{ fp
};
251 AliasClass
livefp(const IRInstruction
& inst
) {
252 return livefp(inst
.marker().fp());
255 //////////////////////////////////////////////////////////////////////
257 // Determine an AliasClass representing any locals in the instruction's frame
258 // which might be accessed via debug_backtrace().
260 const Func
* func_from_fp(SSATmp
* fp
) {
261 if (!fp
) return nullptr;
262 auto fpInst
= fp
->inst();
263 if (fpInst
->is(DefFP
)) return fpInst
->marker().func();
264 if (fpInst
->is(DefFuncEntryFP
)) return fpInst
->extra
<DefFuncEntryFP
>()->func
;
265 if (fpInst
->is(BeginInlining
)) return fpInst
->extra
<BeginInlining
>()->func
;
266 always_assert(false);
269 bool any_frame_has_metadata(SSATmp
* fp
) {
271 auto const func
= func_from_fp(fp
);
272 if (!func
|| func
->lookupVarId(s_86metadata
.get()) != kInvalidId
) {
275 fp
= fp
->inst()->is(BeginInlining
) ? fp
->inst()->src(1) : nullptr;
280 AliasClass
backtrace_locals(const IRInstruction
& inst
) {
281 auto eachFunc
= [&] (auto fn
) {
283 for (auto fp
= inst
.marker().fp(); fp
; ) {
284 ac
|= fn(func_from_fp(fp
), fp
);
285 fp
= fp
->inst()->is(BeginInlining
) ? fp
->inst()->src(1) : nullptr;
290 auto const add86meta
= [&] (const Func
* func
, SSATmp
* fp
) -> AliasClass
{
291 // The 86metadata variable can also exist in a VarEnv, but accessing that is
292 // considered a heap effect, so we can ignore it.
293 auto const local
= func
->lookupVarId(s_86metadata
.get());
294 if (local
== kInvalidId
) return AEmpty
;
295 return ALocal
{ fp
, (uint32_t)local
};
298 // Either there's no func or no frame-pointer. Either way, be conservative and
299 // assume anything can be read. This can happen in test code, for instance.
300 if (!inst
.marker().fp()) return ALocalAny
;
302 if (!RuntimeOption::EnableArgsInBacktraces
) return eachFunc(add86meta
);
304 return eachFunc([&] (const Func
* func
, SSATmp
* fp
) {
306 auto const numParams
= func
->numParams();
308 if (func
->hasReifiedGenerics()) {
309 // First non param local contains reified generics
310 AliasIdSet reifiedgenerics
{ AliasIdSet::IdRange
{numParams
, numParams
+ 1} };
311 ac
|= ALocal
{ fp
, reifiedgenerics
};
314 if (func
->cls() && func
->cls()->hasReifiedGenerics()) {
315 // There is no way to access the SSATmp for ObjectData of `this` here,
316 // so be very pessimistic
320 if (!numParams
) return add86meta(func
, fp
) | ac
;
322 AliasIdSet params
{ AliasIdSet::IdRange
{0, numParams
} };
323 return add86meta(func
, fp
) | ac
| ALocal
{ fp
, params
};
327 /////////////////////////////////////////////////////////////////////
330 * Modify a GeneralEffects to take potential VM re-entry into account. This
331 * affects may-load, may-store, and kills information for the instruction. The
332 * GeneralEffects should already contain AHeapAny in both loads and stores if
333 * it affects those locations for reasons other than re-entry, but does not
334 * need to if it doesn't.
336 * For loads, we need to take into account any locals potentially accessed by
339 * For kills, locations on the eval stack below the re-entry depth should all
342 GeneralEffects
may_reenter(const IRInstruction
& inst
, GeneralEffects x
) {
343 auto const may_reenter_is_ok
=
344 inst
.taken() && inst
.taken()->isCatch();
347 "instruction {} claimed may_reenter, but it isn't allowed to say that",
352 * We want to union `killed_stack' into whatever else the instruction already
353 * said it must kill, but if we end up with an unrepresentable AliasClass we
354 * can't return a set that's too big (the `kills' set is unlike the other
355 * AliasClasses in GeneralEffects in that means it kills /everything/ in the
356 * set, since it's must-information).
358 * If we can't represent the union, just take the stack, in part because we
359 * have some debugging asserts about this right now---but also nothing
360 * actually uses may_reenter with a non-AEmpty kills at the time of this
363 auto const new_kills
= [&] {
364 if (inst
.marker().fp() == nullptr) return AEmpty
;
366 auto const killed_stack
= stack_below(
368 -inst
.marker().spOff() - 1
370 auto const kills_union
= x
.kills
.precise_union(killed_stack
);
371 return kills_union
? *kills_union
: killed_stack
;
374 return GeneralEffects
{
375 x
.loads
| AHeapAny
| backtrace_locals(inst
),
382 //////////////////////////////////////////////////////////////////////
384 GeneralEffects
may_load_store(AliasClass loads
, AliasClass stores
) {
385 return GeneralEffects
{ loads
, stores
, AEmpty
, AEmpty
};
388 GeneralEffects
may_load_store_kill(AliasClass loads
,
391 return GeneralEffects
{ loads
, stores
, AEmpty
, kill
};
394 GeneralEffects
may_load_store_move(AliasClass loads
,
397 assertx(move
<= loads
);
398 return GeneralEffects
{ loads
, stores
, move
, AEmpty
};
401 //////////////////////////////////////////////////////////////////////
404 * Helper for iterator instructions. They all affect some locals, but are
405 * otherwise the same. Value iters touch one local; key-value iters touch two.
407 GeneralEffects
iter_effects(const IRInstruction
& inst
,
411 AliasClass
{ aiter_all(fp
, inst
.extra
<IterData
>()->args
.iterId
) };
412 return may_load_store_kill(
413 iters
| locals
| AHeapAny
,
414 iters
| locals
| AHeapAny
,
420 * Construct effects for InterpOne, using the information in its extra data.
422 * We always consider an InterpOne as potentially doing anything to the heap,
423 * potentially re-entering, potentially raising warnings in the current frame,
424 * potentially reading any locals, and potentially reading/writing any stack
425 * location that isn't below the bottom of the stack.
427 * The extra data for the InterpOne comes with some additional information
428 * about which local(s) it may modify, which is all we try to be more precise
431 GeneralEffects
interp_one_effects(const IRInstruction
& inst
) {
432 auto const extra
= inst
.extra
<InterpOne
>();
433 auto loads
= AHeapAny
| AStackAny
| ALocalAny
| ARdsAny
| livefp(inst
);
434 auto stores
= AHeapAny
| AStackAny
| ARdsAny
;
435 if (extra
->smashesAllLocals
) {
436 stores
= stores
| ALocalAny
;
438 for (auto i
= uint32_t{0}; i
< extra
->nChangedLocals
; ++i
) {
439 stores
= stores
| ALocal
{ inst
.src(1), extra
->changedLocals
[i
].id
};
444 if (isMemberBaseOp(extra
->opcode
)) {
445 stores
= stores
| AMIStateAny
;
446 kills
= kills
| AMIStateAny
;
447 } else if (isMemberDimOp(extra
->opcode
) || isMemberFinalOp(extra
->opcode
)) {
448 stores
= stores
| AMIStateAny
;
449 loads
= loads
| AMIStateAny
;
451 kills
= kills
| AMIStateAny
;
454 return may_load_store_kill(loads
, stores
, kills
);
457 ////////////////////////////////////////////////////////////////////////////////
460 * Construct effects for member instructions that take &tvRef as their last
463 * These instructions never load tvRef, but they might store to it.
465 MemEffects
minstr_with_tvref(const IRInstruction
& inst
) {
466 auto const srcs
= inst
.srcs();
467 assertx(srcs
.back()->isA(TMemToMISCell
));
468 auto const loads
= AHeapAny
| all_pointees(srcs
.subpiece(0, srcs
.size() - 1));
469 auto const stores
= AHeapAny
| all_pointees(inst
);
470 return may_load_store(loads
, stores
);
473 //////////////////////////////////////////////////////////////////////
475 MemEffects
memory_effects_impl(const IRInstruction
& inst
) {
478 //////////////////////////////////////////////////////////////////////
481 // These exits don't leave the current php function, and could head to code
482 // that could read or write anything as far as we know (including frame
487 stack_below(inst
.src(0), inst
.extra
<ReqBindJmp
>()->irSPOff
- 1)
492 stack_below(inst
.src(0), inst
.extra
<ReqRetranslate
>()->irSPOff
- 1)
494 case ReqRetranslateOpt
:
497 stack_below(inst
.src(0), inst
.extra
<ReqRetranslateOpt
>()->offset
- 1)
502 *stack_below(inst
.src(1),
503 inst
.extra
<JmpSwitchDest
>()->spOffBCFromIRSP
- 1).
504 precise_union(AMIStateAny
)
509 *stack_below(inst
.src(1),
510 inst
.extra
<JmpSSwitchDest
>()->offset
- 1).
511 precise_union(AMIStateAny
)
514 //////////////////////////////////////////////////////////////////////
515 // Unusual instructions
518 * The ReturnHook sets up the ActRec so the unwinder knows everything is
519 * already released (i.e. it calls ar->setLocalsDecRefd()).
521 * The eval stack is also dead at this point (the return value is passed to
522 * ReturnHook as src(1), and the ReturnHook may not access the stack).
525 return may_load_store_kill(
526 AHeapAny
| AActRec
{inst
.src(0)}, AHeapAny
,
527 *AStackAny
.precise_union(ALocalAny
)->precise_union(AMIStateAny
)
530 // The suspend hooks can load anything (re-entering the VM), but can't write
532 case SuspendHookAwaitEF
:
533 case SuspendHookAwaitEG
:
534 case SuspendHookAwaitR
:
535 case SuspendHookCreateCont
:
536 case SuspendHookYield
:
537 // TODO: may-load here probably doesn't need to include ALocalAny normally.
538 return may_load_store_kill(AUnknown
, AHeapAny
, AMIStateAny
);
541 * If we're returning from a function, it's ReturnEffects. The RetCtrl
542 * opcode also suspends resumables, which we model as having any possible
546 if (inst
.extra
<RetCtrl
>()->suspendingResumed
) {
547 // Suspending can go anywhere, and doesn't even kill locals.
548 return UnknownEffects
{};
550 return ReturnEffects
{
551 AStackAny
| ALocalAny
| AMIStateAny
| AFBasePtr
555 case AsyncFuncRetSlow
:
556 return ReturnEffects
{ AStackAny
| AMIStateAny
| livefp(inst
.src(1)) };
558 case AsyncSwitchFast
:
559 // Suspending can go anywhere, and doesn't even kill locals.
560 return UnknownEffects
{};
562 case GenericRetDecRefs
:
564 * The may-store information here is AUnknown: even though we know it
565 * doesn't really "store" to the frame locals, the values that used to be
566 * there are no longer available because they are DecRef'd, which we are
567 * required to report as may-store information to make it visible to
568 * reference count optimizations. It's conceptually the same as if it was
569 * storing an Uninit over each of the locals, but the stores of uninits
570 * would be dead so we're not actually doing that.
572 return may_load_store_kill(AUnknown
, AUnknown
, AMIStateAny
);
575 auto const stack_kills
= stack_below(
577 inst
.extra
<EndCatch
>()->offset
- 1
581 stack_kills
| AMIStateTempBase
| AMIStateBase
585 case EnterTCUnwind
: {
586 auto const stack_kills
= stack_below(
588 -inst
.marker().spOff() - 1
592 stack_kills
| AMIStateTempBase
| AMIStateBase
597 * BeginInlining must always be the first instruction in the inlined call. It
598 * defines a new FP for the callee but does not perform any stores or
599 * otherwise initialize the FP.
601 case BeginInlining
: {
603 * SP relative offset of the firstin the inlined call.
605 auto inlineStackOff
=
606 inst
.extra
<BeginInlining
>()->spOffset
+ kNumActRecCells
- 1;
607 return may_load_store_kill(
611 * This prevents stack slots from the caller from being sunk into the
612 * callee. Note that some of these stack slots overlap with the frame
613 * locals of the callee-- those slots are inacessible in the inlined
614 * call as frame and stack locations may not alias.
616 stack_below(inst
.src(0), inlineStackOff
)
621 assertx(inst
.src(0)->inst()->is(BeginInlining
));
622 auto const fp
= inst
.src(0);
623 auto const callee
= inst
.src(0)->inst()->extra
<BeginInlining
>()->func
;
624 const AliasClass ar
= AActRec
{ inst
.src(0) };
625 auto const locals
= [&] () -> AliasClass
{
626 if (!callee
->numLocals()) return AEmpty
;
627 return ALocal
{fp
, AliasIdSet::IdRange(0, callee
->numLocals())};
630 // NB: It's okay if the AliasIdSet for locals cannot be precise. We want to
631 // kill *every* local in the frame so there's nothing else that can
632 // accidentally be included in the set.
633 return may_load_store_kill(AEmpty
, AEmpty
, ar
| locals
| AMIStateAny
);
637 return PureInlineCall
{
641 // Right now when we "publish" a frame by storing it in rvmfp() we
642 // implicitly depend on the AFFunc and AFMeta bits being stored. In the
643 // future we may want to track this explicitly.
645 // We also need to ensure that all of our parent frames have this stored
646 // this information. To achieve this we also register a load on AFBasePtr,
647 // forcing them to also be published. Notice that we doin't actually
648 // depend on this load to properly initialize m_sfp or rvmfp().
649 AliasClass(AFFunc
{ inst
.src(0) }) | AFMeta
{ inst
.src(0) } | AFBasePtr
653 // Unlike InlineCall we don't need to explicitly require the frame be
654 // published. Unlike InlineCall, however, it is not safe to "move" an
655 // InlineReturn, it may only be killed once it has been made redundant by
656 // the removal of its associated InlineCall.
657 return PureInlineReturn
{ AFBasePtr
, inst
.src(0), inst
.src(1) };
660 auto const spOffset
= inst
.extra
<SyncReturnBC
>()->spOffset
;
661 auto const arStack
= actrec(inst
.src(0), spOffset
);
662 return may_load_store(AEmpty
, arStack
);
666 return interp_one_effects(inst
);
670 stack_below(inst
.src(1), -inst
.marker().spOff() - 1) | AMIStateAny
674 return UnknownEffects
{};
676 // NB: on the failure path, these C++ helpers do a fixup and read frame
677 // locals before they throw. They can also invoke the user error handler and
678 // go do whatever they want to non-frame locations.
680 // TODO(#5372569): if we combine dv inits into the same regions we could
681 // possibly avoid storing KindOfUninits if we modify this.
682 case VerifyParamCallable
:
684 case VerifyParamFailHard
:
685 case VerifyParamRecDesc
:
686 return may_load_store(AUnknown
, AHeapAny
);
687 // VerifyParamFail might coerce the parameter to the desired type rather than
689 case VerifyParamFail
: {
690 auto const extra
= inst
.extra
<ParamWithTCData
>();
691 assertx(extra
->paramId
>= 0);
694 ALocal
{inst
.marker().fp(), safe_cast
<uint32_t>(extra
->paramId
)};
695 return may_load_store(AUnknown
, stores
);
697 case VerifyReifiedLocalType
: {
698 auto const extra
= inst
.extra
<ParamData
>();
699 assertx(extra
->paramId
>= 0);
702 ALocal
{inst
.marker().fp(), safe_cast
<uint32_t>(extra
->paramId
)};
703 return may_load_store(AUnknown
, stores
);
705 // However the following ones can't read locals from our frame on the way
706 // out, except as a side effect of raising a warning.
707 case VerifyRetCallable
:
709 case VerifyReifiedReturnType
:
710 case VerifyRetRecDesc
:
711 return may_load_store(AHeapAny
| livefp(inst
), AHeapAny
);
714 case VerifyRetFailHard
:
715 return may_load_store(AHeapAny
| AStackAny
| livefp(inst
), AHeapAny
);
719 case VerifyPropFailHard
:
722 case VerifyPropCoerce
:
723 case VerifyPropCoerceAll
:
724 case VerifyPropRecDesc
:
725 return may_load_store(AHeapAny
| livefp(inst
), AHeapAny
);
729 auto const extra
= inst
.extra
<CallUnpack
>();
731 // Kills. Everything on the stack below the incoming parameters.
732 stack_below(inst
.src(0), extra
->spOffset
- 1) | AMIStateAny
,
734 extra
->numInputs() == 0 ? AEmpty
: AStack
{
736 extra
->spOffset
+ extra
->numInputs() - 1,
737 static_cast<int32_t>(extra
->numInputs())
740 actrec(inst
.src(0), extra
->spOffset
+ extra
->numInputs()),
742 extra
->numOut
== 0 ? AEmpty
: AStack
{
744 extra
->spOffset
+ extra
->numInputs() + kNumActRecCells
+
746 static_cast<int32_t>(extra
->numOut
)
749 backtrace_locals(inst
) | livefp(inst
.src(1))
755 auto const extra
= inst
.extra
<ContEnter
>();
757 // Kills. Everything on the stack.
758 stack_below(inst
.src(0), extra
->spOffset
) | AMIStateAny
,
759 // No inputs. The value being sent is passed explicitly.
761 // ActRec. It is on the heap and we already implicitly assume that
762 // CallEffects can perform arbitrary heap operations.
767 backtrace_locals(inst
) | livefp(inst
.src(1))
773 // If any frames in the inlined stack have metadata we need to materialize
774 // all of the frames so that debug_backtrace() can find it.
775 AliasClass ar
= any_frame_has_metadata(inst
.src(1))
776 ? livefp(inst
.src(1))
777 : AliasClass(AActRec
{inst
.src(1)});
778 auto const extra
= inst
.extra
<Call
>();
780 // Kills. Everything on the stack below the incoming parameters.
781 stack_below(inst
.src(0), extra
->spOffset
- 1) | AMIStateAny
,
783 extra
->numInputs() == 0 ? AEmpty
: AStack
{
785 extra
->spOffset
+ extra
->numInputs() - 1,
786 static_cast<int32_t>(extra
->numInputs())
789 actrec(inst
.src(0), extra
->spOffset
+ extra
->numInputs()),
791 extra
->numOut
== 0 ? AEmpty
: AStack
{
793 extra
->spOffset
+ extra
->numInputs() + kNumActRecCells
+
795 static_cast<int32_t>(extra
->numOut
)
797 // Locals. We intentionally leave off a dependency on AFBasePtr to allow
798 // store-elim to elide the new frame.
799 backtrace_locals(inst
) | ar
805 AliasClass out_stk
= AEmpty
;
806 auto const callee
= inst
.extra
<CallBuiltin
>()->callee
;
807 auto const stk
= [&] () -> AliasClass
{
808 AliasClass ret
= AEmpty
;
809 for (auto i
= uint32_t{2}; i
< inst
.numSrcs(); ++i
) {
810 if (inst
.src(i
)->type() <= TPtrToCell
) {
811 auto const cls
= pointee(inst
.src(i
));
812 if (cls
.maybe(AStackAny
)) {
814 auto const paramOff
= callee
->isMethod() ? 3 : 2;
815 if (i
>= paramOff
&& callee
->isInOut(i
- paramOff
)) {
816 out_stk
= out_stk
| cls
;
823 if (callee
->isFoldable()) {
824 return may_load_store_kill(
825 stk
| AHeapAny
, out_stk
| AHeapAny
, AMIStateAny
828 return may_load_store_kill(
829 stk
| AHeapAny
| ARdsAny
,
830 out_stk
| AHeapAny
| ARdsAny
,
836 // Resumable suspension takes everything from the frame and moves it into the
841 case CreateAFWHNoVV
: {
842 auto const fp
= canonical(inst
.src(0));
843 auto fpInst
= fp
->inst();
844 auto const frame
= [&] () -> AliasClass
{
845 if (fpInst
->is(DefFP
, DefFuncEntryFP
)) return ALocalAny
;
846 assertx(fpInst
->is(BeginInlining
));
847 auto const nlocals
= fpInst
->extra
<BeginInlining
>()->func
->numLocals();
849 ? ALocal
{ fp
, AliasIdSet::IdRange(0, nlocals
)}
852 return may_load_store_move(
853 frame
| AActRec
{ fp
},
859 // AGWH construction updates the AsyncGenerator object.
861 return may_load_store(AHeapAny
| AActRec
{ inst
.src(0) }, AHeapAny
);
865 auto const extra
= inst
.extra
<CreateAAWH
>();
866 auto const frame
= ALocal
{
869 AliasIdSet::IdRange
{ extra
->first
, extra
->first
+ extra
->count
}
872 return may_load_store(frame
, AHeapAny
);
877 auto const extra
= inst
.extra
<CountWHNotDone
>();
878 auto const frame
= ALocal
{
881 AliasIdSet::IdRange
{ extra
->first
, extra
->first
+ extra
->count
}
884 return may_load_store(frame
, AEmpty
);
887 // This re-enters to call extension-defined instance constructors.
888 case ConstructInstance
:
889 return may_load_store(AHeapAny
, AHeapAny
);
891 // Closures don't ever throw or reenter on construction
892 case ConstructClosure
:
893 return IrrelevantEffects
{};
895 case CheckStackOverflow
:
896 case CheckSurpriseFlagsEnter
:
897 case CheckSurpriseAndStack
:
898 return may_load_store(AEmpty
, AEmpty
);
900 //////////////////////////////////////////////////////////////////////
901 // Iterator instructions
907 auto const& args
= inst
.extra
<IterData
>()->args
;
908 assertx(!args
.hasKey());
909 auto const fp
= inst
.src(inst
.op() == IterNext
? 0 : 1);
910 AliasClass val
= ALocal
{ fp
, safe_cast
<uint32_t>(args
.valId
) };
911 return iter_effects(inst
, fp
, val
);
918 auto const& args
= inst
.extra
<IterData
>()->args
;
919 assertx(args
.hasKey());
920 auto const fp
= inst
.src(inst
.op() == IterNextK
? 0 : 1);
921 AliasClass key
= ALocal
{ fp
, safe_cast
<uint32_t>(args
.keyId
) };
922 AliasClass val
= ALocal
{ fp
, safe_cast
<uint32_t>(args
.valId
) };
923 return iter_effects(inst
, fp
, key
| val
);
927 auto const base
= aiter_base(inst
.src(0), iterId(inst
));
928 return may_load_store(AHeapAny
| base
, AHeapAny
);
932 auto const iter
= inst
.extra
<CheckIter
>()->iterId
;
933 return may_load_store(aiter_type(inst
.src(0), iter
), AEmpty
);
937 return PureLoad
{ aiter_base(inst
.src(0), iterId(inst
)) };
940 return PureLoad
{ aiter_pos(inst
.src(0), iterId(inst
)) };
943 return PureLoad
{ aiter_end(inst
.src(0), iterId(inst
)) };
946 return PureStore
{ aiter_base(inst
.src(0), iterId(inst
)), inst
.src(1) };
949 auto const iter
= inst
.extra
<StIterType
>()->iterId
;
950 return PureStore
{ aiter_type(inst
.src(0), iter
), nullptr };
954 return PureStore
{ aiter_pos(inst
.src(0), iterId(inst
)), inst
.src(1) };
957 return PureStore
{ aiter_end(inst
.src(0), iterId(inst
)), inst
.src(1) };
960 auto const iters
= aiter_all(inst
.src(0), iterId(inst
));
961 return may_load_store_kill(AEmpty
, AEmpty
, iters
);
964 //////////////////////////////////////////////////////////////////////
965 // Instructions that explicitly manipulate locals
969 ALocal
{ inst
.src(0), inst
.extra
<StLoc
>()->locId
},
976 auto const extra
= inst
.extra
<StLocRange
>();
979 for (auto locId
= extra
->start
; locId
< extra
->end
; ++locId
) {
980 acls
= acls
| ALocal
{ inst
.src(0), locId
};
982 return PureStore
{ acls
, inst
.src(1), nullptr };
986 return PureLoad
{ ALocal
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
} };
989 case LdLocPseudoMain
:
990 // Note: LdLocPseudoMain is both a guard and a load, so it must not be a
992 return may_load_store(
993 ALocal
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
},
997 case StLocPseudoMain
:
998 // This can store to globals or locals, but we don't have globals supported
999 // in AliasClass yet.
1000 return PureStore
{ AUnknown
, inst
.src(1), nullptr };
1002 //////////////////////////////////////////////////////////////////////
1003 // Pointer-based loads and stores
1006 return PureLoad
{ pointee(inst
.src(0)) };
1008 return PureStore
{ pointee(inst
.src(0)), inst
.src(1), inst
.src(0) };
1010 case StImplicitContext
:
1011 return may_load_store(AEmpty
, AEmpty
);
1014 return PureLoad
{ AHeapAny
};
1017 return PureStore
{ AHeapAny
};
1020 return PureLoad
{ AHeapAny
};
1023 return PureLoad
{ AMIStateBase
};
1026 return PureStore
{ AMIStateBase
, inst
.src(0), nullptr };
1028 case FinishMemberOp
:
1029 return may_load_store_kill(AEmpty
, AEmpty
, AMIStateAny
);
1035 return may_load_store(pointee(inst
.src(0)), AEmpty
);
1037 case CheckRDSInitialized
:
1038 return may_load_store(
1039 ARds
{ inst
.extra
<CheckRDSInitialized
>()->handle
},
1042 case MarkRDSInitialized
:
1043 return may_load_store(
1045 ARds
{ inst
.extra
<MarkRDSInitialized
>()->handle
}
1049 return may_load_store(
1051 AHeapAny
| ARds
{ inst
.extra
<InitProps
>()->cls
->propHandle() }
1055 return may_load_store(
1057 AHeapAny
| ARds
{ inst
.extra
<InitSProps
>()->cls
->sPropInitHandle() }
1060 case LdClsFromClsMeth
:
1061 case LdFuncFromClsMeth
:
1062 case LdFuncFromRFunc
:
1063 case LdGenericsFromRFunc
:
1064 case LdClsFromRClsMeth
:
1065 case LdFuncFromRClsMeth
:
1066 case LdGenericsFromRClsMeth
:
1067 return may_load_store(AEmpty
, AEmpty
);
1069 //////////////////////////////////////////////////////////////////////
1070 // Object/Ref loads/stores
1073 return may_load_store(AEmpty
, APropAny
);
1075 case InitObjMemoSlots
:
1076 // Writes to memo slots, but these are not modeled.
1077 return IrrelevantEffects
{};
1080 // Writes object attributes, but these are not modeled.
1081 return IrrelevantEffects
{};
1083 // Loads $obj->trace, stores $obj->file and $obj->line.
1084 case InitThrowableFileAndLine
:
1085 return may_load_store(AHeapAny
, APropAny
);
1087 //////////////////////////////////////////////////////////////////////
1088 // Array loads and stores
1090 case InitPackedLayoutArray
: {
1091 auto const arr
= inst
.src(0);
1092 auto const val
= inst
.src(1);
1093 auto const idx
= inst
.extra
<InitPackedLayoutArray
>()->index
;
1094 return PureStore
{ AElemI
{ arr
, idx
}, val
, arr
};
1097 case InitMixedLayoutArray
: {
1098 auto const arr
= inst
.src(0);
1099 auto const val
= inst
.src(1);
1100 auto const key
= inst
.extra
<InitMixedLayoutArray
>()->key
;
1101 return PureStore
{ AElemS
{ arr
, key
}, val
, arr
};
1105 case LdPackedElem
: {
1106 auto const base
= inst
.src(0);
1107 auto const key
= inst
.src(1);
1109 key
->hasConstVal() ? AElemI
{ base
, key
->intVal() } : AElemIAny
1113 case InitPackedLayoutArrayLoop
:
1115 auto const extra
= inst
.extra
<InitPackedLayoutArrayLoop
>();
1116 auto const stack_in
= AStack
{
1118 extra
->offset
+ static_cast<int32_t>(extra
->size
) - 1,
1119 static_cast<int32_t>(extra
->size
)
1121 return may_load_store_move(stack_in
, AElemIAny
, stack_in
);
1124 case NewKeysetArray
:
1126 // NewKeysetArray is reading elements from the stack, but writes to a
1127 // completely new array, so we can treat the store set as empty.
1128 auto const extra
= inst
.extra
<NewKeysetArray
>();
1129 auto const stack_in
= AStack
{
1131 extra
->offset
+ static_cast<int32_t>(extra
->size
) - 1,
1132 static_cast<int32_t>(extra
->size
)
1134 return may_load_store_move(stack_in
, AEmpty
, stack_in
);
1137 case NewStructDArray
:
1140 // NewStructArray is reading elements from the stack, but writes to a
1141 // completely new array, so we can treat the store set as empty.
1142 auto const extra
= inst
.extra
<NewStructData
>();
1143 auto const stack_in
= AStack
{
1145 extra
->offset
+ static_cast<int32_t>(extra
->numKeys
) - 1,
1146 static_cast<int32_t>(extra
->numKeys
)
1148 return may_load_store_move(stack_in
, AEmpty
, stack_in
);
1153 auto const extra
= inst
.extra
<NewStructData
>();
1154 auto const stack_in
= AStack
{
1156 extra
->offset
+ static_cast<int32_t>(extra
->numKeys
) - 1,
1157 static_cast<int32_t>(extra
->numKeys
)
1159 return may_load_store_move(stack_in
, AEmpty
, stack_in
);
1161 case MemoGetStaticValue
:
1162 case MemoGetLSBValue
:
1163 case MemoGetInstanceValue
:
1164 // Only reads the memo value (which isn't modeled here).
1165 return may_load_store(AEmpty
, AEmpty
);
1167 case MemoSetStaticValue
:
1168 case MemoSetLSBValue
:
1169 case MemoSetInstanceValue
:
1170 // Writes to the memo value (which isn't modeled)
1171 return may_load_store(AEmpty
, AEmpty
);
1173 case MemoGetStaticCache
:
1174 case MemoGetLSBCache
:
1175 case MemoSetStaticCache
:
1176 case MemoSetLSBCache
: {
1177 // Reads some (non-zero) set of locals for keys, and reads/writes from the
1178 // memo cache (which isn't modeled).
1179 auto const extra
= inst
.extra
<MemoCacheStaticData
>();
1180 auto const frame
= ALocal
{
1183 AliasIdSet::IdRange
{
1185 extra
->keys
.first
+ extra
->keys
.count
1190 return may_load_store(frame
, AEmpty
);
1193 case MemoGetInstanceCache
:
1194 case MemoSetInstanceCache
: {
1195 // Reads some set of locals for keys, and reads/writes from the memo cache
1196 // (which isn't modeled).
1197 auto const extra
= inst
.extra
<MemoCacheInstanceData
>();
1198 auto const frame
= [&]() -> AliasClass
{
1199 // Unlike MemoGet/SetStaticCache, we can have an empty key range here.
1200 if (extra
->keys
.count
== 0) return AEmpty
;
1205 AliasIdSet::IdRange
{
1207 extra
->keys
.first
+ extra
->keys
.count
1212 return may_load_store(frame
, AEmpty
);
1215 case MixedArrayGetK
:
1218 auto const base
= inst
.src(0);
1219 auto const key
= inst
.src(1);
1220 always_assert(key
->isA(TInt
| TStr
));
1221 if (key
->isA(TInt
)) {
1223 key
->hasConstVal() ? AElemI
{ base
, key
->intVal() } : AElemIAny
1226 if (key
->isA(TStr
)) {
1228 key
->hasConstVal() ? AElemS
{ base
, key
->strVal() } : AElemSAny
1231 return PureLoad
{ AElemAny
};
1235 // Array element keys are not tracked by memory effects right now.
1236 return may_load_store(AEmpty
, AEmpty
);
1238 case LdPtrIterVal
: {
1239 // NOTE: The type param for this op restricts the key, not the value.
1240 if (inst
.typeParam() <= TInt
) return PureLoad
{ AElemIAny
};
1241 if (inst
.typeParam() <= TStr
) return PureLoad
{ AElemSAny
};
1242 return PureLoad
{ AElemAny
};
1245 case ElemMixedArrayK
:
1248 return IrrelevantEffects
{};
1251 auto const base
= inst
.src(0);
1252 return may_load_store(AElemI
{ base
, 0 }, AEmpty
);
1255 auto const base
= inst
.src(0);
1256 if (base
->hasConstVal(TArr
)) {
1257 return may_load_store(
1258 AElemI
{ base
, static_cast<int64_t>(base
->arrVal()->size() - 1) },
1261 return may_load_store(AElemIAny
, AEmpty
);
1267 return may_load_store(AElemAny
, AEmpty
);
1271 return may_load_store(AEmpty
, AEmpty
);
1273 case CheckMixedArrayKeys
:
1274 case CheckMixedArrayOffset
:
1275 case CheckDictOffset
:
1276 case CheckKeysetOffset
:
1277 case CheckMissingKeyInArrLike
:
1278 case ProfileMixedArrayAccess
:
1279 case ProfileDictAccess
:
1280 case ProfileKeysetAccess
:
1282 return may_load_store(AHeapAny
, AEmpty
);
1286 return may_load_store(AElemAny
, AEmpty
);
1289 return may_load_store(AElemAny
, AEmpty
);
1293 return may_load_store(AEmpty
, AEmpty
);
1298 case KeysetGetQuiet
:
1302 case AKExistsKeyset
:
1303 return may_load_store(AElemAny
, AEmpty
);
1313 return may_load_store(AElemAny
, AEmpty
);
1316 return may_load_store(AHeapAny
, AHeapAny
);
1318 //////////////////////////////////////////////////////////////////////
1319 // Member instructions
1322 return may_load_store(pointee(inst
.src(0)), AEmpty
);
1325 * Various minstr opcodes that take a PtrToGen in src 0, which may or may not
1326 * point to a frame local or the evaluation stack. Some may read or write to
1327 * that pointer while some only read. They can all re-enter the VM and access
1328 * arbitrary heap locations.
1335 return may_load_store(
1336 AHeapAny
| all_pointees(inst
),
1341 * SetRange behaves like a simpler version of SetElem.
1345 return may_load_store(
1346 AHeapAny
| all_pointees(inst
),
1347 AHeapAny
| pointee(inst
.src(0))
1357 case SetNewElemArray
:
1359 case SetNewElemKeyset
:
1372 // These member ops will load and store from the base lval which they
1373 // take as their first argument, which may point anywhere in the heap.
1374 return may_load_store(
1375 AHeapAny
| all_pointees(inst
),
1376 AHeapAny
| all_pointees(inst
)
1379 case ReservePackedArrayDataNewElem
:
1380 return may_load_store(AHeapAny
, AHeapAny
);
1383 * Intermediate minstr operations. In addition to a base pointer like the
1384 * operations above, these may take a pointer to MInstrState::tvRef, which
1385 * they may store to (but not read from).
1390 return minstr_with_tvref(inst
);
1393 * Collection accessors can read from their inner array buffer, but stores
1394 * COW and behave as if they only affect collection memory locations. We
1395 * don't track those, so it's returning AEmpty for now.
1400 return may_load_store(AHeapAny
, AEmpty
/* Note */);
1404 return may_load_store(AHeapAny
, AEmpty
/* Note */);
1406 case LdInitPropAddr
:
1407 return may_load_store(
1410 safe_cast
<uint16_t>(inst
.extra
<LdInitPropAddr
>()->index
)
1415 return may_load_store(
1416 ARds
{ inst
.extra
<LdInitRDSAddr
>()->handle
},
1420 //////////////////////////////////////////////////////////////////////
1421 // Instructions that allocate new objects, without reading any other memory
1422 // at all, so any effects they have on some types of memory locations we
1423 // track are isolated from anything else we care about.
1428 case NewColFromArray
:
1430 case NewInstanceRaw
:
1437 case AllocStructDArray
:
1438 case AllocStructDict
:
1441 return IrrelevantEffects
{};
1444 // AllocObj re-enters to call constructors, but if it weren't for that we
1445 // could ignore its loads and stores since it's a new object.
1446 return may_load_store(AEmpty
, AEmpty
);
1447 case AllocObjReified
:
1448 // Similar to AllocObj but also stores the reification
1449 return may_load_store(AEmpty
, AHeapAny
);
1451 //////////////////////////////////////////////////////////////////////
1452 // Instructions that explicitly manipulate the stack.
1456 AStack
{ inst
.src(0), inst
.extra
<LdStk
>()->offset
, 1 }
1461 AStack
{ inst
.src(0), inst
.extra
<StStk
>()->offset
, 1 },
1467 // Technically these writes affect the caller's stack, but there is no way
1468 // to actually observe them from within the callee. They can also only
1469 // occur once on any exit path from a function.
1470 return may_load_store(AEmpty
, AEmpty
);
1473 return IrrelevantEffects
{};
1476 return may_load_store(
1477 AStack
{ inst
.src(0), inst
.extra
<CheckStk
>()->offset
, 1 },
1482 return may_load_store(AStackAny
| ALocalAny
, AEmpty
);
1485 // Unreachable code kills every memory location.
1486 return may_load_store_kill(AEmpty
, AEmpty
, AUnknown
);
1488 case ResolveTypeStruct
: {
1489 auto const extra
= inst
.extra
<ResolveTypeStructData
>();
1490 auto const stack_in
= AStack
{
1492 extra
->offset
+ static_cast<int32_t>(extra
->size
) - 1,
1493 static_cast<int32_t>(extra
->size
)
1495 return may_load_store(AliasClass(stack_in
)|AHeapAny
, AHeapAny
);
1499 return may_load_store(AFBasePtr
, AFBasePtr
);
1501 case DefFuncEntryFP
:
1502 return may_load_store(livefp(inst
.src(0)), livefp(inst
.dst()));
1506 return PureLoad
{ AFMeta
{ inst
.src(0) }};
1508 //////////////////////////////////////////////////////////////////////
1509 // Instructions that never read or write memory locations tracked by this
1516 case AdvanceMixedPtrIter
:
1517 case AdvancePackedPtrIter
:
1527 case DefCallNumArgs
:
1536 case EqArrayDataPtr
:
1540 case GetMixedPtrIter
:
1541 case GetPackedPtrIter
:
1551 case LdPackedArrayDataElemAddr
:
1584 case CheckImplicitContextNull
:
1585 case CheckSmashableClass
:
1612 case LdSubClsCnsClsName
:
1614 case CheckSubClsCns
:
1615 case LdClsCnsVecLen
:
1616 case ProfileSubClsCns
:
1618 case IsFunReifiedGenericsMatched
:
1619 case IsClsDynConstructible
:
1620 case JmpPlaceholder
:
1623 case LdSmashableFunc
:
1628 case InstanceOfIfaceVtable
:
1629 case IsTypeStructCached
:
1634 case ProfileInstanceCheck
:
1636 case LookupSPropSlot
:
1639 case ProfileIsTypeStruct
:
1640 return IrrelevantEffects
{};
1646 safe_cast
<uint16_t>(inst
.extra
<StClosureArg
>()->index
)
1652 case StArResumeAddr
:
1653 return PureStore
{ AFMeta
{ inst
.src(0) }, nullptr };
1660 case ContArUpdateIdx
:
1663 return may_load_store(AFContext
{ inst
.src(0) }, AEmpty
);
1667 return PureLoad
{ AFContext
{ inst
.src(0) }};
1670 return PureStore
{ AFContext
{ inst
.src(0) }, inst
.src(1) };
1673 return PureStore
{ AFFunc
{ inst
.src(0) }, inst
.src(1) };
1676 return PureStore
{ AFMeta
{ inst
.src(0) }, nullptr };
1678 case EagerSyncVMRegs
:
1679 return may_load_store(AEmpty
, AEmpty
);
1681 //////////////////////////////////////////////////////////////////////
1682 // Instructions that technically do some things w/ memory, but not in any way
1683 // we currently care about. They however don't return IrrelevantEffects
1684 // because we assume (in refcount-opts) that IrrelevantEffects instructions
1685 // can't even inspect Countable reference count fields, and several of these
1686 // can. All GeneralEffects instructions are assumed to possibly do so.
1691 case AFWHPushTailFrame
:
1703 case CheckSurpriseFlags
:
1705 case ZeroErrorLevel
:
1706 case RestoreErrorLevel
:
1710 case IncProfCounter
:
1711 case IncCallCounter
:
1714 case ContStartedCheck
:
1720 case HasReifiedGenerics
:
1722 case InstanceOfBitmask
:
1723 case NInstanceOfBitmask
:
1724 case InstanceOfIface
:
1725 case InstanceOfRecDesc
:
1726 case InterfaceSupportsArr
:
1727 case InterfaceSupportsVec
:
1728 case InterfaceSupportsDict
:
1729 case InterfaceSupportsKeyset
:
1730 case InterfaceSupportsDbl
:
1731 case InterfaceSupportsInt
:
1732 case InterfaceSupportsStr
:
1736 case DbgAssertRefCount
:
1737 case DbgCheckLocalsDecRefd
:
1760 case LdSSwitchDestFast
:
1769 case LdUnwinderValue
:
1774 case LdContResumeAddr
:
1775 case LdClsCachedSafe
:
1776 case LdRecDescCachedSafe
:
1778 case UnwindCheckSideExit
:
1782 case LdClsMethodCacheCls
:
1783 case LdClsMethodCacheFunc
:
1784 case LdClsMethodFCacheFunc
:
1786 case LdClsTypeCnsClsName
:
1787 case ProfileSwitchDest
:
1789 case LdFuncNumParams
:
1791 case LdMethCallerName
:
1799 case LdSwitchDblIndex
:
1800 case LdSwitchStrIndex
:
1804 case LookupClsMethod
:
1806 case StrictlyIntegerConv
:
1810 return may_load_store(AEmpty
, AEmpty
);
1812 // Some that touch memory we might care about later, but currently don't:
1817 case CountCollection
:
1819 case CheckPackedArrayDataBounds
:
1822 return may_load_store(AEmpty
, AEmpty
);
1824 //////////////////////////////////////////////////////////////////////
1825 // Instructions that can re-enter the VM and touch most heap things. They
1826 // also may generally write to the eval stack below an offset (see
1827 // alias-class.h above AStack for more).
1830 return may_load_store(AHeapAny
, AHeapAny
);
1833 return may_load_store(AHeapAny
, AHeapAny
);
1835 case GetMemoKeyScalar
:
1836 return IrrelevantEffects
{};
1838 // These opcodes raise notices if we access $GLOBALS['GLOBALS'],
1839 // or, due to case insensitivity, $GLOBALS['gLoBAls'], etc.
1842 case LdGblAddrDef
: {
1843 auto const base
= inst
.op() == BaseG
1844 ? may_load_store(AHeapAny
, AHeapAny
)
1845 : may_load_store(AEmpty
, AEmpty
);
1846 auto const& key
= inst
.src(0)->type();
1847 auto const safe
= key
.hasConstVal() && !s_GLOBALS
.equal(key
.strVal());
1848 return safe
? base
: may_reenter(inst
, base
);
1852 return may_load_store(AEmpty
, AEmpty
);
1854 case RaiseRxCallViolation
:
1855 return may_load_store(AFFunc
{inst
.src(0)}, AEmpty
);
1857 case LdClsPropAddrOrNull
: // may run 86{s,p}init, which can autoload
1858 case LdClsPropAddrOrRaise
: // raises errors, and 86{s,p}init
1860 case ThrowArrayIndexException
:
1861 case ThrowArrayKeyException
:
1862 case RaiseClsMethPropConvertNotice
:
1863 case RaiseArraySerializeNotice
:
1864 case RaiseUninitLoc
:
1865 case RaiseUndefProp
:
1866 case RaiseTooManyArg
:
1870 case RaiseHackArrCompatNotice
:
1871 case RaiseForbiddenDynCall
:
1872 case RaiseForbiddenDynConstruct
:
1873 case RaiseStrToClassNotice
:
1874 case CheckClsMethFunc
:
1875 case CheckClsReifiedGenericMismatch
:
1876 case CheckFunReifiedGenericMismatch
:
1879 case Count
: // re-enters on CountableClass
1903 case ConvObjToVArr
: // can invoke PHP
1904 case ConvObjToDArr
: // can invoke PHP
1906 case DefCls
: // autoload
1907 case LdCls
: // autoload
1908 case LdClsCached
: // autoload
1909 case LdFunc
: // autoload
1910 case LdFuncCached
: // autoload
1911 case LdRecDescCached
: // autoload
1912 case LdSwitchObjIndex
: // decrefs arg
1913 case InitClsCns
: // autoload
1914 case LookupClsMethodCache
: // autoload
1915 case LookupClsMethodFCache
: // autoload
1917 case LookupFuncCached
: // autoload
1918 case StringGet
: // raise_notice
1919 case OrdStrIdx
: // raise_notice
1920 case AddNewElem
: // can re-enter
1921 case AddNewElemKeyset
: // can re-enter
1922 case ArrayGet
: // kVPackedKind warnings
1923 case ArraySet
: // kVPackedKind warnings
1934 case LdSSwitchDestSlow
:
1946 case ConvObjToKeyset
:
1947 case ThrowOutOfBounds
:
1948 case ThrowInvalidArrayKey
:
1949 case ThrowInvalidOperation
:
1950 case ThrowCallReifiedFunctionWithoutGenerics
:
1951 case ThrowDivisionByZeroException
:
1952 case ThrowHasThisNeedStatic
:
1953 case ThrowLateInitPropError
:
1954 case ThrowMissingArg
:
1955 case ThrowMissingThis
:
1956 case ThrowParameterWrongType
:
1957 case ThrowParamInOutMismatch
:
1958 case ThrowParamInOutMismatchRange
:
1963 case ThrowAsTypeStructException
:
1964 case PropTypeRedefineCheck
: // Can raise and autoload
1965 case HandleRequestSurprise
:
1966 return may_load_store(AHeapAny
, AHeapAny
);
1969 case RaiseErrorOnInvalidIsAsExpressionType
:
1971 case RecordReifiedGenericsAndGetTSList
:
1972 return may_load_store(AElemAny
, AEmpty
);
1974 case ConvArrToKeyset
: // Decrefs input values
1975 case ConvVecToKeyset
:
1976 case ConvDictToKeyset
:
1977 case ConvDictToDArr
: // These 4 may raise Hack array compat notices
1978 case ConvKeysetToDArr
:
1979 case ConvClsMethToDArr
:
1980 case ConvClsMethToDict
:
1981 case ConvClsMethToKeyset
:
1982 case ConvClsMethToVArr
:
1983 case ConvClsMethToVec
:
1984 return may_load_store(AElemAny
, AEmpty
);
1987 case ConvKeysetToVec
:
1989 case ConvKeysetToDict
:
1992 case ConvDictToVArr
:
1993 case ConvKeysetToVArr
:
1996 return may_load_store(AElemAny
, AEmpty
);
1998 case ReleaseVVAndSkip
: // can decref VarEnv and Locals
2000 may_load_store(AHeapAny
|ALocalAny
|livefp(inst
.src(0)),
2001 AHeapAny
|ALocalAny
|livefp(inst
.src(0)));
2003 // debug_backtrace() traverses stack and WaitHandles on the heap.
2004 case DebugBacktrace
:
2005 case DebugBacktraceFast
:
2006 return may_load_store(AHeapAny
|ALocalAny
|AStackAny
, AHeapAny
);
2008 // This instruction doesn't touch memory we track, except that it may
2009 // re-enter to construct php Exception objects. During this re-entry anything
2010 // can happen (e.g. a surprise flag check could cause a php signal handler to
2011 // run arbitrary code).
2012 case AFWHPrepareChild
:
2013 return may_load_store(AActRec
{ inst
.src(0) }, AEmpty
);
2015 //////////////////////////////////////////////////////////////////////
2016 // The following instructions are used for debugging memory optimizations.
2017 // We can't ignore them, because they can prevent future optimizations;
2018 // eg t1 = LdStk<N>; DbgTrashStk<N>; StStk<N> t1
2019 // If we ignore the DbgTrashStk it looks like the StStk is redundant
2022 return GeneralEffects
{
2023 AEmpty
, AEmpty
, AEmpty
,
2024 AStack
{ inst
.src(0), inst
.extra
<DbgTrashStk
>()->offset
, 1 }
2027 return GeneralEffects
{
2028 AEmpty
, AEmpty
, AEmpty
,
2029 actrec(inst
.src(0), inst
.extra
<DbgTrashFrame
>()->offset
)
2032 return GeneralEffects
{
2033 AEmpty
, AEmpty
, AEmpty
,
2034 pointee(inst
.src(0))
2036 case DbgTrashRetVal
:
2037 return IrrelevantEffects
{};
2039 //////////////////////////////////////////////////////////////////////
2046 //////////////////////////////////////////////////////////////////////
2048 DEBUG_ONLY
bool check_effects(const IRInstruction
& inst
, MemEffects me
) {
2049 SCOPE_ASSERT_DETAIL("Memory Effects") {
2050 return folly::sformat(" inst: {}\n effects: {}\n", inst
, show(me
));
2053 auto check_fp
= [&] (FPRelOffset base
) {
2056 "frame offset is above base frame"
2060 auto check_obj
= [&] (SSATmp
* obj
) {
2062 obj
->type() <= TObj
,
2063 "Non obj pointer in memory effects"
2067 auto check
= [&] (AliasClass a
) {
2068 if (auto const fr
= a
.local()) check_fp(fr
->base
);
2069 if (auto const pr
= a
.prop()) check_obj(pr
->obj
);
2074 [&] (GeneralEffects x
) {
2080 // Locations may-moved always should also count as may-loads.
2081 always_assert(x
.moves
<= x
.loads
);
2083 if (inst
.mayRaiseErrorWithSources()) {
2084 // Any instruction that can raise an error can run a user error handler
2085 // and have arbitrary effects on the heap.
2086 always_assert(AHeapAny
<= x
.loads
);
2087 always_assert(AHeapAny
<= x
.stores
);
2089 * They also ought to kill /something/ on the stack, because of
2090 * possible re-entry. It's not incorrect to leave things out of the
2091 * kills set, but this assertion is here because we shouldn't do it on
2092 * purpose, so this is here until we have a reason not to assert it.
2094 * The mayRaiseError instructions should all be going through
2095 * may_reenter right now, which will kill the stack below the re-entry
2096 * depth---unless the marker for `inst' doesn't have an fp set.
2098 always_assert(inst
.marker().fp() == nullptr ||
2099 AStackAny
.maybe(x
.kills
));
2102 [&] (PureLoad x
) { check(x
.src
); },
2103 [&] (PureStore x
) { check(x
.dst
); },
2104 [&] (ExitEffects x
) { check(x
.live
); check(x
.kills
); },
2105 [&] (IrrelevantEffects
) {},
2106 [&] (UnknownEffects
) {},
2107 [&] (CallEffects x
) { check(x
.kills
);
2112 [&] (PureInlineCall x
) { check(x
.base
);
2114 [&] (PureInlineReturn x
) { check(x
.base
); },
2115 [&] (ReturnEffects x
) { check(x
.kills
); }
2121 //////////////////////////////////////////////////////////////////////
2125 MemEffects
memory_effects(const IRInstruction
& inst
) {
2126 auto const inner
= memory_effects_impl(inst
);
2127 auto const ret
= [&] () -> MemEffects
{
2128 if (!inst
.mayRaiseErrorWithSources()) return inner
;
2133 "Instruction {} has effects {}, but has been marked as MayRaiseError "
2134 "and must use a UnknownEffects, GeneralEffects, or CallEffects type.",
2138 return may_load_store(AUnknown
, AUnknown
);
2141 // Calls are implicitly MayRaise, all other instructions must use the
2142 // GeneralEffects or UnknownEffects class of memory effects
2143 return match
<MemEffects
>(
2145 [&] (GeneralEffects x
) { return may_reenter(inst
, x
); },
2146 [&] (CallEffects x
) { return x
; },
2147 [&] (UnknownEffects x
) { return x
; },
2148 [&] (PureLoad
) { return fail(); },
2149 [&] (PureStore
) { return fail(); },
2150 [&] (ExitEffects
) { return fail(); },
2151 [&] (PureInlineCall
) { return fail(); },
2152 [&] (PureInlineReturn
) { return fail(); },
2153 [&] (IrrelevantEffects
) { return fail(); },
2154 [&] (ReturnEffects
) { return fail(); }
2157 assertx(check_effects(inst
, ret
));
2161 AliasClass
pointee(const SSATmp
* tmp
) {
2162 return pointee(tmp
, nullptr);
2165 //////////////////////////////////////////////////////////////////////
2167 MemEffects
canonicalize(MemEffects me
) {
2168 using R
= MemEffects
;
2171 [&] (GeneralEffects x
) -> R
{
2172 return GeneralEffects
{
2173 canonicalize(x
.loads
),
2174 canonicalize(x
.stores
),
2175 canonicalize(x
.moves
),
2176 canonicalize(x
.kills
)
2179 [&] (PureLoad x
) -> R
{
2180 return PureLoad
{ canonicalize(x
.src
) };
2182 [&] (PureStore x
) -> R
{
2183 return PureStore
{ canonicalize(x
.dst
), x
.value
, x
.dep
};
2185 [&] (ExitEffects x
) -> R
{
2186 return ExitEffects
{ canonicalize(x
.live
), canonicalize(x
.kills
) };
2188 [&] (PureInlineCall x
) -> R
{
2189 return PureInlineCall
{
2190 canonicalize(x
.base
),
2192 canonicalize(x
.actrec
)
2195 [&] (PureInlineReturn x
) -> R
{
2196 return PureInlineReturn
{
2197 canonicalize(x
.base
),
2202 [&] (CallEffects x
) -> R
{
2203 return CallEffects
{
2204 canonicalize(x
.kills
),
2205 canonicalize(x
.inputs
),
2206 canonicalize(x
.actrec
),
2207 canonicalize(x
.outputs
),
2208 canonicalize(x
.locals
)
2211 [&] (ReturnEffects x
) -> R
{
2212 return ReturnEffects
{ canonicalize(x
.kills
) };
2214 [&] (IrrelevantEffects x
) -> R
{ return x
; },
2215 [&] (UnknownEffects x
) -> R
{ return x
; }
2219 //////////////////////////////////////////////////////////////////////
2221 std::string
show(MemEffects effects
) {
2222 using folly::sformat
;
2223 return match
<std::string
>(
2225 [&] (GeneralEffects x
) {
2226 return sformat("mlsmk({} ; {} ; {} ; {})",
2233 [&] (ExitEffects x
) {
2234 return sformat("exit({} ; {})", show(x
.live
), show(x
.kills
));
2236 [&] (PureInlineCall x
) {
2237 return sformat("inline_call({} ; {})",
2242 [&] (PureInlineReturn x
) {
2243 return sformat("inline_return({})", show(x
.base
));
2245 [&] (CallEffects x
) {
2246 return sformat("call({} ; {} ; {} ; {} ; {})",
2254 [&] (PureLoad x
) { return sformat("ld({})", show(x
.src
)); },
2255 [&] (PureStore x
) { return sformat("st({})", show(x
.dst
)); },
2256 [&] (ReturnEffects x
) { return sformat("return({})", show(x
.kills
)); },
2257 [&] (IrrelevantEffects
) { return "IrrelevantEffects"; },
2258 [&] (UnknownEffects
) { return "UnknownEffects"; }
2262 //////////////////////////////////////////////////////////////////////