2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "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/ir-instruction.h"
24 #include "hphp/runtime/vm/jit/ssa-tmp.h"
25 #include "hphp/runtime/vm/jit/analysis.h"
27 #include <folly/Optional.h>
29 namespace HPHP
{ namespace jit
{
33 //////////////////////////////////////////////////////////////////////
35 AliasClass
pointee(const SSATmp
* ptr
) {
36 auto const type
= ptr
->type();
37 always_assert(type
<= TPtrToGen
);
38 auto const maybeRef
= type
.maybe(TPtrToRefGen
);
39 auto const typeNR
= type
- TPtrToRefGen
;
41 auto specific
= [&] () -> folly::Optional
<AliasClass
> {
42 if (typeNR
<= TBottom
) return AEmpty
;
44 auto const sinst
= canonical(ptr
)->inst();
46 if (typeNR
<= TPtrToFrameGen
) {
47 if (sinst
->is(LdLocAddr
)) {
49 AFrame
{ sinst
->src(0), sinst
->extra
<LdLocAddr
>()->locId
}
55 if (typeNR
<= TPtrToStkGen
) {
56 if (sinst
->is(LdStkAddr
)) {
58 AStack
{ sinst
->src(0),
59 sinst
->extra
<LdStkAddr
>()->offset
.offset
, 1 }
65 if (typeNR
<= TPtrToPropGen
) {
66 if (sinst
->is(LdPropAddr
)) {
68 AProp
{ sinst
->src(0),
69 safe_cast
<uint32_t>(sinst
->extra
<LdPropAddr
>()->offsetBytes
) }
75 if (typeNR
<= TPtrToMISGen
) {
76 if (sinst
->is(LdMIStateAddr
)) {
78 AMIState
{ safe_cast
<int32_t>(sinst
->src(1)->intVal()) }
84 if (typeNR
<= TPtrToArrGen
) {
85 if (sinst
->is(LdPackedArrayElemAddr
)) {
86 if (sinst
->src(1)->hasConstVal() && sinst
->src(1)->intVal() >= 0) {
88 AElemI
{ sinst
->src(0), sinst
->src(1)->intVal() }
99 auto ret
= maybeRef
? ARefAny
: AEmpty
;
100 if (specific
) return *specific
| ret
;
103 * None of the above worked, so try to make the smallest union we can based
104 * on the pointer type.
106 if (typeNR
.maybe(TPtrToStkGen
)) ret
= ret
| AStackAny
;
107 if (typeNR
.maybe(TPtrToFrameGen
)) ret
= ret
| AFrameAny
;
108 if (typeNR
.maybe(TPtrToPropGen
)) ret
= ret
| APropAny
;
109 if (typeNR
.maybe(TPtrToArrGen
)) ret
= ret
| AElemAny
;
110 if (typeNR
.maybe(TPtrToMISGen
)) ret
= ret
| AMIStateAny
;
111 if (typeNR
.maybe(TPtrToClsInitGen
)) ret
= ret
| AHeapAny
;
112 if (typeNR
.maybe(TPtrToClsCnsGen
)) ret
= ret
| AHeapAny
;
116 // Return an AliasClass containing all locations pointed to by any PtrToGen
117 // sources to an instruction.
118 AliasClass
all_pointees(const IRInstruction
& inst
) {
119 auto ret
= AliasClass
{AEmpty
};
120 for (auto& src
: inst
.srcs()) {
121 if (src
->type() <= TPtrToGen
) {
122 ret
= ret
| pointee(src
);
128 // Return an AliasClass representing a range of the eval stack that contains
129 // everything below a logical depth.
130 AliasClass
stack_below(SSATmp
* base
, int32_t offset
) {
131 return AStack
{ base
, offset
, std::numeric_limits
<int32_t>::max() };
135 * Modify a GeneralEffects to take potential VM re-entry into account. This
136 * affects may-load, may-store, and kills information for the instruction. The
137 * GeneralEffects should already contain AHeapAny in both loads and stores if
138 * it affects those locations for reasons other than re-entry, but does not
139 * need to if it doesn't.
141 * For loads, we need to take into account EnableArgsInBacktraces: if this flag
142 * is on, any instruction that could re-enter could call debug_backtrace, which
143 * could read the argument locals of any activation record in the callstack.
144 * We don't try to limit the load effects to argument locals here, though, and
145 * just union in all the locals.
147 * For kills, locations on the eval stack below the re-entry depth should all
150 * Important note: because of the `kills' set modifications, an instruction may
151 * not report that it can re-enter if it actually can't. The reason this can
152 * go wrong is that if the instruction was in an inlined function, if we've
153 * removed the DefInlineFP its spOff will not be meaningful (unless it's a
154 * DecRef instruction, which we explicitly adjust in dce.cpp). In this case
155 * the `kills' set will refer to the wrong stack locations. In general this
156 * means instructions that can re-enter must have catch traces---but a few
157 * other instructions are exceptions, either since they are not allowed in
158 * inlined functions or because they take the (possibly-inlined) FramePtr as a
161 GeneralEffects
may_reenter(const IRInstruction
& inst
, GeneralEffects x
) {
162 auto const may_reenter_is_ok
=
163 (inst
.taken() && inst
.taken()->isCatch()) ||
175 "instruction {} claimed may_reenter, but it isn't allowed to say that",
180 * We want to union `killed_stack' into whatever else the instruction already
181 * said it must kill, but if we end up with an unrepresentable AliasClass we
182 * can't return a set that's too big (the `kills' set is unlike the other
183 * AliasClasses in GeneralEffects in that means it kills /everything/ in the
184 * set, since it's must-information).
186 * If we can't represent the union, just take the stack, in part because we
187 * have some debugging asserts about this right now---but also nothing
188 * actually uses may_reenter with a non-AEmpty kills at the time of this
191 auto const killed_stack
=
192 stack_below(inst
.marker().fp(), -inst
.marker().spOff().offset
- 1);
193 auto const kills_union
= x
.kills
.precise_union(killed_stack
);
194 auto const new_kills
= kills_union
? *kills_union
: killed_stack
;
196 return GeneralEffects
{
198 | (RuntimeOption::EnableArgsInBacktraces
? AFrameAny
: AEmpty
),
206 * Modify a GeneralEffects for instructions that could call the user
207 * error handler for the current frame (ie something that can raise
208 * a warning/notice/error, or a builtin call), because the error
209 * handler gets a context array which contains all the locals.
211 GeneralEffects
may_raise(const IRInstruction
& inst
, GeneralEffects x
) {
214 GeneralEffects
{ x
.loads
| AFrameAny
, x
.stores
, x
.moves
, x
.kills
}
218 //////////////////////////////////////////////////////////////////////
220 GeneralEffects
may_load_store(AliasClass loads
, AliasClass stores
) {
221 return GeneralEffects
{ loads
, stores
, AEmpty
, AEmpty
};
224 GeneralEffects
may_load_store_kill(AliasClass loads
,
227 return GeneralEffects
{ loads
, stores
, AEmpty
, kill
};
230 GeneralEffects
may_load_store_move(AliasClass loads
,
233 assertx(move
<= loads
);
234 return GeneralEffects
{ loads
, stores
, move
, AEmpty
};
237 //////////////////////////////////////////////////////////////////////
240 * Helper for iterator instructions. They all affect some locals, but are
241 * otherwise the same.
243 * N.B. Currently the memory for frame iterator slots is not part of the
244 * AliasClass lattice, since we never really manipulate them from the TC yet,
245 * so we don't report the effect these instructions have on it.
247 GeneralEffects
iter_effects(const IRInstruction
& inst
,
250 auto const iterID
= inst
.extra
<IterData
>()->iterId
;
251 AliasClass
const iterPos
= AIterPos
{ fp
, iterID
};
252 AliasClass
const iterBase
= AIterBase
{ fp
, iterID
};
253 auto const iterMem
= iterPos
| iterBase
;
257 locals
| AHeapAny
| iterMem
,
258 locals
| AHeapAny
| iterMem
,
265 * Construct effects for InterpOne, using the information in its extra data.
267 * We always consider an InterpOne as potentially doing anything to the heap,
268 * potentially re-entering, potentially raising warnings in the current frame,
269 * potentially reading any locals, and potentially reading/writing any stack
270 * location that isn't below the bottom of the stack.
272 * The extra data for the InterpOne comes with some additional information
273 * about which local(s) it may modify, which is all we try to be more precise
276 GeneralEffects
interp_one_effects(const IRInstruction
& inst
) {
277 auto const extra
= inst
.extra
<InterpOne
>();
278 auto const loads
= AHeapAny
| AStackAny
| AFrameAny
;
279 AliasClass stores
= AHeapAny
| AStackAny
;
280 if (extra
->smashesAllLocals
) {
281 stores
= stores
| AFrameAny
;
283 for (auto i
= uint32_t{0}; i
< extra
->nChangedLocals
; ++i
) {
284 stores
= stores
| AFrame
{ inst
.src(1), extra
->changedLocals
[i
].id
};
287 return may_raise(inst
, may_load_store_kill(loads
, stores
, AMIStateAny
));
290 //////////////////////////////////////////////////////////////////////
292 MemEffects
memory_effects_impl(const IRInstruction
& inst
) {
295 //////////////////////////////////////////////////////////////////////
298 // These exits don't leave the current php function, and could head to code
299 // that could read or write anything as far as we know (including frame
304 *stack_below(inst
.src(0), inst
.extra
<ReqBindJmp
>()->irSPOff
.offset
- 1).
305 precise_union(AMIStateAny
)
310 *stack_below(inst
.src(1),
311 inst
.extra
<JmpSwitchDest
>()->irSPOff
.offset
- 1).
312 precise_union(AMIStateAny
)
317 *stack_below(inst
.src(1),
318 inst
.extra
<JmpSSwitchDest
>()->offset
.offset
- 1).
319 precise_union(AMIStateAny
)
322 case ReqRetranslateOpt
:
323 return UnknownEffects
{};
325 //////////////////////////////////////////////////////////////////////
326 // Unusual instructions
329 * The ReturnHook sets up the ActRec so the unwinder knows everything is
330 * already released (i.e. it calls ar->setLocalsDecRefd()).
332 * The eval stack is also dead at this point (the return value is passed to
333 * ReturnHook as src(1), and the ReturnHook may not access the stack).
336 // Note, this instruction can re-enter, but doesn't need the may_reenter()
337 // treatmeant because of the special kill semantics for locals and stack.
338 return may_load_store_kill(
340 *AStackAny
.precise_union(AFrameAny
)->precise_union(AMIStateAny
)
343 // The suspend hooks can load anything (re-entering the VM), but can't write
347 // TODO: may-load here probably doesn't need to include AFrameAny normally.
348 return may_reenter(inst
,
349 may_load_store_kill(AUnknown
, AHeapAny
, AMIStateAny
));
352 * If we're returning from a function, it's ReturnEffects. The RetCtrl
353 * opcode also suspends resumables, which we model as having any possible
356 * Note that marking AFrameAny as dead isn't quite right, because that
357 * ought to mean that the preceding StRetVal is dead; but memory effects
358 * ignores StRetVal so the AFrameAny is fine.
361 if (inst
.extra
<RetCtrl
>()->suspendingResumed
) {
362 // Suspending can go anywhere, and doesn't even kill locals.
363 return UnknownEffects
{};
365 return ReturnEffects
{
366 AStackAny
| AFrameAny
| AMIStateAny
370 return ReturnEffects
{
371 stack_below(inst
.src(0), inst
.extra
<AsyncRetCtrl
>()->offset
.offset
- 1) |
375 case GenericRetDecRefs
:
377 * The may-store information here is AUnknown: even though we know it
378 * doesn't really "store" to the frame locals, the values that used to be
379 * there are no longer available because they are DecRef'd, which we are
380 * required to report as may-store information to make it visible to
381 * reference count optimizations. It's conceptually the same as if it was
382 * storing an Uninit over each of the locals, but the stores of uninits
383 * would be dead so we're not actually doing that.
385 return may_reenter(inst
,
386 may_load_store_kill(AUnknown
, AUnknown
, AMIStateAny
));
391 stack_below(inst
.src(1), inst
.extra
<EndCatch
>()->offset
.offset
- 1) |
396 * DefInlineFP has some special treatment here.
398 * It's logically `publishing' a pointer to a pre-live ActRec, making it
399 * live. It doesn't actually load from this ActRec, but after it's done this
400 * the set of things that can load from it is large enough that the easiest
401 * way to model this is to consider it as a load on behalf of `publishing'
402 * the ActRec. Once it's published, it's a live activation record, and
403 * doesn't get written to as if it were a stack slot anymore (we've
404 * effectively converted AStack locations into a frame until the
407 * TODO(#3634984): Additionally, DefInlineFP is marking may-load on all the
408 * locals of the outer frame. This is probably not necessary anymore, but we
409 * added it originally because a store sinking prototype needed to know it
410 * can't push StLocs past a DefInlineFP, because of reserved registers.
411 * Right now it's just here because we need to think about and test it before
415 return may_load_store(
416 AFrameAny
| inline_fp_frame(&inst
),
418 * Note that although DefInlineFP is going to store some things into the
419 * memory for the new frame (m_soff, etc), it's as part of converting it
420 * from a pre-live frame to a live frame. We don't need to report those
421 * effects on memory because they are logically to a 'different location
422 * class' (i.e. an activation record for the callee) than the AStack
423 * locations that represented the pre-live ActRec, even though they are at
424 * the same physical addresses in memory.
430 return ReturnEffects
{ stack_below(inst
.src(0), 2) | AMIStateAny
};
433 return interp_one_effects(inst
);
437 stack_below(inst
.src(1), -inst
.marker().spOff().offset
- 1) | AMIStateAny
441 return UnknownEffects
{};
443 // NB: on the failure path, these C++ helpers do a fixup and read frame
444 // locals before they throw. They can also invoke the user error handler and
445 // go do whatever they want to non-frame locations.
447 // TODO(#5372569): if we combine dv inits into the same regions we could
448 // possibly avoid storing KindOfUninits if we modify this.
449 case VerifyParamCallable
:
451 case VerifyParamFail
:
452 return may_raise(inst
, may_load_store(AUnknown
, AHeapAny
));
453 // However the following ones can't read locals from our frame on the way
454 // out, except as a side effect of raising a warning.
455 case VerifyRetCallable
:
458 return may_raise(inst
, may_load_store(AHeapAny
, AHeapAny
));
462 inst
.extra
<CallArray
>()->destroyLocals
,
464 // The AStackAny on this is more conservative than it could be; see Call
469 return CallEffects
{ false, AMIStateAny
, AStackAny
};
473 auto const extra
= inst
.extra
<Call
>();
475 extra
->destroyLocals
,
477 stack_below(inst
.src(0), extra
->spOffset
.offset
- 1) | AMIStateAny
,
478 // We might side-exit inside the callee, and interpret a return. So we
479 // can read anything anywhere on the eval stack above the call's entry
487 auto const extra
= inst
.extra
<CallBuiltin
>();
488 auto const stk
= [&] () -> AliasClass
{
489 AliasClass ret
= AEmpty
;
490 for (auto i
= uint32_t{2}; i
< inst
.numSrcs(); ++i
) {
491 if (inst
.src(i
)->type() <= TPtrToGen
) {
492 auto const cls
= pointee(inst
.src(i
));
493 if (cls
.maybe(AStackAny
)) {
500 auto const locs
= extra
->destroyLocals
? AFrameAny
: AEmpty
;
502 inst
, may_load_store_kill(stk
| AHeapAny
| locs
, locs
, AMIStateAny
));
505 // Resumable suspension takes everything from the frame and moves it into the
510 return may_load_store_move(AFrameAny
, AHeapAny
, AFrameAny
);
512 // This re-enters to call extension-defined instance constructors.
513 case ConstructInstance
:
514 return may_reenter(inst
, may_load_store(AHeapAny
, AHeapAny
));
516 case CheckStackOverflow
:
517 case CheckSurpriseFlagsEnter
:
518 case CheckSurpriseAndStack
:
519 return may_raise(inst
, may_load_store(AEmpty
, AEmpty
));
522 return UnknownEffects
{};
524 //////////////////////////////////////////////////////////////////////
525 // Iterator instructions
533 AFrame
{ inst
.src(1), inst
.extra
<IterData
>()->valId
}
541 AFrame
{ inst
.src(0), inst
.extra
<IterData
>()->valId
}
548 AliasClass key
= AFrame
{ inst
.src(1), inst
.extra
<IterData
>()->keyId
};
549 AliasClass val
= AFrame
{ inst
.src(1), inst
.extra
<IterData
>()->valId
};
550 return iter_effects(inst
, inst
.src(1), key
| val
);
557 AliasClass key
= AFrame
{ inst
.src(0), inst
.extra
<IterData
>()->keyId
};
558 AliasClass val
= AFrame
{ inst
.src(0), inst
.extra
<IterData
>()->valId
};
559 return iter_effects(inst
, inst
.src(0), key
| val
);
562 //////////////////////////////////////////////////////////////////////
563 // Instructions that explicitly manipulate locals
567 AFrame
{ inst
.src(0), inst
.extra
<StLoc
>()->locId
},
573 auto const extra
= inst
.extra
<StLocRange
>();
576 for (auto locId
= extra
->start
; locId
< extra
->end
; ++locId
) {
577 acls
= acls
| AFrame
{ inst
.src(0), locId
};
579 return PureStore
{ acls
, inst
.src(1) };
583 return PureLoad
{ AFrame
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
} };
586 case LdLocPseudoMain
:
587 // Note: LdLocPseudoMain is both a guard and a load, so it must not be a
589 return may_load_store(
590 AFrame
{ inst
.src(0), inst
.extra
<LocalId
>()->locId
},
594 case StLocPseudoMain
:
595 // This can store to globals or locals, but we don't have globals supported
596 // in AliasClass yet.
597 return PureStore
{ AUnknown
, inst
.src(1) };
599 case ClosureStaticLocInit
:
600 return may_load_store(AFrameAny
, AFrameAny
);
602 //////////////////////////////////////////////////////////////////////
603 // Pointer-based loads and stores
606 return PureLoad
{ pointee(inst
.src(0)) };
608 return PureStore
{ pointee(inst
.src(0)), inst
.src(1) };
610 // TODO(#5962341): These take non-constant offset arguments, and are
611 // currently only used for collections and class property inits, so we aren't
615 inst
.src(0)->type() <= TPtrToRMembCell
616 ? AHeapAny
| AMIStateAny
622 inst
.src(0)->type() <= TPtrToRMembCell
623 ? AHeapAny
| AMIStateAny
629 auto const mem
= pointee(inst
.src(0));
630 return may_load_store(mem
, mem
);
633 return may_load_store(pointee(inst
.src(0)), AEmpty
);
639 return may_load_store(pointee(inst
.src(0)), AEmpty
);
642 return may_load_store(pointee(inst
.src(0)), AEmpty
);
644 //////////////////////////////////////////////////////////////////////
645 // Object/Ref loads/stores
648 return may_load_store(ARef
{ inst
.src(0) }, AEmpty
);
650 return PureLoad
{ ARef
{ inst
.src(0) } };
652 return PureStore
{ ARef
{ inst
.src(0) }, inst
.src(1) };
655 return may_load_store(AEmpty
, APropAny
);
657 //////////////////////////////////////////////////////////////////////
658 // Array loads and stores
660 case InitPackedArray
:
662 AElemI
{ inst
.src(0), inst
.extra
<InitPackedArray
>()->index
},
666 case LdStructArrayElem
:
667 assertx(inst
.src(1)->strVal()->isStatic());
668 return PureLoad
{ AElemS
{ inst
.src(0), inst
.src(1)->strVal() } };
670 case InitPackedArrayLoop
:
672 auto const extra
= inst
.extra
<InitPackedArrayLoop
>();
673 auto const stack_in
= AStack
{
675 extra
->offset
.offset
+ static_cast<int32_t>(extra
->size
) - 1,
676 static_cast<int32_t>(extra
->size
)
678 return may_load_store_move(stack_in
, AElemIAny
, stack_in
);
683 // NewStructArray is reading elements from the stack, but writes to a
684 // completely new array, so we can treat the store set as empty.
685 auto const extra
= inst
.extra
<NewStructArray
>();
686 auto const stack_in
= AStack
{
688 extra
->offset
.offset
+ static_cast<int32_t>(extra
->numKeys
) - 1,
689 static_cast<int32_t>(extra
->numKeys
)
691 return may_load_store_move(stack_in
, AEmpty
, stack_in
);
695 return may_load_store(AElemAny
| ARefAny
, AEmpty
);
697 return may_load_store(AHeapAny
, AEmpty
);
699 return may_load_store(AElemAny
, AEmpty
);
701 return may_reenter(inst
, may_load_store(AHeapAny
, AHeapAny
));
703 //////////////////////////////////////////////////////////////////////
704 // Member instructions
707 * Various minstr opcodes that take a PtrToGen in src 0, which may or may not
708 * point to a frame local or the evaluation stack. These instructions can
709 * all re-enter the VM and access arbitrary heap locations, and some of them
710 * take pointers to MinstrState locations, which they may both load and store
725 case SetNewElemArray
:
729 case SetWithRefNewElem
:
732 // Right now we generally can't limit any of these better than general
733 // re-entry rules, since they can raise warnings and re-enter.
734 assertx(inst
.src(0)->type() <= TPtrToGen
);
735 return may_raise(inst
, may_load_store(
736 AHeapAny
| all_pointees(inst
),
737 AHeapAny
| all_pointees(inst
)
741 * These minstr opcodes either take a PtrToGen or an Obj as the base. The
742 * pointer may point at frame locals or the stack. These instructions can
743 * all re-enter the VM and access arbitrary non-frame/stack locations, as
759 return may_raise(inst
, may_load_store(
760 AHeapAny
| all_pointees(inst
),
761 AHeapAny
| all_pointees(inst
)
765 * Collection accessors can read from their inner array buffer, but stores
766 * COW and behave as if they only affect collection memory locations. We
767 * don't track those, so it's returning AEmpty for now.
773 return may_load_store(AHeapAny
, AEmpty
/* Note */);
776 return may_reenter(inst
, may_load_store(AHeapAny
, AEmpty
/* Note */));
779 //////////////////////////////////////////////////////////////////////
780 // Instructions that allocate new objects, without reading any other memory
781 // at all, so any effects they have on some types of memory locations we
782 // track are isolated from anything else we care about.
788 case AllocPackedArray
:
794 case Box
: // conditional allocation
795 return IrrelevantEffects
{};
798 // AllocObj re-enters to call constructors, but if it weren't for that we
799 // could ignore its loads and stores since it's a new object.
800 return may_reenter(inst
, may_load_store(AEmpty
, AEmpty
));
802 //////////////////////////////////////////////////////////////////////
803 // Instructions that explicitly manipulate the stack.
807 AStack
{ inst
.src(0), inst
.extra
<LdStk
>()->offset
.offset
, 1 }
812 AStack
{ inst
.src(0), inst
.extra
<StStk
>()->offset
.offset
, 1 },
818 auto const spOffset
= inst
.extra
<SpillFrame
>()->spOffset
;
819 return PureSpillFrame
{
822 // SpillFrame's spOffset is to the bottom of where it will store the
823 // ActRec, but AliasClass needs an offset to the highest cell it will
825 spOffset
.offset
+ int32_t{kNumActRecCells
} - 1,
830 // The context is in the highest slot.
831 spOffset
.offset
+ int32_t{kNumActRecCells
} - 1,
838 return may_load_store(
839 AStack
{ inst
.src(0), inst
.extra
<CheckStk
>()->irSpOffset
.offset
, 1 },
842 case CufIterSpillFrame
:
843 return may_load_store(AEmpty
, AStackAny
);
845 // The following may re-enter, and also deal with a stack slot.
848 auto const stk
= AStack
{
849 inst
.src(0), inst
.extra
<CastStk
>()->offset
.offset
, 1
851 return may_raise(inst
, may_load_store(stk
, stk
));
855 auto const stk
= AStack
{
857 inst
.extra
<CoerceStk
>()->offset
.offset
, 1
859 return may_raise(inst
, may_load_store(stk
, stk
));
865 auto aInst
= inst
.src(0)->inst();
866 if (aInst
->is(LdLocAddr
)) {
867 return may_raise(inst
, may_load_store(AFrameAny
, AFrameAny
));
869 return may_raise(inst
, may_load_store(AUnknown
, AUnknown
));
873 // This instruction is essentially a PureLoad, but we don't handle non-TV's
874 // in PureLoad so we have to treat it as may_load_store. We also treat it
875 // as loading an entire ActRec-sized part of the stack, although it only
876 // loads the slot containing the Func.
877 return may_load_store(
880 inst
.extra
<LdARFuncPtr
>()->offset
.offset
+ int32_t{kNumActRecCells
} - 1,
881 int32_t{kNumActRecCells
}
886 //////////////////////////////////////////////////////////////////////
887 // Instructions that never do anything to memory
911 case LdPackedArrayElemAddr
:
949 case LdResumableArObj
:
961 case LdStaticLocCached
:
968 case ExitPlaceholder
:
970 case ProfileObjClass
:
972 case InstanceOfIfaceVtable
:
973 return IrrelevantEffects
{};
975 //////////////////////////////////////////////////////////////////////
976 // Instructions that technically do some things w/ memory, but not in any way
977 // we currently care about. They however don't return IrrelevantEffects
978 // because we assume (in refcount-opts) that IrrelevantEffects instructions
979 // can't even inspect Countable reference count fields, and several of these
980 // can. All GeneralEffects instructions are assumed to possibly do so.
999 case CheckSurpriseFlags
:
1002 case RegisterLiveObj
:
1003 case StContArResume
:
1005 case ZeroErrorLevel
:
1006 case RestoreErrorLevel
:
1008 case CheckInitProps
:
1009 case CheckInitSProps
:
1012 case ContArUpdateIdx
:
1014 case IncProfCounter
:
1016 case IncStatGrouped
:
1019 case ContStartedCheck
:
1023 case NewColFromArray
:
1026 case CountArrayFast
:
1027 case StAsyncArResult
:
1028 case StAsyncArResume
:
1029 case StAsyncArSucceeded
:
1031 case InstanceOfBitmask
:
1032 case NInstanceOfBitmask
:
1033 case InstanceOfIface
:
1034 case InterfaceSupportsArr
:
1035 case InterfaceSupportsDbl
:
1036 case InterfaceSupportsInt
:
1037 case InterfaceSupportsStr
:
1039 case DbgAssertRefCount
:
1056 case IncTransCounter
:
1058 case LdAsyncArParentChain
:
1059 case LdSSwitchDestFast
:
1064 case ConvStrToArr
: // decrefs src, but src is a string
1067 case DerefClsRDSHandle
:
1068 case EagerSyncVMRegs
:
1070 case LdUnwinderValue
:
1082 case LdContResumeAddr
:
1083 case LdClsCachedSafe
:
1085 case UnwindCheckSideExit
:
1088 case LdClsMethodCacheCls
:
1089 case LdClsMethodCacheFunc
:
1090 case LdClsMethodFCacheFunc
:
1091 case ProfilePackedArray
:
1092 case ProfileStructArray
:
1093 case ProfileSwitchDest
:
1094 case LdFuncCachedSafe
:
1095 case LdFuncNumParams
:
1102 case LdSwitchDblIndex
:
1103 case LdSwitchStrIndex
:
1107 case LookupClsRDSHandle
:
1108 case GetCtxFwdCallDyn
:
1111 return may_load_store(AEmpty
, AEmpty
);
1113 // Some that touch memory we might care about later, but currently don't:
1114 case CheckStaticLocInit
:
1115 case StaticLocInitCached
:
1118 case ConvCellToBool
:
1120 case CountCollection
:
1122 case VectorHasImmCopy
:
1123 case CheckPackedArrayBounds
:
1126 return may_load_store(AEmpty
, AEmpty
);
1128 //////////////////////////////////////////////////////////////////////
1129 // Instructions that can re-enter the VM and touch most heap things. They
1130 // also may generally write to the eval stack below an offset (see
1131 // alias-class.h above AStack for more).
1135 auto const src
= inst
.src(0);
1136 // It could decref the inner ref.
1137 auto const maybeRef
= src
->isA(TBoxedCell
) ? ARef
{ src
} :
1138 src
->type().maybe(TBoxedCell
) ? ARefAny
: AEmpty
;
1139 // Need to add maybeRef to the `store' set. See comments about
1140 // `GeneralEffects' in memory-effects.h.
1141 auto const effect
= may_load_store(maybeRef
, maybeRef
);
1142 if (inst
.src(0)->type().maybe(TArr
| TObj
| TBoxedArr
| TBoxedObj
)) {
1143 // Could re-enter to run a destructor.
1144 return may_reenter(inst
, effect
);
1149 case LdArrFPushCuf
: // autoloads
1150 case LdArrFuncCtx
: // autoloads
1151 case LdObjMethod
: // can't autoload, but can decref $this right now
1152 case LdStrFPushCuf
: // autoload
1154 * Note that these instructions make stores to a pre-live actrec on the
1157 * It is probably safe for these instructions to have may-load only from
1158 * the portion of the evaluation stack below the actrec they are
1159 * manipulating, but since there's always going to be either a Call or a
1160 * region exit following it it doesn't help us eliminate anything for now,
1161 * so we just pretend it can read/write anything on the stack.
1163 return may_raise(inst
, may_load_store(AStackAny
, AStackAny
));
1165 case LookupClsMethod
: // autoload, and it writes part of the new actrec
1167 AliasClass effects
= AStack
{
1169 inst
.extra
<LookupClsMethod
>()->offset
.offset
,
1170 int32_t{kNumActRecCells
}
1172 return may_raise(inst
, may_load_store(effects
, effects
));
1175 case LdClsPropAddrOrNull
: // may run 86{s,p}init, which can autoload
1176 case LdClsPropAddrOrRaise
: // raises errors, and 86{s,p}init
1179 case RaiseArrayIndexNotice
:
1180 case RaiseArrayKeyNotice
:
1181 case RaiseUninitLoc
:
1182 case RaiseUndefProp
:
1183 case RaiseMissingArg
:
1189 case Count
: // re-enters on CountableClass
1190 case CIterFree
: // decrefs context object in iter
1200 case ConvCellToArr
: // decrefs src, may read obj props
1201 case ConvCellToObj
: // decrefs src
1202 case ConvObjToArr
: // decrefs src
1207 case LdCls
: // autoload
1208 case LdClsCached
: // autoload
1209 case LdFunc
: // autoload
1210 case LdFuncCached
: // autoload
1211 case LdFuncCachedU
: // autoload
1212 case LdSwitchObjIndex
: // decrefs arg
1213 case LookupClsCns
: // autoload
1214 case LookupClsMethodCache
: // autoload
1215 case LookupClsMethodFCache
: // autoload
1219 case StringGet
: // raise_warning
1220 case ArrayAdd
: // decrefs source
1221 case AddElemIntKey
: // decrefs value
1222 case AddElemStrKey
: // decrefs value
1223 case AddNewElem
: // decrefs value
1224 case ArrayGet
: // kVPackedKind warnings
1225 case ArrayIsset
: // kVPackedKind warnings
1226 case ArraySet
: // kVPackedKind warnings
1227 case ArraySetRef
: // kVPackedKind warnings
1228 case GetMemoKey
: // re-enters to call getInstanceKey() in some cases
1236 case LdSSwitchDestSlow
:
1240 case ColAddNewElemC
:
1241 case CoerceStrToInt
:
1242 case CoerceStrToDbl
:
1243 case CoerceCellToDbl
:
1244 case CoerceCellToInt
:
1245 case CoerceCellToBool
:
1251 case ThrowOutOfBounds
:
1252 return may_raise(inst
, may_load_store(AHeapAny
, AHeapAny
));
1254 case ReleaseVVAndSkip
: // can decref ExtraArgs or VarEnv and Locals
1255 return may_reenter(inst
,
1256 may_load_store(AHeapAny
|AFrameAny
, AHeapAny
|AFrameAny
));
1258 // These two instructions don't touch memory we track, except that they may
1259 // re-enter to construct php Exception objects. During this re-entry anything
1260 // can happen (e.g. a surprise flag check could cause a php signal handler to
1261 // run arbitrary code).
1263 case AFWHPrepareChild
:
1264 return may_reenter(inst
, may_load_store(AEmpty
, AEmpty
));
1266 //////////////////////////////////////////////////////////////////////
1267 // The following instructions are used for debugging memory optimizations, so
1268 // this analyzer should pretend they don't exist.
1273 return IrrelevantEffects
{};
1275 //////////////////////////////////////////////////////////////////////
1282 //////////////////////////////////////////////////////////////////////
1284 DEBUG_ONLY
bool check_effects(const IRInstruction
& inst
, MemEffects me
) {
1285 SCOPE_ASSERT_DETAIL("Memory Effects") {
1286 return folly::sformat(" inst: {}\n effects: {}\n", inst
, show(me
));
1289 auto check_fp
= [&] (SSATmp
* fp
) {
1291 fp
->type() <= TFramePtr
,
1292 "Non frame pointer in memory effects"
1296 auto check_obj
= [&] (SSATmp
* obj
) {
1298 obj
->type() <= TObj
,
1299 "Non obj pointer in memory effects"
1303 auto check
= [&] (AliasClass a
) {
1304 if (auto const fr
= a
.frame()) check_fp(fr
->fp
);
1305 if (auto const pr
= a
.prop()) check_obj(pr
->obj
);
1310 [&] (GeneralEffects x
) {
1316 // Locations may-moved always should also count as may-loads.
1317 always_assert(x
.moves
<= x
.loads
);
1319 if (inst
.mayRaiseError()) {
1320 // Any instruction that can raise an error can run a user error handler
1321 // and have arbitrary effects on the heap.
1322 always_assert(AHeapAny
<= x
.loads
);
1323 always_assert(AHeapAny
<= x
.stores
);
1325 * They also ought to kill /something/ on the stack, because of
1326 * possible re-entry. It's not incorrect to leave things out of the
1327 * kills set, but this assertion is here because we shouldn't do it on
1328 * purpose, so this is here until we have a reason not to assert it.
1330 * The mayRaiseError instructions should all be going through
1331 * may_reenter right now, which will kill the stack below the re-entry
1334 always_assert(AStackAny
.maybe(x
.kills
));
1337 [&] (PureLoad x
) { check(x
.src
); },
1338 [&] (PureStore x
) { check(x
.dst
);
1339 always_assert(x
.value
!= nullptr); },
1340 [&] (PureSpillFrame x
) { check(x
.stk
); check(x
.ctx
);
1341 always_assert(x
.ctx
<= x
.stk
); },
1342 [&] (ExitEffects x
) { check(x
.live
); check(x
.kills
); },
1343 [&] (IrrelevantEffects
) {},
1344 [&] (UnknownEffects
) {},
1345 [&] (CallEffects x
) { check(x
.kills
); check(x
.stack
); },
1346 [&] (ReturnEffects x
) { check(x
.kills
); }
1352 //////////////////////////////////////////////////////////////////////
1356 MemEffects
memory_effects(const IRInstruction
& inst
) {
1357 auto const ret
= memory_effects_impl(inst
);
1358 assertx(check_effects(inst
, ret
));
1362 //////////////////////////////////////////////////////////////////////
1364 MemEffects
canonicalize(MemEffects me
) {
1365 using R
= MemEffects
;
1368 [&] (GeneralEffects x
) -> R
{
1369 return GeneralEffects
{
1370 canonicalize(x
.loads
),
1371 canonicalize(x
.stores
),
1372 canonicalize(x
.moves
),
1373 canonicalize(x
.kills
)
1376 [&] (PureLoad x
) -> R
{
1377 return PureLoad
{ canonicalize(x
.src
) };
1379 [&] (PureStore x
) -> R
{
1380 return PureStore
{ canonicalize(x
.dst
), x
.value
};
1382 [&] (PureSpillFrame x
) -> R
{
1383 return PureSpillFrame
{ canonicalize(x
.stk
), canonicalize(x
.ctx
) };
1385 [&] (ExitEffects x
) -> R
{
1386 return ExitEffects
{ canonicalize(x
.live
), canonicalize(x
.kills
) };
1388 [&] (CallEffects x
) -> R
{
1389 return CallEffects
{
1391 canonicalize(x
.kills
),
1392 canonicalize(x
.stack
)
1395 [&] (ReturnEffects x
) -> R
{
1396 return ReturnEffects
{ canonicalize(x
.kills
) };
1398 [&] (IrrelevantEffects x
) -> R
{ return x
; },
1399 [&] (UnknownEffects x
) -> R
{ return x
; }
1403 //////////////////////////////////////////////////////////////////////
1405 std::string
show(MemEffects effects
) {
1406 using folly::sformat
;
1407 return match
<std::string
>(
1409 [&] (GeneralEffects x
) {
1410 return sformat("mlsmk({} ; {} ; {} ; {})",
1417 [&] (ExitEffects x
) {
1418 return sformat("exit({} ; {})", show(x
.live
), show(x
.kills
));
1420 [&] (CallEffects x
) {
1421 return sformat("call({} ; {})", show(x
.kills
), show(x
.stack
));
1423 [&] (PureSpillFrame x
) {
1424 return sformat("stFrame({} ; {})", show(x
.stk
), show(x
.ctx
));
1426 [&] (PureLoad x
) { return sformat("ld({})", show(x
.src
)); },
1427 [&] (PureStore x
) { return sformat("st({})", show(x
.dst
)); },
1428 [&] (ReturnEffects x
) { return sformat("return({})", show(x
.kills
)); },
1429 [&] (IrrelevantEffects
) { return "IrrelevantEffects"; },
1430 [&] (UnknownEffects
) { return "UnknownEffects"; }
1434 //////////////////////////////////////////////////////////////////////
1436 AliasClass
inline_fp_frame(const IRInstruction
* inst
) {
1439 inst
->extra
<DefInlineFP
>()->spOffset
.offset
+ int32_t{kNumActRecCells
} - 1,
1440 int32_t{kNumActRecCells
}
1444 //////////////////////////////////////////////////////////////////////