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/unique-stubs-ppc64.h"
19 #include "hphp/runtime/base/header-kind.h"
20 #include "hphp/runtime/base/rds-header.h"
21 #include "hphp/runtime/base/runtime-option.h"
22 #include "hphp/runtime/base/stats.h"
23 #include "hphp/runtime/vm/event-hook.h"
25 #include "hphp/runtime/vm/jit/types.h"
26 #include "hphp/runtime/vm/jit/abi-ppc64.h"
27 #include "hphp/runtime/vm/jit/align-ppc64.h"
28 #include "hphp/runtime/vm/jit/code-gen-cf.h"
29 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
30 #include "hphp/runtime/vm/jit/code-gen-tls.h"
31 #include "hphp/runtime/vm/jit/fixup.h"
32 #include "hphp/runtime/vm/jit/phys-reg.h"
33 #include "hphp/runtime/vm/jit/service-requests.h"
34 #include "hphp/runtime/vm/jit/smashable-instr-ppc64.h"
35 #include "hphp/runtime/vm/jit/tc.h"
36 #include "hphp/runtime/vm/jit/translator-inline.h"
37 #include "hphp/runtime/vm/jit/unique-stubs.h"
38 #include "hphp/runtime/vm/jit/unwind-itanium.h"
39 #include "hphp/runtime/vm/jit/vasm-gen.h"
40 #include "hphp/runtime/vm/jit/vasm-instr.h"
42 #include "hphp/ppc64-asm/asm-ppc64.h"
43 #include "hphp/ppc64-asm/decoded-instr-ppc64.h"
44 #include "hphp/util/data-block.h"
46 namespace HPHP
{ namespace jit
{
48 TRACE_SET_MOD(ustubs
);
50 ///////////////////////////////////////////////////////////////////////////////
54 ///////////////////////////////////////////////////////////////////////////////
56 static void alignJmpTarget(CodeBlock
& cb
) {
57 align(cb
, nullptr, Alignment::JmpTarget
, AlignContext::Dead
);
60 ///////////////////////////////////////////////////////////////////////////////
63 * Helper for the freeLocalsHelpers which does the actual work of decrementing
64 * a value's refcount or releasing it.
66 * This helper is reached via call from the various freeLocalHelpers. It
67 * expects `tv' to be the address of a TypedValue with refcounted type `type'
68 * (though it may be static, and we will do nothing in that case).
70 * The `live' registers must be preserved across any native calls (and
71 * generally left untouched).
73 static TCA
emitDecRefHelper(CodeBlock
& cb
, DataBlock
& data
, CGMeta
& fixups
,
74 PhysReg tv
, PhysReg type
, RegSet live
) {
75 return vwrap(cb
, data
, fixups
, [&] (Vout
& v
) {
76 // We use the first argument register for the TV data because we might pass
77 // it to the native release call. It's not live when we enter the helper.
78 auto const data
= rarg(0);
79 v
<< load
{tv
[TVOFF(m_data
)], data
};
81 auto destroy
= [&](Vout
& v
) {
82 PhysRegSaver prs
{v
, live
};
84 auto const dword_size
= sizeof(int64_t);
86 // saving return value on the stack, but keeping it 16-byte aligned
88 v
<< lea
{rsp()[-2 * dword_size
], rsp()};
89 v
<< store
{rfuncln(), rsp()[0]};
91 // The refcount is exactly 1; release the value.
92 // Avoid 'this' pointer overwriting by reserving it as an argument.
93 v
<< callm
{lookupDestructor(v
, type
), arg_regs(1)};
95 // Between where r1 is now and the saved RIP of the call into the
96 // freeLocalsHelpers stub, we have all the live regs we pushed, plus the
97 // stack size reserved for the LR saved right above and the LR offset in
99 v
<< syncpoint
{makeIndirectFixup(prs
.dwordsPushed())};
102 // restore the return value from the stack
103 v
<< load
{rsp()[0], rfuncln()};
104 v
<< lea
{rsp()[2 * dword_size
], rsp()};
105 v
<< mtlr
{rfuncln()};
108 auto const sf
= emitCmpRefCount(v
, OneReference
, data
);
110 if (one_bit_refcount
) {
111 ifThen(v
, CC_E
, sf
, destroy
);
113 ifThen(v
, CC_NL
, sf
, [&] (Vout
& v
) {
114 // The refcount is positive, so the value is refcounted. We need to
115 // either decref or release.
116 ifThen(v
, CC_NE
, sf
, [&] (Vout
& v
) {
117 // The refcount is greater than 1; decref it.
118 emitDecRefCount(v
, data
);
126 // Either we did a decref, or the value was static.
131 TCA
emitFreeLocalsHelpers(CodeBlock
& cb
, DataBlock
& data
, UniqueStubs
& us
) {
132 // The address of the first local is passed in the second argument register.
133 // We use the third and fourth as scratch registers.
134 auto const local
= rarg(1);
135 auto const last
= rarg(2);
136 auto const type
= rarg(3);
139 // This stub is very hot; keep it cache-aligned.
140 align(cb
, &fixups
, Alignment::CacheLine
, AlignContext::Dead
);
142 emitDecRefHelper(cb
, data
, fixups
, local
, type
, local
| last
);
144 auto const decref_local
= [&] (Vout
& v
) {
145 auto const sf
= v
.makeReg();
147 // We can't do a byte load here---we have to sign-extend since we use
148 // `type' as a 32-bit array index to the destructor table.
149 v
<< loadzbl
{local
[TVOFF(m_type
)], type
};
150 emitCmpTVType(v
, sf
, KindOfRefCountThreshold
, type
);
152 ifThen(v
, CC_G
, sf
, [&] (Vout
& v
) {
153 auto const dword_size
= sizeof(int64_t);
155 // saving return value on the stack, but keeping it 16-byte aligned
156 v
<< mflr
{rfuncln()};
157 v
<< lea
{rsp()[-2 * dword_size
], rsp()};
158 v
<< store
{rfuncln(), rsp()[0]};
160 v
<< call
{release
, local
| type
};
162 // restore the return value from the stack
163 v
<< load
{rsp()[0], rfuncln()};
164 v
<< lea
{rsp()[2 * dword_size
], rsp()};
165 v
<< mtlr
{rfuncln()};
169 auto const next_local
= [&] (Vout
& v
) {
170 v
<< addqi
{static_cast<int>(sizeof(TypedValue
)),
171 local
, local
, v
.makeReg()};
176 us
.freeManyLocalsHelper
= vwrap(cb
, data
, fixups
, [&] (Vout
& v
) {
177 // We always unroll the final `kNumFreeLocalsHelpers' decrefs, so only loop
178 // until we hit that point.
179 v
<< lea
{rvmfp()[localOffset(kNumFreeLocalsHelpers
- 1)], last
};
181 doWhile(v
, CC_NZ
, {}, [&](const VregList
& /*in*/, const VregList
& /*out*/) {
182 auto const sf
= v
.makeReg();
186 v
<< cmpq
{ local
, last
, sf
};
191 for (auto i
= kNumFreeLocalsHelpers
- 1; i
>= 0; --i
) {
192 us
.freeLocalsHelpers
[i
] = vwrap(cb
, data
, [&] (Vout
& v
) {
194 if (i
!= 0) next_local(v
);
198 // All the stub entrypoints share the same ret.
199 vwrap(cb
, data
, fixups
, [] (Vout
& v
) { v
<< ret
{}; });
201 // This stub is hot, so make sure to keep it small.
203 // TODO(gut): Currently this assert fails.
204 // Take a closer look when looking at performance
205 always_assert(Stats::enabled() ||
206 (cb
.frontier() - release
<= 4 * cache_line_size()));
209 fixups
.process(nullptr);
213 ///////////////////////////////////////////////////////////////////////////////
215 void assert_tc_saved_rip(void* saved_lr_pointer
) {
216 // saved on enterTCHelper
217 auto const saved_lr
= *reinterpret_cast<uint8_t**>(saved_lr_pointer
);
218 auto const branch_block
= saved_lr
; // next instruction after resumetc's callr
219 auto const jccLen
= smashableJccLen() - ppc64_asm::instr_size_in_bytes
;
220 auto const branch_instr
= branch_block
+ jccLen
;
221 auto const exittc
= tc::ustubs().enterTCExit
;
223 ppc64_asm::DecodedInstruction
const di(branch_instr
);
225 ppc64_asm::DecodedInstruction
const di_target(branch_block
);
226 always_assert(di
.isJmp() && (di_target
.farBranchTarget() == exittc
));
228 always_assert(saved_lr
== exittc
);
232 TCA
emitCallToExit(CodeBlock
& cb
, DataBlock
& data
, const UniqueStubs
& /*us*/) {
233 ppc64_asm::Assembler a
{ cb
};
234 auto const start
= a
.frontier();
236 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
237 vwrap(cb
, data
, [&] (Vout
& v
) {
238 // Not doing it directly as rret(0) == rarg(0) on ppc64
239 Vreg ret_addr
= v
.makeReg();
241 // exittc address pushed on calltc/resumetc.
242 v
<< copy
{rsp(), ret_addr
};
244 // We need to spill the return registers around the assert call.
248 v
<< copy
{ret_addr
, rarg(0)};
249 v
<< call
{TCA(assert_tc_saved_rip
), RegSet(rarg(0))};
256 // Discard the exittc address pushed on calltc/resumetc for balancing the
258 a
.addi(rsp(), rsp(), 8);
260 // Reinitialize r1 for the external code found after enterTCExit's stubret
261 a
.addi(rsfp(), rsp(), 8);
263 // Restore the rvmfp when leaving the VM, which must be the same of rsfp.
264 a
.mr(rvmfp(), rsfp());
266 // Emulate a ret to enterTCExit without actually doing one to avoid
267 // unbalancing the return stack buffer.
268 a
.branchAuto(TCA(tc::ustubs().enterTCExit
));
272 ///////////////////////////////////////////////////////////////////////////////