@@ -2217,6 +2217,10 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType,
22172217 emitCurIG->igFlags &= ~IGF_PROPAGATE_MASK;
22182218 }
22192219
2220+ // since we have emitted a placeholder, the last ins is not longer the last.
2221+ emitLastIns = nullptr ;
2222+ emitLastInsIG = nullptr ;
2223+
22202224#ifdef DEBUG
22212225 if (emitComp->verbose )
22222226 {
@@ -2873,10 +2877,36 @@ bool emitter::emitNoGChelper(CORINFO_METHOD_HANDLE methHnd)
28732877 * Mark the current spot as having a label.
28742878 */
28752879
2876- void * emitter::emitAddLabel (VARSET_VALARG_TP GCvars,
2877- regMaskTP gcrefRegs,
2878- regMaskTP byrefRegs DEBUG_ARG (BasicBlock* block))
2880+ void * emitter::emitAddLabel (VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, BasicBlock* prevBlock)
28792881{
2882+ if (prevBlock != NULL && emitLastInsIsCallWithGC ())
2883+ {
2884+ // We have just emitted a call that can do GC and conservatively recorded what is alive after the call.
2885+ // Now we see that the next instruction may be reachable by a branch with a different liveness.
2886+ // We want to maintain the invariant that the GC info at IP after a GC-capable call is the same
2887+ // regardless how it is reached.
2888+ // One way to fix this is to fish out the call instruction and patch its GC info, but we must be
2889+ // certain that the current IP is indeed reachable after the call.
2890+ // Another way it to add an instruction (NOP or BRK) after the call.
2891+ if (emitThisGCrefRegs != gcrefRegs || emitThisByrefRegs != byrefRegs ||
2892+ !VarSetOps::Equal (emitComp, emitThisGCrefVars, GCvars))
2893+ {
2894+ if (prevBlock->KindIs (BBJ_THROW))
2895+ {
2896+ emitIns (INS_BREAKPOINT);
2897+ }
2898+ else
2899+ {
2900+ // other block kinds should emit something at the end that is not a call.
2901+ assert (prevBlock->KindIs (BBJ_ALWAYS));
2902+ // CONSIDER: We could patch up the previous call instruction with new GC info instead.
2903+ // But that will need to be coordinated with how the GC info vor variables is used.
2904+ // We currently apply that info to the instruction before the call. It may need to change.
2905+ emitIns (INS_nop);
2906+ }
2907+ }
2908+ }
2909+
28802910 /* Create a new IG if the current one is non-empty */
28812911
28822912 if (emitCurIGnonEmpty ())
@@ -3637,6 +3667,7 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int argCnt,
36373667
36383668 /* Make sure we didn't waste space unexpectedly */
36393669 assert (!id->idIsLargeCns ());
3670+ id->idSetIsCall ();
36403671
36413672#ifdef TARGET_XARCH
36423673 /* Store the displacement and make sure the value fit */
@@ -3716,6 +3747,7 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int argCnt,
37163747
37173748 /* Make sure we didn't waste space unexpectedly */
37183749 assert (!id->idIsLargeCns ());
3750+ id->idSetIsCall ();
37193751
37203752 /* Save the live GC registers in the unused register fields */
37213753 assert ((gcrefRegs & RBM_CALLEE_TRASH) == 0 );
@@ -8725,6 +8757,16 @@ void emitter::emitUpdateLiveGCvars(VARSET_VALARG_TP vars, BYTE* addr)
87258757 emitThisGCrefVset = true ;
87268758}
87278759
8760+ /* ****************************************************************************
8761+ *
8762+ * Last emitted instruction is a call and it is not a NoGC call.
8763+ */
8764+
8765+ bool emitter::emitLastInsIsCallWithGC ()
8766+ {
8767+ return emitLastIns != nullptr && emitLastIns->idIsCall () && !emitLastIns->idIsNoGC ();
8768+ }
8769+
87288770/* ****************************************************************************
87298771 *
87308772 * Record a call location for GC purposes (we know that this is a method that
0 commit comments