2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
19 #include <folly/Optional.h>
21 #include "hphp/runtime/vm/act-rec.h"
22 #include "hphp/runtime/vm/bytecode.h"
23 #include "hphp/runtime/vm/func.h"
24 #include "hphp/runtime/vm/native-data.h"
25 #include "hphp/runtime/vm/vm-regs.h"
26 #include "hphp/runtime/vm/jit/types.h"
27 #include "hphp/util/alloc.h"
31 //////////////////////////////////////////////////////////////////////
34 * Indicates how a function got resumed.
36 enum class ResumeMode
: uint8_t {
37 // The function was regularly called and its frame is located on the stack.
38 // This is the only valid mode for regular (non-async, non-generator)
39 // functions. Async functions are executed in this mode (also called eager
40 // execution) until reaching the first blocking await statement. Generators
41 // and async generators run in this mode only during argument type enforcement
42 // and suspend their execution immediately afterwards using the CreateCont
46 // Execution of the function was resumed by the asio scheduler upon completion
47 // of the awaited WaitHandle. Frame of the function is stored on the heap
48 // colocated with the Resumable structure. Valid only for async functions and
49 // async generators. An AsyncGeneratorWaitHandle is associated with async
53 // Execution of the function was resumed by generator iteration (e.g. by
54 // calling next() or send()). Frame of the function is stored on the heap
55 // colocated with the Resumable structure. Valid only for generators and
56 // async generators. Async generators are considered to be eagerly executed
57 // in this mode and don't have any associated AsyncGeneratorWaitHandle.
61 char* resumeModeShortName(ResumeMode resumeMode
);
62 folly::Optional
<ResumeMode
> nameToResumeMode(const std::string
& name
);
64 ALWAYS_INLINE
bool isResumed(const ActRec
* ar
) {
65 assertx(ar
&& ar
->func()->validate());
66 // VM stack does not have resumed ActRecs.
67 if (LIKELY(isValidVMStackAddress(ar
))) return false;
68 // Native stack may have fake ActRecs, which are not resumed.
69 if (UNLIKELY(((uintptr_t)ar
- s_stackLimit
) < s_stackSize
)) return false;
70 // All ActRecs on the heap must be resumed.
74 ResumeMode
resumeModeFromActRecImpl(ActRec
* ar
);
75 ALWAYS_INLINE ResumeMode
resumeModeFromActRec(ActRec
* ar
) {
76 if (LIKELY(!isResumed(ar
))) return ResumeMode::None
;
77 return resumeModeFromActRecImpl(ar
);
81 * Header of the resumable frame used by async functions:
83 * NativeNode* -> +--------------------------------+ low address
84 * | kind=AsyncFuncFrame |
85 * +--------------------------------+
86 * | Function locals and iterators |
87 * Resumable* -> +--------------------------------+
88 * | ActRec in Resumable |
89 * +--------------------------------+
90 * | Rest of Resumable |
91 * ObjectData* -> +--------------------------------+
92 * | c_AsyncFuncWaitHandle |
93 * +--------------------------------+ high address
95 * Header of the native frame used by generators:
97 * NativeNode* -> +--------------------------------+ low address
99 * +--------------------------------+
100 * | Function locals and iterators |
101 * BaseGenerator* -> +--------------------------------+
102 * < NativeData > | ActRec in Resumable |
103 * +--------------------------------+
104 * | Rest of Resumable |
105 * +--------------------------------+
106 * | Rest of [Async]Generator |
107 * ObjectData* -> +--------------------------------+
109 * +--------------------------------+ high address
111 struct alignas(16) Resumable
{
112 // This function is used only by AFWH, temporary till AFWH is converted to HNI
113 static Resumable
* FromObj(ObjectData
* obj
) {
114 return reinterpret_cast<Resumable
*>(obj
) - 1;
116 static const Resumable
* FromObj(const ObjectData
* obj
) {
117 return reinterpret_cast<const Resumable
*>(obj
) - 1;
119 static constexpr ptrdiff_t arOff() {
120 return offsetof(Resumable
, m_actRec
);
122 static constexpr ptrdiff_t resumeAddrOff() {
123 return offsetof(Resumable
, m_resumeAddr
);
125 static constexpr ptrdiff_t suspendOffsetOff() {
126 return offsetof(Resumable
, m_suspendOffset
);
128 static constexpr ptrdiff_t dataOff() {
129 return sizeof(Resumable
);
131 static constexpr size_t getFrameSize(size_t numSlots
) {
132 return numSlots
* sizeof(TypedValue
);
135 // This function is temporary till we move AFWH to HNI
136 static Resumable
* Create(size_t frameSize
, size_t totalSize
) {
138 (void)type_scan::getIndexForMalloc
<ActRec
>();
139 auto node
= new (tl_heap
->objMalloc(totalSize
))
140 NativeNode(HeaderKind::AsyncFuncFrame
,
141 sizeof(NativeNode
) + frameSize
+ sizeof(Resumable
));
142 auto frame
= reinterpret_cast<char*>(node
+ 1);
143 return reinterpret_cast<Resumable
*>(frame
+ frameSize
);
147 void initialize(const ActRec
* fp
, jit::TCA resumeAddr
,
148 Offset suspendOffset
, size_t frameSize
, size_t totalSize
) {
150 assertx(isResumed(fp
) == clone
);
151 DEBUG_ONLY
auto const func
= fp
->func();
153 assertx(func
->isResumable());
154 assertx(func
->contains(suspendOffset
));
155 // Check memory alignment
156 assertx((((uintptr_t) actRec()) & (sizeof(TypedValue
) - 1)) == 0);
159 // Copy ActRec, locals and iterators
160 auto src
= reinterpret_cast<const char*>(fp
) - frameSize
;
161 auto dst
= reinterpret_cast<char*>(actRec()) - frameSize
;
162 wordcpy(dst
, src
, frameSize
+ sizeof(ActRec
));
164 // If we are cloning a Resumable, only copy the ActRec. The
165 // caller will take care of copying locals, setting the VarEnv, etc.
166 // When called from AFWH::Create or Generator::Create we know we are
167 // going to overwrite m_sfp and m_savedRip, so don't copy them here.
168 auto src
= reinterpret_cast<const char*>(fp
);
169 auto dst
= reinterpret_cast<char*>(actRec());
170 wordcpy(dst
+ kNativeFrameSize
,
171 src
+ kNativeFrameSize
,
172 sizeof(ActRec
) - kNativeFrameSize
);
175 // Populate Resumable.
176 m_resumeAddr
= resumeAddr
;
177 m_offsetAndSize
= (totalSize
<< 32 | suspendOffset
);
180 template<class T
> static void Destroy(size_t size
, T
* obj
) {
181 auto const base
= reinterpret_cast<char*>(obj
+ 1) - size
;
183 tl_heap
->objFree(base
, size
);
186 ActRec
* actRec() { return &m_actRec
; }
187 const ActRec
* actRec() const { return &m_actRec
; }
188 jit::TCA
resumeAddr() const { return m_resumeAddr
; }
189 Offset
suspendOffset() const {
190 assertx(m_actRec
.func()->contains(m_suspendOffset
));
191 return m_suspendOffset
;
193 Offset
resumeFromAwaitOffset() const {
194 assertx(m_actRec
.func()->contains(m_suspendOffset
));
195 auto const suspendPC
= m_actRec
.func()->at(m_suspendOffset
);
196 assertx(peek_op(suspendPC
) == OpAwait
|| peek_op(suspendPC
) == OpAwaitAll
);
197 auto const resumeOffset
= m_suspendOffset
+ instrLen(suspendPC
);
198 assertx(m_actRec
.func()->contains(resumeOffset
));
201 Offset
resumeFromYieldOffset() const {
202 assertx(m_actRec
.func()->contains(m_suspendOffset
));
203 // TODO(alexeyt) remove `yield from` and the need for this complexity
204 auto const pc
= m_actRec
.func()->at(m_suspendOffset
);
205 DEBUG_ONLY
auto const suspendedOp
= peek_op(pc
);
206 assertx(suspendedOp
== OpCreateCont
||
207 suspendedOp
== OpYield
||
208 suspendedOp
== OpYieldK
);
209 auto const resumeOffset
= m_suspendOffset
+ instrLen(pc
);
210 assertx(m_actRec
.func()->contains(resumeOffset
));
213 size_t size() const { return m_size
; }
215 void setResumeAddr(jit::TCA resumeAddr
, Offset suspendOffset
) {
216 assertx(m_actRec
.func()->contains(suspendOffset
));
217 m_resumeAddr
= resumeAddr
;
218 m_suspendOffset
= suspendOffset
;
222 // ActRec of the resumed frame.
226 jit::TCA m_resumeAddr
;
228 // Resume offset: bytecode offset from start of Unit's bytecode.
231 Offset m_suspendOffset
;
233 // Size of the memory block that includes this resumable.
236 uint64_t m_offsetAndSize
;
240 static_assert(Resumable::arOff() == 0,
241 "ActRec must be in the beginning of Resumable");
243 //////////////////////////////////////////////////////////////////////