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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-ret.h"
18 #include "hphp/runtime/vm/resumable.h"
19 #include "hphp/runtime/vm/jit/analysis.h"
20 #include "hphp/runtime/vm/jit/types.h"
21 #include "hphp/runtime/vm/jit/irgen.h"
22 #include "hphp/runtime/vm/jit/irgen-exit.h"
23 #include "hphp/runtime/vm/jit/irgen-inlining.h"
24 #include "hphp/runtime/vm/jit/irgen-internal.h"
26 #include "hphp/util/configs/hhir.h"
28 namespace HPHP::jit::irgen
{
32 //////////////////////////////////////////////////////////////////////
34 const StaticString
s_returnHook("SurpriseReturnHook");
37 void retSurpriseCheck(IRGS
& env
, SSATmp
* retVal
, AH afterHook
) {
38 ringbufferMsg(env
, Trace::RBTypeFuncExit
, curFunc(env
)->fullName());
42 gen(env
, CheckSurpriseFlags
, taken
, anyStackRegister(env
));
45 // Return value is no longer on the stack. If the ReturnHook throws, it
46 // is responsible for decrefing it.
47 hint(env
, Block::Hint::Unlikely
);
48 ringbufferMsg(env
, Trace::RBTypeMsg
, s_returnHook
.get());
49 gen(env
, ReturnHook
, fp(env
), retVal
);
55 void freeLocalsAndThis(IRGS
& env
) {
56 auto const localCount
= curFunc(env
)->numLocals();
58 auto const shouldFreeInline
= [&]() -> bool {
59 // We don't want to specialize on arg types for builtins
60 if (curFunc(env
)->arFuncPtr()) return false;
62 if (localCount
> Cfg::HHIR::InliningMaxReturnLocals
) {
65 auto numRefCounted
= int{0};
66 for (auto i
= uint32_t{0}; i
< localCount
; ++i
) {
67 if (env
.irb
->local(i
, DataTypeGeneric
).type
.maybe(TCounted
)) {
71 return numRefCounted
<= Cfg::HHIR::InliningMaxReturnDecRefs
;
74 if (shouldFreeInline
) {
75 decRefLocalsInline(env
);
77 gen(env
, GenericRetDecRefs
, fp(env
));
83 void normalReturn(IRGS
& env
, SSATmp
* retval
, bool suspended
) {
84 assertx(resumeMode(env
) == ResumeMode::None
);
85 assertx(!isInlining(env
));
87 // If we're on the eager side of an async function, we have to zero-out the
88 // TV aux of the return value, because it might be used as a flag if async
89 // eager return was requested.
90 auto const aux
= [&] {
91 if (suspended
) return AuxUnion
{0};
92 if (curFunc(env
)->isAsyncFunction()) {
93 return AuxUnion
{std::numeric_limits
<uint32_t>::max()};
98 auto const data
= RetCtrlData
{ offsetToReturnSlot(env
), false, aux
};
99 gen(env
, RetCtrl
, data
, sp(env
), fp(env
), retval
);
102 void asyncFunctionReturn(IRGS
& env
, SSATmp
* retVal
, bool suspended
) {
103 assertx(!isInlining(env
));
105 if (resumeMode(env
) == ResumeMode::None
) {
106 retSurpriseCheck(env
, retVal
, []{});
108 if (suspended
) return normalReturn(env
, retVal
, true);
110 // Return from an eagerly-executed async function: wrap the return value in
111 // a StaticWaitHandle object and return that normally, unless async eager
112 // return was requested.
113 auto const wrapped
= cond(
116 auto flags
= gen(env
, LdARFlags
, fp(env
));
119 cns(env
, static_cast<int32_t>(1 << ActRec::AsyncEagerRet
)));
120 gen(env
, JmpNZero
, taken
, test
);
123 return gen(env
, CreateSSWH
, retVal
);
128 normalReturn(env
, wrapped
, false);
133 auto const spAdjust
= offsetFromIRSP(env
, BCSPRelOffset
{-1});
135 // When surprise flag is set, the slow path is always used.
136 retSurpriseCheck(env
, retVal
, [&] {
137 gen(env
, AsyncFuncRetSlow
, IRSPRelOffsetData
{ spAdjust
}, sp(env
), fp(env
),
141 // Call stub that will mark this AFWH as finished, unblock parents and
142 // possibly take fast path to resume parent. Leave SP pointing to a single
143 // uninitialized cell which will be filled by the stub.
144 gen(env
, AsyncFuncRet
, IRSPRelOffsetData
{ spAdjust
}, sp(env
), fp(env
),
148 void generatorReturn(IRGS
& env
, SSATmp
* retval
) {
149 assertx(curFunc(env
)->isGenerator());
150 assertx(!isInlining(env
));
152 retSurpriseCheck(env
, retval
, []{});
156 GeneratorState
{ BaseGenerator::State::Done
},
159 if (!curFunc(env
)->isAsync()) {
160 // Clear generator's key.
161 auto const oldKey
= gen(env
, LdContArKey
, TInitCell
, fp(env
));
162 gen(env
, StContArKey
, fp(env
), cns(env
, TInitNull
));
163 decRef(env
, oldKey
, DecRefProfileId::GeneratorReturnOldKey
);
165 // Populate the generator's value with retval to support `getReturn`
166 auto const oldValue
= gen(env
, LdContArValue
, TInitCell
, fp(env
));
167 gen(env
, StContArValue
, fp(env
), retval
);
168 decRef(env
, oldValue
, DecRefProfileId::GeneratorReturnOldValue
);
169 retval
= cns(env
, TInitNull
);
171 assertx(retval
->isA(TInitNull
));
173 if (resumeMode(env
) == ResumeMode::Async
) {
174 auto const spAdjust
= offsetFromIRSP(env
, BCSPRelOffset
{-1});
175 gen(env
, AsyncGenRetR
, IRSPRelOffsetData
{ spAdjust
}, sp(env
), fp(env
));
179 retval
= gen(env
, CreateSSWH
, cns(env
, TInitNull
));
182 // Return control to the caller (Gen::next()).
183 assertx(resumeMode(env
) == ResumeMode::GenIter
);
184 auto const spAdjust
= offsetFromIRSP(env
, BCSPRelOffset
{-1});
185 auto const retData
= RetCtrlData
{ spAdjust
, true, AuxUnion
{0} };
186 gen(env
, RetCtrl
, retData
, sp(env
), fp(env
), retval
);
189 void implRet(IRGS
& env
, bool suspended
) {
190 auto const func
= curFunc(env
);
191 assertx(!suspended
|| func
->isAsyncFunction());
192 assertx(!suspended
|| resumeMode(env
) == ResumeMode::None
);
193 assertx(!isInlining(env
));
195 if (func
->isAsyncFunction() && resumeMode(env
) != ResumeMode::None
) {
196 gen(env
, AsyncFuncRetPrefetch
, fp(env
));
199 freeLocalsAndThis(env
);
201 // Pop the return value. Since it will be teleported to its place in memory,
202 // we don't care about the type.
203 auto const retval
= pop(env
, DataTypeGeneric
);
205 env
.irb
->exceptionStackBoundary();
207 // Async function has its own surprise check.
208 if (func
->isAsyncFunction()) {
209 return asyncFunctionReturn(env
, retval
, suspended
);
212 if (func
->isGenerator()) {
213 return generatorReturn(env
, retval
);
216 assertx(resumeMode(env
) == ResumeMode::None
);
217 retSurpriseCheck(env
, retval
, []{});
218 return normalReturn(env
, retval
, false);
221 //////////////////////////////////////////////////////////////////////
225 IRSPRelOffset
offsetToReturnSlot(IRGS
& env
) {
226 assertx(resumeMode(env
) == ResumeMode::None
);
227 auto const fpOff
= offsetOfFrame(fp(env
));
229 return *fpOff
+ kArRetOff
/ int32_t{sizeof(TypedValue
)};
232 void emitRetC(IRGS
& env
) {
233 if (isInlining(env
)) {
234 assertx(resumeMode(env
) == ResumeMode::None
);
241 void emitRetM(IRGS
& env
, uint32_t nvals
) {
242 assertx(resumeMode(env
) == ResumeMode::None
);
243 assertx(!curFunc(env
)->isResumable());
246 if (isInlining(env
)) {
251 // Pop the return values. Since they will be teleported to their places in
252 // memory, we don't care about their types.
253 for (int i
= 0; i
< nvals
- 1; i
++) {
254 gen(env
, StOutValue
, IndexData(i
), fp(env
), pop(env
, DataTypeGeneric
));
260 void emitRetCSuspended(IRGS
& env
) {
261 assertx(curFunc(env
)->isAsyncFunction());
262 assertx(resumeMode(env
) == ResumeMode::None
);
264 if (isInlining(env
)) {
265 suspendFromInlined(env
, pop(env
, DataTypeGeneric
));
271 //////////////////////////////////////////////////////////////////////