Replace REQ_RETRANSLATE_OPT with a regular C++ call via ustub
[hiphop-php.git] / hphp / runtime / vm / jit / service-requests.cpp
blob0a5fb9d888e99fa082950408a989bd57a66983dd
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 +----------------------------------------------------------------------+
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/stack-offsets.h"
23 #include "hphp/runtime/vm/jit/stub-alloc.h"
24 #include "hphp/runtime/vm/jit/tc.h"
25 #include "hphp/runtime/vm/jit/tc-internal.h"
26 #include "hphp/runtime/vm/jit/translator-inline.h"
27 #include "hphp/runtime/vm/jit/unique-stubs.h"
28 #include "hphp/runtime/vm/jit/vasm-gen.h"
29 #include "hphp/runtime/vm/jit/vasm-instr.h"
30 #include "hphp/runtime/vm/jit/vasm-unit.h"
31 #include "hphp/runtime/vm/resumable.h"
33 #include "hphp/util/arch.h"
34 #include "hphp/util/data-block.h"
35 #include "hphp/util/trace.h"
37 #include "hphp/vixl/a64/macro-assembler-a64.h"
38 #include "hphp/vixl/a64/disasm-a64.h"
40 #include <folly/Optional.h>
42 namespace HPHP { namespace jit { namespace svcreq {
44 ///////////////////////////////////////////////////////////////////////////////
46 TRACE_SET_MOD(servicereq);
48 ///////////////////////////////////////////////////////////////////////////////
50 namespace detail {
52 ///////////////////////////////////////////////////////////////////////////////
55 * Service request stub emitter.
57 * Emit a service request stub of type `sr' at `start' in `cb'.
59 void emit_svcreq(CodeBlock& cb,
60 DataBlock& data,
61 CGMeta& meta,
62 TCA start,
63 bool persist,
64 SBInvOffset spOff,
65 ServiceRequest sr,
66 const ArgVec& argv) {
67 FTRACE(2, "svcreq @{} {}(spOff={}, ", start, to_name(sr), spOff.offset);
69 tracing::Pause _p;
70 tracing::Block _{
71 "emit-svcreq",
72 [&] {
73 return tracing::Props{}
74 .add("service_request", to_name(sr))
75 .add("persist", persist);
79 auto const is_reused = start != cb.frontier();
81 if (!is_reused) cb.assertCanEmit(stub_size());
83 CodeBlock stub;
84 auto const realAddr = is_reused ? start : cb.toDestAddress(start);
85 stub.init(start, realAddr, stub_size(), "svcreq_stub");
88 Vauto vasm{stub, stub, data, meta};
89 auto& v = vasm.main();
91 auto live_out = leave_trace_regs();
93 assertx(argv.size() <= kMaxArgs);
95 // Pick up CondCode arguments first---vasm may optimize immediate loads
96 // into operations which clobber status flags.
97 for (auto i = 0; i < argv.size(); ++i) {
98 auto const& arg = argv[i];
99 if (arg.kind != Arg::Kind::CondCode) continue;
101 FTRACE(2, "c({}), ", cc_names[arg.cc]);
102 v << setcc{arg.cc, r_svcreq_sf(), rbyte(r_svcreq_arg(i))};
105 for (auto i = 0; i < argv.size(); ++i) {
106 auto const& arg = argv[i];
107 auto const r = r_svcreq_arg(i);
109 switch (arg.kind) {
110 case Arg::Kind::Immed:
111 FTRACE(2, "{}, ", arg.imm);
112 v << copy{v.cns(arg.imm), r};
113 break;
114 case Arg::Kind::Address:
115 FTRACE(2, "{}(%rip), ", arg.imm);
116 always_assert(deltaFits(arg.addr - start, sz::dword));
117 v << leap{reg::rip[arg.imm], r};
118 break;
119 case Arg::Kind::CondCode:
120 break;
122 live_out |= r;
124 FTRACE(2, ") : stub@");
126 if (persist) {
127 FTRACE(2, "<none>");
128 v << copy{v.cns(0), r_svcreq_stub()};
129 } else {
130 FTRACE(2, "{}", stub.base());
131 v << leap{reg::rip[int64_t(stub.base())], r_svcreq_stub()};
133 v << copy{v.cns(sr), r_svcreq_req()};
134 v << copy{v.cns(spOff.offset), r_svcreq_spoff()};
136 live_out |= r_svcreq_stub();
137 live_out |= r_svcreq_req();
138 live_out |= r_svcreq_spoff();
140 v << jmpi{tc::ustubs().handleSRHelper, live_out};
142 // We pad ephemeral stubs unconditionally. This is required for
143 // correctness by the x64 code relocator.
144 vasm.unit().padding = !persist;
147 if (!is_reused) cb.skip(stub.used());
150 ///////////////////////////////////////////////////////////////////////////////
154 ///////////////////////////////////////////////////////////////////////////////
156 TCA emit_bindjmp_stub(CodeBlock& cb, DataBlock& data, CGMeta& fixups,
157 SBInvOffset spOff, TCA jmp, SrcKey target) {
158 return emit_ephemeral(
160 data,
161 fixups,
162 allocTCStub(cb, &fixups),
163 spOff,
164 REQ_BIND_JMP,
165 jmp,
166 target.toAtomicInt()
170 TCA emit_bindaddr_stub(CodeBlock& cb, DataBlock& data, CGMeta& fixups,
171 SBInvOffset spOff, TCA* addr, SrcKey target) {
172 // Right now it's possible that addr may not belong to the data segment,
173 // as is the case with SSwitchMap (see #10347945) and thus may not be PIC
174 // addressable. Passing a TCA generates an RIP relative address which can
175 // be handled by the relocation logic, while a TCA* will generate an immediate
176 // address which will not be remapped.
177 if (data.contains((TCA)addr)) {
178 return emit_ephemeral(
180 data,
181 fixups,
182 allocTCStub(cb, &fixups),
183 spOff,
184 REQ_BIND_ADDR,
185 (TCA)addr, // needs to be RIP relative so that we can relocate it
186 target.toAtomicInt()
190 return emit_ephemeral(
192 data,
193 fixups,
194 allocTCStub(cb, &fixups),
195 spOff,
196 REQ_BIND_ADDR,
197 addr,
198 target.toAtomicInt()
202 ///////////////////////////////////////////////////////////////////////////////
204 namespace {
205 std::atomic<bool> s_fullForStub{false};
208 TCA emit_interp_no_translate_stub(SBInvOffset spOff, SrcKey sk) {
209 FTRACE(2, "interp_no_translate_stub @{} {}\n", showShort(sk), spOff.offset);
211 // No point on trying to emit if we already failed once.
212 if (s_fullForStub.load(std::memory_order_relaxed)) {
213 FTRACE(4, " no space for {}, bailing\n", showShort(sk));
214 return nullptr;
217 tracing::Pause _p;
218 tracing::Block _{"emit-interp-no-translate-stub"};
220 auto codeLock = tc::lockCode();
221 auto metaLock = tc::lockMetadata();
223 auto view = tc::code().view();
224 auto& cb = view.frozen();
225 auto& data = view.data();
227 auto const start = vwrap(
229 data,
230 [&] (Vout& v) { emitInterpReqNoTranslate(v, sk, spOff); },
231 false,
232 true /* nullOnFull */
235 // We passed true to nullOnFull, so if the TC was out of space, we
236 // just get a nullptr address.
237 if (!start) {
238 FTRACE(4, " ran out of space while making stub for {}\n", showShort(sk));
239 s_fullForStub.store(true, std::memory_order_relaxed);
241 FTRACE(4, " emitted stub {} for {}\n", start, showShort(sk));
242 return start;
245 ///////////////////////////////////////////////////////////////////////////////