Updating submodules
[hiphop-php.git] / hphp / runtime / vm / bytecode.h
blob4b95a834d3621354ec9d45b72b9454a6e5743c58
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 #pragma once
19 #include "hphp/runtime/base/rds.h"
20 #include "hphp/runtime/base/rds-util.h"
21 #include "hphp/runtime/base/tv-arith.h"
22 #include "hphp/runtime/base/tv-array-like.h"
23 #include "hphp/runtime/base/tv-conversions.h"
24 #include "hphp/runtime/base/tv-mutate.h"
25 #include "hphp/runtime/base/tv-variant.h"
26 #include "hphp/runtime/base/tv-refcount.h"
27 #include "hphp/runtime/base/type-string.h"
29 #include "hphp/runtime/vm/act-rec.h"
30 #include "hphp/runtime/vm/class.h"
31 #include "hphp/runtime/vm/class-meth-data-ref.h"
32 #include "hphp/runtime/vm/func.h"
33 #include "hphp/runtime/vm/iter.h"
34 #include "hphp/runtime/vm/name-value-table.h"
35 #include "hphp/runtime/vm/prologue-flags.h"
36 #include "hphp/runtime/vm/unit.h"
38 #include "hphp/runtime/vm/jit/jit-resume-addr.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.
59 ALWAYS_INLINE
60 void setopBody(tv_lval lhs, SetOpOp op, TypedValue* rhs) {
61 assertx(tvIsPlausible(*lhs));
62 assertx(tvIsPlausible(*rhs));
64 switch (op) {
65 case SetOpOp::PlusEqual: tvAddEq(lhs, *rhs); return;
66 case SetOpOp::MinusEqual: tvSubEq(lhs, *rhs); return;
67 case SetOpOp::MulEqual: tvMulEq(lhs, *rhs); return;
68 case SetOpOp::DivEqual: tvDivEq(lhs, *rhs); return;
69 case SetOpOp::PowEqual: tvPowEq(lhs, *rhs); return;
70 case SetOpOp::ModEqual: tvModEq(lhs, *rhs); return;
71 case SetOpOp::ConcatEqual: tvConcatEq(lhs, *rhs); return;
72 case SetOpOp::AndEqual: tvBitAndEq(lhs, *rhs); return;
73 case SetOpOp::OrEqual: tvBitOrEq(lhs, *rhs); return;
74 case SetOpOp::XorEqual: tvBitXorEq(lhs, *rhs); return;
75 case SetOpOp::SlEqual: tvShlEq(lhs, *rhs); return;
76 case SetOpOp::SrEqual: tvShrEq(lhs, *rhs); return;
78 not_reached();
81 ///////////////////////////////////////////////////////////////////////////////
83 // Creates and initializes the global NamedValue table
84 void createGlobalNVTable();
85 // Collects the variables defined on the global NamedValue table
86 Array getDefinedVariables();
88 ///////////////////////////////////////////////////////////////////////////////
91 * Returns true iff `ar` represents a frame on the VM eval stack or a Resumable
92 * object on the PHP heap.
94 * The `may_be_non_runtime` flag should be set if we aren't guaranteed to be
95 * running in a "Hack runtime" context---e.g., if we're in the JIT or an
96 * extension thread, etc. This function is pretty much guaranteed to return
97 * false if we're not in the runtime, but the caller might be runtime-agnostic.
99 bool isVMFrame(const ActRec* ar, bool may_be_non_runtime = false);
101 ///////////////////////////////////////////////////////////////////////////////
103 void frame_free_locals_no_hook(ActRec* fp);
105 #define tvReturn(x) \
106 ([&] { \
107 TypedValue val_; \
108 new (&val_) Variant(x); \
109 assertx(val_.m_type != KindOfUninit); \
110 return val_; \
111 }())
113 Class* arGetContextClass(const ActRec* ar);
115 ///////////////////////////////////////////////////////////////////////////////
117 // Used by extension functions that take a PHP "callback", since they need to
118 // figure out the callback context once and call it multiple times. (e.g.
119 // array_map, array_filter, ...)
120 struct CallCtx {
121 const Func* func;
122 ObjectData* this_;
123 Class* cls;
124 bool dynamic;
127 constexpr size_t kNumIterCells = sizeof(Iter) / sizeof(TypedValue);
128 constexpr size_t kNumActRecCells = sizeof(ActRec) / sizeof(TypedValue);
130 ///////////////////////////////////////////////////////////////////////////////
133 * We pad all stack overflow checks by a small amount to allow for three
134 * things:
136 * - inlining functions without having to either do another stack
137 * check (or chase down prologues to smash checks to be bigger).
139 * - omitting stack overflow checks on leaf functions
141 * - delaying stack overflow checks on reentry
143 constexpr int kStackCheckReenterPadding = 9;
145 ALWAYS_INLINE
146 int stackCheckPadding() {
147 return RO::EvalStackCheckLeafPadding + kStackCheckReenterPadding;
150 // Interpreter evaluation stack.
151 struct Stack {
152 private:
153 TypedValue* m_elms;
154 TypedValue* m_top;
155 TypedValue* m_base; // Stack grows down, so m_base is beyond the end of
156 // m_elms.
158 public:
159 bool isAllocated() { return m_elms != nullptr; }
160 void* getStackLowAddress() const { return m_elms; }
161 void* getStackHighAddress() const { return m_base; }
162 bool isValidAddress(uintptr_t v) {
163 return v >= uintptr_t(m_elms) && v < uintptr_t(m_base);
165 void requestInit();
166 void requestExit();
168 static const int sSurprisePageSize;
169 static const unsigned sMinStackElms;
170 static void ValidateStackSize();
171 Stack();
172 ~Stack();
174 std::string toString(const ActRec* fp, int offset,
175 std::string prefix="") const;
177 bool wouldOverflow(int numCells) const;
180 * top --
181 * topOfStackOffset --
183 * Accessors for the x64 translator. Do not play on or around.
185 TypedValue*& top() {
186 return m_top;
189 static constexpr size_t topOfStackOffset() {
190 return offsetof(Stack, m_top);
193 static TypedValue* anyFrameStackBase(const ActRec* fp);
194 static TypedValue* frameStackBase(const ActRec* fp);
195 static TypedValue* resumableStackBase(const ActRec* fp);
197 ALWAYS_INLINE
198 size_t count() const {
199 return ((uintptr_t)m_base - (uintptr_t)m_top) / sizeof(TypedValue);
202 // Same as discard(), but meant to replace popC() iff the interpreter knows
203 // for certain that decrementing a refcount is unnecessary.
204 ALWAYS_INLINE
205 void popX() {
206 assertx(m_top != m_base);
207 assertx(!isRefcountedType(m_top->m_type));
208 tvDebugTrash(m_top);
209 m_top++;
212 ALWAYS_INLINE
213 void popC() {
214 assertx(m_top != m_base);
215 assertx(tvIsPlausible(*m_top));
216 tvDecRefGen(m_top);
217 tvDebugTrash(m_top);
218 m_top++;
221 ALWAYS_INLINE
222 void popU() {
223 assertx(m_top != m_base);
224 assertx(m_top->m_type == KindOfUninit);
225 tvDebugTrash(m_top);
226 ++m_top;
229 ALWAYS_INLINE
230 void popTV() {
231 assertx(m_top != m_base);
232 assertx(tvIsPlausible(*m_top));
233 tvDecRefGen(m_top);
234 tvDebugTrash(m_top);
235 m_top++;
238 // popAR() should only be used to tear down a pre-live ActRec. Once
239 // an ActRec is live, it should be torn down using frame_free_locals()
240 // followed by discardAR() or ret().
241 ALWAYS_INLINE
242 void popAR() {
243 assertx(m_top != m_base);
244 ActRec* ar = (ActRec*)m_top;
245 if (ar->func()->cls() && ar->hasThis()) decRefObj(ar->getThis());
246 discardAR();
249 ALWAYS_INLINE
250 void discardAR() {
251 assertx(m_top != m_base);
252 if (debug) {
253 for (int i = 0; i < kNumActRecCells; ++i) {
254 tvDebugTrash(m_top + i);
257 m_top += kNumActRecCells;
258 assertx((uintptr_t)m_top <= (uintptr_t)m_base);
261 ALWAYS_INLINE
262 void retNoTrash() {
263 m_top += kNumActRecCells - 1;
264 assertx((uintptr_t)m_top <= (uintptr_t)m_base);
267 ALWAYS_INLINE
268 void ret() {
269 // Leave part of the activation on the stack, since the return value now
270 // resides there.
271 if (debug) {
272 for (int i = 0; i < kNumActRecCells - 1; ++i) {
273 tvDebugTrash(m_top + i);
276 m_top += kNumActRecCells - 1;
277 assertx((uintptr_t)m_top <= (uintptr_t)m_base);
280 ALWAYS_INLINE
281 void discard() {
282 assertx(m_top != m_base);
283 tvDebugTrash(m_top);
284 m_top++;
287 ALWAYS_INLINE
288 void ndiscard(size_t n) {
289 assertx((uintptr_t)&m_top[n] <= (uintptr_t)m_base);
290 if (debug) {
291 for (int i = 0; i < n; ++i) {
292 tvDebugTrash(m_top + i);
295 m_top += n;
298 ALWAYS_INLINE
299 void trim(TypedValue* c) {
300 assertx(c <= m_base);
301 assertx(m_top <= c);
302 if (debug) {
303 while (m_top < c) tvDebugTrash(m_top++);
304 } else {
305 m_top = c;
309 ALWAYS_INLINE
310 void dup() {
311 assertx(m_top != m_base);
312 assertx(m_top != m_elms);
313 TypedValue* fr = m_top;
314 m_top--;
315 TypedValue* to = m_top;
316 tvDup(*fr, *to);
319 ALWAYS_INLINE
320 void pushUninit() {
321 assertx(m_top != m_elms);
322 m_top--;
323 tvWriteUninit(*m_top);
326 ALWAYS_INLINE
327 void pushNull() {
328 assertx(m_top != m_elms);
329 m_top--;
330 tvWriteNull(*m_top);
333 ALWAYS_INLINE
334 void pushNullUninit() {
335 assertx(m_top != m_elms);
336 m_top--;
337 m_top->m_data.num = 0;
338 m_top->m_type = KindOfUninit;
341 template<DataType t, class T> void pushVal(T v) {
342 assertx(m_top != m_elms);
343 m_top--;
344 *m_top = make_tv<t>(v);
346 ALWAYS_INLINE void pushBool(bool v) { pushVal<KindOfBoolean>(v); }
347 ALWAYS_INLINE void pushInt(int64_t v) { pushVal<KindOfInt64>(v); }
348 ALWAYS_INLINE void pushDouble(double v) { pushVal<KindOfDouble>(v); }
349 ALWAYS_INLINE void pushClass(Class* v) { pushVal<KindOfClass>(v); }
351 // This should only be called directly when the caller has
352 // already adjusted the refcount appropriately
353 ALWAYS_INLINE
354 void pushStringNoRc(StringData* s) {
355 assertx(m_top != m_elms);
356 m_top--;
357 *m_top = make_tv<KindOfString>(s);
360 ALWAYS_INLINE
361 void pushStaticString(const StringData* s) {
362 assertx(s->isStatic()); // No need to call s->incRefCount().
363 assertx(m_top != m_elms);
364 m_top--;
365 *m_top = make_tv<KindOfPersistentString>(s);
368 ALWAYS_INLINE
369 void pushLazyClass(LazyClassData l) {
370 assertx(m_top != m_elms);
371 m_top--;
372 *m_top = make_tv<KindOfLazyClass>(l);
375 ALWAYS_INLINE
376 void pushEnumClassLabel(const StringData* s) {
377 assertx(s->isStatic()); // No need to call s->incRefCount().
378 assertx(m_top != m_elms);
379 m_top--;
380 *m_top = make_tv<KindOfEnumClassLabel>(s);
383 ALWAYS_INLINE
384 void pushArrayLikeNoRc(ArrayData* a) {
385 assertx(m_top != m_elms);
386 m_top--;
387 *m_top = make_array_like_tv(a);
390 ALWAYS_INLINE
391 void pushVecNoRc(ArrayData* a) {
392 assertx(a->isVecType());
393 assertx(m_top != m_elms);
394 m_top--;
395 *m_top = make_tv<KindOfVec>(a);
398 ALWAYS_INLINE
399 void pushDictNoRc(ArrayData* a) {
400 assertx(a->isDictType());
401 assertx(m_top != m_elms);
402 m_top--;
403 *m_top = make_tv<KindOfDict>(a);
406 ALWAYS_INLINE
407 void pushKeysetNoRc(ArrayData* a) {
408 assertx(a->isKeysetType());
409 assertx(m_top != m_elms);
410 m_top--;
411 *m_top = make_tv<KindOfKeyset>(a);
414 ALWAYS_INLINE
415 void pushArrayLike(ArrayData* a) {
416 assertx(a);
417 pushArrayLikeNoRc(a);
418 a->incRefCount();
421 ALWAYS_INLINE
422 void pushVec(ArrayData* a) {
423 assertx(a);
424 pushVecNoRc(a);
425 a->incRefCount();
428 ALWAYS_INLINE
429 void pushDict(ArrayData* a) {
430 assertx(a);
431 pushDictNoRc(a);
432 a->incRefCount();
435 ALWAYS_INLINE
436 void pushKeyset(ArrayData* a) {
437 assertx(a);
438 pushKeysetNoRc(a);
439 a->incRefCount();
442 ALWAYS_INLINE
443 void pushStaticArrayLike(const ArrayData* a) {
444 assertx(a->isStatic()); // No need to call a->incRefCount().
445 assertx(m_top != m_elms);
446 m_top--;
447 *m_top = make_persistent_array_like_tv(const_cast<ArrayData*>(a));
450 ALWAYS_INLINE
451 void pushStaticVec(const ArrayData* a) {
452 assertx(a->isStatic()); // No need to call a->incRefCount().
453 assertx(a->isVecType());
454 assertx(m_top != m_elms);
455 m_top--;
456 *m_top = make_tv<KindOfPersistentVec>(a);
459 ALWAYS_INLINE
460 void pushStaticDict(const ArrayData* a) {
461 assertx(a->isStatic()); // No need to call a->incRefCount().
462 assertx(a->isDictType());
463 assertx(m_top != m_elms);
464 m_top--;
465 *m_top = make_tv<KindOfPersistentDict>(a);
468 ALWAYS_INLINE
469 void pushStaticKeyset(const ArrayData* a) {
470 assertx(a->isStatic()); // No need to call a->incRefCount().
471 assertx(a->isKeysetType());
472 assertx(m_top != m_elms);
473 m_top--;
474 *m_top = make_tv<KindOfPersistentKeyset>(a);
477 // This should only be called directly when the caller has
478 // already adjusted the refcount appropriately
479 ALWAYS_INLINE
480 void pushObjectNoRc(ObjectData* o) {
481 assertx(m_top != m_elms);
482 m_top--;
483 *m_top = make_tv<KindOfObject>(o);
486 ALWAYS_INLINE
487 void pushObject(ObjectData* o) {
488 pushObjectNoRc(o);
489 o->incRefCount();
492 ALWAYS_INLINE
493 void pushFunc(Func* f) {
494 m_top--;
495 *m_top = make_tv<KindOfFunc>(f);
498 ALWAYS_INLINE
499 void pushRFuncNoRc(RFuncData* f) {
500 m_top--;
501 *m_top = make_tv<KindOfRFunc>(f);
504 ALWAYS_INLINE
505 void pushRFunc(RFuncData* f) {
506 pushRFuncNoRc(f);
507 f->incRefCount();
510 ALWAYS_INLINE
511 void pushClsMethNoRc(ClsMethDataRef clsMeth) {
512 m_top--;
513 *m_top = make_tv<KindOfClsMeth>(clsMeth);
516 ALWAYS_INLINE
517 void pushRClsMethNoRc(RClsMethData* clsMeth) {
518 m_top--;
519 *m_top = make_tv<KindOfRClsMeth>(clsMeth);
522 ALWAYS_INLINE
523 void nalloc(size_t n) {
524 assertx((uintptr_t)(m_top - n) <= (uintptr_t)m_base);
525 m_top -= n;
528 ALWAYS_INLINE
529 TypedValue* allocC() {
530 assertx(m_top != m_elms);
531 m_top--;
532 return (TypedValue*)m_top;
535 ALWAYS_INLINE
536 TypedValue* allocTV() {
537 assertx(m_top != m_elms);
538 m_top--;
539 return m_top;
542 ALWAYS_INLINE
543 ActRec* allocA() {
544 assertx((uintptr_t)(m_top - kNumActRecCells) >= (uintptr_t)m_elms);
545 assertx(kNumActRecCells * sizeof(TypedValue) == sizeof(ActRec));
546 m_top -= kNumActRecCells;
547 return (ActRec*)m_top;
550 ALWAYS_INLINE
551 void allocI() {
552 assertx(kNumIterCells * sizeof(TypedValue) == sizeof(Iter));
553 assertx((uintptr_t)(m_top - kNumIterCells) >= (uintptr_t)m_elms);
554 m_top -= kNumIterCells;
557 ALWAYS_INLINE
558 void replaceC(const TypedValue c) {
559 assertx(m_top != m_base);
560 tvDecRefGen(m_top);
561 *m_top = c;
564 template <DataType DT>
565 ALWAYS_INLINE
566 void replaceC() {
567 assertx(m_top != m_base);
568 tvDecRefGen(m_top);
569 *m_top = make_tv<DT>();
572 template <DataType DT, typename T>
573 ALWAYS_INLINE
574 void replaceC(T value) {
575 assertx(m_top != m_base);
576 tvDecRefGen(m_top);
577 *m_top = make_tv<DT>(value);
580 ALWAYS_INLINE
581 void replaceTV(const TypedValue& tv) {
582 assertx(m_top != m_base);
583 tvDecRefGen(m_top);
584 *m_top = tv;
587 template <DataType DT>
588 ALWAYS_INLINE
589 void replaceTV() {
590 assertx(m_top != m_base);
591 tvDecRefGen(m_top);
592 *m_top = make_tv<DT>();
595 template <DataType DT, typename T>
596 ALWAYS_INLINE
597 void replaceTV(T value) {
598 assertx(m_top != m_base);
599 tvDecRefGen(m_top);
600 *m_top = make_tv<DT>(value);
603 ALWAYS_INLINE
604 TypedValue* topC() {
605 assertx(m_top != m_base);
606 return tvAssertPlausible(m_top);
609 ALWAYS_INLINE
610 TypedValue* topTV() {
611 assertx(m_top != m_base);
612 return m_top;
615 ALWAYS_INLINE
616 ActRec* indA(size_t ind) {
617 assertx(m_top != m_base);
618 assertx(count() >= ind + kNumActRecCells);
619 return (ActRec*)&m_top[ind];
622 ALWAYS_INLINE
623 TypedValue* indC(size_t ind) {
624 assertx(m_top != m_base);
625 return tvAssertPlausible(&m_top[ind]);
628 ALWAYS_INLINE
629 TypedValue* indTV(size_t ind) {
630 assertx(m_top != m_base);
631 return &m_top[ind];
635 //////////////////////////////////////////////////////////////////////
637 void flush_evaluation_stack();
640 * Visit all the slots on a live eval stack, stopping when we reach
641 * the supplied activation record.
643 * The stack elements are visited from lower address to higher.
645 * This will not read the VM registers (pc, fp, sp), so it will
646 * perform the requested visitation independent of modifications to
647 * the VM stack or frame pointer.
649 template<class TV, class TVFun>
650 typename maybe_const<TV, TypedValue>::type
651 visitStackElems(const ActRec* const fp, TV* const stackTop, TVFun tvFun) {
652 const TypedValue* const base = Stack::anyFrameStackBase(fp);
653 auto cursor = stackTop;
654 assertx(cursor <= base);
656 while (cursor < base) {
657 tvFun(cursor++);
661 void resetCoverageCounters();
663 // Resolve a Static / Self / Parent class reference to a class.
664 Class* specialClsRefToCls(SpecialClsRef ref);
666 // The interpOne*() methods implement individual opcode handlers.
667 using InterpOneFunc = jit::JitResumeAddr (*) (ActRec*, TypedValue*, Offset);
668 extern InterpOneFunc interpOneEntryPoints[];
670 void doFCall(PrologueFlags prologueFlags, const Func* func,
671 uint32_t numArgsInclUnpack, void* ctx, jit::TCA retAddr);
672 bool funcEntry();
673 jit::JitResumeAddr dispatchBB();
674 Array getDefinedVariables(const ActRec*);
677 * Start a new nested instance of VM either at the beginning of a function
678 * determined by the enterFnAr frame, or at the current vmpc() location.
680 * Execution finishes by either returning, awaiting or yielding from the top
681 * level frame, in which case vmfp()/vmpc() are set to nullptr, or by throwing
682 * an exception, which callers usually process via exception_handler().
684 void enterVMAtFunc(ActRec* enterFnAr, uint32_t numArgsInclUnpack);
685 void enterVMAtCurPC();
686 uint32_t prepareUnpackArgs(const Func* func, uint32_t numArgs,
687 bool checkInOutAnnot);
689 ///////////////////////////////////////////////////////////////////////////////
693 #define incl_HPHP_VM_BYTECODE_INL_H_
694 #include "hphp/runtime/vm/bytecode-inl.h"
695 #undef incl_HPHP_VM_BYTECODE_INL_H_