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/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"
34 bool isVMFrame(const ActRec
* ar
, bool may_be_non_runtime
) {
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
;
42 (ar
->func()->validate(), true)
47 //////////////////////////////////////////////////////////////////////
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;
64 std::string
Fixup::show() const {
65 if (!isValid()) return "invalid";
67 return folly::sformat("indirect ripOff={} extraSpOff={}",
68 ripOffset(), spOffset().offset
);
70 return folly::sformat("direct pcOff={} spOff={}",
71 pcOffset(), spOffset().offset
);
75 //////////////////////////////////////////////////////////////////////
77 namespace FixupMap
{ namespace {
79 constexpr unsigned kInitCapac
= 128;
89 size_t operator()(uint32_t k
) const {
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();
101 TRACE(3, "regsFromActRec: tca %p -> %s\n", tca
, fixup
.show().c_str());
102 outRegs
->pc
= f
->entry() + fixup
.pcOffset();
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
132 auto const nextAR
= frame
.m_actRec
;
133 regsFromActRec(tca
, nextAR
, *fixup
, extraSpOffset
, outVMRegs
);
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
)) {
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));
163 if (!getFrameRegs(frame
, ®s
)) 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
;
176 bool fixupWork(ActRec
* nextRbp
, bool soft
) {
177 assertx(RuntimeOption::EvalJit
);
179 TRACE(1, "fixup(begin):\n");
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");
203 ////////////////////////////////////////////////////////////////////////////////
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
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
);