Move ActRec::Flags::MultiReturn to CallFlags::Flags::HasInOut
[hiphop-php.git] / hphp / runtime / vm / bytecode.h
blob5c60b8a0f0c7f5d91f74fd313ea2fa1aa299bb63
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_BYTECODE_H_
18 #define incl_HPHP_VM_BYTECODE_H_
20 #include "hphp/runtime/base/array-iterator.h"
21 #include "hphp/runtime/base/rds.h"
22 #include "hphp/runtime/base/rds-util.h"
23 #include "hphp/runtime/base/record-array.h"
24 #include "hphp/runtime/base/record-data.h"
25 #include "hphp/runtime/base/tv-arith.h"
26 #include "hphp/runtime/base/tv-conversions.h"
27 #include "hphp/runtime/base/tv-mutate.h"
28 #include "hphp/runtime/base/tv-variant.h"
29 #include "hphp/runtime/base/tv-refcount.h"
31 #include "hphp/runtime/vm/act-rec.h"
32 #include "hphp/runtime/vm/call-flags.h"
33 #include "hphp/runtime/vm/class.h"
34 #include "hphp/runtime/vm/class-meth-data-ref.h"
35 #include "hphp/runtime/vm/func.h"
36 #include "hphp/runtime/vm/name-value-table.h"
37 #include "hphp/runtime/vm/unit.h"
39 #include "hphp/runtime/vm/jit/types.h"
41 #include "hphp/util/arena.h"
42 #include "hphp/util/type-traits.h"
44 #include <type_traits>
46 namespace HPHP {
47 ///////////////////////////////////////////////////////////////////////////////
49 struct ActRec;
50 struct Func;
51 struct Resumable;
53 ///////////////////////////////////////////////////////////////////////////////
55 #define EVAL_FILENAME_SUFFIX ") : eval()'d code"
57 // perform the set(op) operation on lhs & rhs, leaving the result in lhs.
58 // The old value of lhs is decrefed. Caller must call tvToCell() if lhs or
59 // rhs might be a ref.
60 ALWAYS_INLINE
61 void setopBody(tv_lval lhs, SetOpOp op, Cell* rhs) {
62 assertx(cellIsPlausible(*lhs));
63 assertx(cellIsPlausible(*rhs));
65 switch (op) {
66 case SetOpOp::PlusEqual: cellAddEq(lhs, *rhs); return;
67 case SetOpOp::MinusEqual: cellSubEq(lhs, *rhs); return;
68 case SetOpOp::MulEqual: cellMulEq(lhs, *rhs); return;
69 case SetOpOp::DivEqual: cellDivEq(lhs, *rhs); return;
70 case SetOpOp::PowEqual: cellPowEq(lhs, *rhs); return;
71 case SetOpOp::ModEqual: cellModEq(lhs, *rhs); return;
72 case SetOpOp::ConcatEqual: cellConcatEq(lhs, *rhs); return;
73 case SetOpOp::AndEqual: cellBitAndEq(lhs, *rhs); return;
74 case SetOpOp::OrEqual: cellBitOrEq(lhs, *rhs); return;
75 case SetOpOp::XorEqual: cellBitXorEq(lhs, *rhs); return;
76 case SetOpOp::SlEqual: cellShlEq(lhs, *rhs); return;
77 case SetOpOp::SrEqual: cellShrEq(lhs, *rhs); return;
78 case SetOpOp::PlusEqualO: cellAddEqO(lhs, *rhs); return;
79 case SetOpOp::MinusEqualO: cellSubEqO(lhs, *rhs); return;
80 case SetOpOp::MulEqualO: cellMulEqO(lhs, *rhs); return;
82 not_reached();
85 ///////////////////////////////////////////////////////////////////////////////
88 * Variable environment.
90 * A variable environment consists of the locals for the current function
91 * (either pseudo-main, global function, or method), plus any variables that
92 * are dynamically defined.
94 * Logically, a global function or method starts off with a variable
95 * environment that contains only its locals, but a pseudo-main is handed
96 * its caller's existing variable environment. Generally, however, we don't
97 * create a variable environment for global functions or methods until it
98 * actually needs one (i.e. if it is about to include a pseudo-main, or if
99 * it uses dynamic variable lookups).
101 * Named locals always appear in the expected place on the stack, even after
102 * a VarEnv is attached. Internally uses a NameValueTable to hook up names to
103 * the local locations.
105 struct VarEnv {
106 private:
107 NameValueTable m_nvTable;
108 uint16_t m_depth;
109 const bool m_global;
111 public:
112 explicit VarEnv();
113 explicit VarEnv(ActRec* fp);
114 explicit VarEnv(const VarEnv* varEnv, ActRec* fp);
115 ~VarEnv();
117 // Free the VarEnv and locals for the given frame
118 // which must have a VarEnv
119 static void deallocate(ActRec* fp);
121 // Allocates a local VarEnv and attaches it to the existing FP.
122 static VarEnv* createLocal(ActRec* fp);
124 // Allocate a global VarEnv. Initially not attached to any frame.
125 static void createGlobal();
127 VarEnv* clone(ActRec* fp) const;
129 void suspend(const ActRec* oldFP, ActRec* newFP);
130 void enterFP(ActRec* oldFP, ActRec* newFP);
131 void exitFP(ActRec* fp);
133 void set(const StringData* name, tv_rval tv);
134 TypedValue* lookup(const StringData* name);
135 TypedValue* lookupAdd(const StringData* name);
136 bool unset(const StringData* name);
138 Array getDefinedVariables() const;
140 // Used for save/store m_cfp for debugger
141 ActRec* getFP() const { return m_nvTable.getFP(); }
142 bool isGlobalScope() const { return m_global; }
145 ///////////////////////////////////////////////////////////////////////////////
148 * Returns true iff `ar` represents a frame on the VM eval stack or a Resumable
149 * object on the PHP heap.
151 * The `may_be_non_runtime` flag should be set if we aren't guaranteed to be
152 * running in a "Hack runtime" context---e.g., if we're in the JIT or an
153 * extension thread, etc. This function is pretty much guaranteed to return
154 * false if we're not in the runtime, but the caller might be runtime-agnostic.
156 bool isVMFrame(const ActRec* ar, bool may_be_non_runtime = false);
159 * Returns true iff the given address is one of the special debugger return
160 * helpers.
162 bool isDebuggerReturnHelper(void* addr);
165 * If ar->m_savedRip points somewhere in the TC that is not a return helper,
166 * change it to point to an appropriate return helper. The two different
167 * versions are for the different needs of the C++ unwinder and debugger hooks,
168 * respectively.
170 void unwindPreventReturnToTC(ActRec* ar);
171 void debuggerPreventReturnToTC(ActRec* ar);
174 * Call debuggerPreventReturnToTC() on all live VM frames in this thread.
176 void debuggerPreventReturnsToTC();
178 ///////////////////////////////////////////////////////////////////////////////
180 void frame_free_locals_no_hook(ActRec* fp);
182 #define tvReturn(x) \
183 ([&] { \
184 TypedValue val_; \
185 new (&val_) Variant(x); \
186 assertx(!isRefType(val_.m_type) && val_.m_type != KindOfUninit); \
187 return val_; \
188 }())
190 Class* arGetContextClass(const ActRec* ar);
192 ///////////////////////////////////////////////////////////////////////////////
194 // Used by extension functions that take a PHP "callback", since they need to
195 // figure out the callback context once and call it multiple times. (e.g.
196 // array_map, array_filter, ...)
197 struct CallCtx {
198 const Func* func;
199 ObjectData* this_;
200 Class* cls;
201 StringData* invName;
202 bool dynamic;
205 constexpr size_t kNumIterCells = sizeof(Iter) / sizeof(Cell);
206 constexpr size_t kNumActRecCells = sizeof(ActRec) / sizeof(Cell);
208 ///////////////////////////////////////////////////////////////////////////////
211 * We pad all stack overflow checks by a small amount to allow for three
212 * things:
214 * - inlining functions without having to either do another stack
215 * check (or chase down prologues to smash checks to be bigger).
217 * - omitting stack overflow checks on leaf functions
219 * - delaying stack overflow checks on reentry
221 constexpr int kStackCheckLeafPadding = 100;
222 constexpr int kStackCheckReenterPadding = 9;
223 constexpr int kStackCheckPadding = kStackCheckLeafPadding +
224 kStackCheckReenterPadding;
226 // Interpreter evaluation stack.
227 struct Stack {
228 private:
229 TypedValue* m_elms;
230 TypedValue* m_top;
231 TypedValue* m_base; // Stack grows down, so m_base is beyond the end of
232 // m_elms.
234 public:
235 bool isAllocated() { return m_elms != nullptr; }
236 void* getStackLowAddress() const { return m_elms; }
237 void* getStackHighAddress() const { return m_base; }
238 bool isValidAddress(uintptr_t v) {
239 return v >= uintptr_t(m_elms) && v < uintptr_t(m_base);
241 void requestInit();
242 void requestExit();
244 static const int sSurprisePageSize;
245 static const unsigned sMinStackElms;
246 static void ValidateStackSize();
247 Stack();
248 ~Stack();
250 std::string toString(const ActRec* fp, int offset,
251 std::string prefix="") const;
253 bool wouldOverflow(int numCells) const;
256 * top --
257 * topOfStackOffset --
259 * Accessors for the x64 translator. Do not play on or around.
261 TypedValue*& top() {
262 return m_top;
265 static constexpr size_t topOfStackOffset() {
266 return offsetof(Stack, m_top);
269 static TypedValue* anyFrameStackBase(const ActRec* fp);
270 static TypedValue* frameStackBase(const ActRec* fp);
271 static TypedValue* resumableStackBase(const ActRec* fp);
273 ALWAYS_INLINE
274 size_t count() const {
275 return ((uintptr_t)m_base - (uintptr_t)m_top) / sizeof(TypedValue);
278 // Same as discard(), but meant to replace popC() iff the interpreter knows
279 // for certain that decrementing a refcount is unnecessary.
280 ALWAYS_INLINE
281 void popX() {
282 assertx(m_top != m_base);
283 assertx(!isRefcountedType(m_top->m_type));
284 tvDebugTrash(m_top);
285 m_top++;
288 ALWAYS_INLINE
289 void popC() {
290 assertx(m_top != m_base);
291 assertx(cellIsPlausible(*m_top));
292 tvDecRefGen(m_top);
293 tvDebugTrash(m_top);
294 m_top++;
297 ALWAYS_INLINE
298 void popV() {
299 assertx(m_top != m_base);
300 assertx(refIsPlausible(*m_top));
301 tvDecRefRef(m_top);
302 tvDebugTrash(m_top);
303 m_top++;
306 ALWAYS_INLINE
307 void popU() {
308 assertx(m_top != m_base);
309 assertx(m_top->m_type == KindOfUninit);
310 tvDebugTrash(m_top);
311 ++m_top;
314 ALWAYS_INLINE
315 void popTV() {
316 assertx(m_top != m_base);
317 assertx(tvIsPlausible(*m_top));
318 tvDecRefGen(m_top);
319 tvDebugTrash(m_top);
320 m_top++;
323 // popAR() should only be used to tear down a pre-live ActRec. Once
324 // an ActRec is live, it should be torn down using frame_free_locals()
325 // followed by discardAR() or ret().
326 ALWAYS_INLINE
327 void popAR() {
328 assertx(m_top != m_base);
329 ActRec* ar = (ActRec*)m_top;
330 if (ar->func()->cls() && ar->hasThis()) decRefObj(ar->getThis());
331 if (ar->magicDispatch()) {
332 decRefStr(ar->getInvName());
334 discardAR();
337 ALWAYS_INLINE
338 void discardAR() {
339 assertx(m_top != m_base);
340 if (debug) {
341 for (int i = 0; i < kNumActRecCells; ++i) {
342 tvDebugTrash(m_top + i);
345 m_top += kNumActRecCells;
346 assertx((uintptr_t)m_top <= (uintptr_t)m_base);
349 ALWAYS_INLINE
350 void ret() {
351 // Leave part of the activation on the stack, since the return value now
352 // resides there.
353 if (debug) {
354 for (int i = 0; i < kNumActRecCells - 1; ++i) {
355 tvDebugTrash(m_top + i);
358 m_top += kNumActRecCells - 1;
359 assertx((uintptr_t)m_top <= (uintptr_t)m_base);
362 ALWAYS_INLINE
363 void discard() {
364 assertx(m_top != m_base);
365 tvDebugTrash(m_top);
366 m_top++;
369 ALWAYS_INLINE
370 void ndiscard(size_t n) {
371 assertx((uintptr_t)&m_top[n] <= (uintptr_t)m_base);
372 if (debug) {
373 for (int i = 0; i < n; ++i) {
374 tvDebugTrash(m_top + i);
377 m_top += n;
380 ALWAYS_INLINE
381 void trim(Cell* c) {
382 assertx(c <= m_base);
383 assertx(m_top <= c);
384 if (debug) {
385 while (m_top < c) tvDebugTrash(m_top++);
386 } else {
387 m_top = c;
391 ALWAYS_INLINE
392 void dup() {
393 assertx(m_top != m_base);
394 assertx(m_top != m_elms);
395 assertx(!isRefType(m_top->m_type));
396 Cell* fr = m_top;
397 m_top--;
398 Cell* to = m_top;
399 cellDup(*fr, *to);
402 ALWAYS_INLINE
403 void pushUninit() {
404 assertx(m_top != m_elms);
405 m_top--;
406 tvWriteUninit(*m_top);
409 ALWAYS_INLINE
410 void pushNull() {
411 assertx(m_top != m_elms);
412 m_top--;
413 tvWriteNull(*m_top);
416 ALWAYS_INLINE
417 void pushNullUninit() {
418 assertx(m_top != m_elms);
419 m_top--;
420 m_top->m_data.num = 0;
421 m_top->m_type = KindOfUninit;
424 template<DataType t, class T> void pushVal(T v) {
425 assertx(m_top != m_elms);
426 m_top--;
427 *m_top = make_tv<t>(v);
429 ALWAYS_INLINE void pushBool(bool v) { pushVal<KindOfBoolean>(v); }
430 ALWAYS_INLINE void pushInt(int64_t v) { pushVal<KindOfInt64>(v); }
431 ALWAYS_INLINE void pushDouble(double v) { pushVal<KindOfDouble>(v); }
432 ALWAYS_INLINE void pushClass(Class* v) { pushVal<KindOfClass>(v); }
434 // This should only be called directly when the caller has
435 // already adjusted the refcount appropriately
436 ALWAYS_INLINE
437 void pushStringNoRc(StringData* s) {
438 assertx(m_top != m_elms);
439 m_top--;
440 *m_top = make_tv<KindOfString>(s);
443 ALWAYS_INLINE
444 void pushStaticString(const StringData* s) {
445 assertx(s->isStatic()); // No need to call s->incRefCount().
446 assertx(m_top != m_elms);
447 m_top--;
448 *m_top = make_tv<KindOfPersistentString>(s);
451 // These should only be called directly when the caller has
452 // already adjusted the refcount appropriately
453 ALWAYS_INLINE
454 void pushArrayNoRc(ArrayData* a) {
455 assertx(a->isPHPArray());
456 assertx(m_top != m_elms);
457 m_top--;
458 *m_top = make_tv<KindOfArray>(a);
461 ALWAYS_INLINE
462 void pushVecNoRc(ArrayData* a) {
463 assertx(a->isVecArray());
464 assertx(m_top != m_elms);
465 m_top--;
466 *m_top = make_tv<KindOfVec>(a);
469 ALWAYS_INLINE
470 void pushDictNoRc(ArrayData* a) {
471 assertx(a->isDict());
472 assertx(m_top != m_elms);
473 m_top--;
474 *m_top = make_tv<KindOfDict>(a);
477 ALWAYS_INLINE
478 void pushKeysetNoRc(ArrayData* a) {
479 assertx(a->isKeyset());
480 assertx(m_top != m_elms);
481 m_top--;
482 *m_top = make_tv<KindOfKeyset>(a);
485 ALWAYS_INLINE
486 void pushArray(ArrayData* a) {
487 assertx(a);
488 pushArrayNoRc(a);
489 a->incRefCount();
492 ALWAYS_INLINE
493 void pushVec(ArrayData* a) {
494 assertx(a);
495 pushVecNoRc(a);
496 a->incRefCount();
499 ALWAYS_INLINE
500 void pushDict(ArrayData* a) {
501 assertx(a);
502 pushDictNoRc(a);
503 a->incRefCount();
506 ALWAYS_INLINE
507 void pushKeyset(ArrayData* a) {
508 assertx(a);
509 pushKeysetNoRc(a);
510 a->incRefCount();
513 ALWAYS_INLINE
514 void pushStaticArray(const ArrayData* a) {
515 assertx(a->isStatic()); // No need to call a->incRefCount().
516 assertx(a->isPHPArray());
517 assertx(m_top != m_elms);
518 m_top--;
519 *m_top = make_tv<KindOfPersistentArray>(a);
522 ALWAYS_INLINE
523 void pushStaticVec(const ArrayData* a) {
524 assertx(a->isStatic()); // No need to call a->incRefCount().
525 assertx(a->isVecArray());
526 assertx(m_top != m_elms);
527 m_top--;
528 *m_top = make_tv<KindOfPersistentVec>(a);
531 ALWAYS_INLINE
532 void pushStaticDict(const ArrayData* a) {
533 assertx(a->isStatic()); // No need to call a->incRefCount().
534 assertx(a->isDict());
535 assertx(m_top != m_elms);
536 m_top--;
537 *m_top = make_tv<KindOfPersistentDict>(a);
540 ALWAYS_INLINE
541 void pushStaticKeyset(const ArrayData* a) {
542 assertx(a->isStatic()); // No need to call a->incRefCount().
543 assertx(a->isKeyset());
544 assertx(m_top != m_elms);
545 m_top--;
546 *m_top = make_tv<KindOfPersistentKeyset>(a);
549 // This should only be called directly when the caller has
550 // already adjusted the refcount appropriately
551 ALWAYS_INLINE
552 void pushObjectNoRc(ObjectData* o) {
553 assertx(m_top != m_elms);
554 m_top--;
555 *m_top = make_tv<KindOfObject>(o);
558 ALWAYS_INLINE
559 void pushObject(ObjectData* o) {
560 pushObjectNoRc(o);
561 o->incRefCount();
564 ALWAYS_INLINE
565 void pushRecordNoRc(RecordData* r) {
566 assertx(m_top != m_elms);
567 m_top--;
568 *m_top = make_tv<KindOfRecord>(r);
571 ALWAYS_INLINE
572 void pushRecordArrayNoRc(RecordArray* r) {
573 assertx(m_top != m_elms);
574 m_top--;
575 *m_top = make_tv<KindOfArray>(r);
578 ALWAYS_INLINE
579 void pushFunc(Func* f) {
580 m_top--;
581 *m_top = make_tv<KindOfFunc>(f);
584 ALWAYS_INLINE
585 void pushClsMethNoRc(ClsMethDataRef clsMeth) {
586 m_top--;
587 *m_top = make_tv<KindOfClsMeth>(clsMeth);
590 ALWAYS_INLINE
591 void nalloc(size_t n) {
592 assertx((uintptr_t)(m_top - n) <= (uintptr_t)m_base);
593 m_top -= n;
596 ALWAYS_INLINE
597 Cell* allocC() {
598 assertx(m_top != m_elms);
599 m_top--;
600 return (Cell*)m_top;
603 ALWAYS_INLINE
604 Ref* allocV() {
605 assertx(m_top != m_elms);
606 m_top--;
607 return (Ref*)m_top;
610 ALWAYS_INLINE
611 TypedValue* allocTV() {
612 assertx(m_top != m_elms);
613 m_top--;
614 return m_top;
617 ALWAYS_INLINE
618 ActRec* allocA() {
619 assertx((uintptr_t)(m_top - kNumActRecCells) >= (uintptr_t)m_elms);
620 assertx(kNumActRecCells * sizeof(Cell) == sizeof(ActRec));
621 m_top -= kNumActRecCells;
622 return (ActRec*)m_top;
625 ALWAYS_INLINE
626 void allocI() {
627 assertx(kNumIterCells * sizeof(Cell) == sizeof(Iter));
628 assertx((uintptr_t)(m_top - kNumIterCells) >= (uintptr_t)m_elms);
629 m_top -= kNumIterCells;
632 ALWAYS_INLINE
633 void replaceC(const Cell c) {
634 assertx(m_top != m_base);
635 assertx(!isRefType(m_top->m_type));
636 tvDecRefGen(m_top);
637 *m_top = c;
640 template <DataType DT>
641 ALWAYS_INLINE
642 void replaceC() {
643 assertx(m_top != m_base);
644 assertx(!isRefType(m_top->m_type));
645 tvDecRefGen(m_top);
646 *m_top = make_tv<DT>();
649 template <DataType DT, typename T>
650 ALWAYS_INLINE
651 void replaceC(T value) {
652 assertx(m_top != m_base);
653 assertx(!isRefType(m_top->m_type));
654 tvDecRefGen(m_top);
655 *m_top = make_tv<DT>(value);
658 ALWAYS_INLINE
659 void replaceTV(const TypedValue& tv) {
660 assertx(m_top != m_base);
661 tvDecRefGen(m_top);
662 *m_top = tv;
665 template <DataType DT>
666 ALWAYS_INLINE
667 void replaceTV() {
668 assertx(m_top != m_base);
669 tvDecRefGen(m_top);
670 *m_top = make_tv<DT>();
673 template <DataType DT, typename T>
674 ALWAYS_INLINE
675 void replaceTV(T value) {
676 assertx(m_top != m_base);
677 tvDecRefGen(m_top);
678 *m_top = make_tv<DT>(value);
681 ALWAYS_INLINE
682 Cell* topC() {
683 assertx(m_top != m_base);
684 return tvAssertCell(m_top);
687 ALWAYS_INLINE
688 Ref* topV() {
689 assertx(m_top != m_base);
690 assertx(isRefType(m_top->m_type));
691 return (Ref*)m_top;
694 ALWAYS_INLINE
695 TypedValue* topTV() {
696 assertx(m_top != m_base);
697 return m_top;
700 ALWAYS_INLINE
701 ActRec* indA(size_t ind) {
702 assertx(m_top != m_base);
703 assertx(count() > ind + kNumActRecCells);
704 return (ActRec*)&m_top[ind];
707 ALWAYS_INLINE
708 Cell* indC(size_t ind) {
709 assertx(m_top != m_base);
710 assertx(!isRefType(m_top[ind].m_type));
711 return tvAssertCell(&m_top[ind]);
714 ALWAYS_INLINE
715 TypedValue* indTV(size_t ind) {
716 assertx(m_top != m_base);
717 return &m_top[ind];
721 //////////////////////////////////////////////////////////////////////
724 * Visit all the slots on a live eval stack, stopping when we reach
725 * the supplied activation record.
727 * The stack elements are visited from lower address to higher.
729 * This will not read the VM registers (pc, fp, sp), so it will
730 * perform the requested visitation independent of modifications to
731 * the VM stack or frame pointer.
733 template<class TV, class TVFun>
734 typename maybe_const<TV, TypedValue>::type
735 visitStackElems(const ActRec* const fp, TV* const stackTop, TVFun tvFun) {
736 const TypedValue* const base = Stack::anyFrameStackBase(fp);
737 auto cursor = stackTop;
738 assertx(cursor <= base);
740 while (cursor < base) {
741 tvFun(cursor++);
745 void resetCoverageCounters();
747 // The interpOne*() methods implement individual opcode handlers.
748 using InterpOneFunc = jit::TCA (*) (ActRec*, TypedValue*, Offset);
749 extern InterpOneFunc interpOneEntryPoints[];
751 bool doFCallUnpackTC(PC origpc, int32_t numInputs, CallFlags callFlags, void*);
752 bool doFCall(ActRec* ar, uint32_t numArgs, bool hasUnpack, CallFlags callFlags);
753 jit::TCA dispatchBB();
754 void pushFrameSlots(const Func* func, int nparams = 0);
755 Array getDefinedVariables(const ActRec*);
757 enum class StackArgsState { // tells prepareFuncEntry how much work to do
758 // the stack may contain more arguments than the function expects
759 Untrimmed,
760 // the stack has already been trimmed of any extra arguments, which
761 // have been teleported away into a variadic param
762 Trimmed
764 void enterVMAtPseudoMain(ActRec* enterFnAr, VarEnv* varEnv);
765 void enterVMAtFunc(ActRec* enterFnAr, StackArgsState stk, Array&& generics,
766 bool hasInOut, bool allowDynCallNoPointer);
767 void enterVMAtCurPC();
768 void prepareArrayArgs(ActRec* ar, const Cell args, Stack& stack,
769 int nregular, bool checkRefAnnot);
771 ///////////////////////////////////////////////////////////////////////////////
775 #endif