Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / runtime / vm / jit / fixup.h
blob9b3c59bca49926761496f9eab39e52718b15e623
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 #pragma once
19 #include <vector>
20 #include "hphp/runtime/base/execution-context.h"
21 #include "hphp/runtime/vm/tread-hash-map.h"
22 #include "hphp/runtime/vm/vm-regs.h"
23 #include "hphp/runtime/vm/jit/stack-offsets.h"
24 #include "hphp/runtime/vm/jit/types.h"
25 #include "hphp/util/atomic.h"
26 #include "hphp/util/data-block.h"
28 namespace HPHP {
29 struct ExecutionContext;
32 namespace HPHP::jit {
34 //////////////////////////////////////////////////////////////////////
36 ActRec* findVMFrameForDebug();
39 * The Fixup map allows us to reconstruct the state of the VM registers (fp,
40 * sp, and pc) from an up-stack invocation record. Each range of bytes in the
41 * translation cache is associated with a "distance" in both stack cells and
42 * opcode bytes from the beginning of the function. These are known at
43 * translation time.
45 * The way this works is by chasing the native rbp chain to find a rbp that we
46 * know is a VM frame (i.e. is actually a full ActRec). Once we find that,
47 * regsFromActRec is called, which looks to see if the return ip for the frame
48 * before the VM frame has an entry in the fixup map (i.e. if it points into
49 * the translation cache)---if so, it finds the fixup information in one of two
50 * ways:
52 * - a direct Fixup: the normal case.
54 * The Fixup record just stores an offset relative to the ActRec* for vmsp,
55 * and an offset from the start of the func for pc. In the case of
56 * resumable frames the sp offset is relative to Stack::resumableStackBase.
58 * - an indirect Fixup:
60 * This is used when invoking C++ methods in stublogue mode, i.e. a TC code
61 * that was called, but did not set up a full frame, as it is operating on
62 * behalf of the caller. This mode is used in prologues and some shared
63 * stubs on architectures, where the callee's frame is stored immediately
64 * under the caller's sp (currently true on x64, but not arm or ppc).
66 * In this case, some JIT'd code associated with the ActRec* we found made
67 * a call to a shared stub or prologue, and then that code called C++. The
68 * indirect Fixup record stores an offset to the saved frame pointer *two*
69 * levels deeper in C++, that says where the return IP for the call to the
70 * shared stub can be found. I.e., we're trying to chase back two return
71 * ips into the TC.
73 * Note that this means indirect Fixups will not work for C++ code
74 * paths that need to do a fixup without making at least one other
75 * C++ call (because of -momit-leaf-frame-pointers), but for the
76 * current use case this is fine.
78 * Here's a picture of the native stack in the indirect fixup situation:
80 * |..............................|
81 * |..............................|
82 * +------------------------------+ __enterTCHelper
83 * | RetIP to enterTC() |
84 * |-- --|
85 * | savedRbp |
86 * |-- --|
87 * | <other junk> |
88 * +------------------------------+ TC code
89 * | RetIP to enterTCHelper |
90 * |-- --|
91 * | saved %rdi | <from callUnaryStub>
92 * +------------------------------+ STUB (e.g. decRefGeneric)
93 * | RetIP to caller of dtor stub |
94 * |-- --|
95 * | <pushes in dtor stub> |
96 * +------------------------------+ <call to C++>
97 * | RetIP to the dtor stub |
98 * |-- --|
99 * | saved rvmfp() | push %rbp; mov %rsp, %rbp
100 * +-->|-- --|
101 * | | < C++ local variables> |
102 * | +------------------------------+
103 * | | RetIP to first C++ callee | C++ calls another function
104 * | |-- --|
105 * +---| saved native %rbp (*) | points as shown, from mov above
106 * |-- --|
107 * |..............................|
108 * |..............................|
110 * The offset in indirect Fixup is how to get to the "RetIP to caller of
111 * dtor stub", relative to the value in the starred stack slot shown. We
112 * then look that IP up in the fixup map again to find a normal
113 * (non-indirect) Fixup record.
117 //////////////////////////////////////////////////////////////////////
119 struct Fixup {
120 static Fixup direct(int32_t pcOffset, SBInvOffset spOffset) {
121 assertx(pcOffset >= 0);
122 assertx(spOffset.offset >= 0);
123 return Fixup{pcOffset, spOffset};
126 static Fixup indirect(uint32_t qwordsPushed, SBInvOffset extraSpOffset) {
127 auto const ripOffset =
128 kNativeFrameSize + AROFF(m_savedRip) + qwordsPushed * sizeof(uintptr_t);
129 assertx(ripOffset > 0);
130 assertx(extraSpOffset.offset >= 0);
131 return Fixup{-safe_cast<int32_t>(ripOffset), extraSpOffset};
134 static Fixup none() {
135 return Fixup{0, SBInvOffset::invalid()};
138 bool isValid() const { return m_spOffset.isValid(); }
139 bool isIndirect() const { assertx(isValid()); return m_pcOrRipOffset < 0; }
140 uint32_t pcOffset() const { assertx(!isIndirect()); return m_pcOrRipOffset; }
141 uint32_t ripOffset() const { assertx(isIndirect()); return -m_pcOrRipOffset; }
142 SBInvOffset spOffset() const { assertx(isValid()); return m_spOffset; }
143 std::string show() const;
145 void adjustRipOffset(int off) {
146 assertx(isValid() && isIndirect());
147 m_pcOrRipOffset -= off;
150 bool operator==(const Fixup& o) const {
151 return m_pcOrRipOffset == o.m_pcOrRipOffset && m_spOffset == o.m_spOffset;
153 bool operator!=(const Fixup& o) const {
154 return m_pcOrRipOffset != o.m_pcOrRipOffset || m_spOffset != o.m_spOffset;
157 private:
158 Fixup(int32_t pcOrRipOffset, SBInvOffset spOffset)
159 : m_pcOrRipOffset{pcOrRipOffset}
160 , m_spOffset{spOffset}
163 int32_t m_pcOrRipOffset;
164 SBInvOffset m_spOffset;
168 * A record of a VMFrame used in unwinding. It captures a conceptual suspended
169 * frame, including the ActRec/rbp, the rip of the next instruction, and the
170 * CFA of the child frame. Note that m_actRec and m_rip are stored separately
171 * (instead of simply storing the child ActRec), as C++ frames need not lay out
172 * their frames with these fields adjacent.
174 struct VMFrame {
175 ActRec* m_actRec;
176 TCA m_rip;
177 uintptr_t m_prevCfa;
180 namespace FixupMap {
182 * Record a new fixup (or overwrite an existing fixup) at tca.
184 void recordFixup(CTCA tca, const Fixup& fixup);
187 * Find the fixup for tca if it exists (or return nullptr).
189 const Fixup* findFixup(CTCA tca);
192 * Number of entries in the fixup map.
194 size_t size();
197 * Perform a fixup of the VM registers for a leaf VM frame `frame`.
199 * Returns whether we successfully performed the fixup.
201 bool processFixupForVMFrame(VMFrame frame);
204 * Perform a fixup of the VM registers for a stack whose first frame is `rbp`.
206 * Returns whether we successfully performed the fixup. (We assert on failure
207 * if `soft` is not set).
209 bool fixupWork(ActRec* rbp, bool soft = false);
212 namespace detail {
213 void syncVMRegsWork(bool soft); // internal sync work for a dirty vm state
217 * Sync VM registers for the first TC frame in the callstack.
219 inline void syncVMRegs(bool soft = false) {
220 if (regState() == VMRegState::CLEAN) return;
221 #ifndef NDEBUG
222 if (regState() == VMRegState::CLEAN_VERIFY) {
223 auto& regs = vmRegsUnsafe();
224 DEBUG_ONLY auto const fp = regs.fp;
225 DEBUG_ONLY auto const sp = regs.stack.top();
226 DEBUG_ONLY auto const pc = regs.pc;
227 detail::syncVMRegsWork(soft);
228 assertx(regs.fp == fp);
229 assertx(regs.stack.top() == sp);
230 assertx(regs.pc == pc);
231 regState() = VMRegState::CLEAN;
232 return;
234 #endif
235 detail::syncVMRegsWork(soft);
239 * Gets the next fake JIT return address for eager syncs.
241 int32_t getNextFakeReturnAddress();
243 //////////////////////////////////////////////////////////////////////