Use thread-local TC for codegen and relocate translations into the TC
[hiphop-php.git] / hphp / runtime / vm / jit / service-requests.cpp
blobf1f0b2c4bd456e5a0e89b87ae2f530afa1a501cf
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/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/stack-offsets.h"
22 #include "hphp/runtime/vm/jit/stub-alloc.h"
23 #include "hphp/runtime/vm/jit/tc.h"
24 #include "hphp/runtime/vm/jit/translator-inline.h"
25 #include "hphp/runtime/vm/jit/unique-stubs.h"
26 #include "hphp/runtime/vm/jit/vasm-gen.h"
27 #include "hphp/runtime/vm/jit/vasm-instr.h"
28 #include "hphp/runtime/vm/jit/vasm-unit.h"
30 #include "hphp/util/arch.h"
31 #include "hphp/util/data-block.h"
32 #include "hphp/util/trace.h"
34 #include "hphp/vixl/a64/macro-assembler-a64.h"
35 #include "hphp/vixl/a64/disasm-a64.h"
37 #include "hphp/ppc64-asm/decoded-instr-ppc64.h"
39 #include <folly/Optional.h>
41 namespace HPHP { namespace jit { namespace svcreq {
43 ///////////////////////////////////////////////////////////////////////////////
45 TRACE_SET_MOD(servicereq);
47 ///////////////////////////////////////////////////////////////////////////////
49 namespace detail {
51 ///////////////////////////////////////////////////////////////////////////////
54 * Service request stub emitter.
56 * Emit a service request stub of type `sr' at `start' in `cb'.
58 void emit_svcreq(CodeBlock& cb,
59 DataBlock& data,
60 TCA start,
61 bool persist,
62 folly::Optional<FPInvOffset> spOff,
63 ServiceRequest sr,
64 const ArgVec& argv) {
65 FTRACE(2, "svcreq @{} {}(", start, to_name(sr));
67 auto const is_reused = start != cb.frontier();
69 CodeBlock stub;
70 auto const realAddr = is_reused ? start : cb.toDestAddress(start);
71 stub.init(start, realAddr, stub_size(), "svcreq_stub");
74 CGMeta fixups;
75 SCOPE_EXIT { assert(fixups.empty()); };
77 Vauto vasm{stub, stub, data, fixups};
78 auto& v = vasm.main();
80 // If we have an spOff, materialize rvmsp() so that handleSRHelper() can do
81 // a VM reg sync. (When we don't have an spOff, the caller of the service
82 // request was responsible for making sure rvmsp already contained the top
83 // of the stack.)
84 if (spOff) {
85 v << lea{rvmfp()[-cellsToBytes(spOff->offset)], rvmsp()};
88 auto live_out = leave_trace_regs();
90 assert(argv.size() <= kMaxArgs);
92 // Pick up CondCode arguments first---vasm may optimize immediate loads
93 // into operations which clobber status flags.
94 for (auto i = 0; i < argv.size(); ++i) {
95 auto const& arg = argv[i];
96 if (arg.kind != Arg::Kind::CondCode) continue;
98 FTRACE(2, "c({}), ", cc_names[arg.cc]);
99 v << setcc{arg.cc, r_svcreq_sf(), rbyte(r_svcreq_arg(i))};
102 for (auto i = 0; i < argv.size(); ++i) {
103 auto const& arg = argv[i];
104 auto const r = r_svcreq_arg(i);
106 switch (arg.kind) {
107 case Arg::Kind::Immed:
108 FTRACE(2, "{}, ", arg.imm);
109 v << copy{v.cns(arg.imm), r};
110 break;
111 case Arg::Kind::Address:
112 FTRACE(2, "{}(%rip), ", arg.imm);
113 v << leap{reg::rip[arg.imm], r};
114 break;
115 case Arg::Kind::CondCode:
116 break;
118 live_out |= r;
120 FTRACE(2, ") : stub@");
122 if (persist) {
123 FTRACE(2, "<none>");
124 v << copy{v.cns(0), r_svcreq_stub()};
125 } else {
126 FTRACE(2, "{}", stub.base());
127 v << leap{reg::rip[int64_t(stub.base())], r_svcreq_stub()};
129 v << copy{v.cns(sr), r_svcreq_req()};
131 live_out |= r_svcreq_stub();
132 live_out |= r_svcreq_req();
134 v << jmpi{tc::ustubs().handleSRHelper, live_out};
136 // We pad ephemeral stubs unconditionally. This is required for
137 // correctness by the x64 code relocator.
138 vasm.unit().padding = !persist;
141 if (!is_reused) cb.skip(stub.used());
144 ///////////////////////////////////////////////////////////////////////////////
148 ///////////////////////////////////////////////////////////////////////////////
150 TCA emit_bindjmp_stub(CodeBlock& cb, DataBlock& data, CGMeta& fixups,
151 FPInvOffset spOff,
152 TCA jmp, SrcKey target, TransFlags trflags) {
153 return emit_ephemeral(
155 data,
156 allocTCStub(cb, &fixups),
157 target.resumed() ? folly::none : folly::make_optional(spOff),
158 REQ_BIND_JMP,
159 jmp,
160 target.toAtomicInt(),
161 trflags.packed
165 TCA emit_bindaddr_stub(CodeBlock& cb, DataBlock& data, CGMeta& fixups,
166 FPInvOffset spOff,
167 TCA* addr, SrcKey target, TransFlags trflags) {
168 // Right now it's possible that addr isn't PIC addressable, as it may be into
169 // the heap (SSwitchMap binds addresses directly into its heap memory,
170 // see #10347945). Passing a TCA generates an RIP relative address which can
171 // be handled by the relocation logic, while a TCA* will generate an immediate
172 // address which will not be remapped.
173 if (deltaFits((TCA)addr - cb.frontier(), sz::dword)) {
174 return emit_ephemeral(
176 data,
177 allocTCStub(cb, &fixups),
178 target.resumed() ? folly::none : folly::make_optional(spOff),
179 REQ_BIND_ADDR,
180 (TCA)addr, // needs to be RIP relative so that we can relocate it
181 target.toAtomicInt(),
182 trflags.packed
186 return emit_ephemeral(
188 data,
189 allocTCStub(cb, &fixups),
190 target.resumed() ? folly::none : folly::make_optional(spOff),
191 REQ_BIND_ADDR,
192 addr,
193 target.toAtomicInt(),
194 trflags.packed
198 TCA emit_retranslate_stub(CodeBlock& cb, DataBlock& data, FPInvOffset spOff,
199 SrcKey target, TransFlags trflags) {
200 return emit_persistent(
202 data,
203 target.resumed() ? folly::none : folly::make_optional(spOff),
204 REQ_RETRANSLATE,
205 target.offset(),
206 trflags.packed
210 TCA emit_retranslate_opt_stub(CodeBlock& cb, DataBlock& data, FPInvOffset spOff,
211 SrcKey sk) {
212 return emit_persistent(
214 data,
215 sk.resumed() ? folly::none : folly::make_optional(spOff),
216 REQ_RETRANSLATE_OPT,
217 sk.toAtomicInt()
221 ///////////////////////////////////////////////////////////////////////////////
223 namespace x64 {
224 static constexpr int kMovLen = 10;
225 static constexpr int kLeaVmSpLen = 7;
228 namespace arm {
229 // vasm lea is emitted in 4 bytes.
230 // ADD imm
231 static constexpr int kLeaVmSpLen = 4;
232 // The largest of vasm setcc, copy, or leap is emitted in 8 bytes.
233 // AND imm, MOV, or ADRP + ADD imm
234 static constexpr int kMovLen = 8;
235 // The largest of vasm copy or leap is emitted in 8 bytes.
236 // MOV or ADRP + ADD imm
237 static constexpr int kPersist = 8;
238 // vasm copy and jmpi is emitted in 20 bytes.
239 // MOV and 16
240 static constexpr int kSvcReqExit = 20;
243 namespace ppc64 {
244 // Standard ppc64 instructions are 4 bytes long
245 static constexpr int kStdIns = 4;
246 // Leap for ppc64, in worst case, have 5 standard ppc64 instructions.
247 static constexpr int kLeaVMSpLen = kStdIns * 5;
250 size_t stub_size() {
251 // The extra args are the request type and the stub address.
252 constexpr auto kTotalArgs = kMaxArgs + 2;
254 switch (arch()) {
255 case Arch::X64:
256 return kTotalArgs * x64::kMovLen + x64::kLeaVmSpLen;
257 case Arch::ARM:
258 return arm::kLeaVmSpLen +
259 kTotalArgs * arm::kMovLen +
260 arm::kPersist + arm::kSvcReqExit;
261 case Arch::PPC64:
262 // This calculus was based on the amount of emitted instructions in
263 // emit_svcreq.
264 return (ppc64::kStdIns + ppc64::kLeaVMSpLen) * kTotalArgs +
265 ppc64::kLeaVMSpLen + 3 * ppc64::kStdIns;
267 not_reached();
270 ///////////////////////////////////////////////////////////////////////////////
272 FPInvOffset extract_spoff(TCA stub) {
273 switch (arch()) {
274 case Arch::X64: {
275 HPHP::jit::x64::DecodedInstruction instr(stub);
277 // If it's not a lea, vasm optimized a lea{rvmfp, rvmsp} to a mov, so
278 // the offset was 0.
279 if (!instr.isLea()) return FPInvOffset{0};
281 auto const offBytes = safe_cast<int32_t>(instr.offset());
282 always_assert((offBytes % sizeof(Cell)) == 0);
283 return FPInvOffset{-(offBytes / int32_t{sizeof(Cell)})};
286 case Arch::ARM: {
287 auto instr = reinterpret_cast<vixl::Instruction*>(stub);
289 if (instr->IsAddSubImmediate()) {
290 auto const offBytes = safe_cast<int32_t>(instr->ImmAddSub());
291 always_assert((offBytes % sizeof(Cell)) == 0);
293 if (instr->Mask(vixl::AddSubImmediateMask) == vixl::SUB_w_imm ||
294 instr->Mask(vixl::AddSubImmediateMask) == vixl::SUB_x_imm) {
295 return FPInvOffset{offBytes / int32_t{sizeof(Cell)}};
296 } else if (instr->Mask(vixl::AddSubImmediateMask) == vixl::ADD_w_imm ||
297 instr->Mask(vixl::AddSubImmediateMask) == vixl::ADD_x_imm) {
298 return FPInvOffset{-(offBytes / int32_t{sizeof(Cell)})};
300 } else if (instr->IsMovn()) {
301 auto next = instr->NextInstruction();
302 always_assert(next->Mask(vixl::AddSubShiftedMask) == vixl::ADD_w_shift ||
303 next->Mask(vixl::AddSubShiftedMask) == vixl::ADD_x_shift);
304 auto const offBytes = safe_cast<int32_t>(~instr->ImmMoveWide());
305 always_assert((offBytes % sizeof(Cell)) == 0);
306 return FPInvOffset{-(offBytes / int32_t{sizeof(Cell)})};
307 } else if (instr->IsMovz()) {
308 auto next = instr->NextInstruction();
309 always_assert(next->Mask(vixl::AddSubShiftedMask) == vixl::SUB_w_shift ||
310 next->Mask(vixl::AddSubShiftedMask) == vixl::SUB_x_shift);
311 auto const offBytes = safe_cast<int32_t>(instr->ImmMoveWide());
312 always_assert((offBytes % sizeof(Cell)) == 0);
313 return FPInvOffset{offBytes / int32_t{sizeof(Cell)}};
314 } else {
315 always_assert(false && "Expected an instruction that offsets SP");
319 case Arch::PPC64: {
320 ppc64_asm::DecodedInstruction instr(stub);
321 if (!instr.isSpOffsetInstr()) {
322 return FPInvOffset{0};
323 } else {
324 auto const offBytes = safe_cast<int32_t>(instr.offset());
325 return FPInvOffset{-(offBytes / int32_t{sizeof(Cell)})};
329 not_reached();
332 ///////////////////////////////////////////////////////////////////////////////