Skip to content

Commit d198593

Browse files
authored
JIT: Avoid TLS access when restoring async contexts (#127889)
Avoid TLS access when restoring async contexts in the synchronous case at the end of runtime async functions by saving the `Thread` object used when we captured them in a local. NativeAOT test sizes show a minor size regression from initializing the prolog slot and from passing an extra argument, but this transformation is important to be able to optimize the `Thread` accesses in the future.
1 parent 7da2acb commit d198593

16 files changed

Lines changed: 145 additions & 63 deletions

File tree

docs/design/coreclr/botr/clr-abi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ EnC is supported for adding and editing generic methods and methods on generic t
542542

543543
## Async methods
544544

545-
The JIT saves the current `ExecutionContext` and `SynchronizationContext` in runtime async methods and these must be preserved during remap. The new GC encoder includes the state in the EnC frame header size, while for JIT32 the EE expects this state to exist when `CORINFO_ASYNC_SAVE_CONTEXTS` was reported to the JIT from `getMethodInfo`.
545+
The JIT saves the current `Thread`, `ExecutionContext` and `SynchronizationContext` in runtime async methods and these must be preserved during remap. The new GC encoder includes the state in the EnC frame header size, while for JIT32 the EE expects this state to exist when `CORINFO_ASYNC_SAVE_CONTEXTS` was reported to the JIT from `getMethodInfo`.
546546

547547
# Portable entrypoints
548548

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,23 +1132,23 @@ private static void RestoreExecutionContext(Thread thread, ExecutionContext? pre
11321132
}
11331133

11341134
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1135-
private static void CaptureContexts(out ExecutionContext? execCtx, out SynchronizationContext? syncCtx)
1135+
private static void CaptureContexts(out Thread thread, out ExecutionContext? execCtx, out SynchronizationContext? syncCtx)
11361136
{
1137-
Thread thread = Thread.CurrentThreadAssumedInitialized;
1137+
Thread curThread = Thread.CurrentThreadAssumedInitialized;
1138+
thread = curThread;
11381139
// Here we get the execution context for synchronous restoring,
11391140
// not for flowing across suspension to potentially another thread.
11401141
// Therefore we do not need to worry about IsFlowSuppressed
1141-
execCtx = thread._executionContext;
1142-
syncCtx = thread._synchronizationContext;
1142+
execCtx = curThread._executionContext;
1143+
syncCtx = curThread._synchronizationContext;
11431144
}
11441145

1145-
// Restore contexts onto current Thread. If "resumed" then this is not the first starting call for the async method.
1146+
// Restore contexts onto thread. If "resumed" then this is not the first starting call for the async method.
11461147
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1147-
private static void RestoreContexts(bool resumed, ExecutionContext? previousExecCtx, SynchronizationContext? previousSyncCtx)
1148+
private static void RestoreContexts(bool resumed, Thread thread, ExecutionContext? previousExecCtx, SynchronizationContext? previousSyncCtx)
11481149
{
11491150
if (!resumed)
11501151
{
1151-
Thread thread = Thread.CurrentThreadAssumedInitialized;
11521152
if (previousSyncCtx != thread._synchronizationContext)
11531153
{
11541154
thread._synchronizationContext = previousSyncCtx;

src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737

3838
#include <minipal/guid.h>
3939

40-
constexpr GUID JITEEVersionIdentifier = { /* 5635ae9d-ffa5-4336-b027-a383971e3918 */
41-
0x5635ae9d,
42-
0xffa5,
43-
0x4336,
44-
{0xb0, 0x27, 0xa3, 0x83, 0x97, 0x1e, 0x39, 0x18}
40+
constexpr GUID JITEEVersionIdentifier = { /* ab90f94d-0a8e-4718-b9cb-f7f9bcdf6393 */
41+
0xab90f94d,
42+
0x0a8e,
43+
0x4718,
44+
{0xb9, 0xcb, 0xf7, 0xf9, 0xbc, 0xdf, 0x63, 0x93}
4545
};
4646

4747
#endif // JIT_EE_VERSIONING_GUID_H

src/coreclr/inc/patchpointinfo.h

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,16 @@ struct PatchpointInfo
3939
// Initialize
4040
void Initialize(uint32_t localCount, int32_t totalFrameSize)
4141
{
42-
m_calleeSaveRegisters = 0;
43-
m_tier0Version = 0;
44-
m_totalFrameSize = totalFrameSize;
45-
m_numberOfLocals = localCount;
46-
m_genericContextArgOffset = -1;
47-
m_keptAliveThisOffset = -1;
48-
m_securityCookieOffset = -1;
49-
m_monitorAcquiredOffset = -1;
50-
m_asyncExecutionContextOffset = -1;
42+
m_calleeSaveRegisters = 0;
43+
m_tier0Version = 0;
44+
m_totalFrameSize = totalFrameSize;
45+
m_numberOfLocals = localCount;
46+
m_genericContextArgOffset = -1;
47+
m_keptAliveThisOffset = -1;
48+
m_securityCookieOffset = -1;
49+
m_monitorAcquiredOffset = -1;
50+
m_asyncThreadObjectOffset = -1;
51+
m_asyncExecutionContextOffset = -1;
5152
m_asyncSynchronizationContextOffset = -1;
5253
}
5354

@@ -60,6 +61,7 @@ struct PatchpointInfo
6061
m_keptAliveThisOffset = original->m_keptAliveThisOffset;
6162
m_securityCookieOffset = original->m_securityCookieOffset;
6263
m_monitorAcquiredOffset = original->m_monitorAcquiredOffset;
64+
m_asyncThreadObjectOffset = original->m_asyncThreadObjectOffset;
6365
m_asyncExecutionContextOffset = original->m_asyncExecutionContextOffset;
6466
m_asyncSynchronizationContextOffset = original->m_asyncSynchronizationContextOffset;
6567

@@ -151,13 +153,28 @@ struct PatchpointInfo
151153
m_monitorAcquiredOffset = offset;
152154
}
153155

154-
// Original method FP relative offset for async contexts
156+
// Original method FP relative offset for async thread/contexts
157+
int32_t AsyncThreadOffset() const
158+
{
159+
return m_asyncThreadObjectOffset;
160+
}
161+
162+
bool HasAsyncThread() const
163+
{
164+
return m_asyncThreadObjectOffset != -1;
165+
}
166+
167+
void SetAsyncThreadOffset(int32_t offset)
168+
{
169+
m_asyncThreadObjectOffset = offset;
170+
}
171+
155172
int32_t AsyncExecutionContextOffset() const
156173
{
157174
return m_asyncExecutionContextOffset;
158175
}
159176

160-
bool HasAsyncExecutionContextOffset() const
177+
bool HasAsyncExecutionContext() const
161178
{
162179
return m_asyncExecutionContextOffset != -1;
163180
}
@@ -172,7 +189,7 @@ struct PatchpointInfo
172189
return m_asyncSynchronizationContextOffset;
173190
}
174191

175-
bool HasAsyncSynchronizationContextOffset() const
192+
bool HasAsyncSynchronizationContext() const
176193
{
177194
return m_asyncSynchronizationContextOffset != -1;
178195
}
@@ -239,6 +256,7 @@ struct PatchpointInfo
239256
int32_t m_keptAliveThisOffset;
240257
int32_t m_securityCookieOffset;
241258
int32_t m_monitorAcquiredOffset;
259+
int32_t m_asyncThreadObjectOffset;
242260
int32_t m_asyncExecutionContextOffset;
243261
int32_t m_asyncSynchronizationContextOffset;
244262
int32_t m_offsetAndExposureData[];

src/coreclr/interpreter/compiler.cpp

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,7 +2408,7 @@ void InterpCompiler::CreateILVars()
24082408
// add some starting extra space for new vars
24092409
m_varsCapacity = m_numILVars + getEHcount(m_methodInfo) + 64;
24102410
m_pVars = getAllocator(IMK_Var).allocateZeroed<InterpVar>(m_varsCapacity);
2411-
m_varsSize = m_numILVars + hasParamArg + hasContinuationArg + (hasThisPointerShadowCopyAsParamIndex ? 1 : 0) + (m_isAsyncMethodWithContextSaveRestore ? 2 : 0) + getEHcount(m_methodInfo);
2411+
m_varsSize = m_numILVars + hasParamArg + hasContinuationArg + (hasThisPointerShadowCopyAsParamIndex ? 1 : 0) + (m_isAsyncMethodWithContextSaveRestore ? 3 : 0) + getEHcount(m_methodInfo);
24122412

24132413
offset = 0;
24142414

@@ -2509,10 +2509,16 @@ void InterpCompiler::CreateILVars()
25092509

25102510
if (m_isAsyncMethodWithContextSaveRestore)
25112511
{
2512+
m_threadObjVarIndex = index;
2513+
CreateNextLocalVar(index, NULL, InterpTypeO, &offset);
2514+
INTERP_DUMP("alloc Async Thread (var %d) to offset %d\n", m_threadObjVarIndex, m_pVars[m_threadObjVarIndex].offset);
2515+
index++;
2516+
25122517
m_execContextVarIndex = index;
25132518
CreateNextLocalVar(index, NULL, InterpTypeO, &offset);
25142519
INTERP_DUMP("alloc ExecutableContextVar (var %d) to offset %d\n", m_execContextVarIndex, m_pVars[m_execContextVarIndex].offset);
25152520
index++;
2521+
25162522
m_syncContextVarIndex = index;
25172523
CreateNextLocalVar(index, NULL, InterpTypeO, &offset);
25182524
INTERP_DUMP("alloc SyncContextVar (var %d) to offset %d\n", m_syncContextVarIndex, m_pVars[m_syncContextVarIndex].offset);
@@ -4817,21 +4823,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
48174823
m_pLastNewIns->SetSVar(m_continuationArgIndex);
48184824
PushInterpType(InterpTypeI4, NULL);
48194825
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
4820-
int32_t isStartedArg = m_pStackPointer[-1].var;
4821-
m_pStackPointer--;
4822-
4823-
AddIns(INTOP_MOV_P);
4824-
m_pLastNewIns->SetSVar(m_execContextVarIndex);
4825-
PushInterpType(InterpTypeO, NULL);
4826-
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
4827-
int32_t execContextAddressVar = m_pStackPointer[-1].var;
4828-
m_pStackPointer--;
4829-
4830-
AddIns(INTOP_MOV_P);
4831-
m_pLastNewIns->SetSVar(m_syncContextVarIndex);
4832-
PushInterpType(InterpTypeO, NULL);
4833-
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
4834-
int32_t syncContextAddressVar = m_pStackPointer[-1].var;
4826+
int32_t isResumedArg = m_pStackPointer[-1].var;
48354827
m_pStackPointer--;
48364828

48374829
// Create a new dummy var to serve as the dVar of the call
@@ -4851,12 +4843,13 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
48514843

48524844
m_pLastNewIns->flags |= INTERP_INST_FLAG_CALL;
48534845
m_pLastNewIns->info.pCallInfo = new (getAllocator(IMK_CallInfo)) InterpCallInfo();
4854-
int32_t numArgs = 3;
4846+
int32_t numArgs = 4;
48554847
int32_t *callArgs = getAllocator(IMK_CallInfo).allocate<int32_t>(numArgs + 1);
4856-
callArgs[0] = isStartedArg;
4857-
callArgs[1] = execContextAddressVar;
4858-
callArgs[2] = syncContextAddressVar;
4859-
callArgs[3] = CALL_ARGS_TERMINATOR;
4848+
callArgs[0] = isResumedArg;
4849+
callArgs[1] = m_threadObjVarIndex;
4850+
callArgs[2] = m_execContextVarIndex;
4851+
callArgs[3] = m_syncContextVarIndex;
4852+
callArgs[4] = CALL_ARGS_TERMINATOR;
48604853
m_pLastNewIns->info.pCallInfo->pCallArgs = callArgs;
48614854

48624855
m_ip += 5;
@@ -8180,7 +8173,7 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
81808173
INTERP_DUMP("Synchronized method - adding extra IL opcodes for async save/restore\n");
81818174

81828175
// Async methods are methods implemented by adding a try/finally in the method
8183-
// which takes the lock on entry and releases it on exit. To integrate this into the interpreter, we actually
8176+
// which captures contexts on entry and restores them on exit. To integrate this into the interpreter, we actually
81848177
// add a set of extra "IL" opcodes at the end of the method which do the monitor finally and actual return
81858178
// logic.
81868179

@@ -8357,7 +8350,15 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
83578350

83588351
if (m_isAsyncMethodWithContextSaveRestore)
83598352
{
8360-
// Load the address of the execution context/sync context locals, and call AsyncHelpers.CaptureContexts
8353+
// Load the address of the thread/execution context/sync context locals, and call AsyncHelpers.CaptureContexts
8354+
AddIns(INTOP_LDLOCA);
8355+
m_pLastNewIns->SetSVar(m_threadObjVarIndex);
8356+
PushInterpType(InterpTypeByRef, NULL);
8357+
m_pStackPointer[-1].SetAsLocalVariableAddress();
8358+
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
8359+
int32_t threadAddressVar = m_pStackPointer[-1].var;
8360+
m_pStackPointer--;
8361+
83618362
AddIns(INTOP_LDLOCA);
83628363
m_pLastNewIns->SetSVar(m_execContextVarIndex);
83638364
PushInterpType(InterpTypeByRef, NULL);
@@ -8392,11 +8393,12 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
83928393

83938394
m_pLastNewIns->flags |= INTERP_INST_FLAG_CALL;
83948395
m_pLastNewIns->info.pCallInfo = new (getAllocator(IMK_CallInfo)) InterpCallInfo();
8395-
int32_t numArgs = 2;
8396+
int32_t numArgs = 3;
83968397
int32_t *callArgs = getAllocator(IMK_CallInfo).allocate<int32_t>(numArgs + 1);
8397-
callArgs[0] = execContextAddressVar;
8398-
callArgs[1] = syncContextAddressVar;
8399-
callArgs[2] = CALL_ARGS_TERMINATOR;
8398+
callArgs[0] = threadAddressVar;
8399+
callArgs[1] = execContextAddressVar;
8400+
callArgs[2] = syncContextAddressVar;
8401+
callArgs[3] = CALL_ARGS_TERMINATOR;
84008402
m_pLastNewIns->info.pCallInfo->pCallArgs = callArgs;
84018403
}
84028404

src/coreclr/interpreter/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ class InterpCompiler
893893
int32_t m_synchronizedOrAsyncRetValVarIndex = -1; // If the method is synchronized, ret instructions are replaced with a store to this var and a leave to an epilog instruction.
894894
int32_t m_synchronizedFinallyStartOffset = -1; // If the method is synchronized, this is the offset of the start of the finally epilog
895895

896+
int32_t m_threadObjVarIndex = -1; // If the method is async, this is the var index of the Thread local
896897
int32_t m_execContextVarIndex = -1; // If the method is async, this is the var index of the ExecutionContext local
897898
int32_t m_syncContextVarIndex = -1; // If the method is async, this is the var index of the SynchronizationContext local
898899

src/coreclr/jit/async.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ PhaseStatus Compiler::SaveAsyncContexts()
8888
return PhaseStatus::MODIFIED_NOTHING;
8989
}
9090

91-
// Create locals for ExecutionContext and SynchronizationContext
91+
// Create locals for Thread, ExecutionContext and SynchronizationContext
92+
lvaAsyncThreadObjectVar = lvaGrabTemp(false DEBUGARG("Async Thread"));
93+
lvaGetDesc(lvaAsyncThreadObjectVar)->lvType = TYP_REF;
94+
9295
lvaAsyncExecutionContextVar = lvaGrabTemp(false DEBUGARG("Async ExecutionContext"));
9396
lvaGetDesc(lvaAsyncExecutionContextVar)->lvType = TYP_REF;
9497

@@ -97,6 +100,7 @@ PhaseStatus Compiler::SaveAsyncContexts()
97100

98101
if (opts.IsOSR())
99102
{
103+
lvaGetDesc(lvaAsyncThreadObjectVar)->lvIsOSRLocal = true;
100104
lvaGetDesc(lvaAsyncExecutionContextVar)->lvIsOSRLocal = true;
101105
lvaGetDesc(lvaAsyncSynchronizationContextVar)->lvIsOSRLocal = true;
102106
}
@@ -190,8 +194,10 @@ PhaseStatus Compiler::SaveAsyncContexts()
190194
captureCall->gtArgs.PushFront(this,
191195
NewCallArg::Primitive(gtNewLclAddrNode(lvaAsyncSynchronizationContextVar, 0)));
192196
captureCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclAddrNode(lvaAsyncExecutionContextVar, 0)));
193-
lvaGetDesc(lvaAsyncSynchronizationContextVar)->lvHasLdAddrOp = true;
197+
captureCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclAddrNode(lvaAsyncThreadObjectVar, 0)));
198+
lvaGetDesc(lvaAsyncThreadObjectVar)->lvHasLdAddrOp = true;
194199
lvaGetDesc(lvaAsyncExecutionContextVar)->lvHasLdAddrOp = true;
200+
lvaGetDesc(lvaAsyncSynchronizationContextVar)->lvHasLdAddrOp = true;
195201

