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/rds.h"
22 #include "hphp/runtime/base/rds-util.h"
23 #include "hphp/runtime/base/runtime-error.h"
24 #include "hphp/runtime/base/static-string-table.h"
25 #include "hphp/runtime/base/string-data.h"
26 #include "hphp/runtime/base/tv-mutate.h"
27 #include "hphp/runtime/base/tv-variant.h"
28 #include "hphp/runtime/base/type-variant.h"
29 #include "hphp/runtime/vm/named-entity.h"
30 #include "hphp/runtime/vm/unit.h"
32 #include "hphp/runtime/vm/jit/types.h"
33 #include "hphp/runtime/vm/jit/abi.h"
34 #include "hphp/runtime/vm/jit/arg-group.h"
35 #include "hphp/runtime/vm/jit/call-spec.h"
36 #include "hphp/runtime/vm/jit/cls-cns-profile.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/ssa-tmp.h"
41 #include "hphp/runtime/vm/jit/translator-inline.h"
42 #include "hphp/runtime/vm/jit/type.h"
43 #include "hphp/runtime/vm/jit/vasm-gen.h"
44 #include "hphp/runtime/vm/jit/vasm-instr.h"
45 #include "hphp/runtime/vm/jit/vasm-reg.h"
47 #include "hphp/util/asm-x64.h"
48 #include "hphp/util/trace.h"
50 namespace HPHP
{ namespace jit
{ namespace irlower
{
52 TRACE_SET_MOD(irlower
);
54 ///////////////////////////////////////////////////////////////////////////////
56 void cgLdCns(IRLS
& env
, const IRInstruction
* inst
) {
57 auto const cnsName
= inst
->src(0)->strVal();
58 auto const ch
= makeCnsHandle(cnsName
);
59 assertx(rds::isHandleBound(ch
));
60 auto const dst
= dstLoc(env
, inst
, 0);
62 assertx(inst
->taken());
64 auto checkUninit
= [&] {
65 auto const sf
= v
.makeReg();
66 irlower::emitTypeTest(
67 v
, env
, TUninit
, dst
.reg(1), dst
.reg(0), sf
,
68 [&] (ConditionCode cc
, Vreg sfr
) {
69 fwdJcc(v
, env
, cc
, sfr
, inst
->taken());
74 if (rds::isNormalHandle(ch
)) {
75 auto const sf
= checkRDSHandleInitialized(v
, ch
);
76 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
77 loadTV(v
, inst
->dst(), dst
, rvmtl()[ch
]);
79 // When a CLIServer is active requests running in script mode will define
80 // the stdio constants which require lookup via special callbacks. To not
81 // interfere with the server these constants will be defined as
83 if (!RuntimeOption::RepoAuthoritative
) {
84 if (strcasecmp(cnsName
->data(), "stdin") == 0 ||
85 strcasecmp(cnsName
->data(), "stdout") == 0 ||
86 strcasecmp(cnsName
->data(), "stderr") == 0) {
93 auto const pcns
= rds::handleToPtr
<TypedValue
, rds::Mode::Persistent
>(ch
);
95 if (pcns
->m_type
== KindOfUninit
) {
96 loadTV(v
, inst
->dst(), dst
, *v
.cns(pcns
));
99 // Statically known constant.
100 assertx(!dst
.isFullSIMD());
101 switch (pcns
->m_type
) {
103 v
<< copy
{v
.cns(nullptr), dst
.reg(0)};
106 v
<< copy
{v
.cns(!!pcns
->m_data
.num
), dst
.reg(0)};
109 case KindOfPersistentString
:
110 case KindOfPersistentVec
:
111 case KindOfPersistentDict
:
112 case KindOfPersistentKeyset
:
113 case KindOfPersistentArray
:
125 v
<< copy
{v
.cns(pcns
->m_data
.num
), dst
.reg(0)};
128 v
<< copy
{v
.cns(pcns
->m_data
.dbl
), dst
.reg(0)};
133 v
<< copy
{v
.cns(pcns
->m_type
), dst
.reg(1)};
137 ///////////////////////////////////////////////////////////////////////////////
140 tv_rval
lookupCnsImpl(StringData
* nm
) {
143 if (UNLIKELY(rds::s_constants().get() != nullptr)) {
144 cns
= rds::s_constants()->rval(nm
);
147 cns
= Unit::loadCns(const_cast<StringData
*>(nm
));
152 Cell
lookupCnsEHelper(StringData
* nm
) {
153 auto const cns
= lookupCnsImpl(nm
);
154 if (LIKELY(cns
!= nullptr)) {
159 raise_error("Undefined constant '%s'", nm
->data());
162 Cell
lookupCnsEHelperNormal(rds::Handle tv_handle
,
164 assertx(rds::isNormalHandle(tv_handle
));
165 if (UNLIKELY(rds::isHandleInit(tv_handle
))) {
166 auto const tv
= rds::handleToPtr
<TypedValue
, rds::Mode::Normal
>(tv_handle
);
167 if (tv
->m_data
.pcnt
!= nullptr) {
168 auto callback
= (Native::ConstantCallback
)(tv
->m_data
.pcnt
);
169 const Cell
* cns
= callback().asTypedValue();
170 if (LIKELY(cns
->m_type
!= KindOfUninit
)) {
177 assertx(!rds::isHandleInit(tv_handle
));
179 return lookupCnsEHelper(nm
);
182 Cell
lookupCnsEHelperPersistent(rds::Handle tv_handle
,
184 assertx(rds::isPersistentHandle(tv_handle
));
185 auto tv
= rds::handleToPtr
<TypedValue
, rds::Mode::Persistent
>(tv_handle
);
186 assertx(tv
->m_type
== KindOfUninit
);
188 // Deferred system constants.
189 if (UNLIKELY(tv
->m_data
.pcnt
!= nullptr)) {
190 auto callback
= (Native::ConstantCallback
)(tv
->m_data
.pcnt
);
191 const Cell
* cns
= callback().asTypedValue();
192 if (LIKELY(cns
->m_type
!= KindOfUninit
)) {
198 return lookupCnsEHelper(nm
);
201 ///////////////////////////////////////////////////////////////////////////////
203 void cgLookupCnsE(IRLS
& env
, const IRInstruction
* inst
) {
204 auto const cnsName
= inst
->src(0)->strVal();
205 auto const ch
= makeCnsHandle(cnsName
);
206 assertx(rds::isHandleBound(ch
));
208 auto const args
= argGroup(env
, inst
)
214 rds::isNormalHandle(ch
)
215 ? CallSpec::direct(lookupCnsEHelperNormal
)
216 : CallSpec::direct(lookupCnsEHelperPersistent
),
217 callDestTV(env
, inst
),
223 ///////////////////////////////////////////////////////////////////////////////
225 void cgLdClsCns(IRLS
& env
, const IRInstruction
* inst
) {
226 auto const extra
= inst
->extra
<LdClsCns
>();
227 auto const link
= rds::bindClassConstant(extra
->clsName
, extra
->cnsName
);
228 auto const dst
= dstLoc(env
, inst
, 0).reg();
229 auto& v
= vmain(env
);
231 auto const sf
= checkRDSHandleInitialized(v
, link
.handle());
232 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
233 v
<< lea
{rvmtl()[link
.handle()], dst
};
236 void cgLdSubClsCns(IRLS
& env
, const IRInstruction
* inst
) {
237 auto const extra
= inst
->extra
<LdSubClsCns
>();
238 auto const dst
= dstLoc(env
, inst
, 0).reg();
239 auto& v
= vmain(env
);
241 auto const slot
= extra
->slot
;
242 auto const tmp
= v
.makeReg();
243 v
<< load
{srcLoc(env
, inst
, 0).reg()[Class::constantsVecOff()], tmp
};
244 v
<< lea
{tmp
[slot
* sizeof(Class::Const
) + offsetof(Class::Const
, val
)], dst
};
247 void cgLdSubClsCnsClsName(IRLS
& env
, const IRInstruction
* inst
) {
248 auto const extra
= inst
->extra
<LdSubClsCnsClsName
>();
249 auto const dst
= dstLoc(env
, inst
, 0).reg();
250 auto& v
= vmain(env
);
252 auto const slot
= extra
->slot
;
253 auto const tmp
= v
.makeReg();
254 v
<< load
{srcLoc(env
, inst
, 0).reg()[Class::constantsVecOff()], tmp
};
256 auto const offset
= tmp
[slot
* sizeof(Class::Const
) +
257 offsetof(Class::Const
, pointedClsName
)];
258 v
<< load
{offset
, dst
};
260 auto const rawData
= v
.makeReg();
261 auto const offset
= tmp
[slot
* sizeof(Class::Const
) +
262 offsetof(Class::Const
, val
) +
263 offsetof(TypedValue
, m_aux
)];
264 v
<< loadzlq
{offset
, rawData
};
265 v
<< andqi
{static_cast<int32_t>(ConstModifiers::kMask
), rawData
,
270 void cgCheckSubClsCns(IRLS
& env
, const IRInstruction
* inst
) {
271 auto const extra
= inst
->extra
<CheckSubClsCns
>();
272 auto& v
= vmain(env
);
274 auto const slot
= extra
->slot
;
275 auto const tmp
= v
.makeReg();
276 auto const sf
= v
.makeReg();
277 v
<< load
{srcLoc(env
, inst
, 0).reg()[Class::constantsVecOff()], tmp
};
278 emitCmpLowPtr
<StringData
>(v
, sf
, v
.cns(extra
->cnsName
),
279 tmp
[slot
* sizeof(Class::Const
) +
280 offsetof(Class::Const
, name
)]);
281 fwdJcc(v
, env
, CC_NE
, sf
, inst
->taken());
284 void cgLdClsCnsVecLen(IRLS
& env
, const IRInstruction
* inst
) {
285 auto const dst
= dstLoc(env
, inst
, 0).reg();
286 auto const cls
= srcLoc(env
, inst
, 0).reg();
287 auto& v
= vmain(env
);
289 auto const off
= Class::constantsVecLenOff();
292 Class::constantsVecLenSize() == 4,
293 "Class::constantsVecLenSize() must be 4 bytes "
294 "(if you changed it, fix the following code)");
296 v
<< loadzlq
{cls
[off
], dst
};
299 void cgProfileSubClsCns(IRLS
& env
, const IRInstruction
* inst
) {
300 auto const extra
= inst
->extra
<ProfileSubClsCns
>();
302 auto const args
= argGroup(env
, inst
)
303 .addr(rvmtl(), safe_cast
<int32_t>(extra
->handle
))
305 .imm(extra
->cnsName
);
307 auto const dst
= dstLoc(env
, inst
, 0).reg();
309 cgCallHelper(vmain(env
), env
, CallSpec::method(&ClsCnsProfile::reportClsCns
),
310 callDest(dst
), SyncOptions::None
, args
);
314 Cell
lookupClsCnsHelper(TypedValue
* cache
, const NamedEntity
* ne
,
315 const StringData
* cls
, const StringData
* cns
) {
316 auto const clsCns
= g_context
->lookupClsCns(ne
, cls
, cns
);
317 cellDup(clsCns
, *cache
);
321 void cgInitClsCns(IRLS
& env
, const IRInstruction
* inst
) {
322 auto const extra
= inst
->extra
<InitClsCns
>();
323 auto const link
= rds::bindClassConstant(extra
->clsName
, extra
->cnsName
);
324 assertx(link
.isNormal());
325 auto& v
= vmain(env
);
327 auto const args
= argGroup(env
, inst
)
328 .addr(rvmtl(), safe_cast
<int32_t>(link
.handle()))
329 .immPtr(NamedEntity::get(extra
->clsName
))
330 .immPtr(extra
->clsName
)
331 .immPtr(extra
->cnsName
);
333 cgCallHelper(v
, env
, CallSpec::direct(lookupClsCnsHelper
),
334 callDestTV(env
, inst
), SyncOptions::Sync
, args
);
336 markRDSHandleInitialized(v
, link
.handle());
339 void cgLdTypeCns(IRLS
& env
, const IRInstruction
* inst
) {
340 auto const cns
= srcLoc(env
, inst
, 0).reg();
341 auto const ret
= dstLoc(env
, inst
, 0).reg();
343 auto& v
= vmain(env
);
344 auto const sf
= v
.makeReg();
345 v
<< testqi
{0x1, cns
, sf
};
346 fwdJcc(v
, env
, CC_Z
, sf
, inst
->taken());
347 v
<< xorqi
{0x1, cns
, ret
, v
.makeReg()};
350 static ArrayData
* loadClsTypeCnsHelper(
351 const Class
* cls
, const StringData
* name
353 auto typeCns
= cls
->clsCnsGet(name
, ClsCnsLookup::IncludeTypes
);
354 if (typeCns
.m_type
== KindOfUninit
) {
355 if (cls
->hasTypeConstant(name
, true)) {
356 raise_error("Type constant %s::%s is abstract",
357 cls
->name()->data(), name
->data());
359 raise_error("Non-existent type constant %s::%s",
360 cls
->name()->data(), name
->data());
364 assertx(isArrayLikeType(typeCns
.m_type
));
365 assertx(typeCns
.m_data
.parr
->isDictOrDArray());
366 assertx(typeCns
.m_data
.parr
->isStatic());
367 return typeCns
.m_data
.parr
;
370 const StaticString
s_classname("classname");
372 static StringData
* loadClsTypeCnsClsNameHelper(const Class
* cls
,
373 const StringData
* name
) {
374 auto const ts
= loadClsTypeCnsHelper(cls
, name
);
375 if (auto const classname_field
= ts
->rval(s_classname
.get())) {
376 assertx(isStringType(classname_field
.type()));
377 return classname_field
.val().pstr
;
379 raise_error("Type constant %s::%s does not have a 'classname' field",
380 cls
->name()->data(), name
->data());
383 void cgLdClsTypeCns(IRLS
& env
, const IRInstruction
* inst
) {
384 auto const args
= argGroup(env
, inst
).ssa(0).ssa(1);
385 cgCallHelper(vmain(env
), env
, CallSpec::direct(loadClsTypeCnsHelper
),
386 callDest(env
, inst
), SyncOptions::Sync
, args
);
389 void cgLdClsTypeCnsClsName(IRLS
& env
, const IRInstruction
* inst
) {
390 auto const args
= argGroup(env
, inst
).ssa(0).ssa(1);
391 cgCallHelper(vmain(env
), env
, CallSpec::direct(loadClsTypeCnsClsNameHelper
),
392 callDest(env
, inst
), SyncOptions::Sync
, args
);
395 ///////////////////////////////////////////////////////////////////////////////