Mark some folly::Try functions are noexcept
[hiphop-php.git] / hphp / runtime / vm / vm-regs.h
blobf0e2b7348a080a4cee5c947167d469b437163626
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 "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
44 * VMRegAnchor.
47 namespace HPHP {
49 inline VMRegState eagerlyCleanState() {
50 #ifdef NDEBUG
51 return CLEAN;
52 #else
53 return CLEAN_VERIFY;
54 #endif
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() {
74 checkVMRegState();
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() {
91 return vmRegs().fp;
94 inline const unsigned char*& vmpc() {
95 return vmRegs().pc;
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;
126 vmfp() = ar;
127 vmsp() = sp;
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() {
140 * VMRegAnchor _;
141 * runtimeCall();
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
149 * constructed.
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).
155 struct VMRegAnchor {
156 enum Mode { Hard, Soft };
158 explicit VMRegAnchor(Mode mode = Hard);
160 ~VMRegAnchor() {
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) {
170 regState() = m_old;
174 VMRegAnchor(const VMRegAnchor&) = delete;
175 VMRegAnchor& operator=(const VMRegAnchor&) = delete;
177 VMRegState m_old;
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.
197 struct VMRegGuard {
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;
212 #else
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;
220 #endif
221 ~VMRegGuard() { regState() = m_old; }
223 VMRegGuard(const VMRegGuard&) = delete;
224 VMRegGuard& operator=(const VMRegGuard&) = delete;
226 VMRegState m_old;
229 ///////////////////////////////////////////////////////////////////////////////
231 #define SYNC_VM_REGS_SCOPED() \
232 HPHP::VMRegAnchor _anchorUnused
234 ///////////////////////////////////////////////////////////////////////////////