Clean up irgen.h a bit
[hiphop-php.git] / hphp / runtime / vm / jit / fixup.h
blob7173decd4597586a23514315189c07e0cd987d19
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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_
20 #include <vector>
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"
27 namespace HPHP {
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
40 * translation time.
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
47 * ways:
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
62 * ips into the TC.
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() |
74 * |-- --|
75 * | savedRbp |
76 * |-- --|
77 * | <other junk> |
78 * +------------------------------+ TC code
79 * | RetIP to enterTCHelper |
80 * |-- --|
81 * | saved %rdi | <from callUnaryStub>
82 * +------------------------------+ STUB (e.g. decRefGeneric)
83 * | RetIP to caller of dtor stub |
84 * |-- --|
85 * | <pushes in dtor stub> |
86 * +------------------------------+ <call to C++>
87 * | RetIP to the dtor stub |
88 * |-- --|
89 * | saved rvmfp() | push %rbp; mov %rsp, %rbp
90 * +-->|-- --|
91 * | | < C++ local variables> |
92 * | +------------------------------+
93 * | | RetIP to first C++ callee | C++ calls another function
94 * | |-- --|
95 * +---| saved native %rbp (*) | points as shown, from mov above
96 * |-- --|
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 //////////////////////////////////////////////////////////////////////
109 struct Fixup {
110 Fixup(int32_t pcOff, int32_t spOff) : pcOffset{pcOff}, spOffset{spOff} {
111 assertx(pcOffset >= 0);
112 assertx(spOffset >= 0);
115 Fixup() {}
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. */
127 int32_t magic{-1};
128 int32_t returnIpDisp;
131 inline Fixup makeIndirectFixup(int dwordsPushed) {
132 Fixup fix;
133 fix.spOffset = (2 + dwordsPushed) * 8;
134 return fix;
137 struct FixupMap {
138 static constexpr unsigned kInitCapac = 128;
139 TRACE_SET_MOD(fixup);
141 struct VMRegs {
142 PC pc;
143 TypedValue* sp;
144 const ActRec* fp;
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);
155 } else {
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;
163 return &ent->fixup;
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);
174 private:
175 union FixupEntry {
176 explicit FixupEntry(Fixup f) : fixup(f) {}
178 /* Depends on the magic field in an IndirectFixup being -1. */
179 bool isIndirect() const {
180 static_assert(
181 offsetof(IndirectFixup, magic) == offsetof(FixupEntry, firstElem),
182 "Differentiates between Fixup and IndirectFixup by looking at magic."
185 return firstElem < 0;
188 int32_t firstElem;
189 Fixup fixup;
190 IndirectFixup indirect;
193 private:
194 PC pc(const ActRec* ar, const Func* f, const Fixup& fixup) const {
195 assertx(f);
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;
202 assertx(f);
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);
207 outRegs->fp = ar;
209 if (UNLIKELY(ar->resumed())) {
210 TypedValue* stackBase = Stack::resumableStackBase(ar);
211 outRegs->sp = stackBase - fixup.spOffset;
212 } else {
213 outRegs->sp = (TypedValue*)ar - fixup.spOffset;
217 private:
218 TreadHashMap<CTCA,FixupEntry,ctca_identity_hash> m_fixups;
221 //////////////////////////////////////////////////////////////////////
225 #endif