2 +----------------------------------------------------------------------+
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 #include "hphp/runtime/vm/jit/irlower-internal.h"
19 #include "hphp/runtime/base/datatype.h"
20 #include "hphp/runtime/base/execution-context.h"
21 #include "hphp/runtime/base/object-data.h"
22 #include "hphp/runtime/base/rds.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/runtime/base/typed-value.h"
26 #include "hphp/runtime/vm/act-rec.h"
27 #include "hphp/runtime/vm/func.h"
28 #include "hphp/runtime/vm/method-lookup.h"
30 #include "hphp/runtime/vm/jit/types.h"
31 #include "hphp/runtime/vm/jit/abi.h"
32 #include "hphp/runtime/vm/jit/arg-group.h"
33 #include "hphp/runtime/vm/jit/bc-marker.h"
34 #include "hphp/runtime/vm/jit/call-spec.h"
35 #include "hphp/runtime/vm/jit/code-gen-cf.h"
36 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
37 #include "hphp/runtime/vm/jit/extra-data.h"
38 #include "hphp/runtime/vm/jit/ir-instruction.h"
39 #include "hphp/runtime/vm/jit/ir-opcode.h"
40 #include "hphp/runtime/vm/jit/meth-profile.h"
41 #include "hphp/runtime/vm/jit/ssa-tmp.h"
42 #include "hphp/runtime/vm/jit/target-cache.h"
43 #include "hphp/runtime/vm/jit/translator-inline.h"
44 #include "hphp/runtime/vm/jit/type.h"
45 #include "hphp/runtime/vm/jit/vasm-gen.h"
46 #include "hphp/runtime/vm/jit/vasm-instr.h"
47 #include "hphp/runtime/vm/jit/vasm-reg.h"
49 #include "hphp/util/asm-x64.h"
50 #include "hphp/util/trace.h"
52 namespace HPHP
{ namespace jit
{ namespace irlower
{
54 TRACE_SET_MOD(irlower
);
56 ///////////////////////////////////////////////////////////////////////////////
58 void cgLdObjMethod(IRLS
& env
, const IRInstruction
* inst
) {
59 assertx(inst
->taken() && inst
->taken()->isCatch()); // must have catch block
60 using namespace MethodCache
;
62 auto const cls
= srcLoc(env
, inst
, 0).reg();
63 auto const fp
= srcLoc(env
, inst
, 1).reg();
64 auto const extra
= inst
->extra
<LdObjMethodData
>();
66 auto& vc
= vcold(env
);
68 // Allocate the request-local one-way method cache for this lookup.
70 rds::alloc
<Entry
, rds::Mode::Normal
, sizeof(Entry
)>().handle();
71 if (RuntimeOption::EvalPerfDataMap
) {
72 rds::recordRds(handle
, sizeof(TypedValue
), "MethodCache",
73 inst
->marker().func()->fullName()->slice());
76 auto const mc_handler
= extra
->fatal
? tc::ustubs().handlePrimeCacheInitFatal
77 : tc::ustubs().handlePrimeCacheInit
;
80 * The `mcprep' instruction here creates a smashable move, which serves as
81 * the inline cache, or "prime cache" for the method lookup.
83 * On our first time through this codepath in the TC, we "prime" this cache
84 * (which holds across /all/ requests) by smashing the mov immediate to hold
85 * a Func* in the upper 32 bits, and a Class* in the lower 32 bits. This is
86 * not always possible (see handlePrimeCacheInit() for details), in which
87 * case we smash an immediate with some low bits set, so that we always miss
88 * on the inline cache when comparing against our live Class*.
90 * The inline cache is set up so that we always miss initially, and take the
91 * slow path to initialize it. After initialization, we also smash the slow
92 * path call to point instead to a lookup routine for the out-of-line method
93 * cache (allocated above). The inline cache is guaranteed to be set only
94 * once, but the one-way request-local method cache is updated on each miss.
96 auto func_class
= v
.makeReg();
97 v
<< mcprep
{func_class
};
99 // Get the Class* part of the cache line.
100 auto tmp
= v
.makeReg();
101 auto classptr
= v
.makeReg();
102 v
<< movtql
{func_class
, tmp
};
103 v
<< movzlq
{tmp
, classptr
};
105 // Check the inline cache.
106 auto const sf
= v
.makeReg();
107 v
<< cmpq
{classptr
, cls
, sf
};
111 [&] (Vout
& v
) { // then block (unlikely)
112 auto const args
= argGroup(env
, inst
)
113 .imm(safe_cast
<int32_t>(handle
))
114 .addr(fp
, cellsToBytes(extra
->offset
.offset
))
115 .immPtr(extra
->method
)
117 .immPtr(inst
->marker().func()->cls())
120 cgCallHelper(v
, env
, CallSpec::smashable(mc_handler
),
121 kVoidDest
, SyncOptions::Sync
, args
);
123 [&] (Vout
& v
) { // else block (likely)
124 auto const funcptr
= v
.makeReg();
125 v
<< shrqi
{32, func_class
, funcptr
, v
.makeReg()};
127 fp
[cellsToBytes(extra
->offset
.offset
) + AROFF(m_func
)]};
131 ///////////////////////////////////////////////////////////////////////////////
133 IMPL_OPCODE_CALL(LdClsCtor
)
135 template<bool forward
>
136 void lookupClsMethodHelper(Class
* cls
, StringData
* meth
,
137 ActRec
* ar
, ActRec
* fp
) {
140 auto const ctx
= fp
->m_func
->cls();
141 auto const obj
= ctx
&& fp
->hasThis() ? fp
->getThis() : nullptr;
142 auto const res
= lookupClsMethod(f
, cls
, meth
, obj
, ctx
, true);
146 if (res
== LookupResult::MethodFoundNoThis
||
147 res
== LookupResult::MagicCallStaticFound
) {
148 if (!f
->isStaticInPrologue()) {
149 raise_missing_this(f
);
151 if (forward
&& ctx
) {
153 cls
= fp
->getThis()->getVMClass();
155 cls
= fp
->getClass();
161 assertx(res
== LookupResult::MethodFoundWithThis
||
162 res
== LookupResult::MagicCallFound
);
167 if (res
== LookupResult::MagicCallFound
||
168 res
== LookupResult::MagicCallStaticFound
) {
169 ar
->setMagicDispatch(meth
);
173 *arPreliveOverwriteCells(ar
) = make_tv
<KindOfString
>(meth
);
178 void cgLookupClsMethod(IRLS
& env
, const IRInstruction
* inst
) {
179 auto const extra
= inst
->extra
<LookupClsMethod
>();
180 auto const sp
= srcLoc(env
, inst
, 2).reg();
182 auto const args
= argGroup(env
, inst
)
185 .addr(sp
, cellsToBytes(extra
->calleeAROffset
.offset
))
188 if (extra
->forward
) {
189 cgCallHelper(vmain(env
), env
,
190 CallSpec::direct(lookupClsMethodHelper
<true>),
191 callDest(env
, inst
), SyncOptions::Sync
, args
);
193 cgCallHelper(vmain(env
), env
,
194 CallSpec::direct(lookupClsMethodHelper
<false>),
195 callDest(env
, inst
), SyncOptions::Sync
, args
);
199 void cgProfileMethod(IRLS
& env
, const IRInstruction
* inst
) {
200 auto const extra
= inst
->extra
<ProfileCallTargetData
>();
201 auto const sp
= srcLoc(env
, inst
, 0).reg();
203 auto const args
= argGroup(env
, inst
)
204 .addr(rvmtl(), safe_cast
<int32_t>(extra
->handle
))
205 .addr(sp
, cellsToBytes(extra
->bcSPOff
.offset
))
208 cgCallHelper(vmain(env
), env
, CallSpec::method(&MethProfile::reportMeth
),
209 kVoidDest
, SyncOptions::None
, args
);
212 ///////////////////////////////////////////////////////////////////////////////
216 const char* ctxName(const BCMarker
& marker
) {
217 auto const ctx
= marker
.func()->cls();
218 return ctx
? ctx
->name()->data() : ":anonymous:";
223 ///////////////////////////////////////////////////////////////////////////////
225 void cgLookupClsMethodCache(IRLS
& env
, const IRInstruction
* inst
) {
226 auto const extra
= inst
->extra
<ClsMethodData
>();
227 auto const dst
= dstLoc(env
, inst
, 0).reg();
228 auto const fp
= srcLoc(env
, inst
, 0).reg();
229 auto& v
= vmain(env
);
231 auto const ch
= StaticMethodCache::alloc(
234 ctxName(inst
->marker())
237 if (false) { // typecheck
238 UNUSED TypedValue
* fake_fp
= nullptr;
239 const UNUSED Func
* f
= StaticMethodCache::lookup(
248 auto const args
= argGroup(env
, inst
)
250 .immPtr(extra
->namedEntity
)
251 .immPtr(extra
->clsName
)
252 .immPtr(extra
->methodName
)
255 // May raise an error if the class is undefined.
256 cgCallHelper(v
, env
, CallSpec::direct(StaticMethodCache::lookup
),
257 callDest(dst
), SyncOptions::Sync
, args
);
260 void cgLdClsMethodCacheFunc(IRLS
& env
, const IRInstruction
* inst
) {
261 auto const extra
= inst
->extra
<ClsMethodData
>();
262 auto const dst
= dstLoc(env
, inst
, 0).reg();
263 auto& v
= vmain(env
);
265 auto const ch
= StaticMethodCache::alloc(
268 ctxName(inst
->marker())
271 auto const sf
= checkRDSHandleInitialized(v
, ch
);
272 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
273 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodCache
, m_func
)],
274 dst
, sizeof(LowPtr
<const Func
>));
277 void cgLdClsMethodCacheCls(IRLS
& env
, const IRInstruction
* inst
) {
278 auto const extra
= inst
->extra
<ClsMethodData
>();
279 auto const dst
= dstLoc(env
, inst
, 0).reg();
280 auto& v
= vmain(env
);
282 auto const ch
= StaticMethodCache::alloc(
285 ctxName(inst
->marker())
287 assertx(rds::isNormalHandle(ch
));
289 // The StaticMethodCache here is guaranteed to already be initialized in RDS
290 // by the pre-conditions of this instruction.
291 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodCache
, m_cls
)],
292 dst
, sizeof(LowPtr
<const Class
>));
295 void cgLookupClsMethodFCache(IRLS
& env
, const IRInstruction
* inst
) {
296 auto const extra
= inst
->extra
<ClsMethodData
>();
297 auto const dst
= dstLoc(env
, inst
, 0).reg(0);
298 auto const cls
= inst
->src(0)->clsVal();
299 auto const fp
= srcLoc(env
, inst
, 1).reg();
300 auto& v
= vmain(env
);
302 auto const ch
= StaticMethodFCache::alloc(
305 ctxName(inst
->marker())
307 assertx(rds::isNormalHandle(ch
));
309 const Func
* (*lookup
)(rds::Handle
, const Class
*,
310 const StringData
*, TypedValue
*) =
311 StaticMethodFCache::lookup
;
313 auto const args
= argGroup(env
, inst
)
316 .immPtr(extra
->methodName
)
319 cgCallHelper(v
, env
, CallSpec::direct(lookup
),
320 callDest(dst
), SyncOptions::Sync
, args
);
323 void cgLdClsMethodFCacheFunc(IRLS
& env
, const IRInstruction
* inst
) {
324 auto const extra
= inst
->extra
<ClsMethodData
>();
325 auto const dst
= dstLoc(env
, inst
, 0).reg();
326 auto& v
= vmain(env
);
328 auto const ch
= StaticMethodFCache::alloc(
331 ctxName(inst
->marker())
333 assertx(rds::isNormalHandle(ch
));
335 auto const sf
= checkRDSHandleInitialized(v
, ch
);
336 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
337 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodFCache
, m_func
)],
338 dst
, sizeof(LowPtr
<const Func
>));
341 ///////////////////////////////////////////////////////////////////////////////
343 void cgCheckFuncStatic(IRLS
& env
, const IRInstruction
* inst
) {
344 auto const funcPtrReg
= srcLoc(env
, inst
, 0).reg();
345 auto& v
= vmain(env
);
347 auto const sf
= v
.makeReg();
349 static_cast<int32_t>(AttrStatic
),
350 funcPtrReg
[Func::attrsOff()],
353 v
<< jcc
{CC_NZ
, sf
, {label(env
, inst
->next()), label(env
, inst
->taken())}};
356 void cgFwdCtxStaticCall(IRLS
& env
, const IRInstruction
* inst
) {
357 auto const dstCtx
= dstLoc(env
, inst
, 0).reg();
358 auto const srcCtx
= srcLoc(env
, inst
, 0).reg();
359 auto const ty
= inst
->src(0)->type();
361 auto& v
= vmain(env
);
363 auto ctx_from_this
= [] (Vout
& v
, Vreg rthis
, Vreg dst
) {
364 // Load (this->m_cls | 0x1) into `dst'.
365 auto const cls
= emitLdObjClass(v
, rthis
, v
.makeReg());
366 v
<< orqi
{ActRec::kHasClassBit
, cls
, dst
, v
.makeReg()};
371 v
<< copy
{srcCtx
, dstCtx
};
372 } else if (ty
<= TObj
) {
373 ctx_from_this(v
, srcCtx
, dstCtx
);
375 // If we don't know whether we have a $this, we need to check dynamically.
376 auto const sf
= v
.makeReg();
377 v
<< testqi
{ActRec::kHasClassBit
, srcCtx
, sf
};
379 v
, vcold(env
), CC_NZ
, sf
, dstCtx
, [&](Vout
& /*v*/) { return srcCtx
; },
380 [&](Vout
& v
) { return ctx_from_this(v
, srcCtx
, v
.makeReg()); });
384 ///////////////////////////////////////////////////////////////////////////////