2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 #include "hphp/runtime/vm/jit/mcgen-prologue.h"
19 #include "hphp/runtime/vm/jit/mcgen.h"
20 #include "hphp/runtime/vm/jit/mcgen-translate.h"
22 #include "hphp/runtime/vm/jit/func-guard.h"
23 #include "hphp/runtime/vm/jit/prof-data.h"
24 #include "hphp/runtime/vm/jit/smashable-instr.h"
25 #include "hphp/runtime/vm/jit/tc.h"
26 #include "hphp/runtime/vm/jit/vm-protect.h"
27 #include "hphp/runtime/vm/jit/write-lease.h"
29 #include "hphp/util/trace.h"
34 * Given a callee and a number of args, match up to the callee's
35 * argument expectations and dispatch.
37 * Call/return hand-shaking is a bit funny initially. At translation time,
38 * we don't necessarily know what function we're calling. For instance,
42 * Will lead to a set of basic blocks like:
49 * The fcall labelled "b2" above may not be statically bindable in our
52 * We decouple the call work into a per-callsite portion, responsible
53 * for recording the return address, and a per-(callee, numArgs) portion,
54 * responsible for fixing up arguments and dispatching to remaining
55 * code. We call the per-callee portion a "prologue."
57 * Also, we are called from two distinct environments. From REQ_BIND_CALL,
58 * we're running "between" basic blocks, with all VM registers sync'ed.
59 * However, we're also called in the middle of basic blocks, when dropping
60 * entries into func->m_prologues. So don't go around using the
61 * translation-time values of vmfp()/vmsp(), since they have an
62 * unpredictable relationship to the source.
64 namespace HPHP
{ namespace jit
{ namespace mcgen
{
68 TCA
checkCachedPrologue(const Func
* func
, int paramIdx
) {
69 TCA prologue
= (TCA
)func
->getPrologue(paramIdx
);
70 if (prologue
!= tc::ustubs().fcallHelperThunk
) {
71 TRACE(1, "cached prologue %s(%d) -> cached %p\n",
72 func
->fullName()->data(), paramIdx
, prologue
);
73 assertx(tc::isValidCodeAddress(prologue
));
80 void withThis(const Func
* func
, F f
) {
81 if (!func
->hasThisVaries()) {
82 f(func
->mayHaveThis());
90 * Given the proflogueTransId for a TransProflogue translation, regenerate the
91 * prologue (as a TransPrologue).
93 * Returns true iff the prologue had an associated dvInit funclet that was
94 * successfully retranslated as an Optimize translation.
96 bool regeneratePrologue(TransID prologueTransId
, tc::FuncMetaInfo
& info
) {
97 auto rec
= profData()->transRec(prologueTransId
);
98 auto func
= rec
->func();
99 auto nArgs
= rec
->prologueArgs();
101 func
->resetPrologue(nArgs
);
103 // If we're regenerating a prologue, and we want to check shouldTranslate()
104 // but ignore the code size limits. We still want to respect the global
105 // translation limit and other restrictions, though.
106 if (!tc::shouldTranslateNoSizeLimit(func
)) return false;
108 // Double check the prologue array now that we have the write lease
109 // in case another thread snuck in and set the prologue already.
110 if (checkCachedPrologue(func
, nArgs
)) return false;
112 if (RuntimeOption::EvalEnableOptTCBuffer
) {
113 info
.prologues
.emplace_back(rec
);
115 tc::emitFuncPrologueOpt(rec
);
118 // If this prologue has a DV funclet, then invalidate it and return its SrcKey
120 if (nArgs
< func
->numNonVariadicParams()) {
121 auto paramInfo
= func
->params()[nArgs
];
122 if (paramInfo
.hasDefaultValue()) {
124 auto genPrologue
= [&] (bool hasThis
) {
125 SrcKey
funcletSK(func
, paramInfo
.funcletOff
, false, hasThis
);
126 if (profData()->optimized(funcletSK
)) return;
127 auto funcletTransId
= profData()->dvFuncletTransId(funcletSK
);
128 if (funcletTransId
== kInvalidTransID
) return;
129 tc::invalidateSrcKey(funcletSK
);
130 auto args
= TransArgs
{funcletSK
};
131 args
.transId
= funcletTransId
;
132 args
.kind
= TransKind::Optimize
;
133 args
.region
= selectHotRegion(funcletTransId
);
134 auto const spOff
= args
.region
->entry()->initialSpOffset();
135 if (translate(args
, spOff
, info
.tcBuf
.view())) {
136 // Flag that this translation has been retranslated, so that
137 // it's not retranslated again along with the function body.
138 profData()->setOptimized(funcletSK
);
142 withThis(func
, genPrologue
);
143 if (ret
) return true;
150 ////////////////////////////////////////////////////////////////////////////////
153 bool regeneratePrologues(Func
* func
, tc::FuncMetaInfo
& info
) {
154 std::vector
<TransID
> prologTransIDs
;
158 for (int nArgs
= 0; nArgs
< func
->numPrologues(); nArgs
++) {
159 TransID tid
= profData()->proflogueTransId(func
, nArgs
);
160 if (tid
!= kInvalidTransID
) {
161 prologTransIDs
.push_back(tid
);
165 std::sort(prologTransIDs
.begin(), prologTransIDs
.end(),
166 [&](TransID t1
, TransID t2
) -> bool {
167 // This will sort in ascending order.
168 return profData()->transCounter(t2
) >
169 profData()->transCounter(t1
);
172 // Next, we're going to regenerate each prologue along with its DV funclet.
173 // We consider the option of either including the DV funclets in the same
174 // region as the function body or not. Including them in the same region
175 // enables some type information to flow and thus can eliminate some stores
176 // and type checks, but it can also increase the code size by duplicating the
177 // whole function body. Therefore, we only include the function body along
178 // with the DV init if both (a) the function has a single proflogue, and (b)
179 // the size of the function is within a certain threshold.
181 // The mechanism used to keep the function body separate from the DV init is
182 // to temporarily mark the SrcKey for the function body as already optimized.
183 // (The region selectors break a region whenever they hit a SrcKey that has
184 // already been optimized.)
185 auto const includedBody
= prologTransIDs
.size() <= 1 &&
186 func
->past() - func
->base() <= RuntimeOption::EvalJitPGOMaxFuncSizeDupBody
;
188 auto funcBodySk
= [&] (bool hasThis
) {
189 return SrcKey
{func
, func
->base(), false, hasThis
};
194 profData()->setOptimized(funcBodySk(hasThis
));
200 profData()->clearOptimized(funcBodySk(hasThis
));
204 bool emittedAnyDVInit
= false;
205 for (TransID tid
: prologTransIDs
) {
206 emittedAnyDVInit
|= regeneratePrologue(tid
, info
);
209 // If we tried to include the function body along with a DV init, but didn't
210 // end up generating any DV init, then flag that the function body was not
212 return emittedAnyDVInit
&& includedBody
;
215 TCA
getFuncPrologue(Func
* func
, int nPassed
) {
219 TRACE(1, "funcPrologue %s(%d)\n", func
->fullName()->data(), nPassed
);
220 int const numParams
= func
->numNonVariadicParams();
221 int paramIndex
= nPassed
<= numParams
? nPassed
: numParams
+ 1;
223 // Do a quick test before grabbing the write lease
224 if (auto const p
= checkCachedPrologue(func
, paramIndex
)) return p
;
226 auto computeKind
= [&] {
227 return tc::profileFunc(func
) ? TransKind::ProfPrologue
:
228 TransKind::LivePrologue
;
230 LeaseHolder
writer(func
, computeKind());
231 if (!writer
) return nullptr;
233 auto const kind
= computeKind();
234 // Check again now that we have the write lease, in case the answer to
235 // profileFunc() changed.
236 if (!writer
.checkKind(kind
)) return nullptr;
238 if (!tc::shouldTranslate(func
, TransKind::LivePrologue
)) return nullptr;
240 // Double check the prologue array now that we have the write lease
241 // in case another thread snuck in and set the prologue already.
242 if (auto const p
= checkCachedPrologue(func
, paramIndex
)) return p
;
244 return tc::emitFuncPrologue(func
, nPassed
, kind
);