Skip to content

Commit 24b0228

Browse files
authored
Enregister EH var that are single def (#47307)
* Enable EhWriteThry for SingleDef * If EhWriteThru is enabled, DoNotEnregister if variable is not singleDef * Revert code in ExecutionContext.RunInternal * Revert code in AsyncMethodBuildCore.Start() * Make sure we do not reset lvSingleDef * Consitent display of frame offset misc change in superpmi.py * Use lvEHWriteThruCandidate * Do not enregister EH Var that has single use * do not enregister simdtype * add missing comments * jit format * revert an unintended change * jit format * Add missing comments
1 parent ccf2de0 commit 24b0228

9 files changed

Lines changed: 101 additions & 71 deletions

File tree

src/coreclr/jit/compiler.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ class LclVarDsc
454454
// before lvaMarkLocalVars: identifies ref type locals that can get type updates
455455
// after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies
456456

457+
unsigned char lvEhWriteThruCandidate : 1; // variable has a single def and hence is a register candidate if
458+
// if it is an EH variable
459+
460+
unsigned char lvDisqualifyForEhWriteThru : 1; // tracks variable that are disqualified from register candidancy
461+
457462
#if ASSERTION_PROP
458463
unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization
459464
unsigned char lvVolatileHint : 1; // hint for AssertionProp
@@ -7369,6 +7374,24 @@ class Compiler
73697374

73707375
void raMarkStkVars();
73717376

7377+
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
7378+
#if defined(TARGET_AMD64)
7379+
static bool varTypeNeedsPartialCalleeSave(var_types type)
7380+
{
7381+
return (type == TYP_SIMD32);
7382+
}
7383+
#elif defined(TARGET_ARM64)
7384+
static bool varTypeNeedsPartialCalleeSave(var_types type)
7385+
{
7386+
// ARM64 ABI FP Callee save registers only require Callee to save lower 8 Bytes
7387+
// For SIMD types longer than 8 bytes Caller is responsible for saving and restoring Upper bytes.
7388+
return ((type == TYP_SIMD16) || (type == TYP_SIMD12));
7389+
}
7390+
#else // !defined(TARGET_AMD64) && !defined(TARGET_ARM64)
7391+
#error("Unknown target architecture for FEATURE_SIMD")
7392+
#endif // !defined(TARGET_AMD64) && !defined(TARGET_ARM64)
7393+
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
7394+
73727395
protected:
73737396
// Some things are used by both LSRA and regpredict allocators.
73747397

src/coreclr/jit/jitconfigvalues.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ CONFIG_INTEGER(EnablePOPCNT, W("EnablePOPCNT"), 1) // Enable POPCNT
274274
CONFIG_INTEGER(EnableAVX, W("EnableAVX"), 0)
275275
#endif // !defined(TARGET_AMD64) && !defined(TARGET_X86)
276276

277-
CONFIG_INTEGER(EnableEHWriteThru, W("EnableEHWriteThru"), 0) // Enable the register allocator to support EH-write thru:
277+
CONFIG_INTEGER(EnableEHWriteThru, W("EnableEHWriteThru"), 1) // Enable the register allocator to support EH-write thru:
278278
// partial enregistration of vars exposed on EH boundaries
279279
CONFIG_INTEGER(EnableMultiRegLocals, W("EnableMultiRegLocals"), 1) // Enable the enregistration of locals that are
280280
// defined or used in a multireg context.

src/coreclr/jit/lclvars.cpp

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,14 +2614,16 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
26142614
{
26152615
noway_assert(lvaTable[i].lvIsStructField);
26162616
lvaTable[i].lvLiveInOutOfHndlr = 1;
2617-
if (!lvaEnregEHVars)
2617+
// For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
2618+
if (!lvaEnregEHVars || !lvaTable[i].lvEhWriteThruCandidate || lvaTable[i].lvRefCnt() <= 1)
26182619
{
26192620
lvaSetVarDoNotEnregister(i DEBUGARG(DNER_LiveInOutOfHandler));
26202621
}
26212622
}
26222623
}
26232624

2624-
if (!lvaEnregEHVars)
2625+
// For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
2626+
if (!lvaEnregEHVars || !varDsc->lvEhWriteThruCandidate || varDsc->lvRefCnt() <= 1)
26252627
{
26262628
lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LiveInOutOfHandler));
26272629
}
@@ -4040,7 +4042,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
40404042

40414043
/* Record if the variable has a single def or not */
40424044

