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/interp-helpers.h"
29 #include "hphp/runtime/vm/method-lookup.h"
31 #include "hphp/runtime/vm/jit/types.h"
32 #include "hphp/runtime/vm/jit/abi.h"
33 #include "hphp/runtime/vm/jit/arg-group.h"
34 #include "hphp/runtime/vm/jit/bc-marker.h"
35 #include "hphp/runtime/vm/jit/call-spec.h"
36 #include "hphp/runtime/vm/jit/code-gen-cf.h"
37 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
38 #include "hphp/runtime/vm/jit/extra-data.h"
39 #include "hphp/runtime/vm/jit/ir-instruction.h"
40 #include "hphp/runtime/vm/jit/ir-opcode.h"
41 #include "hphp/runtime/vm/jit/meth-profile.h"
42 #include "hphp/runtime/vm/jit/ssa-tmp.h"
43 #include "hphp/runtime/vm/jit/target-cache.h"
44 #include "hphp/runtime/vm/jit/translator-inline.h"
45 #include "hphp/runtime/vm/jit/type.h"
46 #include "hphp/runtime/vm/jit/vasm-gen.h"
47 #include "hphp/runtime/vm/jit/vasm-instr.h"
48 #include "hphp/runtime/vm/jit/vasm-reg.h"
50 #include "hphp/util/asm-x64.h"
51 #include "hphp/util/trace.h"
53 namespace HPHP::jit::irlower
{
55 TRACE_SET_MOD(irlower
);
57 ///////////////////////////////////////////////////////////////////////////////
60 * The `mcprep' instruction here creates a smashable move, which serves as
61 * the inline cache, or "prime cache" for the method lookup.
63 * On our first time through this codepath in the TC, we "prime" this cache
64 * (which holds across /all/ requests) by smashing the mov immediate to hold
65 * a Func* in the upper 32 bits, and a Class* in the lower 32 bits. This is
66 * not always possible (see MethodCache::handleStaticCall() for details), in
67 * which case we smash an immediate with some low bits set, so that we always
68 * miss on the inline cache when comparing against our live Class*.
70 * The inline cache is set up so that we always miss initially, and take the
71 * slow path to initialize it. After initialization, the slow path uses the
72 * out-of-line method cache (allocated above). The inline cache is set only
73 * during the first call(s) (usually once), but the one-way request-local
74 * method cache is updated on each miss.
76 void cgLdSmashable(IRLS
& env
, const IRInstruction
* inst
) {
77 auto const dst
= dstLoc(env
, inst
, 0).reg();
82 void cgCheckSmashableClass(IRLS
& env
, const IRInstruction
* inst
) {
83 auto const smashable
= srcLoc(env
, inst
, 0).reg();
84 auto const cls
= srcLoc(env
, inst
, 1).reg();
87 auto const tmp
= v
.makeReg();
88 auto const smashableCls
= v
.makeReg();
89 v
<< movtql
{smashable
, tmp
};
90 v
<< movzlq
{tmp
, smashableCls
};
92 auto const sf
= v
.makeReg();
93 v
<< cmpq
{smashableCls
, cls
, sf
};
94 v
<< jcc
{CC_NE
, sf
, {label(env
, inst
->next()), label(env
, inst
->taken())}};
97 void cgLdSmashableFunc(IRLS
& env
, const IRInstruction
* inst
) {
98 auto const smashable
= srcLoc(env
, inst
, 0).reg();
99 auto const dst
= dstLoc(env
, inst
, 0).reg();
100 auto& v
= vmain(env
);
102 v
<< shrqi
{32, smashable
, dst
, v
.makeReg()};
105 void cgLdObjMethodD(IRLS
& env
, const IRInstruction
* inst
) {
106 assertx(inst
->taken() && inst
->taken()->isCatch()); // must have catch block
108 auto const target
= CallSpec::direct(MethodCache::handleDynamicCall
);
109 auto const args
= argGroup(env
, inst
)
111 .ssa(1 /* methodName */)
112 .immPtr(inst
->extra
<OptClassAndFuncData
>()->cls
)
113 .immPtr(inst
->extra
<OptClassAndFuncData
>()->func
); // callerFunc
115 auto& v
= vmain(env
);
116 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::Sync
, args
);
119 void cgLdObjMethodS(IRLS
& env
, const IRInstruction
* inst
) {
120 assertx(inst
->taken() && inst
->taken()->isCatch()); // must have catch block
121 using namespace MethodCache
;
123 // Allocate the request-local one-way method cache for this lookup.
125 rds::alloc
<Entry
, rds::Mode::Normal
, sizeof(Entry
)>().handle();
126 if (RuntimeOption::EvalPerfDataMap
) {
127 rds::recordRds(handle
, sizeof(TypedValue
), "MethodCache",
128 inst
->marker().func()->fullName()->slice());
131 auto const target
= CallSpec::direct(MethodCache::handleStaticCall
);
132 auto const args
= argGroup(env
, inst
)
134 .immPtr(inst
->extra
<FuncNameCtxData
>()->name
)
135 .immPtr(inst
->extra
<FuncNameCtxData
>()->context
)
136 .immPtr(inst
->extra
<FuncNameCtxData
>()->func
) // callerFunc
137 .imm(safe_cast
<int32_t>(handle
))
138 .ssa(1 /* smashable */);
140 auto& v
= vmain(env
);
141 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::Sync
, args
);
144 ///////////////////////////////////////////////////////////////////////////////
146 IMPL_OPCODE_CALL(LdClsCtor
)
147 IMPL_OPCODE_CALL(LookupClsMethod
)
149 void cgProfileMethod(IRLS
& env
, const IRInstruction
* inst
) {
150 auto const extra
= inst
->extra
<ProfileCallTargetData
>();
152 auto const args
= argGroup(env
, inst
)
153 .addr(rvmtl(), safe_cast
<int32_t>(extra
->handle
))
156 .immPtr(inst
->marker().func());
158 cgCallHelper(vmain(env
), env
, CallSpec::method(&MethProfile::reportMeth
),
159 kVoidDest
, SyncOptions::None
, args
);
162 ///////////////////////////////////////////////////////////////////////////////
166 StaticString
s_anonymous(":anonymous:");
168 const StringData
* ctxName(const Class
* ctx
) {
169 return ctx
? ctx
->name() : s_anonymous
.get();
174 ///////////////////////////////////////////////////////////////////////////////
176 void cgLookupClsMethodCache(IRLS
& env
, const IRInstruction
* inst
) {
177 auto const extra
= inst
->extra
<ClsMethodData
>();
178 auto const dst
= dstLoc(env
, inst
, 0).reg();
179 auto& v
= vmain(env
);
181 auto const ch
= StaticMethodCache::alloc(
184 ctxName(extra
->context
)
187 if (false) { // typecheck
188 const UNUSED Func
* f
= StaticMethodCache::lookup(
198 auto const args
= argGroup(env
, inst
)
200 .immPtr(extra
->namedType
)
201 .immPtr(extra
->clsName
)
202 .immPtr(extra
->methodName
)
203 .immPtr(extra
->context
)
204 .immPtr(extra
->callerFunc
);
206 // May raise an error if the class is undefined.
207 cgCallHelper(v
, env
, CallSpec::direct(StaticMethodCache::lookup
),
208 callDest(dst
), SyncOptions::Sync
, args
);
211 void cgLdClsMethodCacheFunc(IRLS
& env
, const IRInstruction
* inst
) {
212 auto const extra
= inst
->extra
<ClsMethodData
>();
213 auto const dst
= dstLoc(env
, inst
, 0).reg();
214 auto& v
= vmain(env
);
216 auto const ch
= StaticMethodCache::alloc(
219 ctxName(extra
->context
)
222 auto const sf
= checkRDSHandleInitialized(v
, ch
);
223 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
224 markRDSAccess(v
, ch
);
225 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodCache
, m_func
)],
226 dst
, sizeof(LowPtr
<const Func
>));
229 void cgLdClsMethodCacheCls(IRLS
& env
, const IRInstruction
* inst
) {
230 auto const extra
= inst
->extra
<ClsMethodData
>();
231 auto const dst
= dstLoc(env
, inst
, 0).reg();
232 auto& v
= vmain(env
);
234 auto const ch
= StaticMethodCache::alloc(
237 ctxName(extra
->context
)
239 assertx(rds::isNormalHandle(ch
));
241 markRDSAccess(v
, ch
);
243 // The StaticMethodCache here is guaranteed to already be initialized in RDS
244 // by the pre-conditions of this instruction.
245 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodCache
, m_cls
)],
246 dst
, sizeof(LowPtr
<const Class
>));
249 void cgLookupClsMethodFCache(IRLS
& env
, const IRInstruction
* inst
) {
250 auto const extra
= inst
->extra
<ClsMethodData
>();
251 auto const dst
= dstLoc(env
, inst
, 0).reg(0);
252 auto const cls
= inst
->src(0)->clsVal();
253 auto& v
= vmain(env
);
255 auto const ch
= StaticMethodFCache::alloc(
258 ctxName(extra
->context
)
260 assertx(rds::isNormalHandle(ch
));
262 const Func
* (*lookup
)(rds::Handle
, const Class
*,
263 const StringData
*, const Class
*, const Func
*) =
264 StaticMethodFCache::lookup
;
266 auto const args
= argGroup(env
, inst
)
269 .immPtr(extra
->methodName
)
270 .immPtr(extra
->context
)
271 .immPtr(extra
->callerFunc
);
273 cgCallHelper(v
, env
, CallSpec::direct(lookup
),
274 callDest(dst
), SyncOptions::Sync
, args
);
277 void cgLdClsMethodFCacheFunc(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
= StaticMethodFCache::alloc(
285 ctxName(extra
->context
)
287 assertx(rds::isNormalHandle(ch
));
289 auto const sf
= checkRDSHandleInitialized(v
, ch
);
290 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
291 markRDSAccess(v
, ch
);
292 emitLdLowPtr(v
, rvmtl()[ch
+ offsetof(StaticMethodFCache
, m_func
)],
293 dst
, sizeof(LowPtr
<const Func
>));
296 ///////////////////////////////////////////////////////////////////////////////