Optimize struct element initialization
[hiphop-php.git] / hphp / runtime / vm / resumable.h
blobd09c14dea7fab9ccf908b045e4ebe2634572bf26
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 <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"
29 namespace HPHP {
31 //////////////////////////////////////////////////////////////////////
33 /**
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
43 // opcode.
44 None = 0,
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
50 // generators.
51 Async = 1,
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.
58 GenIter = 2,
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.
71 return true;
74 ResumeMode resumeModeFromActRecImpl(ActRec* ar);
75 ALWAYS_INLINE ResumeMode resumeModeFromActRec(ActRec* ar) {
76 if (LIKELY(!isResumed(ar))) return ResumeMode::None;
77 return resumeModeFromActRecImpl(ar);
80 /**
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
98 * | kind=NativeData |
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* -> +--------------------------------+
108 * | Parent object |
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) {
137 // Allocate memory.
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);
146 template<bool clone>
147 void initialize(const ActRec* fp, jit::TCA resumeAddr,
148 Offset suspendOffset, size_t frameSize, size_t totalSize) {
149 assertx(fp);
150 assertx(isResumed(fp) == clone);
151 DEBUG_ONLY auto const func = fp->func();
152 assertx(func);
153 assertx(func->isResumable());
154 assertx(func->contains(suspendOffset));
155 // Check memory alignment
156 assertx((((uintptr_t) actRec()) & (sizeof(TypedValue) - 1)) == 0);
158 if (!clone) {
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));
163 } else {
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;
182 obj->~T();
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));
199 return 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));
211 return 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;
221 private:
222 // ActRec of the resumed frame.
223 ActRec m_actRec;
225 // Resume address.
226 jit::TCA m_resumeAddr;
228 // Resume offset: bytecode offset from start of Unit's bytecode.
229 union {
230 struct {
231 Offset m_suspendOffset;
233 // Size of the memory block that includes this resumable.
234 int32_t m_size;
236 uint64_t m_offsetAndSize;
240 static_assert(Resumable::arOff() == 0,
241 "ActRec must be in the beginning of Resumable");
243 //////////////////////////////////////////////////////////////////////