Skip to content

Commit be76968

Browse files
authored
[NativeAOT] Port x86 ResumeSP logic from CoreCLR (#99866)
* [NativeAOT] Port most of x86 ResumeSP logic from CoreCLR * Bring back LocAllocSPvar on NativeAOT/x86 ABI, use it in NativeAOT runtime.
1 parent c622e43 commit be76968

10 files changed

+89
-36
lines changed

src/coreclr/jit/lclvars.cpp

+21-21
Original file line numberDiff line numberDiff line change
@@ -4291,7 +4291,7 @@ PhaseStatus Compiler::lvaMarkLocalVars()
42914291

42924292
#endif // !FEATURE_EH_FUNCLETS
42934293

4294-
// PSPSym and LocAllocSPvar are not used by the NativeAOT ABI
4294+
// PSPSym is not used by the NativeAOT ABI
42954295
if (!IsTargetAbi(CORINFO_NATIVEAOT_ABI))
42964296
{
42974297
#if defined(FEATURE_EH_FUNCLETS)
@@ -4303,29 +4303,29 @@ PhaseStatus Compiler::lvaMarkLocalVars()
43034303
lvaSetVarDoNotEnregister(lvaPSPSym DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
43044304
}
43054305
#endif // FEATURE_EH_FUNCLETS
4306+
}
43064307

43074308
#ifdef JIT32_GCENCODER
4308-
// LocAllocSPvar is only required by the implicit frame layout expected by the VM on x86. Whether
4309-
// a function contains a Localloc is conveyed in the GC information, in the InfoHdrSmall.localloc
4310-
// field. The function must have an EBP frame. Then, the VM finds the LocAllocSP slot by assuming
4311-
// the following stack layout:
4312-
//
4313-
// -- higher addresses --
4314-
// saved EBP <-- EBP points here
4315-
// other callee-saved registers // InfoHdrSmall.savedRegsCountExclFP specifies this size
4316-
// optional GS cookie // InfoHdrSmall.security is 1 if this exists
4317-
// LocAllocSP slot
4318-
// -- lower addresses --
4319-
//
4320-
// See also eetwain.cpp::GetLocallocSPOffset() and its callers.
4321-
if (compLocallocUsed)
4322-
{
4323-
lvaLocAllocSPvar = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
4324-
LclVarDsc* locAllocSPvar = lvaGetDesc(lvaLocAllocSPvar);
4325-
locAllocSPvar->lvType = TYP_I_IMPL;
4326-
}
4327-
#endif // JIT32_GCENCODER
4309+
// LocAllocSPvar is only required by the implicit frame layout expected by the VM on x86. Whether
4310+
// a function contains a Localloc is conveyed in the GC information, in the InfoHdrSmall.localloc
4311+
// field. The function must have an EBP frame. Then, the VM finds the LocAllocSP slot by assuming
4312+
// the following stack layout:
4313+
//
4314+
// -- higher addresses --
4315+
// saved EBP <-- EBP points here
4316+
// other callee-saved registers // InfoHdrSmall.savedRegsCountExclFP specifies this size
4317+
// optional GS cookie // InfoHdrSmall.security is 1 if this exists
4318+
// LocAllocSP slot
4319+
// -- lower addresses --
4320+
//
4321+
// See also eetwain.cpp::GetLocallocSPOffset() and its callers.
4322+
if (compLocallocUsed)
4323+
{
4324+
lvaLocAllocSPvar = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
4325+
LclVarDsc* locAllocSPvar = lvaGetDesc(lvaLocAllocSPvar);
4326+
locAllocSPvar->lvType = TYP_I_IMPL;
43284327
}
4328+
#endif // JIT32_GCENCODER
43294329

43304330
// Ref counting is now enabled normally.
43314331
lvaRefCountState = RCS_NORMAL;

src/coreclr/nativeaot/Runtime/ICodeManager.h

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ class ICodeManager
229229
virtual PTR_VOID GetFramePointer(MethodInfo * pMethodInfo,
230230
REGDISPLAY * pRegisterSet) PURE_VIRTUAL
231231

232+
#ifdef TARGET_X86
233+
virtual uintptr_t GetResumeSp(MethodInfo * pMethodInfo,
234+
REGDISPLAY * pRegisterSet) PURE_VIRTUAL
235+
#endif
236+
232237
virtual void EnumGcRefs(MethodInfo * pMethodInfo,
233238
PTR_VOID safePointAddress,
234239
REGDISPLAY * pRegisterSet,

src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -1677,6 +1677,13 @@ void StackFrameIterator::CalculateCurrentMethodState()
16771677
m_effectiveSafePointAddress = m_ControlPC;
16781678
m_FramePointer = GetCodeManager()->GetFramePointer(&m_methodInfo, &m_RegDisplay);
16791679

1680+
#ifdef TARGET_X86
1681+
if (m_dwFlags & UpdateResumeSp)
1682+
{
1683+
m_RegDisplay.ResumeSP = GetCodeManager()->GetResumeSp(&m_methodInfo, &m_RegDisplay);
1684+
}
1685+
#endif
1686+
16801687
m_dwFlags |= MethodStateCalculated;
16811688
}
16821689

src/coreclr/nativeaot/Runtime/StackFrameIterator.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,11 @@ class StackFrameIterator
148148
// When encountering a reverse P/Invoke, unwind directly to the P/Invoke frame using the saved transition frame.
149149
SkipNativeFrames = 0x80,
150150

151+
// Set SP to an address that is valid for funclet resumption (x86 only)
152+
UpdateResumeSp = 0x100,
153+
151154
GcStackWalkFlags = (CollapseFunclets | RemapHardwareFaultsToSafePoint | SkipNativeFrames),
152-
EHStackWalkFlags = ApplyReturnAddressAdjustment,
155+
EHStackWalkFlags = (ApplyReturnAddressAdjustment | UpdateResumeSp),
153156
StackTraceStackWalkFlags = GcStackWalkFlags
154157
};
155158

