Skip to content

Commit d78082b

Browse files
authored
Partial re-revert of dotnet#104336. Only JIT fixes are included. (dotnet#105596)
* Partial re-revert of dotnet#104336. Only JIT fixes are included. * fix for stress issues * comments and some cleanup * JIT format * keep existing code for x86
1 parent bfcca29 commit d78082b

File tree

10 files changed

+47
-65
lines changed

10 files changed

+47
-65
lines changed

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -605,10 +605,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
605605
{
606606
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
607607

608-
// Make sure that the return register is reported as live GC-ref so that any GC that kicks in while
609-
// executing GS cookie check will not collect the object pointed to by REG_INTRET (R0).
610-
if (!pushReg && (compiler->info.compRetNativeType == TYP_REF))
611-
gcInfo.gcRegGCrefSetCur |= RBM_INTRET;
608+
assert(GetEmitter()->emitGCDisabled());
612609

613610
// We need two temporary registers, to load the GS cookie values and compare them. We can't use
614611
// any argument registers if 'pushReg' is true (meaning we have a JMP call). They should be

src/coreclr/jit/codegencommon.cpp

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,8 +1516,22 @@ void CodeGen::genExitCode(BasicBlock* block)
15161516
bool jmpEpilog = block->HasFlag(BBF_HAS_JMP);
15171517
if (compiler->getNeedsGSSecurityCookie())
15181518
{
1519+
#ifndef JIT32_GCENCODER
1520+
// At this point the gc info that we track in codegen is often incorrect,
1521+
// as it could be missing return registers or arg registers (in a case of tail call).
1522+
// GS cookie check will emit a call and that will pass our GC info to emit and potentially mess things up.
1523+
// While we could infer returns/args and force them to be live and it seems to work in JIT32_GCENCODER case,
1524+
// it appears to be nontrivial in more general case.
1525+
// So, instead, we just claim that the whole thing is not GC-interruptible.
1526+
// Effectively this starts the epilog a few instructions earlier.
1527+
//
1528+
// CONSIDER: is that a good place to be that codegen loses track of returns/args at this point?
1529+
GetEmitter()->emitDisableGC();
1530+
#endif
1531+
15191532
genEmitGSCookieCheck(jmpEpilog);
15201533

1534+
#ifdef JIT32_GCENCODER
15211535
if (jmpEpilog)
15221536
{
15231537
// Dev10 642944 -
@@ -1540,6 +1554,7 @@ void CodeGen::genExitCode(BasicBlock* block)
15401554
GetEmitter()->emitThisGCrefRegs = GetEmitter()->emitInitGCrefRegs = gcInfo.gcRegGCrefSetCur;
15411555
GetEmitter()->emitThisByrefRegs = GetEmitter()->emitInitByrefRegs = gcInfo.gcRegByrefSetCur;
15421556
}
1557+
#endif
15431558
}
15441559

15451560
genReserveEpilog(block);
@@ -4728,43 +4743,13 @@ void CodeGen::genReserveProlog(BasicBlock* block)
47284743

47294744
void CodeGen::genReserveEpilog(BasicBlock* block)
47304745
{
4731-
regMaskTP gcrefRegsArg = gcInfo.gcRegGCrefSetCur;
4732-
regMaskTP byrefRegsArg = gcInfo.gcRegByrefSetCur;
4733-
4734-
/* The return value is special-cased: make sure it goes live for the epilog */
4735-
4736-
bool jmpEpilog = block->HasFlag(BBF_HAS_JMP);
4737-
4738-
if (IsFullPtrRegMapRequired() && !jmpEpilog)
4739-
{
4740-
if (varTypeIsGC(compiler->info.compRetNativeType))
4741-
{
4742-
noway_assert(genTypeStSz(compiler->info.compRetNativeType) == genTypeStSz(TYP_I_IMPL));
4743-
4744-
gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetNativeType);
4745-
4746-
switch (compiler->info.compRetNativeType)
4747-
{
4748-
case TYP_REF:
4749-
gcrefRegsArg |= RBM_INTRET;
4750-
break;
4751-
case TYP_BYREF:
4752-
byrefRegsArg |= RBM_INTRET;
4753-
break;
4754-
default:
4755-
break;
4756-
}
4757-
4758-
JITDUMP("Extending return value GC liveness to epilog\n");
4759-
}
4760-
}
4761-
47624746
JITDUMP("Reserving epilog IG for block " FMT_BB "\n", block->bbNum);
47634747

47644748
assert(block != nullptr);
4765-
const VARSET_TP& gcrefVarsArg(GetEmitter()->emitThisGCrefVars);
4766-
GetEmitter()->emitCreatePlaceholderIG(IGPT_EPILOG, block, gcrefVarsArg, gcrefRegsArg, byrefRegsArg,
4767-
block->IsLast());
4749+
// We pass empty GC info, because epilog is always an extend IG and will ignore what we pass.
4750+
// Besides, at this point the GC info that we track in CodeGen is often incorrect.
4751+
// See comments in genExitCode for more info.
4752+
GetEmitter()->emitCreatePlaceholderIG(IGPT_EPILOG, block, VarSetOps::MakeEmpty(compiler), 0, 0, block->IsLast());
47684753
}
47694754

