2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/service-requests.h"
19 #include "hphp/runtime/vm/jit/types.h"
20 #include "hphp/runtime/vm/jit/abi.h"
21 #include "hphp/runtime/vm/jit/align.h"
22 #include "hphp/runtime/vm/jit/func-order.h"
23 #include "hphp/runtime/vm/jit/prof-data.h"
24 #include "hphp/runtime/vm/jit/stack-offsets.h"
25 #include "hphp/runtime/vm/jit/tc.h"
26 #include "hphp/runtime/vm/jit/tc-internal.h"
27 #include "hphp/runtime/vm/jit/tc-record.h"
28 #include "hphp/runtime/vm/jit/trans-db.h"
29 #include "hphp/runtime/vm/jit/trans-rec.h"
30 #include "hphp/runtime/vm/jit/translator-inline.h"
31 #include "hphp/runtime/vm/jit/unique-stubs.h"
32 #include "hphp/runtime/vm/jit/vasm-gen.h"
33 #include "hphp/runtime/vm/jit/vasm-instr.h"
34 #include "hphp/runtime/vm/jit/vasm-unit.h"
35 #include "hphp/runtime/vm/jit/vtune-jit.h"
36 #include "hphp/runtime/vm/resumable.h"
37 #include "hphp/util/arch.h"
38 #include "hphp/util/data-block.h"
39 #include "hphp/util/hash-map.h"
40 #include "hphp/util/trace.h"
42 #include "hphp/vixl/a64/macro-assembler-a64.h"
43 #include "hphp/vixl/a64/disasm-a64.h"
45 namespace HPHP::jit::svcreq
{
47 ///////////////////////////////////////////////////////////////////////////////
49 TRACE_SET_MOD(servicereq
);
51 ///////////////////////////////////////////////////////////////////////////////
55 uint64_t toStubKey(StubType type
, SrcKey sk
, SBInvOffset spOff
) {
56 auto const t
= static_cast<uint8_t>(type
);
57 auto const bcOffOrNumArgs
= sk
.funcEntry() ? sk
.numEntryArgs() : sk
.offset();
58 assertx(t
< (1 << 2));
59 assertx(0 <= bcOffOrNumArgs
&& bcOffOrNumArgs
< (1LL << 30));
60 assertx(0 <= spOff
.offset
&& spOff
.offset
< (1LL << 31));
62 (static_cast<uint64_t>(t
) << 62) +
63 (static_cast<uint64_t>(sk
.funcEntry()) << 61) +
64 (static_cast<uint64_t>(bcOffOrNumArgs
) << 31) +
65 (static_cast<uint64_t>(spOff
.offset
));
68 folly_concurrent_hash_map_simd
<uint64_t, TCA
> s_stubMap
;
70 TCA
typeToHandler(StubType type
) {
72 case StubType::Translate
: return tc::ustubs().handleTranslate
;
73 case StubType::Retranslate
: return tc::ustubs().handleRetranslate
;
74 default: not_reached();
78 TCA
typeToFuncEntryHandler(StubType type
) {
80 case StubType::Translate
: return tc::ustubs().handleTranslateFuncEntry
;
81 case StubType::Retranslate
: return tc::ustubs().handleRetranslateFuncEntry
;
82 default: not_reached();
86 std::string
typeToName(StubType type
) {
88 case StubType::Translate
: return "translate";
89 case StubType::Retranslate
: return "retranslate";
90 default: not_reached();
94 std::atomic
<bool> s_fullForStub
{false};
96 TCA
emitStub(StubType type
, SrcKey sk
, SBInvOffset spOff
) {
97 FTRACE(2, "svcreq::emitStub {} @{} {}\n",
98 typeToName(type
), showShort(sk
), spOff
.offset
);
99 assertx(!sk
.prologue());
101 if (s_fullForStub
.load(std::memory_order_relaxed
)) {
102 FTRACE(4, " no space for {}, bailing\n", showShort(sk
));
107 tracing::Block _
{"svcreq::emitStub"};
109 auto codeLock
= tc::lockCode();
111 auto view
= tc::code().view(TransKind::Anchor
);
112 TCA mainStart
= view
.main().frontier();
113 TCA coldStart
= view
.cold().frontier();
114 TCA frozenStart
= view
.frozen().frontier();
116 auto const emit
= [&] (Vout
& v
) {
117 assertx(!sk
.funcEntry());
118 v
<< copy
{v
.cns(sk
.offset()), rarg(0)};
119 v
<< copy
{v
.cns(spOff
.offset
), rarg(1)};
120 v
<< jmpi
{typeToHandler(type
), leave_trace_regs() | arg_regs(2)};
123 auto const emitFuncEntry
= [&] (Vout
& v
) {
124 assertx(sk
.funcEntry());
125 assertx(spOff
== SBInvOffset
{0});
126 v
<< copy
{v
.cns(sk
.numEntryArgs()), rarg(0)};
127 v
<< jmpi
{typeToFuncEntryHandler(type
), leave_trace_regs() | arg_regs(1)};
130 auto const start
= vwrap(
134 if (!sk
.funcEntry()) {
141 false, /* relocate */
142 true /* nullOnFull */
145 // We passed true to nullOnFull, so if the TC was out of space, we
146 // just get a nullptr address.
148 FTRACE(4, " ran out of space while making stub for {}\n", showShort(sk
));
149 s_fullForStub
.store(true, std::memory_order_relaxed
);
153 assertx(view
.main().frontier() == mainStart
);
154 assertx(view
.cold().frontier() != coldStart
);
155 assertx(view
.frozen().frontier() == frozenStart
);
157 if (RuntimeOption::EvalDumpTCAnchors
) {
158 auto metaLock
= tc::lockMetadata();
159 auto const transID
= profData() && transdb::enabled()
160 ? profData()->allocTransID() : kInvalidTransID
;
161 TransRec
tr(sk
, transID
, TransKind::Anchor
, mainStart
, 0,
162 coldStart
, view
.cold().frontier() - coldStart
, frozenStart
, 0);
163 transdb::addTranslation(tr
);
164 FuncOrder::recordTranslation(tr
);
165 if (RuntimeOption::EvalJitUseVtuneAPI
) {
166 reportTraceletToVtune(sk
.unit(), sk
.func(), tr
);
168 tc::recordTranslationSizes(tr
);
170 assertx(!transdb::enabled() ||
171 transdb::getTransRec(coldStart
)->kind
== TransKind::Anchor
);
174 FTRACE(4, " emitted stub {} for {}\n", start
, showShort(sk
));
180 TCA
getOrEmitStub(StubType type
, SrcKey sk
, SBInvOffset spOff
) {
181 assertx(!sk
.prologue());
183 auto const key
= toStubKey(type
, sk
, spOff
);
184 auto const it
= s_stubMap
.find(key
);
185 if (it
!= s_stubMap
.end()) return it
->second
;
187 auto const stub
= emitStub(type
, sk
, spOff
);
188 if (stub
== nullptr) return nullptr;
190 auto const pair
= s_stubMap
.insert({key
, stub
});
191 if (!pair
.second
) return pair
.first
->second
;
195 ///////////////////////////////////////////////////////////////////////////////
197 TCA
emit_interp_no_translate_stub(SBInvOffset spOff
, SrcKey sk
) {
198 FTRACE(2, "interp_no_translate_stub @{} {}\n", showShort(sk
), spOff
.offset
);
200 // No point on trying to emit if we already failed once.
201 if (s_fullForStub
.load(std::memory_order_relaxed
)) {
202 FTRACE(4, " no space for {}, bailing\n", showShort(sk
));
207 tracing::Block _
{"emit-interp-no-translate-stub"};
209 auto codeLock
= tc::lockCode();
210 auto metaLock
= tc::lockMetadata();
212 auto view
= tc::code().view();
213 auto& cb
= view
.frozen();
214 auto& data
= view
.data();
216 auto const start
= vwrap(
219 [&] (Vout
& v
) { emitInterpReqNoTranslate(v
, sk
, spOff
); },
222 true /* nullOnFull */
225 // We passed true to nullOnFull, so if the TC was out of space, we
226 // just get a nullptr address.
228 FTRACE(4, " ran out of space while making stub for {}\n", showShort(sk
));
229 s_fullForStub
.store(true, std::memory_order_relaxed
);
231 FTRACE(4, " emitted stub {} for {}\n", start
, showShort(sk
));
235 ///////////////////////////////////////////////////////////////////////////////