src/coreclr/nativeaot/Runtime/i386/AsmOffsetsCpu.h

+8-9
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,42 @@
77
//
88
// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix
99

10-
PLAT_ASM_SIZEOF(c0, ExInfo)
10+
PLAT_ASM_SIZEOF(c4, ExInfo)
1111
PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo)
1212
PLAT_ASM_OFFSET(4, ExInfo, m_pExContext)
1313
PLAT_ASM_OFFSET(8, ExInfo, m_exception)
1414
PLAT_ASM_OFFSET(0c, ExInfo, m_kind)
1515
PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber)
1616
PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause)
1717
PLAT_ASM_OFFSET(14, ExInfo, m_frameIter)
18-
PLAT_ASM_OFFSET(bc, ExInfo, m_notifyDebuggerSP)
18+
PLAT_ASM_OFFSET(c0, ExInfo, m_notifyDebuggerSP)
1919

2020
PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP)
2121
PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_FramePointer)
2222
PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread)
2323
PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_Flags)
2424
PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs)
2525

26-
PLAT_ASM_SIZEOF(a8, StackFrameIterator)
26+
PLAT_ASM_SIZEOF(ac, StackFrameIterator)
2727
PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer)
2828
PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC)
2929
PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay)
30-
PLAT_ASM_OFFSET(a0, StackFrameIterator, m_OriginalControlPC)
31-
PLAT_ASM_OFFSET(a4, StackFrameIterator, m_pPreviousTransitionFrame)
30+
PLAT_ASM_OFFSET(a4, StackFrameIterator, m_OriginalControlPC)
31+
PLAT_ASM_OFFSET(a8, StackFrameIterator, m_pPreviousTransitionFrame)
3232

3333
PLAT_ASM_SIZEOF(1c, PAL_LIMITED_CONTEXT)
3434
PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP)
35-
3635
PLAT_ASM_OFFSET(4, PAL_LIMITED_CONTEXT, Rsp)
3736
PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, Rbp)
3837
PLAT_ASM_OFFSET(0c, PAL_LIMITED_CONTEXT, Rdi)
3938
PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, Rsi)
4039
PLAT_ASM_OFFSET(14, PAL_LIMITED_CONTEXT, Rax)
4140
PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, Rbx)
4241

43-
PLAT_ASM_SIZEOF(28, REGDISPLAY)
44-
PLAT_ASM_OFFSET(1c, REGDISPLAY, SP)
45-
42+
PLAT_ASM_SIZEOF(2c, REGDISPLAY)
4643
PLAT_ASM_OFFSET(0c, REGDISPLAY, pRbx)
4744
PLAT_ASM_OFFSET(10, REGDISPLAY, pRbp)
4845
PLAT_ASM_OFFSET(14, REGDISPLAY, pRsi)
4946
PLAT_ASM_OFFSET(18, REGDISPLAY, pRdi)
47+
PLAT_ASM_OFFSET(1c, REGDISPLAY, SP)
48+
PLAT_ASM_OFFSET(28, REGDISPLAY, ResumeSP)

src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ FASTCALL_FUNC RhpCallCatchFunclet, 16
334334

335335
mov ecx, [esp + esp_offsetof_ExInfo] ;; ecx <- current ExInfo *
336336
mov eax, [esp + esp_offsetof_RegDisplay] ;; eax <- REGDISPLAY*
337-
mov eax, [eax + OFFSETOF__REGDISPLAY__SP] ;; eax <- resume SP value
337+
mov eax, [eax + OFFSETOF__REGDISPLAY__ResumeSP] ;; eax <- resume SP value
338338

339339
@@: mov ecx, [ecx + OFFSETOF__ExInfo__m_pPrevExInfo] ;; ecx <- next ExInfo
340340
cmp ecx, 0

