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/containers.h"
18 #include "hphp/runtime/vm/jit/ir-opcode.h"
19 #include "hphp/runtime/vm/jit/trans-rec.h"
20 #include "hphp/runtime/vm/jit/vasm.h"
21 #include "hphp/runtime/vm/jit/vasm-instr.h"
22 #include "hphp/runtime/vm/jit/vasm-text.h"
23 #include "hphp/runtime/vm/jit/vasm-unit.h"
25 #include "hphp/util/data-block.h"
29 namespace HPHP
{ namespace jit
{
33 ///////////////////////////////////////////////////////////////////////////////
35 namespace vasm_detail
{
37 ///////////////////////////////////////////////////////////////////////////////
40 * Nouned verb class used to hide mostly-debug metadata updates from the main
41 * body of vasm_emit().
43 * This is invoked on every Vinstr encountered in order to accumulate mappings
44 * from higher-level representations.
46 struct IRMetadataUpdater
{
47 IRMetadataUpdater(const Venv
& env
, AsmInfo
* asm_info
);
50 * Update IR mappings for a Vinstr.
52 void register_inst(const Vinstr
& inst
);
55 * Update IR mappings at the end of a block.
57 void register_block_end();
60 * Update AsmInfo after the Vunit has been fully emitted.
62 void finish(const jit::vector
<Vlabel
>& labels
);
66 const IRInstruction
* origin
;
71 * Get HHIR mapping info for the current block in `m_env'.
73 jit::vector
<Snippet
>& block_info();
78 const IRInstruction
* m_origin
{nullptr};
79 jit::vector
<jit::vector
<jit::vector
<Snippet
>>> m_area_to_blockinfos
;
80 std::vector
<TransBCMapping
>* m_bcmap
{nullptr};
83 ///////////////////////////////////////////////////////////////////////////////
86 * Is `block' an empty catch block?
88 bool is_empty_catch(const Vblock
& block
);
91 * Register catch blocks for fixups.
93 void register_catch_block(const Venv
& env
, const Venv::LabelPatch
& p
);
96 * Emit a service request stub and register a patch point as needed.
98 void emit_svcreq_stub(Venv
& env
, const Venv::SvcReqPatch
& p
);
101 * Arch-independent emitters.
103 * Return true if the instruction was supported.
105 template <class Inst
>
106 bool emit(Venv
& /*env*/, const Inst
&) {
109 bool emit(Venv
& env
, const callphps
& i
);
110 bool emit(Venv
& env
, const bindjmp
& i
);
111 bool emit(Venv
& env
, const bindjcc
& i
);
112 bool emit(Venv
& env
, const bindaddr
& i
);
113 bool emit(Venv
& env
, const fallback
& i
);
114 bool emit(Venv
& env
, const fallbackcc
& i
);
115 bool emit(Venv
& env
, const retransopt
& i
);
116 bool emit(Venv
& env
, const movqs
& i
);
117 bool emit(Venv
& env
, const debugguardjmp
& i
);
118 bool emit(Venv
& env
, const jmps
& i
);
120 inline bool emit(Venv
& env
, const pushframe
&) {
121 if (env
.frame
== -1) return true; // unreachable block
122 assertx(env
.pending_frames
> 0);
124 --env
.pending_frames
;
128 inline bool emit(Venv
& env
, const popframe
&) {
129 if (env
.frame
== -1) return true; // unreachable block
131 ++env
.pending_frames
;
135 inline bool emit(Venv
& env
, const recordbasenativesp
& i
) {
139 inline bool emit(Venv
& env
, const recordstack
& i
) {
140 env
.record_inline_stack(i
.fakeAddress
);
144 inline void record_frame(Venv
& env
) {
145 auto const& block
= env
.unit
.blocks
[env
.current
];
146 auto const frame
= env
.frame
;
147 auto const start
= env
.framestart
;
148 auto& frames
= env
.unit
.frames
;
149 auto const size
= env
.cb
->frontier() - start
;
150 // It's possible that (a) this block is empty, and (b) cb is full, so the
151 // frontier from the start of the block is actually the first byte after the
152 // block. This is particularly likely when RetranslateAll is in use as
153 // ephemeral code blocks are resizable.
154 always_assert(!size
|| env
.cb
->contains(start
));
155 always_assert((int64_t)size
>= 0);
156 auto const area
= static_cast<uint8_t>(block
.area_idx
);
157 frames
[frame
].sections
[area
].exclusive
+= size
;
158 for (auto f
= block
.frame
; f
!= Vframe::Top
; f
= frames
[f
].parent
) {
159 frames
[f
].sections
[area
].inclusive
+= size
;
161 env
.framestart
= env
.cb
->frontier();
164 inline bool emit(Venv
& env
, const inlinestart
& i
) {
165 if (env
.frame
== -1) return true; // unreachable block
167 ++env
.pending_frames
;
168 always_assert(0 <= i
.id
&& i
.id
< env
.unit
.frames
.size());
173 inline bool emit(Venv
& env
, const inlineend
&) {
174 if (env
.frame
== -1) return true; // unreachable block
175 assertx(env
.pending_frames
> 0);
177 --env
.pending_frames
;
179 env
.frame
= env
.unit
.frames
[env
.frame
].parent
;
180 always_assert(0 <= env
.frame
&& env
.frame
< env
.unit
.frames
.size());
184 template<class Vemit
>
185 void check_nop_interval(Venv
& env
, const Vinstr
& inst
,
186 int& nop_counter
, int nop_interval
) {
187 if (LIKELY(nop_counter
< 0)) return;
190 // These instructions are for exception handling or state syncing and do
191 // not represent any actual work, so they're excluded from the nop counter.
192 case Vinstr::landingpad
:
193 case Vinstr::nothrow
:
194 case Vinstr::syncpoint
:
199 if (--nop_counter
== 0) {
200 // We use a special emit_nop() function rather than emit(nop{}) because
201 // many performance counters exclude nops from their count of retired
202 // instructions. It's up the to arch-specific backends to emit some
203 // real work with no visible side-effects.
204 Vemit(env
).emit_nop();
205 nop_counter
= nop_interval
;
211 void computeFrames(Vunit
& unit
);
213 ///////////////////////////////////////////////////////////////////////////////
217 ///////////////////////////////////////////////////////////////////////////////
219 inline Venv::Venv(Vunit
& unit
, Vtext
& text
, CGMeta
& meta
)
224 vaddrs
.resize(unit
.next_vaddr
);
227 inline void Venv::record_inline_stack(TCA addr
) {
228 uint32_t callOff
= 0;
229 auto const func
= unit
.frames
[frame
].func
;
230 auto const in_builtin
= func
&& func
->isCPPBuiltin();
231 if (origin
&& !in_builtin
) {
232 auto const marker
= origin
->marker();
233 callOff
= marker
.bcOff() - marker
.func()->base();
235 stacks
.emplace_back(addr
, IStack
{frame
- 1, pending_frames
, callOff
});
238 template<class Vemit
>
239 void vasm_emit(Vunit
& unit
, Vtext
& text
, CGMeta
& fixups
,
241 using namespace vasm_detail
;
243 // Lower inlinestart and inlineend instructions to jmps, and annotate blocks
244 // with inlined function parents
247 Venv env
{ unit
, text
, fixups
};
248 env
.addrs
.resize(unit
.blocks
.size());
250 auto labels
= layoutBlocks(unit
);
252 IRMetadataUpdater
irmu(env
, asm_info
);
254 auto const area_start
= [&] (Vlabel b
) {
255 auto area
= unit
.blocks
[b
].area_idx
;
256 return text
.area(area
).start
;
259 // We don't want to put nops in Vunits representing stubs, and those Vunits
260 // don't have a context set.
261 auto const nop_interval
=
262 unit
.context
? RuntimeOption::EvalJitNopInterval
: 0;
263 auto nop_counter
= nop_interval
;
265 for (int i
= 0, n
= labels
.size(); i
< n
; ++i
) {
266 assertx(checkBlockEnd(unit
, labels
[i
]));
269 auto& block
= unit
.blocks
[b
];
271 env
.cb
= &text
.area(block
.area_idx
).code
;
272 env
.addrs
[b
] = env
.cb
->frontier();
273 env
.framestart
= env
.cb
->frontier();
274 env
.frame
= block
.frame
;
275 env
.pending_frames
= std::max
<int32_t>(block
.pending_frames
, 0);
277 { // Compute the next block we will emit into the current area.
278 auto const cur_start
= area_start(labels
[i
]);
280 while (j
< labels
.size() &&
281 cur_start
!= area_start(labels
[j
])) {
284 env
.next
= j
< labels
.size() ? labels
[j
] : Vlabel(unit
.blocks
.size());
288 // We'll replace exception edges to empty catch blocks with the catch
289 // helper unique stub.
290 if (is_empty_catch(block
)) continue;
292 for (auto& inst
: block
.code
) {
293 irmu
.register_inst(inst
);
294 env
.origin
= inst
.origin
;
296 check_nop_interval
<Vemit
>(env
, inst
, nop_counter
, nop_interval
);
299 #define O(name, imms, uses, defs) \
301 if (emit(env, inst.name##_)) break; \
302 Vemit(env).emit(inst.name##_); \
309 if (block
.frame
!= -1) record_frame(env
);
310 irmu
.register_block_end();
313 Vemit::emitVeneers(env
);
315 Vemit::handleLiterals(env
);
317 // Emit service request stubs and register patch points.
318 for (auto& p
: env
.stubs
) emit_svcreq_stub(env
, p
);
320 // Bind any Vaddrs that correspond to Vlabels.
321 for (auto const& p
: env
.pending_vaddrs
) {
322 assertx(env
.addrs
[p
.target
]);
323 env
.vaddrs
[p
.vaddr
] = env
.addrs
[p
.target
];
326 // Patch up jump targets and friends.
329 // Register catch blocks.
330 for (auto& p
: env
.catches
) register_catch_block(env
, p
);
332 // Register inline frames.
333 for (auto& f
: unit
.frames
) {
334 if (f
.parent
== Vframe::Top
) continue; // skip the top frame
335 fixups
.inlineFrames
.emplace_back(IFrame
{f
.func
, f
.callOff
, f
.parent
- 1});
338 // Register inline stacks.
339 fixups
.inlineStacks
= std::move(env
.stacks
);
341 if (unit
.padding
) Vemit::pad(text
.main().code
);
346 ///////////////////////////////////////////////////////////////////////////////