Rename RefcountProfile to IncRefProfile
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-lookup-method.cpp
blob1ff21e0557a75fe7e25570e87c66df3892549f51
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>();
65 auto& v = vmain(env);
66 auto& vc = vcold(env);
68 // Allocate the request-local one-way method cache for this lookup.
69 auto const handle =
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};
109 unlikelyIfThenElse(
110 v, vc, CC_NE, 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)
116 .ssa(0 /* cls */)
117 .immPtr(inst->marker().func()->cls())
118 .reg(func_class);
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()};
126 v << store{funcptr,
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) {
138 try {
139 const Func* f;
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);
144 ar->m_func = f;
146 if (res == LookupResult::MethodFoundNoThis ||
147 res == LookupResult::MagicCallStaticFound) {
148 if (!f->isStaticInPrologue()) {
149 raise_missing_this(f);
151 if (forward && ctx) {
152 if (fp->hasThis()) {
153 cls = fp->getThis()->getVMClass();
154 } else {
155 cls = fp->getClass();
158 ar->setClass(cls);
159 } else {
160 assertx(obj);
161 assertx(res == LookupResult::MethodFoundWithThis ||
162 res == LookupResult::MagicCallFound);
163 obj->incRefCount();
164 ar->setThis(obj);
167 if (res == LookupResult::MagicCallFound ||
168 res == LookupResult::MagicCallStaticFound) {
169 ar->setMagicDispatch(meth);
170 meth->incRefCount();
172 } catch (...) {
173 *arPreliveOverwriteCells(ar) = make_tv<KindOfString>(meth);
174 throw;
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)
183 .ssa(0)
184 .ssa(1)
185 .addr(sp, cellsToBytes(extra->calleeAROffset.offset))
186 .ssa(3);
188 if (extra->forward) {
189 cgCallHelper(vmain(env), env,
190 CallSpec::direct(lookupClsMethodHelper<true>),
191 callDest(env, inst), SyncOptions::Sync, args);
192 } else {
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))
206 .ssa(1);
208 cgCallHelper(vmain(env), env, CallSpec::method(&MethProfile::reportMeth),
209 kVoidDest, SyncOptions::None, args);
212 ///////////////////////////////////////////////////////////////////////////////
214 namespace {
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(
232 extra->clsName,
233 extra->methodName,
234 ctxName(inst->marker())
237 if (false) { // typecheck
238 UNUSED TypedValue* fake_fp = nullptr;
239 const UNUSED Func* f = StaticMethodCache::lookup(
241 extra->namedEntity,
242 extra->clsName,
243 extra->methodName,
244 fake_fp
248 auto const args = argGroup(env, inst)
249 .imm(ch)
250 .immPtr(extra->namedEntity)
251 .immPtr(extra->clsName)
252 .immPtr(extra->methodName)
253 .reg(fp);
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(
266 extra->clsName,
267 extra->methodName,
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(
283 extra->clsName,
284 extra->methodName,
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(
303 cls->name(),
304 extra->methodName,
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)
314 .imm(ch)
315 .immPtr(cls)
316 .immPtr(extra->methodName)
317 .reg(fp);
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(
329 extra->clsName,
330 extra->methodName,
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();
348 v << testlim{
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()};
367 return dst;
370 if (ty <= TCctx) {
371 v << copy{srcCtx, dstCtx};
372 } else if (ty <= TObj) {
373 ctx_from_this(v, srcCtx, dstCtx);
374 } else {
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};
378 unlikelyCond(
379 v, vcold(env), CC_NZ, sf, dstCtx, [&](Vout& /*v*/) { return srcCtx; },
380 [&](Vout& v) { return ctx_from_this(v, srcCtx, v.makeReg()); });
384 ///////////////////////////////////////////////////////////////////////////////