src/coreclr/nativeaot/Runtime/regdisplay.h

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct REGDISPLAY
4949

5050
#ifdef TARGET_X86
5151
TADDR PCTAddr;
52+
// SP for use by catch funclet when resuming execution
53+
uintptr_t ResumeSP;
5254

5355
inline unsigned long *GetEaxLocation() { return (unsigned long *)pRax; }
5456
inline unsigned long *GetEcxLocation() { return (unsigned long *)pRcx; }

src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp

+34-2
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
184184
#endif
185185
}
186186

187-
188187
CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
189188
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
190189
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
@@ -323,7 +322,7 @@ bool CoffNativeCodeManager::IsFilter(MethodInfo * pMethInfo)
323322
}
324323

325324
PTR_VOID CoffNativeCodeManager::GetFramePointer(MethodInfo * pMethInfo,
326-
REGDISPLAY * pRegisterSet)
325+
REGDISPLAY * pRegisterSet)
327326
{
328327
CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo;
329328

@@ -341,6 +340,39 @@ PTR_VOID CoffNativeCodeManager::GetFramePointer(MethodInfo * pMethInfo,
341340
return NULL;
342341
}
343342

343+
#ifdef TARGET_X86
344+
uintptr_t CoffNativeCodeManager::GetResumeSp(MethodInfo * pMethodInfo,
345+
REGDISPLAY * pRegisterSet)
346+
{
347+
PTR_uint8_t gcInfo;
348+
uint32_t codeOffset = GetCodeOffset(pMethodInfo, (PTR_VOID)pRegisterSet->IP, &gcInfo);
349+
350+
hdrInfo infoBuf;
351+
size_t infoSize = DecodeGCHdrInfo(GCInfoToken(gcInfo), codeOffset, &infoBuf);
352+
PTR_CBYTE table = gcInfo + infoSize;
353+
354+
_ASSERTE(infoBuf.epilogOffs == hdrInfo::NOT_IN_EPILOG && infoBuf.prologOffs == hdrInfo::NOT_IN_PROLOG);
355+
356+
bool isESPFrame = !infoBuf.ebpFrame && !infoBuf.doubleAlign;
357+
358+
CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
359+
if (pNativeMethodInfo->mainRuntimeFunction != pNativeMethodInfo->runtimeFunction)
360+
{
361+
// Treat funclet's frame as ESP frame
362+
isESPFrame = true;
363+
}
364+
365+
if (isESPFrame)
366+
{
367+
const uintptr_t curESP = pRegisterSet->SP;
368+
return curESP + GetPushedArgSize(&infoBuf, table, codeOffset);
369+
}
370+
371+
const uintptr_t curEBP = pRegisterSet->GetFP();
372+
return GetOutermostBaseFP(curEBP, &infoBuf);
373+
}
374+
#endif // TARGET_X86
375+
344376
uint32_t CoffNativeCodeManager::GetCodeOffset(MethodInfo* pMethodInfo, PTR_VOID address, /*out*/ PTR_uint8_t* gcInfo)
345377
{
346378
CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;

src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class CoffNativeCodeManager : public ICodeManager
6565
PTR_VOID GetFramePointer(MethodInfo * pMethodInfo,
6666
REGDISPLAY * pRegisterSet);
6767

68+
#ifdef TARGET_X86
69+
uintptr_t GetResumeSp(MethodInfo * pMethodInfo,
70+
REGDISPLAY * pRegisterSet);
71+
#endif
72+
6873
uint32_t GetCodeOffset(MethodInfo * pMethodInfo, PTR_VOID address, /*out*/ PTR_uint8_t* gcInfo);
6974

7075
bool IsSafePoint(PTR_VOID pvAddress);

src/coreclr/vm/gc_unwind_x86.inl

+2-2
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,6 @@ size_t GetLocallocSPOffset(hdrInfo * info)
366366
return position * sizeof(TADDR);
367367
}
368368

369-
#ifndef FEATURE_NATIVEAOT
370369
inline
371370
size_t GetParamTypeArgOffset(hdrInfo * info)
372371
{
@@ -451,6 +450,7 @@ TADDR GetOutermostBaseFP(TADDR ebp, hdrInfo * info)
451450
}
452451
}
453452

453+
#ifndef FEATURE_NATIVEAOT
454454
/*****************************************************************************
455455
*
456456
* For functions with handlers, checks if it is currently in a handler.
@@ -662,7 +662,7 @@ inline size_t GetSizeOfFrameHeaderForEnC(hdrInfo * info)
662662
return sizeof(TADDR) +
663663
GetEndShadowSPSlotsOffset(info, MAX_EnC_HANDLER_NESTING_LEVEL);
664664
}
665-
#endif
665+
#endif // FEATURE_NATIVEAOT
666666

667667
/*****************************************************************************/
668668
static

0 commit comments

Comments
 (0)