2 +----------------------------------------------------------------------+
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
,
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
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.
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
);
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");
68 assertx(rbp
&& "Missing fixup for native call");
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());
76 if (getFrameRegs(rbp
, prevRbp
, ®s
)) {
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
;
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.
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
102 uintptr_t(ar
) - s_stackLimit
>= s_stackSize
&&
103 !sim
->is_on_stack(ar
);
104 assertx(!ret
|| isValidVMStackAddress(ar
) || ar
->resumed());
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
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
);
126 auto* ent
= m_fixups
.find(tca
);
131 if (ent
->isIndirect()) {
136 regsFromActRec(tca
, rbp
, ent
->fixup
, ®s
);
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
;
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
);
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
[] = {
170 "__SystemLib\\func_get_args_sl",
173 "__SystemLib\\func_num_arg_",
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 //////////////////////////////////////////////////////////////////////