3 +----------------------------------------------------------------------+
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 ///////////////////////////////////////////////////////////////////////////////
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 */);
89 CallSpec::direct(TargetCache::lookup
),
96 const Func
* loadUnknownFuncHelper(const StringData
* name
,
97 void (*raiser
)(const StringData
*,
100 CoeffectsAutoGuard _2
;
101 auto const func
= Func::load(name
);
102 if (UNLIKELY(!func
)) raiser(name
, nullptr);
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
);
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
};
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
})};
151 vc
<< phijmp
{done
, vc
.makeTuple({cls2
})};
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
))
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());
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 ///////////////////////////////////////////////////////////////////////////////
216 template<class T
> rds::Handle
handleFrom(
217 const NamedEntity
* ne
,
218 const StringData
* name
222 rds::Handle handleFrom
<Func
>(const NamedEntity
* ne
,
223 const StringData
* name
) {
224 return ne
->getFuncHandle(name
);
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
);
242 v
, vcold(env
), CC_NE
, sf
, dst
,
243 [&] (Vout
& v
) { return fill_cache(v
, ch
); },
245 markRDSAccess(v
, ch
);
246 auto const ptr
= v
.makeReg();
247 emitLdLowPtr(v
, rvmtl()[ch
], ptr
, sizeof(LowPtr
<T
>));
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
; });
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
);
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
290 auto const args
= argGroup(env
, inst
).imm(ch
).ssa(0);
291 cgCallHelper(v
, env
, CallSpec::direct(target
),
292 callDest(ptr
), SyncOptions::Sync
, args
);
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
>));
321 assertx(rds::isPersistentHandle(ch
));
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 ///////////////////////////////////////////////////////////////////////////////