2 +----------------------------------------------------------------------+
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"
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
42 register Cell
* sp
asm("rbx");
44 // TODO(#2056140): we have to decide regalloc conventions for ARM
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
;
59 ActRec
* fp
= (ActRec
*)framePtr
->m_savedRbp
;
61 VMExecutionContext
*ec
= g_vmContext
;
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
;
96 if (firstDVInitializer
!= InvalidAbsoluteOffset
) {
97 g_vmContext
->m_pc
= fp
->m_func
->unit()->entry() + firstDVInitializer
;
99 g_vmContext
->m_pc
= fp
->m_func
->getEntry();
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.
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");
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
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.
156 #elif defined(__AARCH64EL__)
159 # error You sure have your work cut out for you
164 TCA
fcallHelper(ActRec
* ar
) {
167 Translator::Get()->funcPrologue((Func
*)ar
->m_func
, ar
->numArgs(), ar
);
171 return callAndResume(ar
);
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
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
185 DECLARE_FRAME_POINTER(framePtr
);
186 tl_regState
= REGSTATE_CLEAN
;
187 framePtr
->m_savedRip
= ar
->m_savedRip
;
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
205 "call funcBodyHelper\n"
208 #elif defined(__AARCH64EL__)
211 # error You sure have your work cut out for you
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
);
227 func
->setFuncBody(tca
);
229 tca
= Translator::Get()->getResumeHelper();
231 tl_regState
= REGSTATE_DIRTY
;
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
;
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();
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
),
289 if (UNLIKELY(!f
)) return false;
290 CufIter
&cit
= it
->cuf();
298 cit
.setName(invName
);