47704755
/*****************************************************************************

src/coreclr/jit/codegenloongarch64.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4610,12 +4610,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
46104610
{
46114611
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
46124612

4613-
// Make sure that the return register is reported as live GC-ref so that any GC that kicks in while
4614-
// executing GS cookie check will not collect the object pointed to by REG_INTRET (A0).
4615-
if (!pushReg && (compiler->info.compRetNativeType == TYP_REF))
4616-
{
4617-
gcInfo.gcRegGCrefSetCur |= RBM_INTRET;
4618-
}
4613+
assert(GetEmitter()->emitGCDisabled());
46194614

46204615
// We need two temporary registers, to load the GS cookie values and compare them. We can't use
46214616
// any argument registers if 'pushReg' is true (meaning we have a JMP call). They should be

src/coreclr/jit/codegenriscv64.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4664,12 +4664,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
46644664
{
46654665
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
46664666

4667-
// Make sure that the return register is reported as live GC-ref so that any GC that kicks in while
4668-
// executing GS cookie check will not collect the object pointed to by REG_INTRET (A0).
4669-
if (!pushReg && (compiler->info.compRetNativeType == TYP_REF))
4670-
{
4671-
gcInfo.gcRegGCrefSetCur |= RBM_INTRET;
4672-
}
4667+
assert(GetEmitter()->emitGCDisabled());
46734668

46744669
// We need two temporary registers, to load the GS cookie values and compare them. We can't use
46754670
// any argument registers if 'pushReg' is true (meaning we have a JMP call). They should be

src/coreclr/jit/codegenxarch.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,11 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
9696
{
9797
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
9898

99-
// Make sure that EAX is reported as live GC-ref so that any GC that kicks in while
100-
// executing GS cookie check will not collect the object pointed to by EAX.
101-
//
102-
// For Amd64 System V, a two-register-returned struct could be returned in RAX and RDX
103-
// In such case make sure that the correct GC-ness of RDX is reported as well, so
104-
// a GC object pointed by RDX will not be collected.
99+
#ifdef JIT32_GCENCODER
105100
if (!pushReg)
106101
{
102+
// Make sure that EAX is reported as live GC-ref so that any GC that kicks in while
103+
// executing GS cookie check will not collect the object pointed to by EAX.
107104
if (compiler->compMethodReturnsRetBufAddr())
108105
{
109106
// This is for returning in an implicit RetBuf.
@@ -126,6 +123,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
126123
}
127124
}
128125
}
126+
#else
127+
assert(GetEmitter()->emitGCDisabled());
128+
#endif
129129

130130
regNumber regGSCheck;
131131
regMaskTP regMaskGSCheck = RBM_NONE;

src/coreclr/jit/emit.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10427,9 +10427,9 @@ regMaskTP emitter::emitGetGCRegsKilledByNoGCCall(CorInfoHelpFunc helper)
1042710427
// of the last instruction in the region makes GC safe again.
1042810428
// In particular - once the IP is on the first instruction, but not executed it yet,
1042910429
// it is still safe to do GC.
10430-
// The only special case is when NoGC region is used for prologs/epilogs.
10431-
// In such case the GC info could be incorrect until the prolog completes and epilogs
10432-
// may have unwindability restrictions, so the first instruction cannot have GC.
10430+
// The only special case is when NoGC region is used for prologs.
10431+
// In such case the GC info could be incorrect until the prolog completes, so the first
10432+
// instruction cannot have GC.
1043310433

1043410434
void emitter::emitDisableGC()
1043510435
{
@@ -10459,6 +10459,11 @@ void emitter::emitDisableGC()
1045910459
}
1046010460
}
1046110461

10462+
bool emitter::emitGCDisabled()
10463+
{
10464+
return emitNoGCIG == true;
10465+
}
10466+
1046210467
//------------------------------------------------------------------------
1046310468
// emitEnableGC(): Removes a request that the following instruction groups are not GC-interruptible.
1046410469
//

src/coreclr/jit/emit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,6 +2745,7 @@ class emitter
27452745
#if !defined(JIT32_GCENCODER)
27462746
void emitDisableGC();
27472747
void emitEnableGC();
2748+
bool emitGCDisabled();
27482749
#endif // !defined(JIT32_GCENCODER)
27492750

27502751
#if defined(TARGET_XARCH)

src/coreclr/jit/emitinl.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,7 @@ bool emitter::emitGenNoGCLst(Callback& cb)
594594
emitter::instrDesc* id = emitFirstInstrDesc(ig->igData);
595595
assert(id != nullptr);
596596
assert(id->idCodeSize() > 0);
597-
if (!cb(ig->igFuncIdx, ig->igOffs, ig->igSize, id->idCodeSize(),
598-
ig->igFlags & (IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG)))
597+
if (!cb(ig->igFuncIdx, ig->igOffs, ig->igSize, id->idCodeSize(), ig->igFlags & (IGF_FUNCLET_PROLOG)))
599598
{
600599
return false;
601600
}

src/coreclr/jit/gcencode.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4027,8 +4027,7 @@ class InterruptibleRangeReporter
40274027
// Report everything between the previous region and the current
40284028
// region as interruptible.
40294029

4030-
bool operator()(
4031-
unsigned igFuncIdx, unsigned igOffs, unsigned igSize, unsigned firstInstrSize, bool isInPrologOrEpilog)
4030+
bool operator()(unsigned igFuncIdx, unsigned igOffs, unsigned igSize, unsigned firstInstrSize, bool isInProlog)
40324031
{
40334032
if (igOffs < m_uninterruptibleEnd)
40344033
{
@@ -4042,9 +4041,9 @@ class InterruptibleRangeReporter
40424041
if (igOffs > m_uninterruptibleEnd)
40434042
{
40444043
// Once the first instruction in IG executes, we cannot have GC.
4045-
// But it is ok to have GC while the IP is on the first instruction, unless we are in prolog/epilog.
4044+
// But it is ok to have GC while the IP is on the first instruction, unless we are in prolog.
40464045
unsigned interruptibleEnd = igOffs;
4047-
if (!isInPrologOrEpilog)
4046+
if (!isInProlog)
40484047
{
40494048
interruptibleEnd += firstInstrSize;
40504049
}

src/coreclr/nativeaot/Runtime/thread.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,10 +675,16 @@ void Thread::HijackCallback(NATIVE_CONTEXT* pThreadContext, void* pThreadToHijac
675675
if (runtime->IsConservativeStackReportingEnabled() ||
676676
codeManager->IsSafePoint(pvAddress))
677677
{
678+
// IsUnwindable is precise on arm64, but can give false negatives on other architectures.
679+
// (when IP is on the first instruction of an epilog, we still can unwind,
680+
// but we can tell if the instruction is the first only if we can navigate instructions backwards and check)
681+
// The preciseness of IsUnwindable is tracked in https://github.com/dotnet/runtime/issues/101932
682+
#if defined(TARGET_ARM64)
678683
// we may not be able to unwind in some locations, such as epilogs.
679684
// such locations should not contain safe points.
680685
// when scanning conservatively we do not need to unwind
681686
ASSERT(codeManager->IsUnwindable(pvAddress) || runtime->IsConservativeStackReportingEnabled());
687+
#endif
682688

683689
// if we are not given a thread to hijack
684690
// perform in-line wait on the current thread

0 commit comments

Comments
 (0)