2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 #ifndef incl_HPHP_FIXUP_H_
18 #define incl_HPHP_FIXUP_H_
21 #include "hphp/runtime/vm/jit/types.h"
22 #include "hphp/runtime/base/execution-context.h"
23 #include "hphp/runtime/vm/tread-hash-map.h"
24 #include "hphp/util/atomic.h"
25 #include "hphp/util/data-block.h"
28 struct ExecutionContext
;
31 namespace HPHP
{ namespace jit
{
33 //////////////////////////////////////////////////////////////////////
36 * The Fixup map allows us to reconstruct the state of the VM registers (fp,
37 * sp, and pc) from an up-stack invocation record. Each range of bytes in the
38 * translation cache is associated with a "distance" in both stack cells and
39 * opcode bytes from the beginning of the function. These are known at
42 * The way this works is by chasing the native rbp chain to find a rbp that we
43 * know is a VM frame (i.e. is actually a full ActRec). Once we find that,
44 * regsFromActRec is called, which looks to see if the return ip for the frame
45 * before the VM frame has an entry in the fixup map (i.e. if it points into
46 * the translation cache)---if so, it finds the fixup information in one of two
49 * - Fixup: the normal case.
51 * The Fixup record just stores an offset relative to the ActRec* for vmsp,
52 * and an offset from the start of the func for pc. In the case of
53 * resumable frames the sp offset is relative to Stack::resumableStackBase.
55 * - IndirectFixup: this is used for some shared stubs in the TC.
57 * In this case, some JIT'd code associated with the ActRec* we found made
58 * a call to a shared stub, and then that stub called C++. The
59 * IndirectFixup record stores an offset to the saved frame pointer *two*
60 * levels deeper in C++, that says where the return IP for the call to the
61 * shared stub can be found. I.e., we're trying to chase back two return
64 * Note that this means IndirectFixups will not work for C++ code paths
65 * that need to do a fixup without making at least one other C++ call, but
66 * for the current use case this is fine.
68 * Here's a picture of the native stack in the indirect fixup situation:
70 * |..............................|
71 * |..............................|
72 * +------------------------------+ __enterTCHelper
73 * | RetIP to enterTC() |
78 * +------------------------------+ TC code
79 * | RetIP to enterTCHelper |
81 * | saved %rdi | <from callUnaryStub>
82 * +------------------------------+ STUB (e.g. decRefGeneric)
83 * | RetIP to caller of dtor stub |
85 * | <pushes in dtor stub> |
86 * +------------------------------+ <call to C++>
87 * | RetIP to the dtor stub |
89 * | saved rvmfp() | push %rbp; mov %rsp, %rbp
91 * | | < C++ local variables> |
92 * | +------------------------------+
93 * | | RetIP to first C++ callee | C++ calls another function
95 * +---| saved native %rbp (*) | points as shown, from mov above
97 * |..............................|
98 * |..............................|
100 * The offset in IndirectFixup is how to get to the "RetIP to caller of
101 * dtor stub", relative to the value in the starred stack slot shown. We
102 * then look that IP up in the fixup map again to find a normal
103 * (non-indirect) Fixup record.
107 //////////////////////////////////////////////////////////////////////
110 Fixup(int32_t pcOff
, int32_t spOff
) : pcOffset
{pcOff
}, spOffset
{spOff
} {
111 assertx(pcOffset
>= 0);
112 assertx(spOffset
>= 0);
117 bool isValid() const { return pcOffset
>= 0 && spOffset
>= 0; }
119 int32_t pcOffset
{-1};
120 int32_t spOffset
{-1};
123 struct IndirectFixup
{
124 explicit IndirectFixup(int retIpDisp
) : returnIpDisp
{retIpDisp
} {}
126 /* FixupEntry uses magic to differentiate between IndirectFixup and Fixup. */
128 int32_t returnIpDisp
;
131 inline Fixup
makeIndirectFixup(int dwordsPushed
) {
133 fix
.spOffset
= (2 + dwordsPushed
) * 8;
138 static constexpr unsigned kInitCapac
= 128;
139 TRACE_SET_MOD(fixup
);
147 FixupMap() : m_fixups(kInitCapac
) {}
149 void recordFixup(CTCA tca
, const Fixup
& fixup
) {
150 TRACE(3, "FixupMapImpl::recordFixup: tca %p -> (pcOff %d, spOff %d)\n",
151 tca
, fixup
.pcOffset
, fixup
.spOffset
);
153 if (auto pos
= m_fixups
.find(tca
)) {
154 *pos
= FixupEntry(fixup
);
156 m_fixups
.insert(tca
, FixupEntry(fixup
));
160 const Fixup
* findFixup(CTCA tca
) const {
161 auto ent
= m_fixups
.find(tca
);
162 if (!ent
) return nullptr;
166 bool getFrameRegs(const ActRec
* ar
, VMRegs
* outVMRegs
) const;
168 void fixup(ExecutionContext
* ec
) const;
169 void fixupWork(ExecutionContext
* ec
, ActRec
* rbp
) const;
170 void fixupWorkSimulated(ExecutionContext
* ec
) const;
172 static bool eagerRecord(const Func
* func
);
176 explicit FixupEntry(Fixup f
) : fixup(f
) {}
178 /* Depends on the magic field in an IndirectFixup being -1. */
179 bool isIndirect() const {
181 offsetof(IndirectFixup
, magic
) == offsetof(FixupEntry
, firstElem
),
182 "Differentiates between Fixup and IndirectFixup by looking at magic."
185 return firstElem
< 0;
190 IndirectFixup indirect
;
194 PC
pc(const ActRec
* ar
, const Func
* f
, const Fixup
& fixup
) const {
196 return f
->getEntry() + fixup
.pcOffset
;
199 void regsFromActRec(CTCA tca
, const ActRec
* ar
, const Fixup
& fixup
,
200 VMRegs
* outRegs
) const {
201 const Func
* f
= ar
->m_func
;
203 TRACE(3, "regsFromActRec:: tca %p -> (pcOff %d, spOff %d)\n",
204 (void*)tca
, fixup
.pcOffset
, fixup
.spOffset
);
205 assertx(fixup
.spOffset
>= 0);
206 outRegs
->pc
= pc(ar
, f
, fixup
);
209 if (UNLIKELY(ar
->resumed())) {
210 TypedValue
* stackBase
= Stack::resumableStackBase(ar
);
211 outRegs
->sp
= stackBase
- fixup
.spOffset
;
213 outRegs
->sp
= (TypedValue
*)ar
- fixup
.spOffset
;
218 TreadHashMap
<CTCA
,FixupEntry
,ctca_identity_hash
> m_fixups
;
221 //////////////////////////////////////////////////////////////////////