Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / runtime / vm / jit / fixup.cpp
blobee26a014267eda1cc70bcafa65b00c736c471fb6
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/fixup.h"
19 #include "hphp/runtime/base/stats.h"
20 #include "hphp/runtime/vm/resumable.h"
21 #include "hphp/runtime/vm/vm-regs.h"
23 #include "hphp/runtime/vm/jit/abi-arm.h"
24 #include "hphp/runtime/vm/jit/code-cache.h"
25 #include "hphp/runtime/vm/jit/tc.h"
26 #include "hphp/runtime/vm/jit/translator-inline.h"
28 #include "hphp/util/data-block.h"
30 TRACE_SET_MOD(fixup);
32 namespace HPHP {
34 bool isVMFrame(const ActRec* ar, bool may_be_non_runtime) {
35 assertx(ar);
36 // Determine whether the frame pointer is outside the native stack, cleverly
37 // using a single unsigned comparison to do both halves of the bounds check.
38 auto const ret = uintptr_t(ar) - s_stackLimit >= s_stackSize;
39 assertx(
40 !ret ||
41 may_be_non_runtime ||
42 (ar->func()->validate(), true)
44 return ret;
47 //////////////////////////////////////////////////////////////////////
49 namespace jit {
51 ActRec* findVMFrameForDebug() {
52 DECLARE_FRAME_POINTER(framePtr);
53 auto rbp = (ActRec*) framePtr;
55 while (!isVMFrame(rbp, true)) {
56 auto const nextRbp = rbp->m_sfp;
57 if (nextRbp == nullptr || nextRbp == rbp) return nullptr;
58 rbp = nextRbp;
61 return rbp;
64 std::string Fixup::show() const {
65 if (!isValid()) return "invalid";
66 if (isIndirect()) {
67 return folly::sformat("indirect ripOff={} extraSpOff={}",
68 ripOffset(), spOffset().offset);
69 } else {
70 return folly::sformat("direct pcOff={} spOff={}",
71 pcOffset(), spOffset().offset);
75 //////////////////////////////////////////////////////////////////////
77 namespace FixupMap { namespace {
79 constexpr unsigned kInitCapac = 128;
81 struct VMRegs {
82 PC pc;
83 TypedValue* sp;
84 const ActRec* fp;
85 TCA retAddr;
88 struct FixupHash {
89 size_t operator()(uint32_t k) const {
90 return hash_int64(k);
94 TreadHashMap<uint32_t,Fixup,FixupHash> s_fixups{kInitCapac};
96 void regsFromActRec(TCA tca, const ActRec* ar, const Fixup& fixup,
97 SBInvOffset extraSpOffset, VMRegs* outRegs) {
98 assertx(!fixup.isIndirect());
99 const Func* f = ar->func();
100 assertx(f);
101 TRACE(3, "regsFromActRec: tca %p -> %s\n", tca, fixup.show().c_str());
102 outRegs->pc = f->entry() + fixup.pcOffset();
103 outRegs->fp = ar;
104 outRegs->retAddr = tca;
106 auto const stackBase = Stack::anyFrameStackBase(ar);
107 outRegs->sp = stackBase - fixup.spOffset().offset - extraSpOffset.offset;
110 //////////////////////////////////////////////////////////////////////
112 bool getFrameRegs(VMFrame frame, VMRegs* outVMRegs) {
113 auto tca = frame.m_rip;
114 auto fixup = s_fixups.find(tc::addrToOffset(tca));
115 if (!fixup) return false;
117 auto extraSpOffset = SBInvOffset{0};
118 if (fixup->isIndirect()) {
119 auto const ar = frame.m_prevCfa - kNativeFrameSize;
120 TRACE(3, "getFrameRegs: fp %p -> %s\n", (void*) ar, fixup->show().c_str());
121 auto const ripAddr = ar + fixup->ripOffset();
122 tca = *reinterpret_cast<TCA*>(ripAddr);
123 extraSpOffset = fixup->spOffset();
124 fixup = s_fixups.find(tc::addrToOffset(tca));
125 assertx(fixup && "Missing fixup for indirect fixup");
126 assertx(!fixup->isIndirect() && "Invalid doubly indirect fixup");
129 // Non-obvious off-by-one fun: if the *return address* points into the TC,
130 // then the frame we were running on in the TC is actually the previous
131 // frame.
132 auto const nextAR = frame.m_actRec;
133 regsFromActRec(tca, nextAR, *fixup, extraSpOffset, outVMRegs);
134 return true;
137 //////////////////////////////////////////////////////////////////////
140 void recordFixup(CTCA tca, const Fixup& fixup) {
141 TRACE(3, "recordFixup: tca %p -> %s\n", tca, fixup.show().c_str());
143 assertx(fixup.isValid());
144 auto const offset = tc::addrToOffset(tca);
145 if (auto pos = s_fixups.find(offset)) {
146 *pos = fixup;
147 } else {
148 s_fixups.insert(offset, fixup);
152 const Fixup* findFixup(CTCA tca) {
153 auto const fixup = s_fixups.find(tc::addrToOffset(tca));
154 return fixup ? &*fixup : nullptr;
157 size_t size() { return s_fixups.size(); }
159 bool processFixupForVMFrame(VMFrame frame) {
160 assertx(isVMFrame(frame.m_actRec, false));
162 VMRegs regs;
163 if (!getFrameRegs(frame, &regs)) return false;
165 TRACE(2, "fixup(end): func %s fp %p sp %p pc %p retAddr %p\n",
166 regs.fp->func()->name()->data(),
167 regs.fp, regs.sp, regs.pc, regs.retAddr);
168 auto& vmRegs = vmRegsUnsafe();
169 vmRegs.fp = const_cast<ActRec*>(regs.fp);
170 vmRegs.pc = reinterpret_cast<PC>(regs.pc);
171 vmRegs.stack.top() = regs.sp;
172 vmRegs.jitReturnAddr = regs.retAddr;
173 return true;
176 bool fixupWork(ActRec* nextRbp, bool soft) {
177 assertx(RuntimeOption::EvalJit);
179 TRACE(1, "fixup(begin):\n");
181 while (true) {
182 auto const rbp = nextRbp;
183 nextRbp = rbp->m_sfp;
185 if (UNLIKELY(soft) && (!nextRbp || nextRbp == rbp)) return false;
186 assertx(nextRbp && nextRbp != rbp && "Missing fixup for native call");
188 TRACE(2, "considering frame %p, %p\n", rbp, (void*)rbp->m_savedRip);
190 if (isVMFrame(nextRbp, soft)) {
191 TRACE(2, "fixup checking vm frame %s\n",
192 nextRbp->func()->name()->data());
193 auto const cfa = uintptr_t(rbp) + kNativeFrameSize;
194 auto const frame = VMFrame{nextRbp, TCA(rbp->m_savedRip), cfa};
195 auto const res = processFixupForVMFrame(frame);
196 if (res || LIKELY(soft)) return res;
197 always_assert(false && "Fixup expected for leafmost VM frame");
200 return false;
203 ////////////////////////////////////////////////////////////////////////////////
206 namespace detail {
207 void syncVMRegsWork(bool soft) {
208 // Start looking for fixup entries at the current (C++) frame. This
209 // will walk the frames upward until we find a TC frame.
210 DECLARE_FRAME_POINTER(framePtr);
211 auto fp = regState() >= VMRegState::GUARDED_THRESHOLD ?
212 (ActRec*)regState() : framePtr;
214 // TODO(mcolavita): This is incorrect for C++ routines with padding after
215 // their CFA.
216 auto const synced = FixupMap::fixupWork(fp, soft);
218 if (synced) regState() = VMRegState::CLEAN;
219 Stats::inc(Stats::TC_Sync);
223 static std::atomic<int32_t> s_nextFakeAddress{-1};
225 int32_t getNextFakeReturnAddress() {
226 return s_nextFakeAddress.fetch_sub(1, std::memory_order_relaxed);