2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/jit/mc-generator.h"
20 #include "hphp/runtime/vm/jit/irgen-ringbuffer.h"
21 #include "hphp/runtime/vm/jit/irgen-exit.h"
22 #include "hphp/runtime/vm/jit/irgen-inlining.h"
24 #include "hphp/runtime/vm/jit/irgen-internal.h"
26 namespace HPHP
{ namespace jit
{ namespace irgen
{
30 //////////////////////////////////////////////////////////////////////
32 void implRet(HTS
& env
, Type type
) {
33 auto const func
= curFunc(env
);
34 if (func
->attrs() & AttrMayUseVV
) {
35 // Note: this has to be the first thing, because we cannot bail after
36 // we start decRefing locs because then there'll be no corresponding
37 // bytecode boundaries until the end of RetC
38 gen(env
, ReleaseVVOrExit
, makeExitSlow(env
), fp(env
));
41 // Pop the return value. Since it will be teleported to its place in memory,
42 // we don't care about the type.
43 auto const catchBlock
= makeCatch(env
);
44 auto retVal
= pop(env
, type
, func
->isGenerator() ? DataTypeSpecific
47 // Free local variables. We do the decrefs inline if there are less
48 // refcounted locals than a threshold.
49 auto const localCount
= func
->numLocals();
50 auto const shouldFreeInline
= mcg
->useLLVM() || [&]() -> bool {
51 auto const count
= mcg
->numTranslations(
52 env
.irb
->unit().context().srcKey());
53 constexpr int kTooPolyRet
= 6;
54 if (localCount
> 0 && count
> kTooPolyRet
) return false;
55 auto numRefCounted
= int{0};
56 for (auto i
= uint32_t{0}; i
< localCount
; ++i
) {
57 if (env
.irb
->localType(i
, DataTypeGeneric
).maybeCounted()) {
61 return numRefCounted
<= RuntimeOption::EvalHHIRInliningMaxReturnDecRefs
;
63 if (shouldFreeInline
) {
64 decRefLocalsInline(env
);
65 for (unsigned i
= 0; i
< localCount
; ++i
) {
66 env
.irb
->constrainLocal(i
, DataTypeCountness
, "inlined RetC/V");
69 gen(env
, GenericRetDecRefs
, fp(env
));
73 if (func
->mayHaveThis()) {
74 gen(env
, DecRefThis
, fp(env
));
77 // Call the FunctionReturn hook and put the return value on the stack so that
78 // the unwinder would decref it.
79 retSurpriseCheck(env
, fp(env
), retVal
, catchBlock
, false);
81 // In async function, wrap the return value into succeeded StaticWaitHandle.
82 if (!resumed(env
) && func
->isAsyncFunction()) {
83 retVal
= gen(env
, CreateSSWH
, retVal
);
86 // Type profile return value.
87 if (RuntimeOption::EvalRuntimeTypeProfile
) {
88 gen(env
, TypeProfileFunc
, TypeProfileData(-1), retVal
, cns(env
, func
));
92 SSATmp
* resumableObj
= nullptr;
94 // Store the return value.
95 gen(env
, StRetVal
, fp(env
), retVal
);
98 stack
= gen(env
, RetAdjustStack
, fp(env
));
99 } else if (func
->isAsyncFunction()) {
100 // Load the parent chain.
101 auto parentChain
= gen(env
, LdAsyncArParentChain
, fp(env
));
103 // Mark the async function as succeeded.
104 gen(env
, StAsyncArSucceeded
, fp(env
));
106 // Store the return value.
107 gen(env
, StAsyncArResult
, fp(env
), retVal
);
110 gen(env
, ABCUnblock
, parentChain
);
113 stack
= spillStack(env
);
115 // Get the AsyncFunctionWaitHandle.
116 resumableObj
= gen(env
, LdResumableArObj
, fp(env
));
117 } else if (func
->isNonAsyncGenerator()) {
118 // Clear generator's key and value.
119 auto const oldKey
= gen(env
, LdContArKey
, Type::Cell
, fp(env
));
120 gen(env
, StContArKey
, fp(env
), cns(env
, Type::InitNull
));
121 gen(env
, DecRef
, oldKey
);
123 auto const oldValue
= gen(env
, LdContArValue
, Type::Cell
, fp(env
));
124 gen(env
, StContArValue
, fp(env
), cns(env
, Type::InitNull
));
125 gen(env
, DecRef
, oldValue
);
127 // Mark generator as finished.
130 GeneratorState
{ BaseGenerator::State::Done
},
133 // Push return value of next()/send()/raise().
134 push(env
, cns(env
, Type::InitNull
));
137 stack
= spillStack(env
);
142 // Grab caller info from ActRec.
143 auto const retAddr
= gen(env
, LdRetAddr
, fp(env
));
144 auto const frame
= gen(env
, FreeActRec
, fp(env
));
146 // Drop reference to this resumable. The reference to the object storing
147 // the frame is implicitly owned by the execution. TakeRef is used to inform
148 // the refcount optimizer about this fact.
149 if (resumableObj
!= nullptr) {
150 gen(env
, TakeRef
, resumableObj
);
151 gen(env
, DecRef
, resumableObj
);
154 // Return control to the caller.
155 gen(env
, RetCtrl
, RetCtrlData(false), stack
, frame
, retAddr
);
158 //////////////////////////////////////////////////////////////////////
162 void retSurpriseCheck(HTS
& env
,
166 bool suspendingResumed
) {
167 ringbuffer(env
, Trace::RBTypeFuncExit
, curFunc(env
)->fullName());
170 gen(env
, CheckSurpriseFlags
, taken
);
173 env
.irb
->hint(Block::Hint::Unlikely
);
174 if (retVal
!= nullptr) {
177 RetCtrlData(suspendingResumed
),
184 RetCtrlData(suspendingResumed
),
187 cns(env
, suspendingResumed
));
193 void emitRetC(HTS
& env
) {
194 if (curFunc(env
)->isAsyncGenerator()) PUNT(RetC
-AsyncGenerator
);
196 if (isInlining(env
)) {
197 assert(!resumed(env
));
198 retFromInlined(env
, Type::Cell
);
200 implRet(env
, Type::Cell
);
204 void emitRetV(HTS
& env
) {
205 assert(!resumed(env
));
206 assert(!curFunc(env
)->isResumable());
207 if (isInlining(env
)) {
208 retFromInlined(env
, Type::BoxedCell
);
210 implRet(env
, Type::BoxedCell
);
214 //////////////////////////////////////////////////////////////////////