Skip to content

Commit afc9f65

Browse files
authored
Don't set GCState to 1 in PInvoke Epilog (dotnet#38303)
* Don't set GCState to 1 in PInvoke Epilog Remove the incorrect setting of GCState and fix the liveness computation for compLvFrameListRoot for tailcalls. Fix dotnet#38192
1 parent a926345 commit afc9f65

3 files changed

Lines changed: 26 additions & 31 deletions

File tree

src/coreclr/src/jit/liveness.cpp

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -342,21 +342,21 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
342342
fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed);
343343
fgCurMemoryHavoc |= memoryKindSet(GcHeap, ByrefExposed);
344344
}
345-
}
346345

347-
// If this is a p/invoke unmanaged call or if this is a tail-call
346+
// If this is a p/invoke unmanaged call or if this is a tail-call via helper,
348347
// and we have an unmanaged p/invoke call in the method,
349348
// then we're going to run the p/invoke epilog.
350349
// So we mark the FrameRoot as used by this instruction.
351350
// This ensures that the block->bbVarUse will contain
352351
// the FrameRoot local var if is it a tracked variable.
353352

354-
if ((tree->AsCall()->IsUnmanaged() || tree->AsCall()->IsTailCall()) && compMethodRequiresPInvokeFrame())
353+
if ((tree->AsCall()->IsUnmanaged() || tree->AsCall()->IsTailCallViaJitHelper()) &&
354+
compMethodRequiresPInvokeFrame())
355355
{
356356
assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
357357
if (!opts.ShouldUsePInvokeHelpers())
358358
{
359-
/* Get the TCB local and mark it as used */
359+
// Get the FrameRoot local and mark it as used.
360360

361361
noway_assert(info.compLvFrameListRoot < lvaCount);
362362

@@ -373,6 +373,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
373373
}
374374

375375
break;
376+
}
376377

377378
default:
378379

@@ -504,7 +505,7 @@ void Compiler::fgPerBlockLocalVarLiveness()
504505
}
505506
}
506507

507-
/* Get the TCB local and mark it as used */
508+
// Mark the FrameListRoot as used, if applicable.
508509

509510
if (block->bbJumpKind == BBJ_RETURN && compMethodRequiresPInvokeFrame())
510511
{
@@ -513,13 +514,22 @@ void Compiler::fgPerBlockLocalVarLiveness()
513514
{
514515
noway_assert(info.compLvFrameListRoot < lvaCount);
515516

516-
LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot];
517-
518-
if (varDsc->lvTracked)
517+
// 32-bit targets always pop the frame in the epilog.
518+
// For 64-bit targets, we only do this in the epilog for IL stubs;
519+
// for non-IL stubs the frame is popped after every PInvoke call.
520+
CLANG_FORMAT_COMMENT_ANCHOR;
521+
#ifdef TARGET_64BIT
522+
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB))
523+
#endif
519524
{
520-
if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex))
525+
LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot];
526+
527+
if (varDsc->lvTracked)
521528
{
522-
VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex);
529+
if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex))
530+
{
531+
VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex);
532+
}
523533
}
524534
}
525535
}
@@ -1437,16 +1447,16 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call)
14371447
{
14381448
assert(call != nullptr);
14391449

1440-
// If this is a tail-call and we have any unmanaged p/invoke calls in
1441-
// the method then we're going to run the p/invoke epilog
1450+
// If this is a tail-call via helper, and we have any unmanaged p/invoke calls in
1451+
// the method, then we're going to run the p/invoke epilog
14421452
// So we mark the FrameRoot as used by this instruction.
14431453
// This ensure that this variable is kept alive at the tail-call
1444-
if (call->IsTailCall() && compMethodRequiresPInvokeFrame())
1454+
if (call->IsTailCallViaJitHelper() && compMethodRequiresPInvokeFrame())
14451455
{
14461456
assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
14471457
if (!opts.ShouldUsePInvokeHelpers())
14481458
{
1449-
/* Get the TCB local and make it live */
1459+
// Get the FrameListRoot local and make it live.
14501460

14511461
noway_assert(info.compLvFrameListRoot < lvaCount);
14521462

@@ -1465,7 +1475,7 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call)
14651475
/* Is this call to unmanaged code? */
14661476
if (call->IsUnmanaged() && compMethodRequiresPInvokeFrame())
14671477
{
1468-
/* Get the TCB local and make it live */
1478+
// Get the FrameListRoot local and make it live.
14691479
assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
14701480
if (!opts.ShouldUsePInvokeHelpers())
14711481
{

src/coreclr/src/jit/lower.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4055,20 +4055,6 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree*
40554055
// Example3: GT_JMP. After inserting PME execution order would be: PME, GT_JMP
40564056
// That is after PME, args for GT_JMP call will be setup.
40574057

4058-
// TODO-Cleanup: setting GCState to 1 seems to be redundant as InsertPInvokeCallProlog will set it to zero before a
4059-
// PInvoke call and InsertPInvokeCallEpilog() will set it back to 1 after the PInvoke. Though this is redundant,
4060-
// it is harmeless.
4061-
// Note that liveness is artificially extending the life of compLvFrameListRoot var if the method being compiled has
4062-
// PInvokes. Deleting the below stmnt would cause an an assert in lsra.cpp::SetLastUses() since compLvFrameListRoot
4063-
// will be live-in to a BBJ_RETURN block without any uses. Long term we need to fix liveness for x64 case to
4064-
// properly extend the life of compLvFrameListRoot var.
4065-
//
4066-
// Thread.offsetOfGcState = 0/1
4067-
// That is [tcb + offsetOfGcState] = 1
4068-
GenTree* storeGCState = SetGCState(1);
4069-
returnBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeGCState));
4070-
ContainCheckStoreIndir(storeGCState->AsIndir());
4071-
40724058
// Pop the frame if necessary. This always happens in the epilog on 32-bit targets. For 64-bit targets, we only do
40734059
// this in the epilog for IL stubs; for non-IL stubs the frame is popped after every PInvoke call.
40744060
CLANG_FORMAT_COMMENT_ANCHOR;

src/coreclr/tests/src/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ public static int Main(string[] args)
4242
TestUnmanagedCallersOnlyValid();
4343
TestUnmanagedCallersOnlyValid_OnNewNativeThread();
4444
TestUnmanagedCallersOnlyValid_PrepareMethod();
45-
// Fails due to https://github.com/dotnet/runtime/issues/38192
46-
//TestUnmanagedCallersOnlyMultipleTimesValid();
45+
TestUnmanagedCallersOnlyMultipleTimesValid();
4746
NegativeTest_NonStaticMethod();
4847
NegativeTest_ViaDelegate();
4948
NegativeTest_NonBlittable();

0 commit comments

Comments
 (0)