4043-
if (!varDsc->lvDisqualify) // If this variable is already disqualified we can skip this
4045+
if (!varDsc->lvDisqualify) // If this variable is already disqualified, we can skip this
40444046
{
40454047
if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable
40464048
{
@@ -4075,6 +4077,34 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
40754077
BlockSetOps::AddElemD(this, varDsc->lvRefBlks, block->bbNum);
40764078
}
40774079
}
4080+
4081+
if (!varDsc->lvDisqualifyForEhWriteThru) // If this EH var already disqualified, we can skip this
4082+
{
4083+
if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable
4084+
{
4085+
bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0;
4086+
bool bbIsReturn = block->bbJumpKind == BBJ_RETURN;
4087+
bool needsExplicitZeroInit = fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn);
4088+
4089+
if (varDsc->lvEhWriteThruCandidate || needsExplicitZeroInit)
4090+
{
4091+
varDsc->lvEhWriteThruCandidate = false;
4092+
varDsc->lvDisqualifyForEhWriteThru = true;
4093+
}
4094+
else
4095+
{
4096+
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
4097+
// TODO-CQ: If the varType needs partial callee save, conservatively do not enregister
4098+
// such variable. In future, need to enable enregisteration for such variables.
4099+
if (!varTypeNeedsPartialCalleeSave(varDsc->lvType))
4100+
#endif
4101+
{
4102+
varDsc->lvEhWriteThruCandidate = true;
4103+
}
4104+
}
4105+
}
4106+
}
4107+
40784108
#endif // ASSERTION_PROP
40794109

40804110
bool allowStructs = false;
@@ -4178,6 +4208,8 @@ void Compiler::lvaMarkLocalVars(BasicBlock* block, bool isRecompute)
41784208

41794209
Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
41804210
{
4211+
// TODO: Stop passing isRecompute once we are sure that this assert is never hit.
4212+
assert(!m_isRecompute);
41814213
m_compiler->lvaMarkLclRefs(*use, m_block, m_stmt, m_isRecompute);
41824214
return WALK_CONTINUE;
41834215
}
@@ -4437,7 +4469,13 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
44374469

44384470
// Set initial value for lvSingleDef for explicit and implicit
44394471
// argument locals as they are "defined" on entry.
4440-
varDsc->lvSingleDef = varDsc->lvIsParam;
4472+
// However, if we are just recomputing the ref counts, retain the value
4473+
// that was set by past phases.
4474+
if (!isRecompute)
4475+
{
4476+
varDsc->lvSingleDef = varDsc->lvIsParam;
4477+
varDsc->lvEhWriteThruCandidate = varDsc->lvIsParam;
4478+
}
44414479
}
44424480

44434481
// Remember current state of generic context use, and prepare
@@ -7194,7 +7232,7 @@ void Compiler::lvaDumpFrameLocation(unsigned lclNum)
71947232
baseReg = EBPbased ? REG_FPBASE : REG_SPBASE;
71957233
#endif
71967234

