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/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 ///////////////////////////////////////////////////////////////////////////////
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
,
62 folly::Optional
<FPInvOffset
> spOff
,
65 FTRACE(2, "svcreq @{} {}(", start
, to_name(sr
));
67 auto const is_reused
= start
!= cb
.frontier();
70 auto const realAddr
= is_reused
? start
: cb
.toDestAddress(start
);
71 stub
.init(start
, realAddr
, stub_size(), "svcreq_stub");
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
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
);
107 case Arg::Kind::Immed
:
108 FTRACE(2, "{}, ", arg
.imm
);
109 v
<< copy
{v
.cns(arg
.imm
), r
};
111 case Arg::Kind::Address
:
112 FTRACE(2, "{}(%rip), ", arg
.imm
);
113 v
<< leap
{reg::rip
[arg
.imm
], r
};
115 case Arg::Kind::CondCode
:
120 FTRACE(2, ") : stub@");
124 v
<< copy
{v
.cns(0), r_svcreq_stub()};
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
,
152 TCA jmp
, SrcKey target
, TransFlags trflags
) {
153 return emit_ephemeral(
156 allocTCStub(cb
, &fixups
),
157 target
.resumed() ? folly::none
: folly::make_optional(spOff
),
160 target
.toAtomicInt(),
165 TCA
emit_bindaddr_stub(CodeBlock
& cb
, DataBlock
& data
, CGMeta
& fixups
,
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(
177 allocTCStub(cb
, &fixups
),
178 target
.resumed() ? folly::none
: folly::make_optional(spOff
),
180 (TCA
)addr
, // needs to be RIP relative so that we can relocate it
181 target
.toAtomicInt(),
186 return emit_ephemeral(
189 allocTCStub(cb
, &fixups
),
190 target
.resumed() ? folly::none
: folly::make_optional(spOff
),
193 target
.toAtomicInt(),
198 TCA
emit_retranslate_stub(CodeBlock
& cb
, DataBlock
& data
, FPInvOffset spOff
,
199 SrcKey target
, TransFlags trflags
) {
200 return emit_persistent(
203 target
.resumed() ? folly::none
: folly::make_optional(spOff
),
210 TCA
emit_retranslate_opt_stub(CodeBlock
& cb
, DataBlock
& data
, FPInvOffset spOff
,
212 return emit_persistent(
215 sk
.resumed() ? folly::none
: folly::make_optional(spOff
),
221 ///////////////////////////////////////////////////////////////////////////////
224 static constexpr int kMovLen
= 10;
225 static constexpr int kLeaVmSpLen
= 7;
229 // vasm lea is emitted in 4 bytes.
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.
240 static constexpr int kSvcReqExit
= 20;
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;
251 // The extra args are the request type and the stub address.
252 constexpr auto kTotalArgs
= kMaxArgs
+ 2;
256 return kTotalArgs
* x64::kMovLen
+ x64::kLeaVmSpLen
;
258 return arm::kLeaVmSpLen
+
259 kTotalArgs
* arm::kMovLen
+
260 arm::kPersist
+ arm::kSvcReqExit
;
262 // This calculus was based on the amount of emitted instructions in
264 return (ppc64::kStdIns
+ ppc64::kLeaVMSpLen
) * kTotalArgs
+
265 ppc64::kLeaVMSpLen
+ 3 * ppc64::kStdIns
;
270 ///////////////////////////////////////////////////////////////////////////////
272 FPInvOffset
extract_spoff(TCA stub
) {
275 HPHP::jit::x64::DecodedInstruction
instr(stub
);
277 // If it's not a lea, vasm optimized a lea{rvmfp, rvmsp} to a mov, so
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
)})};
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
)}};
315 always_assert(false && "Expected an instruction that offsets SP");
320 ppc64_asm::DecodedInstruction
instr(stub
);
321 if (!instr
.isSpOffsetInstr()) {
322 return FPInvOffset
{0};
324 auto const offBytes
= safe_cast
<int32_t>(instr
.offset());
325 return FPInvOffset
{-(offBytes
/ int32_t{sizeof(Cell
)})};
332 ///////////////////////////////////////////////////////////////////////////////