196202
CORINFO_CALL_INFO callInfo = {};
197203
callInfo.hMethod = captureCall->gtCallMethHnd;
@@ -224,6 +230,7 @@ PhaseStatus Compiler::SaveAsyncContexts()
224230
restoreCall->gtArgs.PushFront(this,
225231
NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncSynchronizationContextVar, TYP_REF)));
226232
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncExecutionContextVar, TYP_REF)));
233+
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncThreadObjectVar, TYP_REF)));
227234
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(resumed));
228235

229236
Statement* restoreStmt = fgNewStmtFromTree(restoreCall);
@@ -391,6 +398,7 @@ BasicBlock* Compiler::CreateReturnBB(unsigned* mergedReturnLcl)
391398
restoreCall->gtArgs.PushFront(this,
392399
NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncSynchronizationContextVar, TYP_REF)));
393400
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncExecutionContextVar, TYP_REF)));
401+
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclVarNode(lvaAsyncThreadObjectVar, TYP_REF)));
394402
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(resumed));
395403

396404
// This restore is an inline candidate (unlike the fault one)
@@ -993,6 +1001,10 @@ void AsyncTransformation::CreateLiveSetForSuspension(BasicBlock*
9931001
call->VisitLocalDefs(m_compiler, visitDef);
9941002

9951003
// Exclude method-level context locals (only live on synchronous path)
1004+
if (m_compiler->lvaAsyncThreadObjectVar != BAD_VAR_NUM)
1005+
{
1006+
excludedLocals.AddOrUpdate(m_compiler->lvaAsyncThreadObjectVar, true);
1007+
}
9961008
if (m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM)
9971009
{
9981010
excludedLocals.AddOrUpdate(m_compiler->lvaAsyncSynchronizationContextVar, true);
@@ -2312,7 +2324,7 @@ void AsyncTransformation::RestoreContexts(BasicBlock* block, GenTreeCall* call,
23122324
JITDUMP(" Call [%06u] has async contexts; will restore on suspension\n", Compiler::dspTreeID(call));
23132325

23142326
// Insert call
2315-
// AsyncHelpers.RestoreContexts(resumed, execContext, syncContext);
2327+
// AsyncHelpers.RestoreContextsOnSuspension(resumed, execContext, syncContext);
23162328

23172329
GenTree* resumedPlaceholder = m_compiler->gtNewIconNode(0);
23182330
GenTree* execContextPlaceholder = m_compiler->gtNewNull();

src/coreclr/jit/codegenarm.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,10 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
24912491
{
24922492
saveSizeWithPSP += TARGET_POINTER_SIZE;
24932493
}
2494+
if (m_compiler->lvaAsyncThreadObjectVar != BAD_VAR_NUM)
2495+
{
2496+
saveSizeWithPSP += TARGET_POINTER_SIZE;
2497+
}
24942498
if (m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM)
24952499
{
24962500
saveSizeWithPSP += TARGET_POINTER_SIZE;

src/coreclr/jit/codegenarm64.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,11 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
17351735
saveRegsSize += m_compiler->lvaLclStackHomeSize(m_compiler->lvaMonAcquired);
17361736
}
17371737

1738+
if ((m_compiler->lvaAsyncThreadObjectVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR())
1739+
{
1740+
saveRegsSize += m_compiler->lvaLclStackHomeSize(m_compiler->lvaAsyncThreadObjectVar);
1741+
}
1742+
17381743
if ((m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR())
17391744
{
17401745
saveRegsSize += m_compiler->lvaLclStackHomeSize(m_compiler->lvaAsyncExecutionContextVar);

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3832,7 +3832,7 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
38323832
// -saved off FP
38333833
// -all callee-preserved registers in case of varargs
38343834
// -saved bool for synchronized methods
3835-
// -async contexts for async methods
3835+
// -thread/async contexts for async methods
38363836

38373837
int preservedAreaSize = (2 + genCountBits((uint64_t)RBM_ENC_CALLEE_SAVED)) * REGSIZE_BYTES;
38383838

@@ -3852,6 +3852,13 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
38523852
assert(m_compiler->lvaGetCallerSPRelativeOffset(m_compiler->lvaMonAcquired) == -preservedAreaSize);
38533853
}
38543854

3855+
if (m_compiler->lvaAsyncThreadObjectVar != BAD_VAR_NUM)
3856+
{
3857+
preservedAreaSize += TARGET_POINTER_SIZE;
3858+
3859+
assert(m_compiler->lvaGetCallerSPRelativeOffset(m_compiler->lvaAsyncThreadObjectVar) == -preservedAreaSize);
3860+
}
3861+
38553862
if (m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM)
38563863
{
38573864
preservedAreaSize += TARGET_POINTER_SIZE;

0 commit comments

Comments
 (0)