Move unique stubs to unique-stubs-resumable.cpp
[hiphop-php.git] / hphp / runtime / vm / jit / unique-stubs-ppc64.cpp
blob066669ec553617cebb0a92df92331070afb2495e
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/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 ///////////////////////////////////////////////////////////////////////////////
52 namespace ppc64 {
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
87 v << mflr{rfuncln()};
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
98 // the frame.
99 v << syncpoint{makeIndirectFixup(prs.dwordsPushed())};
100 // fallthru
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);
112 } else {
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);
119 v << ret{live};
122 destroy(v);
126 // Either we did a decref, or the value was static.
127 v << ret{live};
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);
137 CGMeta fixups;
139 // This stub is very hot; keep it cache-aligned.
140 align(cb, &fixups, Alignment::CacheLine, AlignContext::Dead);
141 auto const release =
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()};
174 alignJmpTarget(cb);
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();
184 decref_local(v);
185 next_local(v);
186 v << cmpq{ local, last, sf };
187 return sf;
191 for (auto i = kNumFreeLocalsHelpers - 1; i >= 0; --i) {
192 us.freeLocalsHelpers[i] = vwrap(cb, data, [&] (Vout& v) {
193 decref_local(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.
202 #if 0
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()));
207 #endif
209 fixups.process(nullptr);
210 return release;
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);
224 if (di.isJmp()) {
225 ppc64_asm::DecodedInstruction const di_target(branch_block);
226 always_assert(di.isJmp() && (di_target.farBranchTarget() == exittc));
227 } else {
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.
245 v << push{rret(0)};
246 v << push{rret(1)};
248 v << copy{ret_addr, rarg(0)};
249 v << call{TCA(assert_tc_saved_rip), RegSet(rarg(0))};
251 v << pop{rret(1)};
252 v << pop{rret(0)};
256 // Discard the exittc address pushed on calltc/resumetc for balancing the
257 // stack next.
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));
269 return start;
272 ///////////////////////////////////////////////////////////////////////////////