Add `recordbasenativesp` instr and support it in vasm-xls
[hiphop-php.git] / hphp / runtime / vm / jit / vasm-internal-inl.h
blob2ec8bb7e9de1e84950e2cddf1b309056660476c5
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/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"
27 #include <vector>
29 namespace HPHP { namespace jit {
31 struct IRInstruction;
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);
64 private:
65 struct Snippet {
66 const IRInstruction* origin;
67 TcaRange range;
71 * Get HHIR mapping info for the current block in `m_env'.
73 jit::vector<Snippet>& block_info();
75 private:
76 const Venv& m_env;
77 AsmInfo* m_asm_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&) {
107 return false;
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;
125 return true;
128 inline bool emit(Venv& env, const popframe&) {
129 if (env.frame == -1) return true; // unreachable block
131 ++env.pending_frames;
132 return true;
135 inline bool emit(Venv& env, const recordbasenativesp& i) {
136 return true;
139 inline bool emit(Venv& env, const recordstack& i) {
140 env.record_inline_stack(i.fakeAddress);
141 return true;
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());
169 record_frame(env);
170 env.frame = i.id;
171 return true;
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;
178 record_frame(env);
179 env.frame = env.unit.frames[env.frame].parent;
180 always_assert(0 <= env.frame && env.frame < env.unit.frames.size());
181 return true;
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;
189 switch (inst.op) {
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:
195 case Vinstr::unwind:
196 break;
198 default:
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;
207 break;
211 void computeFrames(Vunit& unit);
213 ///////////////////////////////////////////////////////////////////////////////
217 ///////////////////////////////////////////////////////////////////////////////
219 inline Venv::Venv(Vunit& unit, Vtext& text, CGMeta& meta)
220 : unit(unit)
221 , text(text)
222 , meta(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,
240 AsmInfo* asm_info) {
241 using namespace vasm_detail;
243 // Lower inlinestart and inlineend instructions to jmps, and annotate blocks
244 // with inlined function parents
245 computeFrames(unit);
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]));
268 auto b = 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]);
279 auto j = i + 1;
280 while (j < labels.size() &&
281 cur_start != area_start(labels[j])) {
282 j++;
284 env.next = j < labels.size() ? labels[j] : Vlabel(unit.blocks.size());
285 env.current = b;
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);
298 switch (inst.op) {
299 #define O(name, imms, uses, defs) \
300 case Vinstr::name: \
301 if (emit(env, inst.name##_)) break; \
302 Vemit(env).emit(inst.name##_); \
303 break;
304 VASM_OPCODES
305 #undef O
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.
327 Vemit::patch(env);
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);
343 irmu.finish(labels);
346 ///////////////////////////////////////////////////////////////////////////////