Use thread-local TC for codegen and relocate translations into the TC
[hiphop-php.git] / hphp / runtime / vm / jit / mcgen-prologue.cpp
blobaa84c7b69ca7ad0ff40e01a303ce6dc24a57c181
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
31 TRACE_SET_MOD(mcg);
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,
40 * f(g());
42 * Will lead to a set of basic blocks like:
44 * b1: pushfuncd "f"
45 * pushfuncd "g"
46 * fcall
47 * b2: fcall
49 * The fcall labelled "b2" above may not be statically bindable in our
50 * execution model.
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 {
66 namespace {
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));
74 return prologue;
76 return nullptr;
79 template <class F>
80 void withThis(const Func* func, F f) {
81 if (!func->hasThisVaries()) {
82 f(func->mayHaveThis());
83 return;
85 f(true);
86 f(false);
89 /**
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);
114 } else {
115 tc::emitFuncPrologueOpt(rec);
118 // If this prologue has a DV funclet, then invalidate it and return its SrcKey
119 // and TransID
120 if (nArgs < func->numNonVariadicParams()) {
121 auto paramInfo = func->params()[nArgs];
122 if (paramInfo.hasDefaultValue()) {
123 bool ret = false;
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);
139 ret = true;
142 withThis(func, genPrologue);
143 if (ret) return true;
147 return false;
150 ////////////////////////////////////////////////////////////////////////////////
153 bool regeneratePrologues(Func* func, tc::FuncMetaInfo& info) {
154 std::vector<TransID> prologTransIDs;
156 VMProtect _;
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};
191 if (!includedBody) {
192 withThis(func,
193 [&] (bool hasThis) {
194 profData()->setOptimized(funcBodySk(hasThis));
197 SCOPE_EXIT{
198 withThis(func,
199 [&] (bool 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
211 // included.
212 return emittedAnyDVInit && includedBody;
215 TCA getFuncPrologue(Func* func, int nPassed) {
216 VMProtect _;
218 func->validate();
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);