Basic JIT support for Records
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-create.cpp
blob2d5221f8ec70a667bb2e729dd0253789ccdd255a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-create.h"
18 #include "hphp/runtime/base/packed-array.h"
19 #include "hphp/runtime/ext/std/ext_std_errorfunc.h"
20 #include "hphp/runtime/vm/class.h"
21 #include "hphp/runtime/vm/jit/extra-data.h"
22 #include "hphp/runtime/vm/jit/irgen.h"
23 #include "hphp/runtime/vm/jit/irgen-exit.h"
24 #include "hphp/runtime/vm/jit/irgen-interpone.h"
25 #include "hphp/runtime/vm/jit/irgen-internal.h"
26 #include "hphp/runtime/vm/jit/irgen-sprop-global.h"
27 #include "hphp/runtime/vm/jit/irgen-types.h"
29 namespace HPHP { namespace jit { namespace irgen {
31 namespace {
33 //////////////////////////////////////////////////////////////////////
35 const StaticString s_uuinvoke("__invoke");
36 const StaticString s_traceOpts("traceOpts");
37 const StaticString s_trace("trace");
38 const StaticString s_file("file");
39 const StaticString s_line("line");
41 //////////////////////////////////////////////////////////////////////
43 void initProps(IRGS& env, const Class* cls) {
44 cls->initPropHandle();
45 ifThen(
46 env,
47 [&] (Block* taken) {
48 gen(env, CheckRDSInitialized, taken, RDSHandleData { cls->propHandle() });
50 [&] {
51 hint(env, Block::Hint::Unlikely);
52 gen(env, InitProps, ClassData(cls));
57 void initThrowable(IRGS& env, const Class* cls, SSATmp* throwable) {
58 assertx(cls->classof(SystemLib::s_ErrorClass) ||
59 cls->classof(SystemLib::s_ExceptionClass));
60 assertx(throwable->type() <= TObj);
62 // Root of the class hierarchy.
63 auto const rootCls = cls->classof(SystemLib::s_ExceptionClass)
64 ? SystemLib::s_ExceptionClass : SystemLib::s_ErrorClass;
66 auto const propAddr = [&] (Slot idx) {
67 return gen(
68 env,
69 LdPropAddr,
70 ByteOffsetData { (ptrdiff_t)rootCls->declPropOffset(idx) },
71 TUncounted.lval(Ptr::Prop),
72 throwable
76 // Load Exception::$traceOpts
77 auto const lookup = ldClsPropAddrKnown(
78 env,
79 SystemLib::s_ExceptionClass,
80 s_traceOpts.get(),
81 false
83 assertx(!lookup.tc->isCheckable());
84 auto const sprop = lookup.propPtr;
85 assertx(sprop);
87 auto const trace = cond(
88 env,
89 [&] (Block* taken) {
90 gen(env, CheckTypeMem, TInt, taken, sprop);
92 [&] {
93 // sprop is an integer, load it
94 auto const opts = gen(env, LdMem, TInt, sprop);
95 return cond(
96 env,
97 [&] (Block* taken) {
98 if (!RuntimeOption::EnableArgsInBacktraces) {
99 auto const filterOpts =
100 gen(env, AndInt, opts, cns(env, ~k_DEBUG_BACKTRACE_IGNORE_ARGS));
102 gen(env, JmpNZero, taken, filterOpts);
103 } else {
104 auto const safe =
105 gen(env, EqInt, opts, cns(env, k_DEBUG_BACKTRACE_IGNORE_ARGS));
106 gen(env, JmpZero, taken, safe);
109 [&] {
110 // traceOpts is default value, use fast lazy construction
111 if (RuntimeOption::EvalEnableCompactBacktrace) {
112 return gen(env, DebugBacktraceFast);
114 return gen(env, DebugBacktrace, cns(env, 0));
116 [&] {
117 // Call debug_backtrace(traceOpts)
118 return gen(env, DebugBacktrace, opts);
121 [&] {
122 // sprop is a garbage, use default traceOpts value (0)
123 if (!RuntimeOption::EnableArgsInBacktraces &&
124 RuntimeOption::EvalEnableCompactBacktrace) {
125 return gen(env, DebugBacktraceFast);
127 return gen(env, DebugBacktrace, cns(env, 0));
131 // $throwable->trace = $trace
132 auto const traceIdx = rootCls->lookupDeclProp(s_trace.get());
133 assertx(traceIdx != kInvalidSlot);
134 assertx(!rootCls->declPropTypeConstraint(traceIdx).isCheckable());
135 gen(env, StMem, propAddr(traceIdx), trace);
137 // Populate $throwable->{file,line}
138 if (UNLIKELY(curFunc(env)->isBuiltin())) {
139 gen(env, InitThrowableFileAndLine, throwable);
140 } else {
141 auto const fileIdx = rootCls->lookupDeclProp(s_file.get());
142 auto const lineIdx = rootCls->lookupDeclProp(s_line.get());
143 auto const unit = curFunc(env)->unit();
144 auto const line = unit->getLineNumber(bcOff(env));
145 assertx(rootCls->declPropTypeConstraint(fileIdx).isString());
146 assertx(rootCls->declPropTypeConstraint(lineIdx).isInt());
147 gen(env, StMem, propAddr(fileIdx), cns(env, unit->filepath()));
148 gen(env, StMem, propAddr(lineIdx), cns(env, line));
152 void checkPropTypeRedefs(IRGS& env, const Class* cls) {
153 assertx(cls->maybeRedefinesPropTypes());
154 assertx(cls->parent());
155 assertx(RuntimeOption::EvalCheckPropTypeHints > 0);
157 ifThen(
158 env,
159 [&] (Block* taken) {
160 gen(
161 env,
162 CheckRDSInitialized,
163 taken,
164 RDSHandleData { cls->checkedPropTypeRedefinesHandle() }
167 [&] {
168 hint(env, Block::Hint::Unlikely);
169 IRUnit::Hinter h(env.irb->unit(), Block::Hint::Unlikely);
171 auto const parent = cls->parent();
172 if (parent->maybeRedefinesPropTypes()) checkPropTypeRedefs(env, parent);
173 for (auto const& prop : cls->declProperties()) {
174 if (prop.attrs & AttrNoBadRedeclare) continue;
175 auto const slot = parent->lookupDeclProp(prop.name);
176 assertx(slot != kInvalidSlot);
177 gen(env, PropTypeRedefineCheck, cns(env, cls), cns(env, slot));
180 gen(
181 env,
182 MarkRDSInitialized,
183 RDSHandleData { cls->checkedPropTypeRedefinesHandle() }
189 void checkPropInitialValues(IRGS& env, const Class* cls) {
190 assertx(cls->needsPropInitialValueCheck());
191 assertx(RuntimeOption::EvalCheckPropTypeHints > 0);
193 ifThen(
194 env,
195 [&] (Block* taken) {
196 gen(
197 env,
198 CheckRDSInitialized,
199 taken,
200 RDSHandleData { cls->checkedPropInitialValuesHandle() }
203 [&] {
204 hint(env, Block::Hint::Unlikely);
205 IRUnit::Hinter h(env.irb->unit(), Block::Hint::Unlikely);
207 auto const& props = cls->declProperties();
208 for (Slot slot = 0; slot < props.size(); ++slot) {
209 auto const& prop = props[slot];
210 if (prop.attrs & AttrInitialSatisfiesTC) continue;
211 auto const& tc = prop.typeConstraint;
212 if (!tc.isCheckable()) continue;
213 const TypedValue& tv = cls->declPropInit()[slot];
214 if (tv.m_type == KindOfUninit) continue;
215 verifyPropType(
216 env,
217 cns(env, cls),
218 &tc,
219 slot,
220 cns(env, tv),
221 cns(env, makeStaticString(prop.name)),
222 false
226 gen(
227 env,
228 MarkRDSInitialized,
229 RDSHandleData { cls->checkedPropInitialValuesHandle() }
235 void initObjProps(IRGS& env, const Class* cls, SSATmp* obj) {
236 auto const nprops = cls->numDeclProperties();
238 if (nprops <= RuntimeOption::EvalHHIRInliningMaxInitObjProps &&
239 cls->pinitVec().size() == 0) {
240 if (cls->hasMemoSlots()) {
241 gen(env, InitObjMemoSlots, ClassData(cls), obj);
243 for (int i = 0; i < nprops; ++i) {
244 const TypedValue& tv = cls->declPropInit()[i];
245 auto const val = cns(env, tv);
246 auto const addr = gen(
247 env,
248 LdPropAddr,
249 ByteOffsetData { (ptrdiff_t)(cls->declPropOffset(i)) },
250 TLvalToPropGen,
253 gen(env, StMem, addr, val);
255 } else {
256 gen(env, InitObjProps, ClassData(cls), obj);
260 //////////////////////////////////////////////////////////////////////
264 void initSProps(IRGS& env, const Class* cls) {
265 cls->initSPropHandles();
266 if (rds::isPersistentHandle(cls->sPropInitHandle())) return;
267 ifThen(
268 env,
269 [&] (Block* taken) {
270 gen(
271 env,
272 CheckRDSInitialized,
273 taken,
274 RDSHandleData { cls->sPropInitHandle() }
277 [&] {
278 hint(env, Block::Hint::Unlikely);
279 gen(env, InitSProps, ClassData(cls));
284 //////////////////////////////////////////////////////////////////////
286 SSATmp* allocObjFast(IRGS& env, const Class* cls) {
287 // Make sure our property init vectors are all set up.
288 auto const props = cls->pinitVec().size() > 0;
289 auto const sprops = cls->numStaticProperties() > 0;
290 auto const redefine = cls->maybeRedefinesPropTypes();
291 auto const propVal = cls->needsPropInitialValueCheck();
292 assertx(
293 (props || sprops || redefine || propVal) == cls->needInitialization()
295 if (cls->needInitialization()) {
296 if (redefine) checkPropTypeRedefs(env, cls);
297 if (propVal) checkPropInitialValues(env, cls);
298 if (props) initProps(env, cls);
299 if (sprops) initSProps(env, cls);
303 * Allocate the object. This must happen after we do sinits for consistency
304 * with the interpreter about o_id assignments. Also, the prop
305 * initialization above can throw, so we don't want to have the object
306 * allocated already.
308 SSATmp* obj;
309 if (cls->instanceCtor()) {
310 // If it's an extension class with a custom instance initializer,
311 // use it to construct the object.
312 obj = gen(env, ConstructInstance, ClassData(cls));
313 } else {
314 // Construct a new instance of PHP class.
315 obj = gen(env, NewInstanceRaw, ClassData(cls));
317 // Initialize the properties.
318 initObjProps(env, cls, obj);
321 // Initialize Throwable.
322 if (cls->needsInitThrowable()) {
323 initThrowable(env, cls, obj);
326 return obj;
329 //////////////////////////////////////////////////////////////////////
332 * The CreateCl opcode is specified as not being allowed before the
333 * class it creates exists, and closure classes are always unique.
335 * This means even if we're not in RepoAuthoritative mode, as long as
336 * this code is reachable it will always use the same closure Class*,
337 * so we can just burn it into the TC without using RDS.
339 void emitCreateCl(IRGS& env, uint32_t numParams, uint32_t clsIx) {
340 auto const preCls = curFunc(env)->unit()->lookupPreClassId(clsIx);
341 auto cls = Unit::defClosure(preCls);
343 assertx(cls);
344 assertx(cls->attrs() & AttrUnique);
346 cls = cls->rescope(const_cast<Class*>(curClass(env)));
348 auto const func = cls->getCachedInvoke();
350 auto const closure = allocObjFast(env, cls);
352 auto const live_ctx = [&] {
353 auto const ldctx = ldCtx(env);
354 if (!ldctx->type().maybe(TObj)) {
355 return ldctx;
357 if (func->isStatic()) {
358 return gen(env, FwdCtxStaticCall, ldctx);
360 gen(env, IncRef, ldctx);
361 return ldctx;
362 }();
364 gen(env, StClosureCtx, closure, live_ctx);
366 SSATmp** args = (SSATmp**)alloca(sizeof(SSATmp*) * numParams);
367 for (int32_t i = 0; i < numParams; ++i) {
368 args[numParams - i - 1] = popF(env);
371 int32_t propId = 0;
372 for (; propId < numParams; ++propId) {
373 gen(
374 env,
375 StClosureArg,
376 ByteOffsetData { safe_cast<ptrdiff_t>(cls->declPropOffset(propId)) },
377 closure,
378 args[propId]
382 assertx(cls->numDeclProperties() == func->numStaticLocals() + numParams);
384 // Closure static variables are per instance, and need to start
385 // uninitialized. After numParams use vars, the remaining instance
386 // properties hold any static locals.
387 for (int32_t numDeclProperties = cls->numDeclProperties();
388 propId < numDeclProperties;
389 ++propId) {
390 gen(
391 env,
392 StClosureArg,
393 ByteOffsetData { safe_cast<ptrdiff_t>(cls->declPropOffset(propId)) },
394 closure,
395 cns(env, TUninit)
399 push(env, closure);
402 void emitNewArray(IRGS& env, uint32_t capacity) {
403 if (capacity == 0) {
404 push(env, cns(env, staticEmptyArray()));
405 } else {
406 push(env, gen(env, NewArray, cns(env, capacity)));
410 void emitNewMixedArray(IRGS& env, uint32_t capacity) {
411 if (capacity == 0) {
412 push(env, cns(env, staticEmptyArray()));
413 } else {
414 push(env, gen(env, NewMixedArray, cns(env, capacity)));
418 void emitNewDArray(IRGS& env, uint32_t capacity) {
419 assertx(!RuntimeOption::EvalHackArrDVArrs);
420 if (capacity == 0) {
421 push(env, cns(env, staticEmptyDArray()));
422 } else {
423 push(env, gen(env, NewDArray, cns(env, capacity)));
427 void emitNewDictArray(IRGS& env, uint32_t capacity) {
428 push(env, gen(env, NewDictArray, cns(env, capacity)));
431 void emitNewKeysetArray(IRGS& env, uint32_t numArgs) {
432 auto const array = gen(
433 env,
434 NewKeysetArray,
435 NewKeysetArrayData {
436 spOffBCFromIRSP(env),
437 static_cast<uint32_t>(numArgs)
439 sp(env)
441 discard(env, numArgs);
442 push(env, array);
445 void emitNewLikeArrayL(IRGS& env, int32_t id, uint32_t capacity) {
446 auto const ldrefExit = makeExit(env);
447 auto const ldPMExit = makePseudoMainExit(env);
448 auto const ld = ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeSpecific);
450 SSATmp* arr;
451 if (ld->isA(TArr)) {
452 arr = gen(env, NewLikeArray, ld, cns(env, capacity));
453 } else {
454 capacity = (capacity ? capacity : MixedArray::SmallSize);
455 arr = gen(env, NewArray, cns(env, capacity));
457 push(env, arr);
460 namespace {
462 ALWAYS_INLINE
463 void emitNewPackedLayoutArray(IRGS& env, uint32_t numArgs, Opcode op) {
464 auto const array = gen(
465 env,
467 PackedArrayData { numArgs }
469 static constexpr auto kMaxUnrolledInitArray = 8;
470 if (numArgs > kMaxUnrolledInitArray) {
471 gen(
472 env,
473 InitPackedLayoutArrayLoop,
474 InitPackedArrayLoopData {
475 spOffBCFromIRSP(env),
476 numArgs
478 array,
479 sp(env)
481 discard(env, numArgs);
482 push(env, array);
483 return;
486 for (int i = 0; i < numArgs; ++i) {
487 gen(
488 env,
489 InitPackedLayoutArray,
490 IndexData { static_cast<uint32_t>(numArgs - i - 1) },
491 array,
492 popC(env, DataTypeGeneric)
495 push(env, array);
500 void emitNewPackedArray(IRGS& env, uint32_t numArgs) {
501 emitNewPackedLayoutArray(env, numArgs, AllocPackedArray);
504 void emitNewVecArray(IRGS& env, uint32_t numArgs) {
505 emitNewPackedLayoutArray(env, numArgs, AllocVecArray);
508 void emitNewVArray(IRGS& env, uint32_t numArgs) {
509 assertx(!RuntimeOption::EvalHackArrDVArrs);
510 emitNewPackedLayoutArray(env, numArgs, AllocVArray);
513 namespace {
515 void newStructImpl(IRGS& env, const ImmVector& immVec, Opcode op) {
516 auto const numArgs = immVec.size();
517 auto const ids = immVec.vec32();
519 NewStructData extra;
520 extra.offset = spOffBCFromIRSP(env);
521 extra.numKeys = numArgs;
522 extra.keys = new (env.unit.arena()) StringData*[numArgs];
523 for (auto i = size_t{0}; i < numArgs; ++i) {
524 extra.keys[i] = curUnit(env)->lookupLitstrId(ids[i]);
527 auto const structData = gen(env, op, extra, sp(env));
528 discard(env, numArgs);
529 push(env, structData);
534 void emitNewStructArray(IRGS& env, const ImmVector& immVec) {
535 newStructImpl(env, immVec, NewStructArray);
538 void emitNewStructDArray(IRGS& env, const ImmVector& immVec) {
539 assertx(!RuntimeOption::EvalHackArrDVArrs);
540 newStructImpl(env, immVec, NewStructDArray);
543 void emitNewStructDict(IRGS& env, const ImmVector& immVec) {
544 newStructImpl(env, immVec, NewStructDict);
547 void emitAddElemC(IRGS& env) {
548 // This is just to peek at the types; they'll be consumed for real down below
549 // and we don't want to constrain it if we're just going to InterpOne.
550 auto const kt = topC(env, BCSPRelOffset{1}, DataTypeGeneric)->type();
551 auto const at = topC(env, BCSPRelOffset{2}, DataTypeGeneric)->type();
552 Opcode op;
553 if (at <= TArr) {
554 if (kt <= TInt) {
555 op = AddElemIntKey;
556 } else if (kt <= TStr) {
557 op = AddElemStrKey;
558 } else {
559 interpOne(env, TArr, 3);
560 return;
562 } else if (at <= TDict) {
563 if (kt <= TInt) {
564 op = DictAddElemIntKey;
565 } else if (kt <= TStr) {
566 op = DictAddElemStrKey;
567 } else {
568 interpOne(env, TDict, 3);
569 return;
571 } else {
572 PUNT(AddElemC-BadArr);
575 // val is teleported from the stack to the array, so we don't have to do any
576 // refcounting.
577 auto const val = popC(env, DataTypeGeneric);
578 auto const key = popC(env);
579 auto const arr = popC(env);
580 // The AddElem* instructions decref their args, so don't decref pop'ed
581 // values.
582 push(env, gen(env, op, arr, key, val));
585 void emitAddNewElemC(IRGS& env) {
586 auto const arrType = topC(env, BCSPRelOffset{1})->type();
587 if (!arrType.subtypeOfAny(TArr, TKeyset, TVec)) {
588 return interpOne(env, *env.currentNormalizedInstruction);
590 auto const val = popC(env, DataTypeCountness);
591 auto const arr = popC(env);
592 push(
593 env,
594 gen(
595 env,
596 [&]{
597 if (arr->isA(TArr)) return AddNewElem;
598 if (arr->isA(TKeyset)) return AddNewElemKeyset;
599 if (arr->isA(TVec)) return AddNewElemVec;
600 always_assert(false);
601 }(),
602 arr,
606 decRef(env, val);
609 void emitNewCol(IRGS& env, CollectionType type) {
610 assertx(type != CollectionType::Pair);
611 push(env, gen(env, NewCol, NewColData{type}));
614 void emitNewPair(IRGS& env) {
615 auto const c1 = popC(env, DataTypeGeneric);
616 auto const c2 = popC(env, DataTypeGeneric);
617 // elements were pushed onto the stack in the order they should appear
618 // in the pair, so the top of the stack should become the second element
619 push(env, gen(env, NewPair, c2, c1));
622 void emitNewRecord(IRGS& env, const StringData* name, const ImmVector& immVec) {
623 auto const cachedRec = gen(env, LdRecCached, cns(env, name));
624 auto const numArgs = immVec.size();
625 auto const ids = immVec.vec32();
626 NewStructData extra;
627 extra.offset = spOffBCFromIRSP(env);
628 extra.numKeys = numArgs;
629 extra.keys = new (env.unit.arena()) StringData*[numArgs];
630 for (auto i = size_t{0}; i < numArgs; ++i) {
631 extra.keys[i] = curUnit(env)->lookupLitstrId(ids[i]);
633 auto const recData = gen(env, NewRecord, extra, cachedRec, sp(env));
634 discard(env, numArgs);
635 push(env, recData);
638 void emitColFromArray(IRGS& env, CollectionType type) {
639 assertx(type != CollectionType::Pair);
640 auto const arr = popC(env);
641 if (UNLIKELY(!arr->isA(TVec) && !arr->isA(TDict))) {
642 PUNT(BadColType);
644 if (UNLIKELY(arr->isA(TVec) && type != CollectionType::Vector &&
645 type != CollectionType::ImmVector)) {
646 PUNT(ColTypeMismatch);
648 if (UNLIKELY(arr->isA(TDict) && (type == CollectionType::Vector ||
649 type == CollectionType::ImmVector))) {
650 PUNT(ColTypeMismatch);
652 push(env, gen(env, NewColFromArray, NewColData{type}, arr));
655 void emitStaticLocInit(IRGS& env, int32_t locId, const StringData* name) {
656 auto const func = curFunc(env);
657 if (func->isPseudoMain()) PUNT(StaticLocInit);
659 auto const value = popC(env);
661 // Closures and generators from closures don't satisfy the "one static per
662 // source location" rule that the inline fastpath requires
663 auto const box = [&]{
664 if (func->isClosureBody()) {
665 assertx(func->isClosureBody());
666 assertx(!func->hasVariadicCaptureParam());
667 auto const obj = gen(
668 env, LdLoc, TObj, LocalId(func->numParams()), fp(env));
670 auto const theStatic = gen(env,
671 LdClosureStaticLoc,
672 StaticLocName { func, name },
673 obj);
674 ifThen(
675 env,
676 [&] (Block* taken) {
677 gen(env, CheckTypeMem, TBoxedCell, taken, theStatic);
679 [&] {
680 hint(env, Block::Hint::Unlikely);
681 gen(env, StMem, theStatic, value);
682 gen(env, BoxPtr, theStatic);
685 return gen(env, LdMem, TBoxedCell, theStatic);
688 ifThen(
689 env,
690 [&] (Block* taken) {
691 gen(
692 env,
693 CheckStaticLoc,
694 StaticLocName { func, name },
695 taken
698 [&] {
699 hint(env, Block::Hint::Unlikely);
700 gen(
701 env,
702 InitStaticLoc,
703 StaticLocName { func, name },
704 value
708 return gen(env, LdStaticLoc, StaticLocName { func, name });
709 }();
711 gen(env, IncRef, box);
712 auto const oldValue = ldLoc(env, locId, nullptr, DataTypeSpecific);
713 stLocRaw(env, locId, fp(env), box);
714 decRef(env, oldValue);
715 // We don't need to decref value---it's a bytecode invariant that
716 // our Cell was not ref-counted.
719 void emitStaticLocCheck(IRGS& env, int32_t locId, const StringData* name) {
720 auto const func = curFunc(env);
721 if (func->isPseudoMain()) PUNT(StaticLocCheck);
723 auto bindLocal = [&] (SSATmp* box) {
724 gen(env, IncRef, box);
725 auto const oldValue = ldLoc(env, locId, nullptr, DataTypeGeneric);
726 stLocRaw(env, locId, fp(env), box);
727 decRef(env, oldValue);
728 return cns(env, true);
731 auto const inited = [&] {
732 if (func->isClosureBody()) {
733 auto const obj = gen(
734 env, LdLoc, TObj, LocalId(func->numParams()), fp(env));
735 auto const theStatic = gen(env,
736 LdClosureStaticLoc,
737 StaticLocName { func, name },
738 obj);
739 return cond(
740 env,
741 [&] (Block* taken) {
742 gen(env, CheckTypeMem, TBoxedCell, taken, theStatic);
744 [&] {
745 return bindLocal(gen(env, LdMem, TInitCell, theStatic));
747 [&] {
748 hint(env, Block::Hint::Unlikely);
749 return cns(env, false);
754 return cond(
755 env,
756 [&] (Block* taken) {
757 gen(
758 env,
759 CheckStaticLoc,
760 StaticLocName { func, name },
761 taken
764 [&] {
765 // Next: the static local is already initialized
766 return bindLocal(gen(env, LdStaticLoc, StaticLocName { func, name }));
768 [&] { // Taken: need to initialize the static local
769 return cns(env, false);
772 }();
774 push(env, inited);
777 void emitStaticLocDef(IRGS& env, int32_t locId, const StringData* name) {
778 auto const func = curFunc(env);
779 if (func->isPseudoMain()) PUNT(StaticLocDef);
781 auto const value = popC(env);
783 auto const box = [&] {
784 if (func->isClosureBody()) {
785 auto const obj = gen(
786 env, LdLoc, TObj, LocalId(func->numParams()), fp(env));
787 auto const theStatic = gen(env,
788 LdClosureStaticLoc,
789 StaticLocName { func, name },
790 obj);
791 gen(env, StMem, theStatic, value);
792 auto const boxedStatic = gen(env, BoxPtr, theStatic);
793 return gen(env, LdMem, TBoxedInitCell, boxedStatic);
796 auto init = [&] {
797 gen(
798 env,
799 InitStaticLoc,
800 StaticLocName { func, name },
801 value
805 if (func->isMemoizeWrapper() && !func->numParams()) {
806 ifThenElse(
807 env,
808 [&] (Block* taken) {
809 gen(
810 env,
811 CheckStaticLoc,
812 StaticLocName { func, name },
813 taken
816 [&] {
817 hint(env, Block::Hint::Unlikely);
818 auto oldBox = gen(env, LdStaticLoc, StaticLocName { func, name });
819 auto oldVal = gen(env, LdRef, TInitCell, oldBox);
820 init();
821 decRef(env, oldVal);
823 [&] {
824 init();
827 } else {
828 init();
831 return gen(
832 env,
833 LdStaticLoc,
834 StaticLocName { func, name }
836 }();
838 gen(env, IncRef, box);
839 auto const oldValue = ldLoc(env, locId, nullptr, DataTypeGeneric);
840 stLocRaw(env, locId, fp(env), box);
841 decRef(env, oldValue);
844 void emitCheckReifiedGenericMismatch(IRGS& env) {
845 auto const cls = curClass(env);
846 if (!cls) {
847 // no static context class, so this will raise an error
848 interpOne(env, *env.currentNormalizedInstruction);
849 return;
851 auto const reified_generics = popC(env);
852 gen(env, CheckClsReifiedGenericMismatch, ClassData{cls}, reified_generics);
855 //////////////////////////////////////////////////////////////////////