Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-lookup-cls-func.cpp
blobfaecf94fb31267abb3cb852a881a6837ae7e01f3
2 /*
3 +----------------------------------------------------------------------+
4 | HipHop for PHP |
5 +----------------------------------------------------------------------+
6 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/vm/jit/irlower-internal.h"
20 #include "hphp/runtime/base/autoload-handler.h"
21 #include "hphp/runtime/base/builtin-functions.h"
22 #include "hphp/runtime/base/object-data.h"
23 #include "hphp/runtime/base/rds.h"
24 #include "hphp/runtime/base/runtime-error.h"
25 #include "hphp/runtime/base/string-data.h"
26 #include "hphp/runtime/base/strings.h"
27 #include "hphp/runtime/base/tv-mutate.h"
28 #include "hphp/runtime/base/tv-variant.h"
29 #include "hphp/runtime/base/type-string.h"
30 #include "hphp/runtime/base/typed-value.h"
31 #include "hphp/runtime/base/vanilla-vec.h"
33 #include "hphp/runtime/vm/act-rec.h"
34 #include "hphp/runtime/vm/class.h"
35 #include "hphp/runtime/vm/func.h"
36 #include "hphp/runtime/vm/interp-helpers.h"
37 #include "hphp/runtime/vm/method-lookup.h"
38 #include "hphp/runtime/vm/named-entity.h"
39 #include "hphp/runtime/vm/unit.h"
40 #include "hphp/runtime/vm/unit-util.h"
41 #include "hphp/runtime/vm/vm-regs.h"
43 #include "hphp/system/systemlib.h"
45 #include "hphp/runtime/vm/jit/types.h"
46 #include "hphp/runtime/vm/jit/abi.h"
47 #include "hphp/runtime/vm/jit/arg-group.h"
48 #include "hphp/runtime/vm/jit/bc-marker.h"
49 #include "hphp/runtime/vm/jit/call-spec.h"
50 #include "hphp/runtime/vm/jit/code-gen-cf.h"
51 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
52 #include "hphp/runtime/vm/jit/extra-data.h"
53 #include "hphp/runtime/vm/jit/ir-instruction.h"
54 #include "hphp/runtime/vm/jit/ir-opcode.h"
55 #include "hphp/runtime/vm/jit/ssa-tmp.h"
56 #include "hphp/runtime/vm/jit/target-cache.h"
57 #include "hphp/runtime/vm/jit/translator-inline.h"
58 #include "hphp/runtime/vm/jit/translator-runtime.h"
59 #include "hphp/runtime/vm/jit/type.h"
60 #include "hphp/runtime/vm/jit/vasm-gen.h"
61 #include "hphp/runtime/vm/jit/vasm-instr.h"
62 #include "hphp/runtime/vm/jit/vasm-reg.h"
64 #include "hphp/util/asm-x64.h"
65 #include "hphp/util/trace.h"
67 #include <type_traits>
69 namespace HPHP::jit::irlower {
71 TRACE_SET_MOD(irlower);
73 ///////////////////////////////////////////////////////////////////////////////
75 namespace {
77 template<class TargetCache>
78 void implLdMeta(IRLS& env, const IRInstruction* inst, Vout& v, Vreg dst) {
79 auto const is_func = std::is_same<TargetCache,FuncCache>::value;
81 auto const ch = TargetCache::alloc();
82 rds::recordRds(ch, sizeof(TargetCache), is_func ? "FuncCache" : "ClassCache",
83 inst->marker().func()->fullName()->slice());
85 auto args = argGroup(env, inst).imm(ch).ssa(0 /* name */);
86 cgCallHelper(
88 env,
89 CallSpec::direct(TargetCache::lookup),
90 callDest(dst),
91 SyncOptions::Sync,
92 args
96 const Func* loadUnknownFuncHelper(const StringData* name,
97 void (*raiser)(const StringData*,
98 const Class*)) {
99 VMRegAnchor _;
100 CoeffectsAutoGuard _2;
101 auto const func = Func::load(name);
102 if (UNLIKELY(!func)) raiser(name, nullptr);
103 return func;
106 const Class* lookupCls(const StringData* sd) {
107 return Class::lookup(sd);
110 void implLdOrLookupCls(IRLS& env, const IRInstruction* inst, bool lookup) {
111 auto const src = srcLoc(env, inst, 0).reg();
112 auto const dst = dstLoc(env, inst, 0).reg();
114 auto const fallback = [&](Vout& v, Vreg out) {
115 if (!lookup) return implLdMeta<ClassCache>(env, inst, v, out);
116 auto const args = argGroup(env, inst).ssa(0);
117 cgCallHelper(v, env, CallSpec::direct(lookupCls),
118 callDest(out), SyncOptions::None, args);
121 if (!RO::RepoAuthoritative) return fallback(vmain(env), dst);
123 auto& v = vmain(env);
124 auto& vc = vcold(env);
125 auto done = v.makeBlock();
126 auto then = vc.makeBlock();
128 auto const sf1 = v.makeReg();
129 auto const sf2 = v.makeReg();
130 auto const cls1 = v.makeReg();
131 auto const cls2 = v.makeReg();
133 auto const mask = static_cast<int8_t>(StringData::kIsSymbolMask);
134 v << testbim{mask, src[StringData::isSymbolOffset()], sf1};
135 fwdJcc(v, env, CC_E, sf1, then);
136 if (use_lowptr) {
137 auto const low = v.makeReg();
138 v << loadl{src[StringData::cachedClassOffset()], low};
139 v << testl{low, low, sf2};
140 fwdJcc(v, env, CC_E, sf2, then);
141 v << movzlq{low, cls1};
142 } else {
143 v << load{src[StringData::cachedClassOffset()], cls1};
144 v << testq{cls1, cls1, sf2};
145 fwdJcc(v, env, CC_E, sf2, then);
147 v << phijmp{done, v.makeTuple({cls1})};
149 vc = then;
150 fallback(vc, cls2);
151 vc << phijmp{done, vc.makeTuple({cls2})};
153 v = done;
154 v << phidef{v.makeTuple({dst})};
159 void cgLdCls(IRLS& env, const IRInstruction* inst) {
160 implLdOrLookupCls(env, inst, false);
163 void cgLookupClsRDS(IRLS& env, const IRInstruction* inst) {
164 implLdOrLookupCls(env, inst, true);
167 void cgLdFunc(IRLS& env, const IRInstruction* inst) {
168 auto const dst = dstLoc(env, inst, 0).reg();
169 implLdMeta<FuncCache>(env, inst, vmain(env), dst);
172 ///////////////////////////////////////////////////////////////////////////////
174 const Class* autoloadKnownPersistentType(rds::Handle h,
175 const StringData* name) {
176 assertx(rds::isPersistentHandle(h));
177 AutoloadHandler::s_instance->autoloadType(
178 StrNR(const_cast<StringData*>(name))
180 auto const ptr =
181 rds::handleToRef<LowPtr<Class>, rds::Mode::Persistent>(h).get();
182 // Autoloader should have inited it as a side-effect.
183 if (UNLIKELY(!ptr)) raise_error(Strings::UNKNOWN_CLASS, name->data());
184 return ptr;
187 const Class* lookupKnownType(rds::Handle cache_handle,
188 const StringData* name) {
189 assertx(rds::isNormalHandle(cache_handle));
190 // The caller should already have checked.
191 assertx(!rds::isHandleInit(cache_handle));
193 AutoloadHandler::s_instance->autoloadType(
194 StrNR(const_cast<StringData*>(name))
197 // Autoloader should have inited it as a side-effect.
198 if (UNLIKELY(!rds::isHandleInit(cache_handle, rds::NormalTag{}))) {
199 raise_error(Strings::UNKNOWN_CLASS, name->data());
201 return rds::handleToRef<LowPtr<Class>, rds::Mode::Normal>(cache_handle).get();
204 const Func* loadUnknownFunc(const StringData* name) {
205 return loadUnknownFuncHelper(name, raise_call_to_undefined);
208 const Func* lookupUnknownFunc(const StringData* name) {
209 return loadUnknownFuncHelper(name, raise_resolve_undefined);
212 ///////////////////////////////////////////////////////////////////////////////
214 namespace {
216 template<class T> rds::Handle handleFrom(
217 const NamedEntity* ne,
218 const StringData* name
221 template<>
222 rds::Handle handleFrom<Func>(const NamedEntity* ne,
223 const StringData* name) {
224 return ne->getFuncHandle(name);
226 template<>
227 rds::Handle handleFrom<Class>(const NamedEntity* ne,
228 const StringData* name) {
229 return ne->getClassHandle(name);
232 template<class T, class SlowPath>
233 void implLdCached(IRLS& env, const IRInstruction* inst,
234 const StringData* name, SlowPath fill_cache) {
235 auto const dst = dstLoc(env, inst, 0).reg();
236 auto const ch = handleFrom<T>(NamedEntity::get(name), name);
237 auto& v = vmain(env);
239 if (rds::isNormalHandle(ch)) {
240 auto const sf = checkRDSHandleInitialized(v, ch);
241 unlikelyCond(
242 v, vcold(env), CC_NE, sf, dst,
243 [&] (Vout& v) { return fill_cache(v, ch); },
244 [&] (Vout& v) {
245 markRDSAccess(v, ch);
246 auto const ptr = v.makeReg();
247 emitLdLowPtr(v, rvmtl()[ch], ptr, sizeof(LowPtr<T>));
248 return ptr;
251 } else {
252 auto const pptr = rds::handleToPtr<LowPtr<T>, rds::Mode::Persistent>(ch);
253 markRDSAccess(v, ch);
254 auto const ptr = v.makeReg();
255 emitLdLowPtr(v, *v.cns(pptr), ptr, sizeof(LowPtr<T>));
257 auto const sf = v.makeReg();
258 v << testq{ptr, ptr, sf};
259 unlikelyCond(v, vcold(env), CC_Z, sf, dst,
260 [&](Vout& v) { return fill_cache(v, ch); },
261 [&](Vout& /*v*/) { return ptr; });
265 template<Opcode opc>
266 void ldFuncCachedHelper(IRLS& env, const IRInstruction* inst,
267 const CallSpec& call) {
268 auto const extra = inst->extra<opc>();
270 implLdCached<Func>(env, inst, extra->name, [&] (Vout& v, rds::Handle) {
271 auto const ptr = v.makeReg();
272 auto const args = argGroup(env, inst).immPtr(extra->name);
273 cgCallHelper(v, env, call, callDest(ptr), SyncOptions::Sync, args);
274 return ptr;
280 ///////////////////////////////////////////////////////////////////////////////
282 void cgLdClsCached(IRLS& env, const IRInstruction* inst) {
283 auto const name = inst->src(0)->strVal();
285 implLdCached<Class>(env, inst, name, [&] (Vout& v, rds::Handle ch) {
286 auto const ptr = v.makeReg();
287 auto const target = rds::isPersistentHandle(ch)
288 ? autoloadKnownPersistentType
289 : lookupKnownType;
290 auto const args = argGroup(env, inst).imm(ch).ssa(0);
291 cgCallHelper(v, env, CallSpec::direct(target),
292 callDest(ptr), SyncOptions::Sync, args);
293 return ptr;
297 void cgLdFuncCached(IRLS& env, const IRInstruction* inst) {
298 ldFuncCachedHelper<LdFuncCached>(
299 env, inst, CallSpec::direct(loadUnknownFunc)
303 void cgLookupFuncCached(IRLS& env, const IRInstruction* inst) {
304 ldFuncCachedHelper<LookupFuncCached>(
305 env, inst, CallSpec::direct(lookupUnknownFunc)
309 void cgLdClsCachedSafe(IRLS& env, const IRInstruction* inst) {
310 auto const name = inst->src(0)->strVal();
311 auto const dst = dstLoc(env, inst, 0).reg();
312 auto const ch = handleFrom<Class>(NamedEntity::get(name), name);
313 auto& v = vmain(env);
315 if (rds::isNormalHandle(ch)) {
316 auto const sf = checkRDSHandleInitialized(v, ch);
317 fwdJcc(v, env, CC_NE, sf, inst->taken());
318 markRDSAccess(v, ch);
319 emitLdLowPtr(v, rvmtl()[ch], dst, sizeof(LowPtr<Class>));
320 } else {
321 assertx(rds::isPersistentHandle(ch));
322 auto const pptr =
323 rds::handleToPtr<LowPtr<Class>, rds::Mode::Persistent>(ch);
324 markRDSAccess(v, ch);
325 emitLdLowPtr(v, *v.cns(pptr), dst, sizeof(LowPtr<Class>));
329 ///////////////////////////////////////////////////////////////////////////////
331 IMPL_OPCODE_CALL(OODeclExists)
333 ///////////////////////////////////////////////////////////////////////////////