Various llvm backend bugfixes
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-ret.cpp
blob812b1632f74d9cb0f48dc125d6cd3a131173de6a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 {
28 namespace {
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
45 : DataTypeGeneric);
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()) {
58 ++numRefCounted;
61 return numRefCounted <= RuntimeOption::EvalHHIRInliningMaxReturnDecRefs;
62 }();
63 if (shouldFreeInline) {
64 decRefLocalsInline(env);
65 for (unsigned i = 0; i < localCount; ++i) {
66 env.irb->constrainLocal(i, DataTypeCountness, "inlined RetC/V");
68 } else {
69 gen(env, GenericRetDecRefs, fp(env));
72 // Free $this.
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));
91 SSATmp* stack;
92 SSATmp* resumableObj = nullptr;
93 if (!resumed(env)) {
94 // Store the return value.
95 gen(env, StRetVal, fp(env), retVal);
97 // Free ActRec.
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);
109 // Unblock parents.
110 gen(env, ABCUnblock, parentChain);
112 // Sync SP.
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.
128 gen(env,
129 StContArState,
130 GeneratorState { BaseGenerator::State::Done },
131 fp(env));
133 // Push return value of next()/send()/raise().
134 push(env, cns(env, Type::InitNull));
136 // Sync SP.
137 stack = spillStack(env);
138 } else {
139 not_reached();
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,
163 SSATmp* frame,
164 SSATmp* retVal,
165 Block* catchBlock,
166 bool suspendingResumed) {
167 ringbuffer(env, Trace::RBTypeFuncExit, curFunc(env)->fullName());
168 env.irb->ifThen(
169 [&](Block* taken) {
170 gen(env, CheckSurpriseFlags, taken);
172 [&] {
173 env.irb->hint(Block::Hint::Unlikely);
174 if (retVal != nullptr) {
175 gen(env,
176 FunctionReturnHook,
177 RetCtrlData(suspendingResumed),
178 catchBlock,
179 frame,
180 retVal);
181 } else {
182 gen(env,
183 FunctionSuspendHook,
184 RetCtrlData(suspendingResumed),
185 catchBlock,
186 frame,
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);
199 } else {
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);
209 } else {
210 implRet(env, Type::BoxedCell);
214 //////////////////////////////////////////////////////////////////////