2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-create.h"
18 #include "hphp/runtime/base/packed-array.h"
19 #include "hphp/runtime/vm/jit/irgen-exit.h"
20 #include "hphp/runtime/vm/jit/irgen-interpone.h"
21 #include "hphp/runtime/vm/jit/irgen-internal.h"
23 namespace HPHP
{ namespace jit
{ namespace irgen
{
27 //////////////////////////////////////////////////////////////////////
29 const StaticString
s_uuinvoke("__invoke");
31 //////////////////////////////////////////////////////////////////////
33 void initProps(IRGS
& env
, const Class
* cls
) {
34 cls
->initPropHandle();
38 gen(env
, CheckInitProps
, taken
, ClassData(cls
));
41 hint(env
, Block::Hint::Unlikely
);
42 gen(env
, InitProps
, ClassData(cls
));
47 //////////////////////////////////////////////////////////////////////
51 //////////////////////////////////////////////////////////////////////
53 void initSProps(IRGS
& env
, const Class
* cls
) {
54 cls
->initSPropHandles();
55 if (rds::isPersistentHandle(cls
->sPropInitHandle())) return;
59 gen(env
, CheckInitSProps
, taken
, ClassData(cls
));
62 hint(env
, Block::Hint::Unlikely
);
63 gen(env
, InitSProps
, ClassData(cls
));
68 SSATmp
* allocObjFast(IRGS
& env
, const Class
* cls
) {
69 // CustomInstance classes always go through IR:AllocObj and
70 // ObjectData::newInstance()
71 assert(!cls
->callsCustomInstanceInit());
73 auto registerObj
= [&] (SSATmp
* obj
) {
74 if (RuntimeOption::EnableObjDestructCall
&& cls
->getDtor()) {
75 gen(env
, RegisterLiveObj
, obj
);
80 // If it's an extension class with a custom instance initializer,
81 // that init function does all the work.
82 if (cls
->instanceCtor()) {
83 auto const obj
= gen(env
, ConstructInstance
, ClassData(cls
));
84 return registerObj(obj
);
87 // Make sure our property init vectors are all set up.
88 const bool props
= cls
->pinitVec().size() > 0;
89 const bool sprops
= cls
->numStaticProperties() > 0;
90 assertx((props
|| sprops
) == cls
->needInitialization());
91 if (cls
->needInitialization()) {
92 if (props
) initProps(env
, cls
);
93 if (sprops
) initSProps(env
, cls
);
97 * Allocate the object. This must happen after we do sinits for consistency
98 * with the interpreter about o_id assignments. Also, the prop
99 * initialization above can throw, so we don't want to have the object
102 auto const ssaObj
= gen(env
, NewInstanceRaw
, ClassData(cls
));
104 // Initialize the properties
105 gen(env
, InitObjProps
, ClassData(cls
), ssaObj
);
106 return registerObj(ssaObj
);
109 //////////////////////////////////////////////////////////////////////
112 * The CreateCl opcode is specified as not being allowed before the
113 * class it creates exists, and closure classes are always unique.
115 * This means even if we're not in RepoAuthoritative mode, as long as
116 * this code is reachable it will always use the same closure Class*,
117 * so we can just burn it into the TC without using RDS.
119 void emitCreateCl(IRGS
& env
, int32_t numParams
, const StringData
* clsName
) {
120 auto cls
= Unit::lookupClassOrUniqueClass(clsName
)->rescope(
121 const_cast<Class
*>(curClass(env
))
123 assertx(cls
&& (cls
->attrs() & AttrUnique
));
125 auto const func
= cls
->getCachedInvoke();
127 auto const closure
= allocObjFast(env
, cls
);
129 auto const ctx
= [&]{
130 if (!curClass(env
)) return cns(env
, nullptr);
131 auto const ldctx
= gen(env
, LdCtx
, fp(env
));
132 if (func
->isStatic()) {
133 return gen(env
, ConvClsToCctx
, gen(env
, LdClsCtx
, ldctx
));
135 gen(env
, IncRefCtx
, ldctx
);
139 gen(env
, StClosureCtx
, closure
, ctx
);
141 SSATmp
** args
= (SSATmp
**)alloca(sizeof(SSATmp
*) * numParams
);
142 for (int32_t i
= 0; i
< numParams
; ++i
) {
143 args
[numParams
- i
- 1] = popF(env
);
147 for (; propId
< numParams
; ++propId
) {
151 PropByteOffset(cls
->declPropOffset(propId
)),
157 assertx(cls
->numDeclProperties() == func
->numStaticLocals() + numParams
);
159 // Closure static variables are per instance, and need to start
160 // uninitialized. After numParams use vars, the remaining instance
161 // properties hold any static locals.
162 for (int32_t numDeclProperties
= cls
->numDeclProperties();
163 propId
< numDeclProperties
;
168 PropByteOffset(cls
->declPropOffset(propId
)),
177 void emitNewArray(IRGS
& env
, int32_t capacity
) {
179 push(env
, cns(env
, staticEmptyArray()));
181 if (auto newCap
= PackedArray::getMaxCapInPlaceFast(capacity
)) {
182 assertx(newCap
> static_cast<uint32_t>(capacity
));
185 push(env
, gen(env
, NewArray
, cns(env
, capacity
)));
189 void emitNewMixedArray(IRGS
& env
, int32_t capacity
) {
191 push(env
, cns(env
, staticEmptyArray()));
193 push(env
, gen(env
, NewMixedArray
, cns(env
, capacity
)));
197 void emitNewDictArray(IRGS
& env
, int32_t capacity
) {
198 push(env
, gen(env
, NewDictArray
, cns(env
, capacity
)));
201 void emitNewLikeArrayL(IRGS
& env
, int32_t id
, int32_t capacity
) {
202 auto const ldrefExit
= makeExit(env
);
203 auto const ldPMExit
= makePseudoMainExit(env
);
204 auto const ld
= ldLocInner(env
, id
, ldrefExit
, ldPMExit
, DataTypeSpecific
);
208 arr
= gen(env
, NewLikeArray
, ld
, cns(env
, capacity
));
210 capacity
= (capacity
? capacity
: MixedArray::SmallSize
);
211 arr
= gen(env
, NewArray
, cns(env
, capacity
));
216 void emitNewPackedArray(IRGS
& env
, int32_t numArgs
) {
217 if (numArgs
> CapCode::Threshold
) {
218 PUNT(NewPackedArray
-UnrealisticallyHuge
);
221 auto const array
= gen(
224 PackedArrayData
{ static_cast<uint32_t>(numArgs
) }
226 static constexpr auto kMaxUnrolledInitArray
= 8;
227 if (numArgs
> kMaxUnrolledInitArray
) {
231 InitPackedArrayLoopData
{
232 offsetFromIRSP(env
, BCSPOffset
{0}),
233 static_cast<uint32_t>(numArgs
)
238 discard(env
, numArgs
);
243 for (int i
= 0; i
< numArgs
; ++i
) {
247 IndexData
{ static_cast<uint32_t>(numArgs
- i
- 1) },
249 popC(env
, DataTypeGeneric
)
255 void emitNewStructArray(IRGS
& env
, const ImmVector
& immVec
) {
256 auto const numArgs
= immVec
.size();
257 auto const ids
= immVec
.vec32();
260 extra
.offset
= offsetFromIRSP(env
, BCSPOffset
{0});
261 extra
.numKeys
= numArgs
;
262 extra
.keys
= new (env
.unit
.arena()) StringData
*[numArgs
];
263 for (auto i
= size_t{0}; i
< numArgs
; ++i
) {
264 extra
.keys
[i
] = curUnit(env
)->lookupLitstrId(ids
[i
]);
267 discard(env
, numArgs
);
268 push(env
, gen(env
, NewStructArray
, extra
, sp(env
)));
271 void emitAddElemC(IRGS
& env
) {
272 // This is just to peek at the type; it'll be consumed for real down below and
273 // we don't want to constrain it if we're just going to InterpOne.
274 auto const kt
= topC(env
, BCSPOffset
{1}, DataTypeGeneric
)->type();
278 } else if (kt
<= TStr
) {
281 interpOne(env
, TArr
, 3);
285 // val is teleported from the stack to the array, so we don't have to do any
287 auto const val
= popC(env
, DataTypeGeneric
);
288 auto const key
= popC(env
);
289 auto const arr
= popC(env
);
290 // The AddElem* instructions decref their args, so don't decref pop'ed
292 push(env
, gen(env
, op
, arr
, key
, val
));
295 void emitAddNewElemC(IRGS
& env
) {
296 if (!topC(env
, BCSPOffset
{1})->isA(TArr
)) {
297 return interpOne(env
, TArr
, 2);
300 auto const val
= popC(env
);
301 auto const arr
= popC(env
);
302 // The AddNewElem helper decrefs its args, so don't decref pop'ed values.
303 push(env
, gen(env
, AddNewElem
, arr
, val
));
306 void emitNewCol(IRGS
& env
, int type
) {
307 push(env
, gen(env
, NewCol
, NewColData
{type
}));
310 void emitColFromArray(IRGS
& env
, int type
) {
311 auto const arr
= popC(env
);
312 push(env
, gen(env
, NewColFromArray
, NewColData
{type
}, arr
));
315 void emitMapAddElemC(IRGS
& env
) {
316 if (!topC(env
, BCSPOffset
{2})->isA(TObj
)) {
317 return interpOne(env
, TObj
, 3);
319 if (!topC(env
, BCSPOffset
{1}, DataTypeGeneric
)->type().
320 subtypeOfAny(TInt
, TStr
)) {
321 interpOne(env
, TObj
, 3);
325 auto const val
= popC(env
);
326 auto const key
= popC(env
);
327 auto const coll
= popC(env
);
328 push(env
, gen(env
, MapAddElemC
, coll
, key
, val
));
332 void emitColAddNewElemC(IRGS
& env
) {
333 if (!topC(env
, BCSPOffset
{1})->isA(TObj
)) {
334 return interpOne(env
, TObj
, 2);
337 auto const val
= popC(env
);
338 auto const coll
= popC(env
);
339 // The AddNewElem helper decrefs its args, so don't decref pop'ed values.
340 push(env
, gen(env
, ColAddNewElemC
, coll
, val
));
343 void emitStaticLocInit(IRGS
& env
, int32_t locId
, const StringData
* name
) {
344 if (curFunc(env
)->isPseudoMain()) PUNT(StaticLocInit
);
346 auto const ldPMExit
= makePseudoMainExit(env
);
347 auto const value
= popC(env
);
349 // Closures and generators from closures don't satisfy the "one static per
350 // source location" rule that the inline fastpath requires
351 auto const box
= [&]{
352 if (curFunc(env
)->isClosureBody()) {
353 return gen(env
, ClosureStaticLocInit
, cns(env
, name
), fp(env
), value
);
356 auto const cachedBox
=
357 gen(env
, LdStaticLocCached
, StaticLocName
{ curFunc(env
), name
});
361 gen(env
, CheckStaticLocInit
, taken
, cachedBox
);
364 hint(env
, Block::Hint::Unlikely
);
365 gen(env
, StaticLocInitCached
, cachedBox
, value
);
370 gen(env
, IncRef
, box
);
371 auto const oldValue
= ldLoc(env
, locId
, ldPMExit
, DataTypeSpecific
);
372 stLocRaw(env
, locId
, fp(env
), box
);
373 decRef(env
, oldValue
);
374 // We don't need to decref value---it's a bytecode invariant that
375 // our Cell was not ref-counted.
378 void emitStaticLoc(IRGS
& env
, int32_t locId
, const StringData
* name
) {
379 if (curFunc(env
)->isPseudoMain()) PUNT(StaticLoc
);
381 auto const ldPMExit
= makePseudoMainExit(env
);
383 auto const box
= curFunc(env
)->isClosureBody() ?
384 gen(env
, ClosureStaticLocInit
,
385 cns(env
, name
), fp(env
), cns(env
, TUninit
)) :
386 gen(env
, LdStaticLocCached
, StaticLocName
{ curFunc(env
), name
});
388 auto const res
= cond(
391 gen(env
, CheckStaticLocInit
, taken
, box
);
393 [&] { // Next: the static local is already initialized
394 return cns(env
, true);
396 [&] { // Taken: need to initialize the static local
398 * Even though this path is "cold", we're not marking it
399 * unlikely because the size of the instructions this will
400 * generate is about 10 bytes, which is not much larger than the
401 * 5 byte jump to acold would be.
403 * One note about StaticLoc: we're literally always going to
404 * generate a fallthrough trace here that is cold (the code that
405 * initializes the static local). TODO(#2894612).
407 gen(env
, StaticLocInitCached
, box
, cns(env
, TInitNull
));
408 return cns(env
, false);
410 gen(env
, IncRef
, box
);
411 auto const oldValue
= ldLoc(env
, locId
, ldPMExit
, DataTypeGeneric
);
412 stLocRaw(env
, locId
, fp(env
), box
);
413 decRef(env
, oldValue
);
417 //////////////////////////////////////////////////////////////////////