Move runtime/eval/runtime/file_repository.* to runtime/base
[hiphop-php.git] / hphp / runtime / vm / jit / translator-x64-helpers.cpp
blobde30bbb3b02efeccd95a8d6fc1ce1b664e876302
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/translator-inline.h"
17 #include "hphp/runtime/vm/jit/translator-x64.h"
18 #include "hphp/runtime/vm/jit/targetcache.h"
19 #include "hphp/runtime/base/file_repository.h"
20 #include "hphp/runtime/vm/event_hook.h"
21 #include "hphp/runtime/base/builtin_functions.h"
22 #include "hphp/runtime/base/stats.h"
24 namespace HPHP {
25 namespace Transl {
27 inline bool
28 checkEval(HPHP::Eval::PhpFile* efile) {
29 return !TargetCache::testAndSetBit(efile->getId());
33 * reserve the VM stack pointer within this translation unit
35 * Note that for rbp we use a local register asm
36 * variable. But for rbx, we need to update it, and
37 * local register asm vars are saved and restored just
38 * like any other local. So I put this function in its own
39 * file to avoid impacting the rest of translator-x64.cpp
41 #ifdef __x86_64__
42 register Cell* sp asm("rbx");
43 #else
44 // TODO(#2056140): we have to decide regalloc conventions for ARM
45 Cell* sp;
46 #endif
48 void TranslatorX64::reqLitHelper(const ReqLitStaticArgs* args) {
49 DECLARE_FRAME_POINTER(framePtr);
51 HPHP::Eval::PhpFile* efile = args->m_efile;
52 if (!checkEval(efile)) {
53 Stats::inc(Stats::PseudoMain_Guarded);
54 sp->m_type = KindOfBoolean;
55 sp->m_data.num = 1;
56 return;
59 ActRec* fp = (ActRec*)framePtr->m_savedRbp;
61 VMExecutionContext *ec = g_vmContext;
62 ec->m_fp = fp;
63 ec->m_stack.top() = sp + 1;
64 PC pc = curUnit()->at(args->m_pcOff);
66 tl_regState = REGSTATE_CLEAN;
67 Unit *unit = efile->unit();
68 bool runPseudoMain = ec->evalUnit(unit, args->m_local, pc,
69 EventHook::PseudoMain);
70 tl_regState = REGSTATE_DIRTY;
71 if (!runPseudoMain) return;
73 ec->m_fp->m_savedRip = framePtr->m_savedRip;
74 // smash our return and frame pointer chain
75 framePtr->m_savedRip = (uint64_t)args->m_pseudoMain;
76 sp = ec->m_stack.top();
77 framePtr->m_savedRbp = (uint64_t)ec->m_fp;
80 static void setupAfterProlog(ActRec* fp) {
81 g_vmContext->m_fp = fp;
82 g_vmContext->m_stack.top() = sp;
83 int nargs = fp->numArgs();
84 int nparams = fp->m_func->numParams();
85 Offset firstDVInitializer = InvalidAbsoluteOffset;
86 if (nargs < nparams) {
87 const Func::ParamInfoVec& paramInfo = fp->m_func->params();
88 for (int i = nargs; i < nparams; ++i) {
89 Offset dvInitializer = paramInfo[i].funcletOff();
90 if (dvInitializer != InvalidAbsoluteOffset) {
91 firstDVInitializer = dvInitializer;
92 break;
96 if (firstDVInitializer != InvalidAbsoluteOffset) {
97 g_vmContext->m_pc = fp->m_func->unit()->entry() + firstDVInitializer;
98 } else {
99 g_vmContext->m_pc = fp->m_func->getEntry();
104 * fcallHelperThunk--
106 * Func's prologue entries initially point here. Vector into fcallHelper,
107 * where we can translate a new prologue.
110 static TCA callAndResume(ActRec *ar) {
111 if (!ar->m_func->isClonedClosure()) {
113 * If the func is a cloned closure, then the original
114 * closure has already run the prolog, and the prologs
115 * array is just being used as entry points for the
116 * dv funclets. Dont run the prolog again.
118 VMRegAnchor _(ar);
119 uint64_t rip = ar->m_savedRip;
120 g_vmContext->doFCall(ar, g_vmContext->m_pc);
121 ar->m_savedRip = rip;
122 return Translator::Get()->getResumeHelperRet();
124 setupAfterProlog(ar);
125 return Translator::Get()->getResumeHelper();
128 static_assert(rStashedAR == reg::r15,
129 "__fcallHelperThunk needs to be modified for ABI changes");
130 asm(
131 ".byte 0\n"
132 ".align 16\n"
133 ".globl __fcallHelperThunk\n"
134 "__fcallHelperThunk:\n"
135 #if defined(__x86_64__)
136 // fcallHelper is used for prologs, and (in the case of
137 // closures) for dispatch to the function body. In the first
138 // case, there's a call, in the second, there's a jmp.
139 // We can differentiate by comparing r15 and rVmFp
140 "mov %r15, %rdi\n"
141 "cmp %r15, %rbp\n"
142 "jne 1f\n"
143 "call fcallHelper\n"
144 "jmp *%rax\n"
145 // fcallHelper may call doFCall. doFCall changes the return ip
146 // pointed to by r15 so that it points to TranslatorX64::m_retHelper,
147 // which does a REQ_POST_INTERP_RET service request. So we need to
148 // to pop the return address into r15 + m_savedRip before calling
149 // fcallHelper, and then push it back from r15 + m_savedRip after
150 // fcallHelper returns in case it has changed it.
151 "1: pop 0x8(%r15)\n"
152 "call fcallHelper\n"
153 "push 0x8(%r15)\n"
154 "jmp *%rax\n"
155 "ud2\n"
156 #elif defined(__AARCH64EL__)
157 "brk 0\n"
158 #else
159 # error You sure have your work cut out for you
160 #endif
163 extern "C"
164 TCA fcallHelper(ActRec* ar) {
165 try {
166 TCA tca =
167 Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs(), ar);
168 if (tca) {
169 return tca;
171 return callAndResume(ar);
172 } catch (...) {
174 The return address is set to __fcallHelperThunk,
175 which has no unwind information. Its "logically"
176 part of the tc, but the c++ unwinder wont know
177 that. So point our return address at the called
178 function's return address (which will be in the
179 tc).
180 Note that the registers really are clean - we
181 just came from callAndResume which cleaned
182 them for us - so we just have to tell the unwinder
183 that.
185 DECLARE_FRAME_POINTER(framePtr);
186 tl_regState = REGSTATE_CLEAN;
187 framePtr->m_savedRip = ar->m_savedRip;
188 throw;
192 asm (
193 ".byte 0\n"
194 ".align 16\n"
195 ".globl __funcBodyHelperThunk\n"
196 "__funcBodyHelperThunk:\n"
197 #if defined(__x86_64__)
199 * when this helper is called, its as if by a jmp
200 * direct from the tc (its actually called by smashing
201 * the return address of fCallArrayHelper). So we dont
202 * need to worry about stack parity
204 "mov %rbp, %rdi\n"
205 "call funcBodyHelper\n"
206 "jmp *%rax\n"
207 "ud2\n"
208 #elif defined(__AARCH64EL__)
209 "brk 0\n"
210 #else
211 # error You sure have your work cut out for you
212 #endif
216 * This is used to generate an entry point for the entry
217 * to a function, after the prologue has run.
219 TCA funcBodyHelper(ActRec* fp) {
220 setupAfterProlog(fp);
221 tl_regState = REGSTATE_CLEAN;
222 Func* func = const_cast<Func*>(fp->m_func);
224 TCA tca = tx64->getCallArrayProlog(func);
226 if (tca) {
227 func->setFuncBody(tca);
228 } else {
229 tca = Translator::Get()->getResumeHelper();
231 tl_regState = REGSTATE_DIRTY;
232 return tca;
235 void TranslatorX64::fCallArrayHelper(const Offset pcOff, const Offset pcNext) {
236 DECLARE_FRAME_POINTER(framePtr);
237 ActRec* fp = (ActRec*)framePtr->m_savedRbp;
239 VMExecutionContext *ec = g_vmContext;
240 ec->m_fp = fp;
241 ec->m_stack.top() = sp;
242 ec->m_pc = curUnit()->at(pcOff);
243 PC pc = curUnit()->at(pcNext);
245 tl_regState = REGSTATE_CLEAN;
246 bool runFunc = ec->doFCallArray(pc);
247 sp = ec->m_stack.top();
248 tl_regState = REGSTATE_DIRTY;
249 if (!runFunc) return;
251 ec->m_fp->m_savedRip = framePtr->m_savedRip;
252 // smash our return and frame pointer chain
253 framePtr->m_savedRip = (uint64_t)ec->m_fp->m_func->getFuncBody();
254 framePtr->m_savedRbp = (uint64_t)ec->m_fp;
257 void functionEnterHelper(const ActRec* ar) {
258 DECLARE_FRAME_POINTER(framePtr);
260 uint64_t savedRip = ar->m_savedRip;
261 uint64_t savedRbp = ar->m_savedRbp;
262 if (LIKELY(EventHook::onFunctionEnter(ar, EventHook::NormalFunc))) return;
263 /* We need to skip the function.
264 FunctionEnter already cleaned up ar, and pushed the return value,
265 so all we need to do is return to where ar would have returned to,
266 with rbp set to ar's outer frame.
268 framePtr->m_savedRip = savedRip;
269 framePtr->m_savedRbp = savedRbp;
270 sp = g_vmContext->m_stack.top();
273 HOT_FUNC_VM
274 int64_t decodeCufIterHelper(Iter* it, TypedValue func) {
275 DECLARE_FRAME_POINTER(framePtr);
277 ObjectData* obj = nullptr;
278 HPHP::Class* cls = nullptr;
279 StringData* invName = nullptr;
281 auto ar = (ActRec*)framePtr->m_savedRbp;
282 if (LIKELY(ar->m_func->isBuiltin())) {
283 ar = g_vmContext->getOuterVMFrame(ar);
285 const Func* f = vm_decode_function(tvAsVariant(&func),
286 ar, false,
287 obj, cls, invName,
288 false);
289 if (UNLIKELY(!f)) return false;
290 CufIter &cit = it->cuf();
291 cit.setFunc(f);
292 if (obj) {
293 cit.setCtx(obj);
294 obj->incRefCount();
295 } else {
296 cit.setCtx(cls);
298 cit.setName(invName);
299 return true;
302 } } // HPHP::Transl