7197-
printf("[%2s%1s0x%02X] ", getRegName(baseReg), (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
7235+
printf("[%2s%1s%02XH] ", getRegName(baseReg), (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
71987236
}
71997237

72007238
/*****************************************************************************

src/coreclr/jit/lsra.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,7 +1781,7 @@ void LinearScan::identifyCandidates()
17811781

17821782
if (varDsc->lvLiveInOutOfHndlr)
17831783
{
1784-
newInt->isWriteThru = true;
1784+
newInt->isWriteThru = varDsc->lvEhWriteThruCandidate;
17851785
setIntervalAsSpilled(newInt);
17861786
}
17871787

@@ -1796,7 +1796,7 @@ void LinearScan::identifyCandidates()
17961796
// Additionally, when we are generating code for a target with partial SIMD callee-save
17971797
// (AVX on non-UNIX amd64 and 16-byte vectors on arm64), we keep a separate set of the
17981798
// LargeVectorType vars.
1799-
if (varTypeNeedsPartialCalleeSave(varDsc->lvType))
1799+
if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType))
18001800
{
18011801
largeVectorVarCount++;
18021802
VarSetOps::AddElemD(compiler, largeVectorVars, varDsc->lvVarIndex);
@@ -5050,7 +5050,7 @@ void LinearScan::processBlockEndLocations(BasicBlock* currentBlock)
50505050
}
50515051
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
50525052
// Ensure that we have no partially-spilled large vector locals.
5053-
assert(!varTypeNeedsPartialCalleeSave(interval->registerType) || !interval->isPartiallySpilled);
5053+
assert(!Compiler::varTypeNeedsPartialCalleeSave(interval->registerType) || !interval->isPartiallySpilled);
50545054
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
50555055
}
50565056
INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_END_BB));
@@ -6922,7 +6922,7 @@ void LinearScan::insertUpperVectorSave(GenTree* tree,
69226922
}
69236923

69246924
LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum;
6925-
assert(varTypeNeedsPartialCalleeSave(varDsc->lvType));
6925+
assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType));
69266926

69276927
// On Arm64, we must always have a register to save the upper half,
69286928
// while on x86 we can spill directly to memory.
@@ -7003,7 +7003,7 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree,
70037003
// lclVar as spilled).
70047004
assert(lclVarReg != REG_NA);
70057005
LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum;
7006-
assert(varTypeNeedsPartialCalleeSave(varDsc->lvType));
7006+
assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType));
70077007

70087008
GenTree* restoreLcl = nullptr;
70097009
restoreLcl = compiler->gtNewLclvNode(lclVarInterval->varNum, varDsc->lvType);

src/coreclr/jit/lsra.h

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ class LinearScan : public LinearScanInterface
985985

986986
void resolveConflictingDefAndUse(Interval* interval, RefPosition* defRefPosition);
987987

988-
void buildRefPositionsForNode(GenTree* tree, BasicBlock* block, LsraLocation loc);
988+
void buildRefPositionsForNode(GenTree* tree, LsraLocation loc);
989989

990990
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
991991
void buildUpperVectorSaveRefPositions(GenTree* tree, LsraLocation currentLoc, regMaskTP fpCalleeKillSet);
@@ -1497,23 +1497,10 @@ class LinearScan : public LinearScanInterface
14971497

14981498
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
14991499
#if defined(TARGET_AMD64)
1500-
static bool varTypeNeedsPartialCalleeSave(var_types type)
1501-
{
1502-
return (type == TYP_SIMD32);
1503-
}
15041500
static const var_types LargeVectorSaveType = TYP_SIMD16;
15051501
#elif defined(TARGET_ARM64)
1506-
static bool varTypeNeedsPartialCalleeSave(var_types type)
1507-
{
1508-
// ARM64 ABI FP Callee save registers only require Callee to save lower 8 Bytes
1509-
// For SIMD types longer than 8 bytes Caller is responsible for saving and restoring Upper bytes.
1510-
return ((type == TYP_SIMD16) || (type == TYP_SIMD12));
1511-
}
1512-
static const var_types LargeVectorSaveType = TYP_DOUBLE;
1513-
#else // !defined(TARGET_AMD64) && !defined(TARGET_ARM64)
1514-
#error("Unknown target architecture for FEATURE_SIMD")
1502+
static const var_types LargeVectorSaveType = TYP_DOUBLE;
15151503
#endif // !defined(TARGET_AMD64) && !defined(TARGET_ARM64)
1516-
15171504
// Set of large vector (TYP_SIMD32 on AVX) variables.
15181505
VARSET_TP largeVectorVars;
15191506
// Set of large vector (TYP_SIMD32 on AVX) variables to consider for callee-save registers.

src/coreclr/jit/lsrabuild.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
11601160
{
11611161
LclVarDsc* varDsc = compiler->lvaGetDescByTrackedIndex(varIndex);
11621162
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
1163-
if (varTypeNeedsPartialCalleeSave(varDsc->lvType))
1163+
if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType))
11641164
{
11651165
if (!VarSetOps::IsMember(compiler, largeVectorCalleeSaveCandidateVars, varIndex))
11661166
{
@@ -1424,7 +1424,7 @@ void LinearScan::buildInternalRegisterUses()
14241424
void LinearScan::makeUpperVectorInterval(unsigned varIndex)
14251425
{
14261426
Interval* lclVarInterval = getIntervalForLocalVar(varIndex);
1427-
assert(varTypeNeedsPartialCalleeSave(lclVarInterval->registerType));
1427+
assert(Compiler::varTypeNeedsPartialCalleeSave(lclVarInterval->registerType));
14281428
Interval* newInt = newInterval(LargeVectorSaveType);
14291429
newInt->relatedInterval = lclVarInterval;
14301430
newInt->isUpperVector = true;
@@ -1506,7 +1506,7 @@ void LinearScan::buildUpperVectorSaveRefPositions(GenTree* tree, LsraLocation cu
15061506
for (RefInfoListNode *listNode = defList.Begin(), *end = defList.End(); listNode != end;
15071507
listNode = listNode->Next())
15081508
{
1509-
if (varTypeNeedsPartialCalleeSave(listNode->treeNode->TypeGet()))
1509+
if (Compiler::varTypeNeedsPartialCalleeSave(listNode->treeNode->TypeGet()))
15101510
{
15111511
// In the rare case where such an interval is live across nested calls, we don't need to insert another.
15121512
if (listNode->ref->getInterval()->recentRefPosition->refType != RefTypeUpperVectorSave)
@@ -1637,10 +1637,9 @@ int LinearScan::ComputeAvailableSrcCount(GenTree* node)
16371637
//
16381638
// Arguments:
16391639
// tree - The node for which we are building RefPositions
1640-
// block - The BasicBlock in which the node resides
16411640
// currentLoc - The LsraLocation of the given node
16421641
//
1643-
void LinearScan::buildRefPositionsForNode(GenTree* tree, BasicBlock* block, LsraLocation currentLoc)
1642+
void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc)
16441643
{
16451644
// The LIR traversal doesn't visit GT_LIST or GT_ARGPLACE nodes.
16461645
// GT_CLS_VAR nodes should have been eliminated by rationalizer.
@@ -2351,7 +2350,7 @@ void LinearScan::buildIntervals()
23512350
node->SetRegNum(node->GetRegNum());
23522351
#endif
23532352

2354-
buildRefPositionsForNode(node, block, currentLoc);
2353+
buildRefPositionsForNode(node, currentLoc);
23552354

23562355
#ifdef DEBUG
23572356
if (currentLoc > maxNodeLocation)
@@ -3232,7 +3231,7 @@ void LinearScan::BuildStoreLocDef(GenTreeLclVarCommon* storeLoc,
32323231
def->regOptional = true;
32333232
}
32343233
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
3235-
if (varTypeNeedsPartialCalleeSave(varDefInterval->registerType))
3234+
if (Compiler::varTypeNeedsPartialCalleeSave(varDefInterval->registerType))
32363235
{
32373236
varDefInterval->isPartiallySpilled = false;
32383237
}

src/coreclr/scripts/superpmi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ def save_repro_mc_files(temp_location, coreclr_args, repro_base_command_line):
13461346
shutil.copy2(item, repro_location)
13471347

13481348
logging.info("")
1349-
logging.info("Repro .mc files created for failures:")
1349+
logging.info("Repro {} .mc file(s) created for failures:".format(len(repro_files)))
13501350
for item in repro_files:
13511351
logging.info(item)
13521352

src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderCore.cs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,31 @@ public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TS
2525
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
2626
}
2727

28-
// enregistrer variables with 0 post-fix so they can be used in registers without EH forcing them to stack
29-
// Capture references to Thread Contexts
30-
Thread currentThread0 = Thread.CurrentThread;
31-
Thread currentThread = currentThread0;
32-
ExecutionContext? previousExecutionCtx0 = currentThread0._executionContext;
28+
Thread currentThread = Thread.CurrentThread;
3329

3430
// Store current ExecutionContext and SynchronizationContext as "previousXxx".
3531
// This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
3632
// so that they won't "leak" out of the first await.
37-
ExecutionContext? previousExecutionCtx = previousExecutionCtx0;
38-
SynchronizationContext? previousSyncCtx = currentThread0._synchronizationContext;
33+
ExecutionContext? previousExecutionCtx = currentThread._executionContext;
34+
SynchronizationContext? previousSyncCtx = currentThread._synchronizationContext;
3935

4036
try
4137
{
4238
stateMachine.MoveNext();
4339
}
4440
finally
4541
{
46-
// Re-enregistrer variables post EH with 1 post-fix so they can be used in registers rather than from stack
47-
SynchronizationContext? previousSyncCtx1 = previousSyncCtx;
48-
Thread currentThread1 = currentThread;
4942
// The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
50-
if (previousSyncCtx1 != currentThread1._synchronizationContext)
43+
if (previousSyncCtx != currentThread._synchronizationContext)
5144
{
5245
// Restore changed SynchronizationContext back to previous
53-
currentThread1._synchronizationContext = previousSyncCtx1;
46+
currentThread._synchronizationContext = previousSyncCtx;
5447
}
5548

56-
ExecutionContext? previousExecutionCtx1 = previousExecutionCtx;
57-
ExecutionContext? currentExecutionCtx1 = currentThread1._executionContext;
58-
if (previousExecutionCtx1 != currentExecutionCtx1)
49+
ExecutionContext? currentExecutionCtx = currentThread._executionContext;
50+
if (previousExecutionCtx != currentExecutionCtx)
5951
{
60-
ExecutionContext.RestoreChangedContextToThread(currentThread1, previousExecutionCtx1, currentExecutionCtx1);
52+
ExecutionContext.RestoreChangedContextToThread(currentThread, previousExecutionCtx, currentExecutionCtx);
6153
}
6254
}
6355
}

0 commit comments

Comments
 (0)