Switch-related cleanup
[hiphop-php.git] / hphp / runtime / vm / jit / fixup.cpp
blob57aa7303a16be8e830bba128c061d9d10752bafd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/fixup.h"
18 #include "hphp/vixl/a64/simulator-a64.h"
20 #include "hphp/runtime/vm/vm-regs.h"
21 #include "hphp/runtime/vm/jit/abi-arm.h"
22 #include "hphp/runtime/vm/jit/mc-generator.h"
23 #include "hphp/runtime/vm/jit/translator-inline.h"
24 #include "hphp/util/data-block.h"
26 namespace HPHP { namespace jit {
28 //////////////////////////////////////////////////////////////////////
30 bool FixupMap::getFrameRegs(const ActRec* ar,
31 const ActRec* prevAr,
32 VMRegs* outVMRegs) const {
33 CTCA tca = (CTCA)ar->m_savedRip;
34 // Non-obvious off-by-one fun: if the *return address* points into the TC,
35 // then the frame we were running on in the TC is actually the previous
36 // frame.
37 ar = ar->m_sfp;
38 auto* ent = m_fixups.find(tca);
39 if (!ent) return false;
40 if (ent->isIndirect()) {
41 // Note: if indirect fixups happen frequently enough, we could
42 // just compare savedRip to be less than some threshold where
43 // stubs in a.code stop.
44 assertx(prevAr);
45 auto pRealRip = ent->indirect.returnIpDisp +
46 uintptr_t(prevAr->m_sfp);
47 ent = m_fixups.find(*reinterpret_cast<CTCA*>(pRealRip));
48 assertx(ent && !ent->isIndirect());
50 regsFromActRec(tca, ar, ent->fixup, outVMRegs);
51 return true;
54 void FixupMap::recordIndirectFixup(CodeAddress frontier, int dwordsPushed) {
55 recordIndirectFixup(frontier, IndirectFixup((2 + dwordsPushed) * 8));
58 void FixupMap::fixupWork(ExecutionContext* ec, ActRec* rbp) const {
59 assertx(RuntimeOption::EvalJit);
61 TRACE(1, "fixup(begin):\n");
63 auto* nextRbp = rbp;
64 rbp = 0;
65 do {
66 auto* prevRbp = rbp;
67 rbp = nextRbp;
68 assertx(rbp && "Missing fixup for native call");
69 nextRbp = rbp->m_sfp;
70 TRACE(2, "considering frame %p, %p\n", rbp, (void*)rbp->m_savedRip);
72 if (isVMFrame(nextRbp)) {
73 TRACE(2, "fixup checking vm frame %s\n",
74 nextRbp->m_func->name()->data());
75 VMRegs regs;
76 if (getFrameRegs(rbp, prevRbp, &regs)) {
77 TRACE(2, "fixup(end): func %s fp %p sp %p pc %p\n",
78 regs.fp->m_func->name()->data(),
79 regs.fp, regs.sp, regs.pc);
80 auto& vmRegs = vmRegsUnsafe();
81 vmRegs.fp = const_cast<ActRec*>(regs.fp);
82 vmRegs.pc = reinterpret_cast<PC>(regs.pc);
83 vmRegs.stack.top() = regs.sp;
84 return;
87 } while (rbp && rbp != nextRbp);
89 // OK, we've exhausted the entire actRec chain. We are only
90 // invoking ::fixup() from contexts that were known to be called out
91 // of the TC, so this cannot happen.
92 always_assert(false);
95 void FixupMap::fixupWorkSimulated(ExecutionContext* ec) const {
96 TRACE(1, "fixup(begin):\n");
98 auto isVMFrame = [] (ActRec* ar, const vixl::Simulator* sim) {
99 // If this assert is failing, you may have forgotten a sync point somewhere
100 assertx(ar);
101 bool ret =
102 uintptr_t(ar) - s_stackLimit >= s_stackSize &&
103 !sim->is_on_stack(ar);
104 assertx(!ret || isValidVMStackAddress(ar) || ar->resumed());
105 return ret;
108 // For each nested simulator (corresponding to nested VM invocations), look at
109 // its PC to find a potential fixup key.
111 // Callstack walking is necessary, because we may get called from a
112 // uniqueStub.
113 for (int i = ec->m_activeSims.size() - 1; i >= 0; --i) {
114 auto const* sim = ec->m_activeSims[i];
115 auto* rbp = reinterpret_cast<ActRec*>(sim->xreg(jit::arm::rVmFp.code()));
116 auto tca = reinterpret_cast<TCA>(sim->pc());
117 TRACE(2, "considering frame %p, %p\n", rbp, tca);
119 while (rbp && !isVMFrame(rbp, sim)) {
120 tca = reinterpret_cast<TCA>(rbp->m_savedRip);
121 rbp = rbp->m_sfp;
124 if (!rbp) continue;
126 auto* ent = m_fixups.find(tca);
127 if (!ent) {
128 continue;
131 if (ent->isIndirect()) {
132 not_implemented();
135 VMRegs regs;
136 regsFromActRec(tca, rbp, ent->fixup, &regs);
137 TRACE(2, "fixup(end): func %s fp %p sp %p pc %p\b",
138 regs.fp->m_func->name()->data(),
139 regs.fp, regs.sp, regs.pc);
140 auto& vmRegs = vmRegsUnsafe();
141 vmRegs.fp = const_cast<ActRec*>(regs.fp);
142 vmRegs.pc = reinterpret_cast<PC>(regs.pc);
143 vmRegs.stack.top() = regs.sp;
144 return;
147 // This shouldn't be reached.
148 always_assert(false);
151 void FixupMap::fixup(ExecutionContext* ec) const {
152 if (RuntimeOption::EvalSimulateARM) {
153 // Walking the C++ stack doesn't work in simulation mode. Fortunately, the
154 // execution context has a stack of simulators, which we consult instead.
155 fixupWorkSimulated(ec);
156 } else {
157 // Start looking for fixup entries at the current (C++) frame. This
158 // will walk the frames upward until we find a TC frame.
159 DECLARE_FRAME_POINTER(framePtr);
160 fixupWork(ec, framePtr);
164 /* This is somewhat hacky. It decides which helpers/builtins should
165 * use eager vmreganchor based on profile information. Using eager
166 * vmreganchor for all helper calls is a perf regression. */
167 bool FixupMap::eagerRecord(const Func* func) {
168 const char* list[] = {
169 "func_get_args",
170 "__SystemLib\\func_get_args_sl",
171 "get_called_class",
172 "func_num_args",
173 "__SystemLib\\func_num_arg_",
174 "array_filter",
175 "array_map",
176 "__SystemLib\\func_slice_args",
179 for (auto str : list) {
180 if (!strcmp(func->name()->data(), str)) return true;
183 return func->cls() &&
184 !strcmp(func->cls()->name()->data(), "HH\\WaitHandle") &&
185 !strcmp(func->name()->data(), "join");
188 //////////////////////////////////////////////////////////////////////