2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
19 #include "hphp/runtime/base/execution-context.h"
20 #include "hphp/runtime/base/rds-header.h"
21 #include "hphp/runtime/base/typed-value.h"
22 #include "hphp/runtime/vm/bytecode.h"
25 * This file contains accessors for the three primary VM registers:
27 * vmpc(): PC pointing to the currently executing bytecode instruction
28 * vmfp(): ActRec* pointing to the current frame
29 * vmsp(): TypedValue* pointing to the top of the eval stack
31 * The registers are physically located in the RDS header struct (defined in
32 * runtime/base/rds-header.h), allowing efficient access from translated code
33 * when needed. They are generally not kept up-to-date in translated code,
34 * though, so there are times when it is not safe to use them. This is tracked
35 * in the regState() variable, which is automatically checked in the accessors
36 * defined here. Certain parts of the runtime do need access to the registers
37 * while their state is expected to be dirty; the vmRegsUnsafe() function is
38 * provided for these rare cases.
40 * In a C++ function potentially called from translated code, VMRegAnchor
41 * should be used before accessing any of these registers. jit::FixupMap is
42 * currently responsible for doing the work required to sync the VM registers,
43 * though this is an implementation detail and should not matter to users of
49 inline VMRegState
eagerlyCleanState() {
57 inline VMRegState
& regState() {
58 return rds::header()->regState
;
61 inline void checkVMRegState() {
62 assertx(regState() == VMRegState::CLEAN
);
65 inline void checkVMRegStateGuarded() {
66 assertx(regState() != VMRegState::DIRTY
);
69 inline VMRegs
& vmRegsUnsafe() {
70 return rds::header()->vmRegs
;
73 inline VMRegs
& vmRegs() {
75 return vmRegsUnsafe();
78 inline Stack
& vmStack() {
79 return vmRegs().stack
;
82 inline bool isValidVMStackAddress(const void* addr
) {
83 return vmRegsUnsafe().stack
.isValidAddress(uintptr_t(addr
));
86 inline TypedValue
*& vmsp() {
87 return vmRegs().stack
.top();
90 inline ActRec
*& vmfp() {
94 inline const unsigned char*& vmpc() {
98 inline Offset
pcOff() {
99 return vmfp()->func()->offsetOf(vmpc());
102 inline ActRec
*& vmFirstAR() {
103 // This is safe because firstAR is always updated directly.
104 return vmRegsUnsafe().firstAR
;
107 inline MInstrState
& vmMInstrState() {
108 // This is safe because mInstrState is always updated directly.
109 return vmRegsUnsafe().mInstrState
;
112 inline ActRec
*& vmJitCalledFrame() {
113 return vmRegsUnsafe().jitCalledFrame
;
116 inline jit::TCA
& vmJitReturnAddr() {
117 return vmRegsUnsafe().jitReturnAddr
;
120 inline void assert_native_stack_aligned() {
121 assertx(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) % 16 == 0);
124 inline void interp_set_regs(ActRec
* ar
, TypedValue
* sp
, Offset pcOff
) {
125 regState() = VMRegState::CLEAN
;
128 vmpc() = ar
->func()->at(pcOff
);
129 vmJitReturnAddr() = nullptr; // We never elide frames around an interpOne
132 ///////////////////////////////////////////////////////////////////////////////
135 * This class is used as a scoped guard around code that is called from the JIT
136 * which needs the VM to be in a consistent state. JIT helpers use it to guard
137 * calls into HHVM's runtime. It is used like this:
139 * void helperFunction() {
144 * VMRegAnchor should also be used before entering a C library compiled with
145 * -fomit-frame-pointer which will call back into HHVM. If VMRegAnchor is not
146 * used, HHVM's runtime will attempt to traverse the native stack, and will
147 * assert or crash if it attempts to parse a part of the stack with no frame
148 * pointers. VMRegAnchor forces the stack traversal to be done when it is
151 * A VMRegAnchor in "soft" mode will look for stashed VM metadata and only sync
152 * VM state if it finds it. (The default behavior is "hard" mode, where we
153 * assert if we don't find the fixup state).
156 enum Mode
{ Hard
, Soft
};
158 explicit VMRegAnchor(Mode mode
= Hard
);
161 // In debug mode, we use VMProtect to ensure that we don't write back to
162 // VMRegs from the JIT. When we drop a soft VMRegAnchor and fail to sync
163 // VMRegs, don't store the (unchanged) state - doing so asserts.
165 // We don't do this check in release mode, because VMRegAnchor is hot and
166 // we don't need the extra check. We just do an idempotent store instead.
167 if (debug
&& regState() != VMRegState::CLEAN
) {
168 assertx(regState() == m_old
);
169 } else if (m_old
< VMRegState::GUARDED_THRESHOLD
) {
174 VMRegAnchor(const VMRegAnchor
&) = delete;
175 VMRegAnchor
& operator=(const VMRegAnchor
&) = delete;
181 * A scoped guard used around native code that is called from the JIT and which
182 * /may conditionally/ need the VM to be in a consistent state.
184 * Using VMRegAnchor by itself would mean that in some cases---where we perform
185 * many independent operations which only conditionally require syncing---we'd
186 * have to choose between always (and sometimes spuriously) syncing, or syncing
187 * multiple times when we could have synced just once.
189 * VMRegGuard is intended to be used around these conditional syncs (i.e.,
190 * conditional instantiations of VMRegAnchor). It changes regState() to
191 * GUARDED, which tells sub-scoped VMRegAnchors that they may keep it set to
192 * CLEAN after they finish syncing.
194 * VMRegGuard also saves the current fp, making it suitable for guarding
195 * callbacks through library code that was compiled without frame pointers.
199 * If we know the frame pointer returned by DECLARE_FRAME_POINTER is accurate,
200 * we can use ALWAYS_INLINE, and grab the frame pointer.
201 * If not, we have to use NEVER_INLINE to ensure we're one level in from the
202 * guard... but thats not quite enough because VMRegGuard::VMRegGuard is a
203 * leaf function, and so might not have a frame
205 #ifdef FRAME_POINTER_IS_ACCURATE
206 ALWAYS_INLINE
VMRegGuard() : m_old(regState()) {
207 if (regState() == VMRegState::DIRTY
) {
208 DECLARE_FRAME_POINTER(framePtr
);
209 regState() = (VMRegState
)(uintptr_t)framePtr
;
213 NEVER_INLINE
VMRegGuard() : m_old(regState()) {
214 if (regState() == VMRegState::DIRTY
) {
215 DECLARE_FRAME_POINTER(framePtr
);
216 auto const fp
= isVMFrame(framePtr
->m_sfp
) ? framePtr
: framePtr
->m_sfp
;
217 regState() = (VMRegState
)(uintptr_t)fp
;
221 ~VMRegGuard() { regState() = m_old
; }
223 VMRegGuard(const VMRegGuard
&) = delete;
224 VMRegGuard
& operator=(const VMRegGuard
&) = delete;
229 ///////////////////////////////////////////////////////////////////////////////
231 #define SYNC_VM_REGS_SCOPED() \
232 HPHP::VMRegAnchor _anchorUnused
234 ///////////////////////////////////////////////////////////////////////////////