Add specialized string comparison IR ops
[hiphop-php.git] / hphp / runtime / vm / jit / memory-effects.cpp
blob2f963ab42544463d8edcadca8ecce266c5289baa
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 {
31 namespace {
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)) {
48 return AliasClass {
49 AFrame { sinst->src(0), sinst->extra<LdLocAddr>()->locId }
52 return AFrameAny;
55 if (typeNR <= TPtrToStkGen) {
56 if (sinst->is(LdStkAddr)) {
57 return AliasClass {
58 AStack { sinst->src(0),
59 sinst->extra<LdStkAddr>()->offset.offset, 1 }
62 return AStackAny;
65 if (typeNR <= TPtrToPropGen) {
66 if (sinst->is(LdPropAddr)) {
67 return AliasClass {
68 AProp { sinst->src(0),
69 safe_cast<uint32_t>(sinst->extra<LdPropAddr>()->offsetBytes) }
72 return APropAny;
75 if (typeNR <= TPtrToMISGen) {
76 if (sinst->is(LdMIStateAddr)) {
77 return AliasClass {
78 AMIState { safe_cast<int32_t>(sinst->src(1)->intVal()) }
81 return AMIStateAny;
84 if (typeNR <= TPtrToArrGen) {
85 if (sinst->is(LdPackedArrayElemAddr)) {
86 if (sinst->src(1)->hasConstVal() && sinst->src(1)->intVal() >= 0) {
87 return AliasClass {
88 AElemI { sinst->src(0), sinst->src(1)->intVal() }
91 return AElemIAny;
93 return AElemAny;
96 return folly::none;
97 }();
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;
113 return ret;
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);
125 return ret;
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
148 * be added.
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
159 * source.
161 GeneralEffects may_reenter(const IRInstruction& inst, GeneralEffects x) {
162 auto const may_reenter_is_ok =
163 (inst.taken() && inst.taken()->isCatch()) ||
164 inst.is(DecRef,
165 ReleaseVVAndSkip,
166 CIterFree,
167 MIterFree,
168 MIterNext,
169 MIterNextK,
170 IterFree,
171 ABCUnblock,
172 GenericRetDecRefs);
173 always_assert_flog(
174 may_reenter_is_ok,
175 "instruction {} claimed may_reenter, but it isn't allowed to say that",
176 inst
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
189 * writing anyway.
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 {
197 x.loads | AHeapAny
198 | (RuntimeOption::EnableArgsInBacktraces ? AFrameAny : AEmpty),
199 x.stores | AHeapAny,
200 x.moves,
201 new_kills
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) {
212 return may_reenter(
213 inst,
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,
225 AliasClass stores,
226 AliasClass kill) {
227 return GeneralEffects { loads, stores, AEmpty, kill };
230 GeneralEffects may_load_store_move(AliasClass loads,
231 AliasClass stores,
232 AliasClass move) {
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,
248 SSATmp* fp,
249 AliasClass locals) {
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;
254 return may_reenter(
255 inst,
256 may_load_store_kill(
257 locals | AHeapAny | iterMem,
258 locals | AHeapAny | iterMem,
259 AMIStateAny
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
274 * about right now.
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;
282 } else {
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) {
293 switch (inst.op()) {
295 //////////////////////////////////////////////////////////////////////
296 // Region exits
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
300 // locals).
301 case ReqBindJmp:
302 return ExitEffects {
303 AUnknown,
304 *stack_below(inst.src(0), inst.extra<ReqBindJmp>()->irSPOff.offset - 1).
305 precise_union(AMIStateAny)
307 case JmpSwitchDest:
308 return ExitEffects {
309 AUnknown,
310 *stack_below(inst.src(1),
311 inst.extra<JmpSwitchDest>()->irSPOff.offset - 1).
312 precise_union(AMIStateAny)
314 case JmpSSwitchDest:
315 return ExitEffects {
316 AUnknown,
317 *stack_below(inst.src(1),
318 inst.extra<JmpSSwitchDest>()->offset.offset - 1).
319 precise_union(AMIStateAny)
321 case ReqRetranslate:
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).
335 case ReturnHook:
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(
339 AHeapAny, AHeapAny,
340 *AStackAny.precise_union(AFrameAny)->precise_union(AMIStateAny)
343 // The suspend hooks can load anything (re-entering the VM), but can't write
344 // to frame locals.
345 case SuspendHookE:
346 case SuspendHookR:
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
354 * effects.
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.
360 case RetCtrl:
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
369 case AsyncRetCtrl:
370 return ReturnEffects {
371 stack_below(inst.src(0), inst.extra<AsyncRetCtrl>()->offset.offset - 1) |
372 AMIStateAny
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));
388 case EndCatch:
389 return ExitEffects {
390 AUnknown,
391 stack_below(inst.src(1), inst.extra<EndCatch>()->offset.offset - 1) |
392 AMIStateAny
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
405 * InlineReturn).
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
412 * removing that set.
414 case DefInlineFP:
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.
426 AEmpty
429 case InlineReturn:
430 return ReturnEffects { stack_below(inst.src(0), 2) | AMIStateAny };
432 case InterpOne:
433 return interp_one_effects(inst);
434 case InterpOneCF:
435 return ExitEffects {
436 AUnknown,
437 stack_below(inst.src(1), -inst.marker().spOff().offset - 1) | AMIStateAny
440 case NativeImpl:
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:
450 case VerifyParamCls:
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:
456 case VerifyRetCls:
457 case VerifyRetFail:
458 return may_raise(inst, may_load_store(AHeapAny, AHeapAny));
460 case CallArray:
461 return CallEffects {
462 inst.extra<CallArray>()->destroyLocals,
463 AMIStateAny,
464 // The AStackAny on this is more conservative than it could be; see Call
465 // and CallBuiltin.
466 AStackAny
468 case ContEnter:
469 return CallEffects { false, AMIStateAny, AStackAny };
471 case Call:
473 auto const extra = inst.extra<Call>();
474 return CallEffects {
475 extra->destroyLocals,
476 // kill
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
480 // depth here.
481 AStackAny
485 case CallBuiltin:
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)) {
494 ret = ret | cls;
498 return ret;
499 }();
500 auto const locs = extra->destroyLocals ? AFrameAny : AEmpty;
501 return may_raise(
502 inst, may_load_store_kill(stk | AHeapAny | locs, locs, AMIStateAny));
505 // Resumable suspension takes everything from the frame and moves it into the
506 // heap.
507 case CreateAFWH:
508 case CreateAFWHNoVV:
509 case CreateCont:
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));
521 case InitExtraArgs:
522 return UnknownEffects {};
524 //////////////////////////////////////////////////////////////////////
525 // Iterator instructions
527 case IterInit:
528 case MIterInit:
529 case WIterInit:
530 return iter_effects(
531 inst,
532 inst.src(1),
533 AFrame { inst.src(1), inst.extra<IterData>()->valId }
535 case IterNext:
536 case MIterNext:
537 case WIterNext:
538 return iter_effects(
539 inst,
540 inst.src(0),
541 AFrame { inst.src(0), inst.extra<IterData>()->valId }
544 case IterInitK:
545 case MIterInitK:
546 case WIterInitK:
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);
553 case IterNextK:
554 case MIterNextK:
555 case WIterNextK:
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
565 case StLoc:
566 return PureStore {
567 AFrame { inst.src(0), inst.extra<StLoc>()->locId },
568 inst.src(1)
571 case StLocRange:
573 auto const extra = inst.extra<StLocRange>();
574 auto acls = AEmpty;
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) };
582 case LdLoc:
583 return PureLoad { AFrame { inst.src(0), inst.extra<LocalId>()->locId } };
585 case CheckLoc:
586 case LdLocPseudoMain:
587 // Note: LdLocPseudoMain is both a guard and a load, so it must not be a
588 // PureLoad.
589 return may_load_store(
590 AFrame { inst.src(0), inst.extra<LocalId>()->locId },
591 AEmpty
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
605 case LdMem:
606 return PureLoad { pointee(inst.src(0)) };
607 case StMem:
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
612 // hooked up yet.
613 case StElem:
614 return PureStore {
615 inst.src(0)->type() <= TPtrToRMembCell
616 ? AHeapAny | AMIStateAny
617 : AUnknown,
618 inst.src(2)
620 case LdElem:
621 return PureLoad {
622 inst.src(0)->type() <= TPtrToRMembCell
623 ? AHeapAny | AMIStateAny
624 : AUnknown
627 case BoxPtr:
629 auto const mem = pointee(inst.src(0));
630 return may_load_store(mem, mem);
632 case UnboxPtr:
633 return may_load_store(pointee(inst.src(0)), AEmpty);
635 case IsNTypeMem:
636 case IsTypeMem:
637 case CheckTypeMem:
638 case DbgAssertPtr:
639 return may_load_store(pointee(inst.src(0)), AEmpty);
641 case CheckInitMem:
642 return may_load_store(pointee(inst.src(0)), AEmpty);
644 //////////////////////////////////////////////////////////////////////
645 // Object/Ref loads/stores
647 case CheckRefInner:
648 return may_load_store(ARef { inst.src(0) }, AEmpty);
649 case LdRef:
650 return PureLoad { ARef { inst.src(0) } };
651 case StRef:
652 return PureStore { ARef { inst.src(0) }, inst.src(1) };
654 case InitObjProps:
655 return may_load_store(AEmpty, APropAny);
657 //////////////////////////////////////////////////////////////////////
658 // Array loads and stores
660 case InitPackedArray:
661 return PureStore {
662 AElemI { inst.src(0), inst.extra<InitPackedArray>()->index },
663 inst.src(1)
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 {
674 inst.src(1),
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);
681 case NewStructArray:
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 {
687 inst.src(0),
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);
694 case ArrayIdx:
695 return may_load_store(AElemAny | ARefAny, AEmpty);
696 case MapIdx:
697 return may_load_store(AHeapAny, AEmpty);
698 case AKExistsArr:
699 return may_load_store(AElemAny, AEmpty);
700 case AKExistsObj:
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
711 * from if present.
713 case CGetElem:
714 case ElemArray:
715 case ElemArrayW:
716 case ElemX:
717 case EmptyElem:
718 case IssetElem:
719 case BindElem:
720 case BindNewElem:
721 case ElemDX:
722 case ElemUX:
723 case IncDecElem:
724 case SetElem:
725 case SetNewElemArray:
726 case SetNewElem:
727 case SetOpElem:
728 case SetWithRefElem:
729 case SetWithRefNewElem:
730 case UnsetElem:
731 case VGetElem:
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
744 * well.
746 case CGetProp:
747 case CGetPropQ:
748 case EmptyProp:
749 case IssetProp:
750 case PropX:
751 case PropQ:
752 case UnsetProp:
753 case BindProp:
754 case IncDecProp:
755 case PropDX:
756 case SetOpProp:
757 case SetProp:
758 case VGetProp:
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.
769 case MapIsset:
770 case PairIsset:
771 case VectorDoCow:
772 case VectorIsset:
773 return may_load_store(AHeapAny, AEmpty /* Note */);
774 case MapGet:
775 case MapSet:
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.
784 case NewArray:
785 case NewCol:
786 case NewInstanceRaw:
787 case NewMixedArray:
788 case AllocPackedArray:
789 case ConvBoolToArr:
790 case ConvDblToStr:
791 case ConvDblToArr:
792 case ConvIntToArr:
793 case ConvIntToStr:
794 case Box: // conditional allocation
795 return IrrelevantEffects {};
797 case AllocObj:
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.
805 case LdStk:
806 return PureLoad {
807 AStack { inst.src(0), inst.extra<LdStk>()->offset.offset, 1 }
810 case StStk:
811 return PureStore {
812 AStack { inst.src(0), inst.extra<StStk>()->offset.offset, 1 },
813 inst.src(1)
816 case SpillFrame:
818 auto const spOffset = inst.extra<SpillFrame>()->spOffset;
819 return PureSpillFrame {
820 AStack {
821 inst.src(0),
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
824 // store.
825 spOffset.offset + int32_t{kNumActRecCells} - 1,
826 kNumActRecCells
828 AStack {
829 inst.src(0),
830 // The context is in the highest slot.
831 spOffset.offset + int32_t{kNumActRecCells} - 1,
837 case CheckStk:
838 return may_load_store(
839 AStack { inst.src(0), inst.extra<CheckStk>()->irSpOffset.offset, 1 },
840 AEmpty
842 case CufIterSpillFrame:
843 return may_load_store(AEmpty, AStackAny);
845 // The following may re-enter, and also deal with a stack slot.
846 case CastStk:
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));
853 case CoerceStk:
855 auto const stk = AStack {
856 inst.src(0),
857 inst.extra<CoerceStk>()->offset.offset, 1
859 return may_raise(inst, may_load_store(stk, stk));
862 case CastMem:
863 case CoerceMem:
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));
872 case LdARFuncPtr:
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(
878 AStack {
879 inst.src(0),
880 inst.extra<LdARFuncPtr>()->offset.offset + int32_t{kNumActRecCells} - 1,
881 int32_t{kNumActRecCells}
883 AEmpty
886 //////////////////////////////////////////////////////////////////////
887 // Instructions that never do anything to memory
889 case AssertStk:
890 case HintStkInner:
891 case AbsDbl:
892 case AddDbl:
893 case AddInt:
894 case AddIntO:
895 case AndInt:
896 case AssertLoc:
897 case AssertType:
898 case DefFP:
899 case DefSP:
900 case EndGuards:
901 case EqDbl:
902 case EqInt:
903 case GteInt:
904 case GtInt:
905 case HintLocInner:
906 case Jmp:
907 case JmpNZero:
908 case JmpZero:
909 case LdPropAddr:
910 case LdStkAddr:
911 case LdPackedArrayElemAddr:
912 case LteDbl:
913 case LteInt:
914 case LtInt:
915 case GtDbl:
916 case GteDbl:
917 case LtDbl:
918 case DivDbl:
919 case MulDbl:
920 case MulInt:
921 case MulIntO:
922 case NeqDbl:
923 case NeqInt:
924 case SubDbl:
925 case SubInt:
926 case SubIntO:
927 case XorBool:
928 case XorInt:
929 case OrInt:
930 case AssertNonNull:
931 case CheckNonNull:
932 case CheckNullptr:
933 case Ceil:
934 case Floor:
935 case DefLabel:
936 case CheckInit:
937 case Nop:
938 case ClsNeq:
939 case Mod:
940 case Conjure:
941 case DefMIStateBase:
942 case Halt:
943 case ConvBoolToInt:
944 case ConvBoolToDbl:
945 case DbgAssertType:
946 case DefConst:
947 case LdLocAddr:
948 case Sqrt:
949 case LdResumableArObj:
950 case Shl:
951 case Shr:
952 case IsNType:
953 case IsType:
954 case Mov:
955 case ConvClsToCctx:
956 case ConvDblToBool:
957 case ConvDblToInt:
958 case IsScalarType:
959 case LdMIStateAddr:
960 case LdPairBase:
961 case LdStaticLocCached:
962 case CheckCtxThis:
963 case CastCtxThis:
964 case LdARNumParams:
965 case LdRDSAddr:
966 case PredictLoc:
967 case PredictStk:
968 case ExitPlaceholder:
969 case CheckRange:
970 case ProfileObjClass:
971 case LdIfaceMethod:
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.
982 case DecRefNZ:
983 case AFWHBlockOn:
984 case IncRef:
985 case IncRefCtx:
986 case LdClosureCtx:
987 case StClosureCtx:
988 case StClosureArg:
989 case StContArKey:
990 case StContArValue:
991 case StRetVal:
992 case ConvStrToInt:
993 case OrdStr:
994 case CreateSSWH:
995 case NewLikeArray:
996 case CheckRefs:
997 case LdClsCctx:
998 case BeginCatch:
999 case CheckSurpriseFlags:
1000 case CheckType:
1001 case FreeActRec:
1002 case RegisterLiveObj:
1003 case StContArResume:
1004 case StContArState:
1005 case ZeroErrorLevel:
1006 case RestoreErrorLevel:
1007 case CheckCold:
1008 case CheckInitProps:
1009 case CheckInitSProps:
1010 case ContArIncIdx:
1011 case ContArIncKey:
1012 case ContArUpdateIdx:
1013 case ContValid:
1014 case IncProfCounter:
1015 case IncStat:
1016 case IncStatGrouped:
1017 case CountBytecode:
1018 case ContPreNext:
1019 case ContStartedCheck:
1020 case ConvArrToBool:
1021 case ConvArrToDbl:
1022 case ConvArrToInt:
1023 case NewColFromArray:
1024 case ConvBoolToStr:
1025 case CountArray:
1026 case CountArrayFast:
1027 case StAsyncArResult:
1028 case StAsyncArResume:
1029 case StAsyncArSucceeded:
1030 case InstanceOf:
1031 case InstanceOfBitmask:
1032 case NInstanceOfBitmask:
1033 case InstanceOfIface:
1034 case InterfaceSupportsArr:
1035 case InterfaceSupportsDbl:
1036 case InterfaceSupportsInt:
1037 case InterfaceSupportsStr:
1038 case IsWaitHandle:
1039 case DbgAssertRefCount:
1040 case NSame:
1041 case Same:
1042 case Gt:
1043 case Gte:
1044 case Eq:
1045 case Lt:
1046 case Lte:
1047 case Neq:
1048 case GtStr:
1049 case GteStr:
1050 case LtStr:
1051 case LteStr:
1052 case EqStr:
1053 case NeqStr:
1054 case SameStr:
1055 case NSameStr:
1056 case IncTransCounter:
1057 case LdBindAddr:
1058 case LdAsyncArParentChain:
1059 case LdSSwitchDestFast:
1060 case RBTraceEntry:
1061 case RBTraceMsg:
1062 case ConvIntToBool:
1063 case ConvIntToDbl:
1064 case ConvStrToArr: // decrefs src, but src is a string
1065 case ConvStrToBool:
1066 case ConvStrToDbl:
1067 case DerefClsRDSHandle:
1068 case EagerSyncVMRegs:
1069 case ExtendsClass:
1070 case LdUnwinderValue:
1071 case GetCtxFwdCall:
1072 case LdCtx:
1073 case LdCctx:
1074 case LdClosure:
1075 case LdClsName:
1076 case LdAFWHActRec:
1077 case LdClsCtx:
1078 case LdContActRec:
1079 case LdContArKey:
1080 case LdContArValue:
1081 case LdContField:
1082 case LdContResumeAddr:
1083 case LdClsCachedSafe:
1084 case LdClsInitData:
1085 case UnwindCheckSideExit:
1086 case LdCns:
1087 case LdClsMethod:
1088 case LdClsMethodCacheCls:
1089 case LdClsMethodCacheFunc:
1090 case LdClsMethodFCacheFunc:
1091 case ProfilePackedArray:
1092 case ProfileStructArray:
1093 case ProfileSwitchDest:
1094 case LdFuncCachedSafe:
1095 case LdFuncNumParams:
1096 case LdGblAddr:
1097 case LdGblAddrDef:
1098 case LdObjClass:
1099 case LdObjInvoke:
1100 case LdStrLen:
1101 case StringIsset:
1102 case LdSwitchDblIndex:
1103 case LdSwitchStrIndex:
1104 case LdVectorBase:
1105 case LdWHResult:
1106 case LdWHState:
1107 case LookupClsRDSHandle:
1108 case GetCtxFwdCallDyn:
1109 case DbgTraceCall:
1110 case InitCtx:
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:
1116 case ColIsEmpty:
1117 case ColIsNEmpty:
1118 case ConvCellToBool:
1119 case ConvObjToBool:
1120 case CountCollection:
1121 case LdVectorSize:
1122 case VectorHasImmCopy:
1123 case CheckPackedArrayBounds:
1124 case LdColArray:
1125 case EnterFrame:
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).
1133 case DecRef:
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);
1146 return 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
1155 * eval stack.
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 {
1168 inst.src(2),
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
1177 case BaseG:
1178 case Clone:
1179 case RaiseArrayIndexNotice:
1180 case RaiseArrayKeyNotice:
1181 case RaiseUninitLoc:
1182 case RaiseUndefProp:
1183 case RaiseMissingArg:
1184 case RaiseError:
1185 case RaiseNotice:
1186 case RaiseWarning:
1187 case ConvCellToStr:
1188 case ConvObjToStr:
1189 case Count: // re-enters on CountableClass
1190 case CIterFree: // decrefs context object in iter
1191 case MIterFree:
1192 case IterFree:
1193 case EqX:
1194 case GteX:
1195 case GtX:
1196 case LteX:
1197 case LtX:
1198 case NeqX:
1199 case DecodeCufIter:
1200 case ConvCellToArr: // decrefs src, may read obj props
1201 case ConvCellToObj: // decrefs src
1202 case ConvObjToArr: // decrefs src
1203 case GenericIdx:
1204 case InitProps:
1205 case InitSProps:
1206 case OODeclExists:
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
1216 case LookupCns:
1217 case LookupCnsE:
1218 case LookupCnsU:
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
1229 case LdClsCtor:
1230 case ConcatStrStr:
1231 case PrintStr:
1232 case PrintBool:
1233 case PrintInt:
1234 case ConcatIntStr:
1235 case ConcatStrInt:
1236 case LdSSwitchDestSlow:
1237 case ConvObjToDbl:
1238 case ConvObjToInt:
1239 case MapAddElemC:
1240 case ColAddNewElemC:
1241 case CoerceStrToInt:
1242 case CoerceStrToDbl:
1243 case CoerceCellToDbl:
1244 case CoerceCellToInt:
1245 case CoerceCellToBool:
1246 case ConvCellToInt:
1247 case ConvResToStr:
1248 case ConcatStr3:
1249 case ConcatStr4:
1250 case ConvCellToDbl:
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).
1262 case ABCUnblock:
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.
1270 case DbgTrashStk:
1271 case DbgTrashFrame:
1272 case DbgTrashMem:
1273 return IrrelevantEffects {};
1275 //////////////////////////////////////////////////////////////////////
1279 not_reached();
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) {
1290 always_assert_flog(
1291 fp->type() <= TFramePtr,
1292 "Non frame pointer in memory effects"
1296 auto check_obj = [&] (SSATmp* obj) {
1297 always_assert_flog(
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);
1308 match<void>(
1310 [&] (GeneralEffects x) {
1311 check(x.loads);
1312 check(x.stores);
1313 check(x.moves);
1314 check(x.kills);
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
1332 * depth.
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); }
1349 return true;
1352 //////////////////////////////////////////////////////////////////////
1356 MemEffects memory_effects(const IRInstruction& inst) {
1357 auto const ret = memory_effects_impl(inst);
1358 assertx(check_effects(inst, ret));
1359 return ret;
1362 //////////////////////////////////////////////////////////////////////
1364 MemEffects canonicalize(MemEffects me) {
1365 using R = MemEffects;
1366 return match<R>(
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 {
1390 x.destroys_locals,
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>(
1408 effects,
1409 [&] (GeneralEffects x) {
1410 return sformat("mlsmk({} ; {} ; {} ; {})",
1411 show(x.loads),
1412 show(x.stores),
1413 show(x.moves),
1414 show(x.kills)
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) {
1437 return AStack {
1438 inst->src(0),
1439 inst->extra<DefInlineFP>()->spOffset.offset + int32_t{kNumActRecCells} - 1,
1440 int32_t{kNumActRecCells}
1444 //////////////////////////////////////////////////////////////////////