forked from facebook/hhvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfixup.h
More file actions
191 lines (168 loc) · 6.79 KB
/
Copy pathfixup.h
File metadata and controls
191 lines (168 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_FIXUP_H_
#define incl_HPHP_FIXUP_H_
#include <vector>
#include "hphp/runtime/vm/jit/types.h"
#include "hphp/runtime/base/execution-context.h"
#include "hphp/runtime/vm/tread-hash-map.h"
#include "hphp/runtime/vm/vm-regs.h"
#include "hphp/util/atomic.h"
#include "hphp/util/data-block.h"
namespace HPHP {
struct ExecutionContext;
}
namespace HPHP { namespace jit {
//////////////////////////////////////////////////////////////////////
/*
* The Fixup map allows us to reconstruct the state of the VM registers (fp,
* sp, and pc) from an up-stack invocation record. Each range of bytes in the
* translation cache is associated with a "distance" in both stack cells and
* opcode bytes from the beginning of the function. These are known at
* translation time.
*
* The way this works is by chasing the native rbp chain to find a rbp that we
* know is a VM frame (i.e. is actually a full ActRec). Once we find that,
* regsFromActRec is called, which looks to see if the return ip for the frame
* before the VM frame has an entry in the fixup map (i.e. if it points into
* the translation cache)---if so, it finds the fixup information in one of two
* ways:
*
* - Fixup: the normal case.
*
* The Fixup record just stores an offset relative to the ActRec* for vmsp,
* and an offset from the start of the func for pc. In the case of
* resumable frames the sp offset is relative to Stack::resumableStackBase.
*
* - IndirectFixup:
*
* This can be used for some shared stubs in the TC, to avoid
* setting up a full frame, on architectures where the calee's
* frame is stored immediately under the caller's sp (currently
* true of x64 but not arm or ppc).
*
* In this case, some JIT'd code associated with the ActRec* we found made
* a call to a shared stub, and then that stub called C++. The
* IndirectFixup record stores an offset to the saved frame pointer *two*
* levels deeper in C++, that says where the return IP for the call to the
* shared stub can be found. I.e., we're trying to chase back two return
* ips into the TC.
*
* Note that this means IndirectFixups will not work for C++ code
* paths that need to do a fixup without making at least one other
* C++ call (because of -momit-leaf-frame-pointers), but for the
* current use case this is fine.
*
* Here's a picture of the native stack in the indirect fixup situation:
*
* |..............................|
* |..............................|
* +------------------------------+ __enterTCHelper
* | RetIP to enterTC() |
* |-- --|
* | savedRbp |
* |-- --|
* | <other junk> |
* +------------------------------+ TC code
* | RetIP to enterTCHelper |
* |-- --|
* | saved %rdi | <from callUnaryStub>
* +------------------------------+ STUB (e.g. decRefGeneric)
* | RetIP to caller of dtor stub |
* |-- --|
* | <pushes in dtor stub> |
* +------------------------------+ <call to C++>
* | RetIP to the dtor stub |
* |-- --|
* | saved rvmfp() | push %rbp; mov %rsp, %rbp
* +-->|-- --|
* | | < C++ local variables> |
* | +------------------------------+
* | | RetIP to first C++ callee | C++ calls another function
* | |-- --|
* +---| saved native %rbp (*) | points as shown, from mov above
* |-- --|
* |..............................|
* |..............................|
*
* The offset in IndirectFixup is how to get to the "RetIP to caller of
* dtor stub", relative to the value in the starred stack slot shown. We
* then look that IP up in the fixup map again to find a normal
* (non-indirect) Fixup record.
*
*/
//////////////////////////////////////////////////////////////////////
struct Fixup {
Fixup(int32_t pcOff, int32_t spOff) : pcOffset{pcOff}, spOffset{spOff} {
assertx(pcOffset >= 0);
assertx(spOffset >= 0);
}
Fixup() {}
bool isValid() const { return spOffset >= 0; }
bool operator==(const Fixup& o) const {
return pcOffset == o.pcOffset && spOffset == o.spOffset;
}
bool operator!=(const Fixup& o) const {
return pcOffset != o.pcOffset || spOffset != o.spOffset;
}
int32_t pcOffset{-1};
int32_t spOffset{-1};
};
inline Fixup makeIndirectFixup(int dwordsPushed) {
Fixup fix;
fix.spOffset = kNativeFrameSize +
AROFF(m_savedRip) +
dwordsPushed * sizeof(uintptr_t);
return fix;
}
namespace FixupMap {
/*
* Record a new fixup (or overwrite an existing fixup) at tca.
*/
void recordFixup(CTCA tca, const Fixup& fixup);
/*
* Find the fixup for tca if it exists (or return nullptr).
*/
const Fixup* findFixup(CTCA tca);
/*
* Number of entries in the fixup map.
*/
size_t size();
/*
* Perform a fixup of the VM registers for a stack whose first frame is `rbp`.
*
* Returns whether we successfully performed the fixup. (We assert on failure
* if `soft` is not set).
*/
bool fixupWork(ActRec* rbp, bool soft = false);
/*
* Returns true if calls to func should use an EagerVMRegAnchor.
*/
bool eagerRecord(const Func* func);
}
namespace detail {
void syncVMRegsWork(bool soft); // internal sync work for a dirty vm state
}
/*
* Sync VM registers for the first TC frame in the callstack.
*/
inline void syncVMRegs(bool soft = false) {
if (tl_regState == VMRegState::CLEAN) return;
detail::syncVMRegsWork(soft);
}
//////////////////////////////////////////////////////////////////////
}}
#endif