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
216 lines (182 loc) · 7.47 KB
/
fixup.h
File metadata and controls
216 lines (182 loc) · 7.47 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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2016 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/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 is used for some shared stubs in the TC.
*
* 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, 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 pcOffset >= 0 && spOffset >= 0; }
int32_t pcOffset{-1};
int32_t spOffset{-1};
};
struct IndirectFixup {
explicit IndirectFixup(int retIpDisp) : returnIpDisp{retIpDisp} {}
/* FixupEntry uses magic to differentiate between IndirectFixup and Fixup. */
int32_t magic{-1};
int32_t returnIpDisp;
};
inline Fixup makeIndirectFixup(int dwordsPushed) {
Fixup fix;
fix.spOffset = kNativeFrameSize +
AROFF(m_savedRip) +
dwordsPushed * sizeof(uintptr_t);
return fix;
}
struct FixupMap {
static constexpr unsigned kInitCapac = 128;
TRACE_SET_MOD(fixup);
struct VMRegs {
PC pc;
TypedValue* sp;
const ActRec* fp;
};
FixupMap() : m_fixups(kInitCapac) {}
void recordFixup(CTCA tca, const Fixup& fixup);
const Fixup* findFixup(CTCA tca) const;
size_t size() const { return m_fixups.size(); }
bool getFrameRegs(const ActRec* ar, VMRegs* outVMRegs) const;
void fixup(ExecutionContext* ec) const;
void fixupWork(ExecutionContext* ec, ActRec* rbp) const;
void fixupWorkSimulated(ExecutionContext* ec) const;
static bool eagerRecord(const Func* func);
private:
union FixupEntry {
explicit FixupEntry(Fixup f) : fixup(f) {}
/* Depends on the magic field in an IndirectFixup being -1. */
bool isIndirect() const {
static_assert(
offsetof(IndirectFixup, magic) == offsetof(FixupEntry, firstElem),
"Differentiates between Fixup and IndirectFixup by looking at magic."
);
return firstElem < 0;
}
int32_t firstElem;
Fixup fixup;
IndirectFixup indirect;
};
private:
PC pc(const ActRec* ar, const Func* f, const Fixup& fixup) const {
assertx(f);
return f->getEntry() + fixup.pcOffset;
}
void regsFromActRec(CTCA tca, const ActRec* ar, const Fixup& fixup,
VMRegs* outRegs) const {
const Func* f = ar->m_func;
assertx(f);
TRACE(3, "regsFromActRec:: tca %p -> (pcOff %d, spOff %d)\n",
(void*)tca, fixup.pcOffset, fixup.spOffset);
assertx(fixup.spOffset >= 0);
outRegs->pc = pc(ar, f, fixup);
outRegs->fp = ar;
if (UNLIKELY(ar->resumed())) {
TypedValue* stackBase = Stack::resumableStackBase(ar);
outRegs->sp = stackBase - fixup.spOffset;
} else {
outRegs->sp = (TypedValue*)ar - fixup.spOffset;
}
}
private:
TreadHashMap<uint32_t,FixupEntry,std::hash<uint32_t>> m_fixups;
};
//////////////////////////////////////////////////////////////////////
}}
#endif