2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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_RUNTIME_VM_VM_REGS_H_
18 #define incl_HPHP_RUNTIME_VM_VM_REGS_H_
20 #include "hphp/runtime/base/execution-context.h"
21 #include "hphp/runtime/base/rds-header.h"
22 #include "hphp/runtime/base/typed-value.h"
23 #include "hphp/runtime/vm/bytecode.h"
26 * This file contains accessors for the three primary VM registers:
28 * vmpc(): PC pointing to the currently executing bytecode instruction
29 * vmfp(): ActRec* pointing to the current frame
30 * vmsp(): Cell* pointing to the top of the eval stack
32 * The registers are physically located in the RDS header struct (defined in
33 * runtime/base/rds-header.h), allowing efficient access from translated code
34 * when needed. They are generally not kept up-to-date in translated code,
35 * though, so there are times when it is not safe to use them. This is tracked
36 * in the tl_regState variable, which is automatically checked in the accessors
37 * defined here. Certain parts of the runtime do need access to the registers
38 * while their state is expected to be dirty; the vmRegsUnsafe() function is
39 * provided for these rare cases.
41 * In a C++ function potentially called from translated code, VMRegAnchor
42 * should be used before accessing any of these registers. jit::MCGenerator is
43 * currently responsible for doing the work required to sync the VM registers,
44 * though this is an implementation detail and should not matter to users of
51 * DIRTY when the live register state is spread across the stack and Fixups.
52 * CLEAN when it has been sync'ed into RDS.
54 enum class VMRegState
: uint8_t {
58 extern __thread VMRegState tl_regState
;
60 inline void checkVMRegState() {
61 assert(tl_regState
== VMRegState::CLEAN
);
64 inline bool vmRegStateIsDirty() {
65 return tl_regState
== VMRegState::DIRTY
;
68 inline VMRegs
& vmRegsUnsafe() {
69 return rds::header()->vmRegs
;
72 inline VMRegs
& vmRegs() {
74 return vmRegsUnsafe();
77 inline Stack
& vmStack() {
78 return vmRegs().stack
;
81 inline bool isValidVMStackAddress(const void* addr
) {
82 return vmRegsUnsafe().stack
.isValidAddress(uintptr_t(addr
));
85 inline Cell
*& vmsp() {
86 return vmRegs().stack
.top();
89 inline ActRec
*& vmfp() {
93 inline const unsigned char*& vmpc() {
97 inline ActRec
*& vmFirstAR() {
98 // This is safe because firstAR is always updated directly.
99 return vmRegsUnsafe().firstAR
;
102 inline MInstrState
& vmMInstrState() {
103 // This is safe because mInstrState is always updated directly.
104 return vmRegsUnsafe().mInstrState
;
107 inline ActRec
*& vmJitCalledFrame() {
108 return vmRegsUnsafe().jitCalledFrame
;
111 inline void assert_native_stack_aligned() {
113 assert(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) % 16 == 0);
117 inline void interp_set_regs(ActRec
* ar
, Cell
* sp
, Offset pcOff
) {
118 assert(tl_regState
== VMRegState::DIRTY
);
119 tl_regState
= VMRegState::CLEAN
;
122 vmpc() = ar
->unit()->at(pcOff
);
126 * This class is used as a scoped guard around code that is called from the JIT
127 * which needs the VM to be in a consistent state. JIT helpers use it to guard
128 * calls into HHVM's runtime. It is used like this:
130 * void helperFunction() {
135 * VMRegAnchor should also be used before entering a C library compiled with
136 * -fomit-frame-pointer which will call back into HHVM. If VMRegAnchor is not
137 * used, HHVM's runtime will attempt to traverse the native stack, and will
138 * assert or crash if it attempts to parse a part of the stack with no frame
139 * pointers. VMRegAnchor forces the stack traversal to be done when it is
142 struct VMRegAnchor
: private boost::noncopyable
{
146 explicit VMRegAnchor(ActRec
* ar
);
154 * This class is used as an invocation guard equivalent to VMRegAnchor, except
155 * the sync is assumed to have already been done. This was part of a
156 * project aimed at improving performance by doing the fixup in advance, i.e.
157 * eagerly -- the benefits turned out to be marginal or negative in most cases.
159 struct EagerVMRegAnchor
{
164 auto& regs
= vmRegsUnsafe();
165 DEBUG_ONLY
auto const fp
= regs
.fp
;
166 DEBUG_ONLY
auto const sp
= regs
.stack
.top();
167 DEBUG_ONLY
auto const pc
= regs
.pc
;
169 assert(regs
.fp
== fp
);
170 assert(regs
.stack
.top() == sp
);
171 assert(regs
.pc
== pc
);
174 tl_regState
= VMRegState::CLEAN
;
177 ~EagerVMRegAnchor() {
182 inline ActRec
* regAnchorFP(Offset
* pc
= nullptr) {
183 // In builtins, m_fp points to the caller's frame if called through
184 // FCallBuiltin, else it points to the builtin's frame, in which case,
185 // getPrevVMState() gets the caller's frame. In addition, we need to skip
186 // over php-defined builtin functions in order to find the true context.
187 auto const context
= g_context
.getNoCheck();
189 if (pc
) *pc
= cur
->m_func
->unit()->offsetOf(vmpc());
190 while (cur
&& cur
->skipFrame()) {
191 cur
= context
->getPrevVMState(cur
, pc
);
196 inline ActRec
* regAnchorFPForArgs() {
197 // Like regAnchorFP, but only account for FCallBuiltin
198 auto const context
= g_context
.getNoCheck();
199 ActRec
* cur
= vmfp();
200 if (cur
&& cur
->m_func
->isCPPBuiltin()) {
201 cur
= context
->getPrevVMState(cur
);
206 struct EagerCallerFrame
: public EagerVMRegAnchor
{
207 ActRec
* operator()() {
208 return regAnchorFP();
210 ActRec
* actRecForArgs() { return regAnchorFPForArgs(); }
214 // VM helper to retrieve the frame pointer from the TC. This is
215 // a common need for extensions.
216 struct CallerFrame
: public VMRegAnchor
{
217 template<class... Args
>
218 ActRec
* operator()(Args
&&... args
) {
219 return regAnchorFP(std::forward
<Args
>(args
)...);
221 ActRec
* actRecForArgs() { return regAnchorFPForArgs(); }
224 #define SYNC_VM_REGS_SCOPED() \
225 HPHP::VMRegAnchor _anchorUnused