@@ -2232,6 +2232,10 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType,
2232
2232
emitCurIG->igFlags &= ~IGF_PROPAGATE_MASK;
2233
2233
}
2234
2234
2235
+ // since we have emitted a placeholder, the last ins is not longer the last.
2236
+ emitLastIns = nullptr;
2237
+ emitLastInsIG = nullptr;
2238
+
2235
2239
#ifdef DEBUG
2236
2240
if (emitComp->verbose)
2237
2241
{
@@ -2895,10 +2899,36 @@ bool emitter::emitNoGChelper(CORINFO_METHOD_HANDLE methHnd)
2895
2899
* Mark the current spot as having a label.
2896
2900
*/
2897
2901
2898
- void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars,
2899
- regMaskTP gcrefRegs,
2900
- regMaskTP byrefRegs DEBUG_ARG(BasicBlock* block))
2902
+ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, BasicBlock* prevBlock)
2901
2903
{
2904
+ if (emitLastInsIsCallWithGC())
2905
+ {
2906
+ // We have just emitted a call that can do GC and conservatively recorded what is alive after the call.
2907
+ // Now we see that the next instruction may be reachable by a branch with a different liveness.
2908
+ // We want to maintain the invariant that the GC info at IP after a GC-capable call is the same
2909
+ // regardless how it is reached.
2910
+ // One way to fix this is to fish out the call instruction and patch its GC info, but we must be
2911
+ // certain that the current IP is indeed reachable after the call.
2912
+ // Another way it to add an instruction (NOP or BRK) after the call.
2913
+ if (emitThisGCrefRegs != gcrefRegs || emitThisByrefRegs != byrefRegs ||
2914
+ !VarSetOps::Equal(emitComp, emitThisGCrefVars, GCvars))
2915
+ {
2916
+ if (prevBlock->KindIs(BBJ_THROW))
2917
+ {
2918
+ emitIns(INS_BREAKPOINT);
2919
+ }
2920
+ else
2921
+ {
2922
+ // other block kinds should emit something at the end that is not a call.
2923
+ assert(prevBlock->KindIs(BBJ_ALWAYS));
2924
+ // CONSIDER: We could patch up the previous call instruction with new GC info instead.
2925
+ // But that will need to be coordinated with how the GC info vor variables is used.
2926
+ // We currently apply that info to the instruction before the call. It may need to change.
2927
+ emitIns(INS_nop);
2928
+ }
2929
+ }
2930
+ }
2931
+
2902
2932
/* Create a new IG if the current one is non-empty */
2903
2933
2904
2934
if (emitCurIGnonEmpty())
@@ -3663,6 +3693,7 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int argCnt,
3663
3693
3664
3694
/* Make sure we didn't waste space unexpectedly */
3665
3695
assert(!id->idIsLargeCns());
3696
+ id->idSetIsCall();
3666
3697
3667
3698
#ifdef TARGET_XARCH
3668
3699
/* Store the displacement and make sure the value fit */
@@ -3742,6 +3773,7 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int argCnt,
3742
3773
3743
3774
/* Make sure we didn't waste space unexpectedly */
3744
3775
assert(!id->idIsLargeCns());
3776
+ id->idSetIsCall();
3745
3777
3746
3778
/* Save the live GC registers in the unused register fields */
3747
3779
assert((gcrefRegs & RBM_CALLEE_TRASH) == 0);
@@ -8754,6 +8786,16 @@ void emitter::emitUpdateLiveGCvars(VARSET_VALARG_TP vars, BYTE* addr)
8754
8786
emitThisGCrefVset = true;
8755
8787
}
8756
8788
8789
+ /*****************************************************************************
8790
+ *
8791
+ * Last emitted instruction is a call and it is not a NoGC call.
8792
+ */
8793
+
8794
+ bool emitter::emitLastInsIsCallWithGC()
8795
+ {
8796
+ return emitLastIns != nullptr && emitLastIns->idIsCall() && !emitLastIns->idIsNoGC();
8797
+ }
8798
+
8757
8799
/*****************************************************************************
8758
8800
*
8759
8801
* Record a call location for GC purposes (we know that this is a method that
0 commit comments