Clean up irgen.h a bit
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-create.cpp
blobe39b8325571483aff23c34346a8797ed40c7719d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 {
25 namespace {
27 //////////////////////////////////////////////////////////////////////
29 const StaticString s_uuinvoke("__invoke");
31 //////////////////////////////////////////////////////////////////////
33 void initProps(IRGS& env, const Class* cls) {
34 cls->initPropHandle();
35 ifThen(
36 env,
37 [&] (Block* taken) {
38 gen(env, CheckInitProps, taken, ClassData(cls));
40 [&] {
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;
56 ifThen(
57 env,
58 [&] (Block* taken) {
59 gen(env, CheckInitSProps, taken, ClassData(cls));
61 [&] {
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);
77 return 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
100 * allocated already.
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);
136 return ldctx;
137 }();
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);
146 int32_t propId = 0;
147 for (; propId < numParams; ++propId) {
148 gen(
149 env,
150 StClosureArg,
151 PropByteOffset(cls->declPropOffset(propId)),
152 closure,
153 args[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;
164 ++propId) {
165 gen(
166 env,
167 StClosureArg,
168 PropByteOffset(cls->declPropOffset(propId)),
169 closure,
170 cns(env, TUninit)
174 push(env, closure);
177 void emitNewArray(IRGS& env, int32_t capacity) {
178 if (capacity == 0) {
179 push(env, cns(env, staticEmptyArray()));
180 } else {
181 if (auto newCap = PackedArray::getMaxCapInPlaceFast(capacity)) {
182 assertx(newCap > static_cast<uint32_t>(capacity));
183 capacity = newCap;
185 push(env, gen(env, NewArray, cns(env, capacity)));
189 void emitNewMixedArray(IRGS& env, int32_t capacity) {
190 if (capacity == 0) {
191 push(env, cns(env, staticEmptyArray()));
192 } else {
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);
206 SSATmp* arr;
207 if (ld->isA(TArr)) {
208 arr = gen(env, NewLikeArray, ld, cns(env, capacity));
209 } else {
210 capacity = (capacity ? capacity : MixedArray::SmallSize);
211 arr = gen(env, NewArray, cns(env, capacity));
213 push(env, arr);
216 void emitNewPackedArray(IRGS& env, int32_t numArgs) {
217 if (numArgs > CapCode::Threshold) {
218 PUNT(NewPackedArray-UnrealisticallyHuge);
221 auto const array = gen(
222 env,
223 AllocPackedArray,
224 PackedArrayData { static_cast<uint32_t>(numArgs) }
226 static constexpr auto kMaxUnrolledInitArray = 8;
227 if (numArgs > kMaxUnrolledInitArray) {
228 gen(
229 env,
230 InitPackedArrayLoop,
231 InitPackedArrayLoopData {
232 offsetFromIRSP(env, BCSPOffset{0}),
233 static_cast<uint32_t>(numArgs)
235 array,
236 sp(env)
238 discard(env, numArgs);
239 push(env, array);
240 return;
243 for (int i = 0; i < numArgs; ++i) {
244 gen(
245 env,
246 InitPackedArray,
247 IndexData { static_cast<uint32_t>(numArgs - i - 1) },
248 array,
249 popC(env, DataTypeGeneric)
252 push(env, array);
255 void emitNewStructArray(IRGS& env, const ImmVector& immVec) {
256 auto const numArgs = immVec.size();
257 auto const ids = immVec.vec32();
259 NewStructData extra;
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();
275 Opcode op;
276 if (kt <= TInt) {
277 op = AddElemIntKey;
278 } else if (kt <= TStr) {
279 op = AddElemStrKey;
280 } else {
281 interpOne(env, TArr, 3);
282 return;
285 // val is teleported from the stack to the array, so we don't have to do any
286 // refcounting.
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
291 // values.
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);
322 return;
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));
329 decRef(env, key);
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 });
358 ifThen(
359 env,
360 [&] (Block* taken) {
361 gen(env, CheckStaticLocInit, taken, cachedBox);
363 [&] {
364 hint(env, Block::Hint::Unlikely);
365 gen(env, StaticLocInitCached, cachedBox, value);
368 return cachedBox;
369 }();
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(
389 env,
390 [&] (Block* taken) {
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);
414 push(env, res);
417 //////////////////////////////////////////////////////////////////////