Fix ServiceInterceptor::getValueAsType
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-ret.cpp
blobcf74b82aa3c593d42c3e7aaef6ddc768e7356e51
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 +----------------------------------------------------------------------+
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 {
30 namespace {
32 //////////////////////////////////////////////////////////////////////
34 const StaticString s_returnHook("SurpriseReturnHook");
36 template<class AH>
37 void retSurpriseCheck(IRGS& env, SSATmp* retVal, AH afterHook) {
38 ringbufferMsg(env, Trace::RBTypeFuncExit, curFunc(env)->fullName());
39 ifThen(
40 env,
41 [&] (Block* taken) {
42 gen(env, CheckSurpriseFlags, taken, anyStackRegister(env));
44 [&] {
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);
50 afterHook();
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) {
63 return false;
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)) {
68 ++numRefCounted;
71 return numRefCounted <= Cfg::HHIR::InliningMaxReturnDecRefs;
72 }();
74 if (shouldFreeInline) {
75 decRefLocalsInline(env);
76 } else {
77 gen(env, GenericRetDecRefs, fp(env));
80 decRefThis(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()};
95 return AuxUnion{0};
96 }();
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(
114 env,
115 [&] (Block* taken) {
116 auto flags = gen(env, LdARFlags, fp(env));
117 auto test = gen(
118 env, AndInt, flags,
119 cns(env, static_cast<int32_t>(1 << ActRec::AsyncEagerRet)));
120 gen(env, JmpNZero, taken, test);
122 [&] {
123 return gen(env, CreateSSWH, retVal);
125 [&] {
126 return retVal;
128 normalReturn(env, wrapped, false);
129 return;
131 assertx(!suspended);
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),
138 retVal);
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),
145 retVal);
148 void generatorReturn(IRGS& env, SSATmp* retval) {
149 assertx(curFunc(env)->isGenerator());
150 assertx(!isInlining(env));
152 retSurpriseCheck(env, retval, []{});
154 gen(env,
155 StContArState,
156 GeneratorState { BaseGenerator::State::Done },
157 fp(env));
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);
170 } else {
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));
176 return;
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);
204 updateMarker(env);
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));
228 assertx(fpOff);
229 return *fpOff + kArRetOff / int32_t{sizeof(TypedValue)};
232 void emitRetC(IRGS& env) {
233 if (isInlining(env)) {
234 assertx(resumeMode(env) == ResumeMode::None);
235 retFromInlined(env);
236 } else {
237 implRet(env, false);
241 void emitRetM(IRGS& env, uint32_t nvals) {
242 assertx(resumeMode(env) == ResumeMode::None);
243 assertx(!curFunc(env)->isResumable());
244 assertx(nvals > 1);
246 if (isInlining(env)) {
247 retFromInlined(env);
248 return;
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));
257 implRet(env, false);
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));
266 } else {
267 implRet(env, true);
271 //////////////////////////////////////////////////////////////////////