Remove .hh_file from hhas
[hiphop-php.git] / hphp / runtime / vm / vm-regs.h
blob741130ca64d5ea4f3b72041253fbb400b4008d9c
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 #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"
24 #include "hphp/runtime/vm/call-flags.h"
27 * This file contains accessors for the three primary VM registers:
29 * vmpc(): PC pointing to the currently executing bytecode instruction
30 * vmfp(): ActRec* pointing to the current frame
31 * vmsp(): TypedValue* pointing to the top of the eval stack
33 * The registers are physically located in the RDS header struct (defined in
34 * runtime/base/rds-header.h), allowing efficient access from translated code
35 * when needed. They are generally not kept up-to-date in translated code,
36 * though, so there are times when it is not safe to use them. This is tracked
37 * in the tl_regState variable, which is automatically checked in the accessors
38 * defined here. Certain parts of the runtime do need access to the registers
39 * while their state is expected to be dirty; the vmRegsUnsafe() function is
40 * provided for these rare cases.
42 * In a C++ function potentially called from translated code, VMRegAnchor
43 * should be used before accessing any of these registers. jit::FixupMap is
44 * currently responsible for doing the work required to sync the VM registers,
45 * though this is an implementation detail and should not matter to users of
46 * VMRegAnchor.
49 namespace HPHP {
52 * The current sync-state of the RDS vmRegs().
54 * CLEAN means that the RDS vmRegs are sync'd. DIRTY means we need to sync
55 * them (by traversing the stack and looking up fixups)---this is what the
56 * value of tl_regState should be whenever we enter native code from translated
57 * PHP code.
59 * Values above GUARDED_THRESHOLD are a special case of dirty which indicates
60 * that the state will be reset to DIRTY (via a scope guard) when returning to
61 * PHP code, and the actual value can be used as a start point for following the
62 * c++ callchain back into the VM. This makes it suitable for guarding callbacks
63 * through code compiled without frame pointers, and in places where we may
64 * end up needing to clean the registers multiple times.
66 enum VMRegState : uintptr_t {
67 CLEAN,
68 DIRTY,
69 GUARDED_THRESHOLD
71 extern __thread VMRegState tl_regState;
73 inline void checkVMRegState() {
74 assertx(tl_regState == VMRegState::CLEAN);
77 inline void checkVMRegStateGuarded() {
78 assertx(tl_regState != VMRegState::DIRTY);
81 inline VMRegs& vmRegsUnsafe() {
82 return rds::header()->vmRegs;
85 inline VMRegs& vmRegs() {
86 checkVMRegState();
87 return vmRegsUnsafe();
90 inline Stack& vmStack() {
91 return vmRegs().stack;
94 inline bool isValidVMStackAddress(const void* addr) {
95 return vmRegsUnsafe().stack.isValidAddress(uintptr_t(addr));
98 inline TypedValue*& vmsp() {
99 return vmRegs().stack.top();
102 inline ActRec*& vmfp() {
103 return vmRegs().fp;
106 inline const unsigned char*& vmpc() {
107 return vmRegs().pc;
110 inline Offset pcOff() {
111 return vmfp()->m_func->unit()->offsetOf(vmpc());
114 inline ActRec*& vmFirstAR() {
115 // This is safe because firstAR is always updated directly.
116 return vmRegsUnsafe().firstAR;
119 inline MInstrState& vmMInstrState() {
120 // This is safe because mInstrState is always updated directly.
121 return vmRegsUnsafe().mInstrState;
124 inline ActRec*& vmJitCalledFrame() {
125 return vmRegsUnsafe().jitCalledFrame;
128 inline jit::TCA& vmJitReturnAddr() {
129 return vmRegsUnsafe().jitReturnAddr;
132 inline void assert_native_stack_aligned() {
133 #ifndef _MSC_VER
134 assertx(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) % 16 == 0);
135 #endif
138 inline void interp_set_regs(ActRec* ar, TypedValue* sp, Offset pcOff) {
139 assertx(tl_regState == VMRegState::DIRTY);
140 tl_regState = VMRegState::CLEAN;
141 vmfp() = ar;
142 vmsp() = sp;
143 vmpc() = ar->unit()->at(pcOff);
144 vmJitReturnAddr() = nullptr; // We never elide frames around an interpOne
148 * Return the first VM frame that is a parent of this function's call frame.
150 ActRec* callerFrameHelper();
152 ///////////////////////////////////////////////////////////////////////////////
155 * This class is used as a scoped guard around code that is called from the JIT
156 * which needs the VM to be in a consistent state. JIT helpers use it to guard
157 * calls into HHVM's runtime. It is used like this:
159 * void helperFunction() {
160 * VMRegAnchor _;
161 * runtimeCall();
164 * VMRegAnchor should also be used before entering a C library compiled with
165 * -fomit-frame-pointer which will call back into HHVM. If VMRegAnchor is not
166 * used, HHVM's runtime will attempt to traverse the native stack, and will
167 * assert or crash if it attempts to parse a part of the stack with no frame
168 * pointers. VMRegAnchor forces the stack traversal to be done when it is
169 * constructed.
171 * A VMRegAnchor in "soft" mode will look for stashed VM metadata and only sync
172 * VM state if it finds it. (The default behavior is "hard" mode, where we
173 * assert if we don't find the fixup state).
175 struct VMRegAnchor {
176 enum Mode { Hard, Soft };
178 explicit VMRegAnchor(Mode mode = Hard);
180 ~VMRegAnchor() {
181 if (m_old < VMRegState::GUARDED_THRESHOLD) {
182 tl_regState = m_old;
186 VMRegAnchor(const VMRegAnchor&) = delete;
187 VMRegAnchor& operator=(const VMRegAnchor&) = delete;
189 VMRegState m_old;
193 * This class is used as an invocation guard equivalent to VMRegAnchor, except
194 * the sync is assumed to have already been done. This was part of a
195 * project aimed at improving performance by doing the fixup in advance, i.e.
196 * eagerly -- the benefits turned out to be marginal or negative in most cases.
198 struct EagerVMRegAnchor {
199 EagerVMRegAnchor() {
200 if (debug) {
201 auto& regs = vmRegsUnsafe();
202 DEBUG_ONLY auto const fp = regs.fp;
203 DEBUG_ONLY auto const sp = regs.stack.top();
204 DEBUG_ONLY auto const pc = regs.pc;
205 VMRegAnchor _;
206 assertx(regs.fp == fp);
207 assertx(regs.stack.top() == sp);
208 assertx(regs.pc == pc);
210 assertx(tl_regState < VMRegState::GUARDED_THRESHOLD);
211 m_old = tl_regState;
212 tl_regState = VMRegState::CLEAN;
215 ~EagerVMRegAnchor() {
216 tl_regState = m_old;
219 VMRegState m_old;
223 * A scoped guard used around native code that is called from the JIT and which
224 * /may conditionally/ need the VM to be in a consistent state.
226 * Using VMRegAnchor by itself would mean that in some cases---where we perform
227 * many independent operations which only conditionally require syncing---we'd
228 * have to choose between always (and sometimes spuriously) syncing, or syncing
229 * multiple times when we could have synced just once.
231 * VMRegGuard is intended to be used around these conditional syncs (i.e.,
232 * conditional instantiations of VMRegAnchor). It changes tl_regState to
233 * GUARDED, which tells sub-scoped VMRegAnchors that they may keep it set to
234 * CLEAN after they finish syncing.
236 * VMRegGuard also saves the current fp, making it suitable for guarding
237 * callbacks through library code that was compiled without frame pointers.
239 struct VMRegGuard {
241 * If we know the frame pointer returned by DECLARE_FRAME_POINTER is accurate,
242 * we can use ALWAYS_INLINE, and grab the frame pointer.
243 * If not, we have to use NEVER_INLINE to ensure we're one level in from the
244 * guard... but thats not quite enough because VMRegGuard::VMRegGuard is a
245 * leaf function, and so might not have a frame
247 #ifdef FRAME_POINTER_IS_ACCURATE
248 ALWAYS_INLINE VMRegGuard() : m_old(tl_regState) {
249 if (tl_regState == VMRegState::DIRTY) {
250 DECLARE_FRAME_POINTER(framePtr);
251 tl_regState = (VMRegState)(uintptr_t)framePtr;
254 #else
255 NEVER_INLINE VMRegGuard() : m_old(tl_regState) {
256 if (tl_regState == VMRegState::DIRTY) {
257 DECLARE_FRAME_POINTER(framePtr);
258 auto const fp = isVMFrame(framePtr->m_sfp) ? framePtr : framePtr->m_sfp;
259 tl_regState = (VMRegState)(uintptr_t)fp;
262 #endif
263 ~VMRegGuard() { tl_regState = m_old; }
265 VMRegGuard(const VMRegGuard&) = delete;
266 VMRegGuard& operator=(const VMRegGuard&) = delete;
268 VMRegState m_old;
271 ///////////////////////////////////////////////////////////////////////////////
273 #define SYNC_VM_REGS_SCOPED() \
274 HPHP::VMRegAnchor _anchorUnused
276 ///////////////////////////////////////////////////////////////////////////////
280 #endif