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 +----------------------------------------------------------------------+
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
{
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();
48 gen(env
, CheckRDSInitialized
, taken
, RDSHandleData
{ cls
->propHandle() });
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
) {
70 ByteOffsetData
{ (ptrdiff_t)rootCls
->declPropOffset(idx
) },
71 TUncounted
.lval(Ptr::Prop
),
76 // Load Exception::$traceOpts
77 auto const lookup
= ldClsPropAddrKnown(
79 SystemLib::s_ExceptionClass
,
83 assertx(!lookup
.tc
->isCheckable());
84 auto const sprop
= lookup
.propPtr
;
87 auto const trace
= cond(
90 gen(env
, CheckTypeMem
, TInt
, taken
, sprop
);
93 // sprop is an integer, load it
94 auto const opts
= gen(env
, LdMem
, TInt
, sprop
);
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
);
105 gen(env
, EqInt
, opts
, cns(env
, k_DEBUG_BACKTRACE_IGNORE_ARGS
));
106 gen(env
, JmpZero
, taken
, safe
);
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));
117 // Call debug_backtrace(traceOpts)
118 return gen(env
, DebugBacktrace
, opts
);
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
);
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);
164 RDSHandleData
{ cls
->checkedPropTypeRedefinesHandle() }
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
));
183 RDSHandleData
{ cls
->checkedPropTypeRedefinesHandle() }
189 void checkPropInitialValues(IRGS
& env
, const Class
* cls
) {
190 assertx(cls
->needsPropInitialValueCheck());
191 assertx(RuntimeOption::EvalCheckPropTypeHints
> 0);
200 RDSHandleData
{ cls
->checkedPropInitialValuesHandle() }
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;
221 cns(env
, makeStaticString(prop
.name
)),
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(
249 ByteOffsetData
{ (ptrdiff_t)(cls
->declPropOffset(i
)) },
253 gen(env
, StMem
, addr
, val
);
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;
274 RDSHandleData
{ cls
->sPropInitHandle() }
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();
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
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
));
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
);
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
);
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
)) {
357 if (func
->isStatic()) {
358 return gen(env
, FwdCtxStaticCall
, ldctx
);
360 gen(env
, IncRef
, ldctx
);
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
);
372 for (; propId
< numParams
; ++propId
) {
376 ByteOffsetData
{ safe_cast
<ptrdiff_t>(cls
->declPropOffset(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
;
393 ByteOffsetData
{ safe_cast
<ptrdiff_t>(cls
->declPropOffset(propId
)) },
402 void emitNewArray(IRGS
& env
, uint32_t capacity
) {
404 push(env
, cns(env
, staticEmptyArray()));
406 push(env
, gen(env
, NewArray
, cns(env
, capacity
)));
410 void emitNewMixedArray(IRGS
& env
, uint32_t capacity
) {
412 push(env
, cns(env
, staticEmptyArray()));
414 push(env
, gen(env
, NewMixedArray
, cns(env
, capacity
)));
418 void emitNewDArray(IRGS
& env
, uint32_t capacity
) {
419 assertx(!RuntimeOption::EvalHackArrDVArrs
);
421 push(env
, cns(env
, staticEmptyDArray()));
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(
436 spOffBCFromIRSP(env
),
437 static_cast<uint32_t>(numArgs
)
441 discard(env
, numArgs
);
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
);
452 arr
= gen(env
, NewLikeArray
, ld
, cns(env
, capacity
));
454 capacity
= (capacity
? capacity
: MixedArray::SmallSize
);
455 arr
= gen(env
, NewArray
, cns(env
, capacity
));
463 void emitNewPackedLayoutArray(IRGS
& env
, uint32_t numArgs
, Opcode op
) {
464 auto const array
= gen(
467 PackedArrayData
{ numArgs
}
469 static constexpr auto kMaxUnrolledInitArray
= 8;
470 if (numArgs
> kMaxUnrolledInitArray
) {
473 InitPackedLayoutArrayLoop
,
474 InitPackedArrayLoopData
{
475 spOffBCFromIRSP(env
),
481 discard(env
, numArgs
);
486 for (int i
= 0; i
< numArgs
; ++i
) {
489 InitPackedLayoutArray
,
490 IndexData
{ static_cast<uint32_t>(numArgs
- i
- 1) },
492 popC(env
, DataTypeGeneric
)
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
);
515 void newStructImpl(IRGS
& env
, const ImmVector
& immVec
, Opcode op
) {
516 auto const numArgs
= immVec
.size();
517 auto const ids
= immVec
.vec32();
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();
556 } else if (kt
<= TStr
) {
559 interpOne(env
, TArr
, 3);
562 } else if (at
<= TDict
) {
564 op
= DictAddElemIntKey
;
565 } else if (kt
<= TStr
) {
566 op
= DictAddElemStrKey
;
568 interpOne(env
, TDict
, 3);
572 PUNT(AddElemC
-BadArr
);
575 // val is teleported from the stack to the array, so we don't have to do any
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
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
);
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);
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();
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
);
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
))) {
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
,
672 StaticLocName
{ func
, name
},
677 gen(env
, CheckTypeMem
, TBoxedCell
, taken
, theStatic
);
680 hint(env
, Block::Hint::Unlikely
);
681 gen(env
, StMem
, theStatic
, value
);
682 gen(env
, BoxPtr
, theStatic
);
685 return gen(env
, LdMem
, TBoxedCell
, theStatic
);
694 StaticLocName
{ func
, name
},
699 hint(env
, Block::Hint::Unlikely
);
703 StaticLocName
{ func
, name
},
708 return gen(env
, LdStaticLoc
, StaticLocName
{ func
, name
});
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
,
737 StaticLocName
{ func
, name
},
742 gen(env
, CheckTypeMem
, TBoxedCell
, taken
, theStatic
);
745 return bindLocal(gen(env
, LdMem
, TInitCell
, theStatic
));
748 hint(env
, Block::Hint::Unlikely
);
749 return cns(env
, false);
760 StaticLocName
{ func
, name
},
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);
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
,
789 StaticLocName
{ func
, name
},
791 gen(env
, StMem
, theStatic
, value
);
792 auto const boxedStatic
= gen(env
, BoxPtr
, theStatic
);
793 return gen(env
, LdMem
, TBoxedInitCell
, boxedStatic
);
800 StaticLocName
{ func
, name
},
805 if (func
->isMemoizeWrapper() && !func
->numParams()) {
812 StaticLocName
{ func
, name
},
817 hint(env
, Block::Hint::Unlikely
);
818 auto oldBox
= gen(env
, LdStaticLoc
, StaticLocName
{ func
, name
});
819 auto oldVal
= gen(env
, LdRef
, TInitCell
, oldBox
);
834 StaticLocName
{ func
, name
}
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
);
847 // no static context class, so this will raise an error
848 interpOne(env
, *env
.currentNormalizedInstruction
);
851 auto const reified_generics
= popC(env
);
852 gen(env
, CheckClsReifiedGenericMismatch
, ClassData
{cls
}, reified_generics
);
855 //////////////////////////////////////////////////////////////////////