Use RATs from HHBBC in init_use_vars
[hiphop-php.git] / hphp / runtime / vm / vm-regs.h
blob57cb87b6f3d7e6cc9e8386046f95da4323a9c879
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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
45 * VMRegAnchor.
48 namespace HPHP {
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 {
55 CLEAN,
56 DIRTY
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() {
73 checkVMRegState();
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() {
90 return vmRegs().fp;
93 inline const unsigned char*& vmpc() {
94 return vmRegs().pc;
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() {
112 #ifndef _MSC_VER
113 assert(reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) % 16 == 0);
114 #endif
117 inline void interp_set_regs(ActRec* ar, Cell* sp, Offset pcOff) {
118 assert(tl_regState == VMRegState::DIRTY);
119 tl_regState = VMRegState::CLEAN;
120 vmfp() = ar;
121 vmsp() = sp;
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() {
131 * VMRegAnchor _;
132 * runtimeCall();
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
140 * constructed.
142 struct VMRegAnchor : private boost::noncopyable {
143 VMRegState m_old;
145 VMRegAnchor();
146 explicit VMRegAnchor(ActRec* ar);
148 ~VMRegAnchor() {
149 tl_regState = m_old;
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 {
160 VMRegState m_old;
162 EagerVMRegAnchor() {
163 if (debug) {
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;
168 VMRegAnchor _;
169 assert(regs.fp == fp);
170 assert(regs.stack.top() == sp);
171 assert(regs.pc == pc);
173 m_old = tl_regState;
174 tl_regState = VMRegState::CLEAN;
177 ~EagerVMRegAnchor() {
178 tl_regState = m_old;
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();
188 auto cur = vmfp();
189 if (pc) *pc = cur->m_func->unit()->offsetOf(vmpc());
190 while (cur && cur->skipFrame()) {
191 cur = context->getPrevVMState(cur, pc);
193 return cur;
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);
203 return 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
229 #endif