Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/coreclr/vm/amd64/getstate.S
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,20 @@ LEAF_ENTRY GetCurrentIP, _TEXT
ret

LEAF_END GetCurrentIP, _TEXT

// void SaveInterpCalleeSavedRegisters(CalleeSavedRegisters* regs, void* fpRegs)
// Saves callee-saved GP registers into the provided buffer.
// The register order matches the CalleeSavedRegisters struct:
// R12, R13, R14, R15, Rbx, Rbp
// No FP callee-saved registers on Unix AMD64, second arg is unused.
LEAF_ENTRY SaveInterpCalleeSavedRegisters, _TEXT

mov [rdi + 0], r12
mov [rdi + 8], r13
mov [rdi + 16], r14
mov [rdi + 24], r15
mov [rdi + 32], rbx
mov [rdi + 40], rbp
ret

LEAF_END SaveInterpCalleeSavedRegisters, _TEXT
30 changes: 30 additions & 0 deletions src/coreclr/vm/amd64/getstate.asm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@ LEAF_ENTRY GetCurrentIP, _TEXT
LEAF_END GetCurrentIP, _TEXT


; void SaveInterpCalleeSavedRegisters(CalleeSavedRegisters* regs, M128A* fpRegs)
; Saves callee-saved GP registers into the provided buffer.
; Also saves xmm6-xmm15 into fpRegs.
; The register order matches the Windows CalleeSavedRegisters struct:
; Rdi, Rsi, Rbx, Rbp, R12, R13, R14, R15
LEAF_ENTRY SaveInterpCalleeSavedRegisters, _TEXT

mov [rcx + 0], rdi
mov [rcx + 8], rsi
mov [rcx + 16], rbx
mov [rcx + 24], rbp
mov [rcx + 32], r12
mov [rcx + 40], r13
mov [rcx + 48], r14
mov [rcx + 56], r15
movdqa [rdx + 0], xmm6
movdqa [rdx + 16], xmm7
movdqa [rdx + 32], xmm8
movdqa [rdx + 48], xmm9
movdqa [rdx + 64], xmm10
movdqa [rdx + 80], xmm11
movdqa [rdx + 96], xmm12
movdqa [rdx + 112], xmm13
movdqa [rdx + 128], xmm14
movdqa [rdx + 144], xmm15
ret

LEAF_END SaveInterpCalleeSavedRegisters, _TEXT


LEAF_ENTRY GetRBP, _TEXT

mov rax, rbp
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/vm/arm/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
bx lr
LEAF_END GetCurrentSP, _TEXT

// void SaveInterpCalleeSavedRegisters(CalleeSavedRegisters* regs, UINT64* fpRegs)
// Saves callee-saved GP and FP registers into the provided buffers.
// GP order: r4, r5, r6, r7, r8, r9, r10, r11(fp), r14(lr)
// FP: d8-d15
LEAF_ENTRY SaveInterpCalleeSavedRegisters, _TEXT
stm r0, {r4-r11, lr}
vstm r1, {d8-d15}
bx lr
LEAF_END SaveInterpCalleeSavedRegisters, _TEXT

//-----------------------------------------------------------------------------
// This helper routine enregisters the appropriate arguments and makes the
// actual call.
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/vm/arm64/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ LEAF_ENTRY GetCurrentSP, _TEXT
ret lr
LEAF_END GetCurrentSP, _TEXT

// void SaveInterpCalleeSavedRegisters(CalleeSavedRegisters* regs, UINT64* fpRegs)
// Saves callee-saved GP and FP registers into the provided buffers.
LEAF_ENTRY SaveInterpCalleeSavedRegisters, _TEXT
stp x29, x30, [x0, #0]
stp x19, x20, [x0, #16]
stp x21, x22, [x0, #32]
stp x23, x24, [x0, #48]
stp x25, x26, [x0, #64]
stp x27, x28, [x0, #80]
stp d8, d9, [x1, #0]
stp d10, d11, [x1, #16]
stp d12, d13, [x1, #32]
stp d14, d15, [x1, #48]
ret lr
LEAF_END SaveInterpCalleeSavedRegisters, _TEXT

// DWORD64 __stdcall GetDataCacheZeroIDReg(void)
LEAF_ENTRY GetDataCacheZeroIDReg, _TEXT
mrs x0, dczid_el0
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/vm/arm64/asmhelpers.asm
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@
ret lr
LEAF_END

;; void SaveInterpCalleeSavedRegisters(CalleeSavedRegisters* regs, UINT64* fpRegs)
LEAF_ENTRY SaveInterpCalleeSavedRegisters
stp x29, x30, [x0, #0]
stp x19, x20, [x0, #16]
stp x21, x22, [x0, #32]
stp x23, x24, [x0, #48]
stp x25, x26, [x0, #64]
stp x27, x28, [x0, #80]
stp d8, d9, [x1, #0]
stp d10, d11, [x1, #16]
stp d12, d13, [x1, #32]
stp d14, d15, [x1, #48]
ret lr
LEAF_END

;; DWORD64 __stdcall GetDataCacheZeroIDReg(void);
LEAF_ENTRY GetDataCacheZeroIDReg
mrs x0, dczid_el0
Expand Down
176 changes: 159 additions & 17 deletions src/coreclr/vm/eetwain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1912,35 +1912,177 @@ void InterpreterCodeManager::ResumeAfterCatch(CONTEXT *pContext, size_t targetSS
throw ResumeAfterCatchException(resumeSP, resumeIP);
#else
Thread *pThread = GetThread();
Comment thread
am11 marked this conversation as resolved.
InterpreterFrame * pInterpreterFrame = (InterpreterFrame*)pThread->GetFrame();
InterpreterFrame *pInterpreterFrame = (InterpreterFrame*)pThread->GetFrame();

ClrCaptureContext(pContext);
#if defined(TARGET_LOONGARCH64)
CONTEXT preservedContext = *pContext;
#endif

// Build a context from the callee-saved registers, SP, and IP saved from
// InterpExecMethod's try block. This avoids OS-based unwinding (PAL_VirtualUnwind /
// VirtualUnwindCallFrame). The saved IP is inside the try block, which is required
// for the C++ exception handler to route ThrowResumeAfterCatchException to the catch
// clause in InterpExecMethod.
memset(pContext, 0, sizeof(CONTEXT));
pContext->ContextFlags = CONTEXT_FULL;
SetSP(pContext, pInterpreterFrame->GetInterpExecMethodSP());
SetIP(pContext, pInterpreterFrame->GetInterpExecMethodIP());

TADDR targetSP = pInterpreterFrame->GetInterpExecMethodSP();
CalleeSavedRegisters* pRegs = pInterpreterFrame->GetCalleeSavedRegisters();

// We are resuming in interpreter frame. So we need to skip all native, JIT and AOT generated frames until we reach
// the resumeSP
do
{
if (ExecutionManager::IsManagedCode(GetIP(pContext)))
#if defined(TARGET_ARM64)
pContext->Fp = pRegs->x29;
pContext->Lr = pRegs->x30;
pContext->X19 = pRegs->x19;
pContext->X20 = pRegs->x20;
pContext->X21 = pRegs->x21;
pContext->X22 = pRegs->x22;
pContext->X23 = pRegs->x23;
pContext->X24 = pRegs->x24;
pContext->X25 = pRegs->x25;
pContext->X26 = pRegs->x26;
pContext->X27 = pRegs->x27;
pContext->X28 = pRegs->x28;
// Restore FP callee-saved d8-d15 into CONTEXT V[8]-V[15]
UINT64* fpRegs = (UINT64*)pInterpreterFrame->GetFPCalleeSavedRegisters();
for (int i = 0; i < 8; i++)
{
pContext->V[8 + i].Low = fpRegs[i];
pContext->V[8 + i].High = 0;
}
#elif defined(TARGET_ARM)
pContext->R4 = pRegs->r4;
pContext->R5 = pRegs->r5;
pContext->R6 = pRegs->r6;
pContext->R7 = pRegs->r7;
pContext->R8 = pRegs->r8;
pContext->R9 = pRegs->r9;
pContext->R10 = pRegs->r10;
pContext->R11 = pRegs->r11;
pContext->Lr = pRegs->r14;
// Restore FP callee-saved d8-d15 into CONTEXT D[8]-D[15]
UINT64* fpRegs = (UINT64*)pInterpreterFrame->GetFPCalleeSavedRegisters();
for (int i = 0; i < 8; i++)
{
pContext->D[8 + i] = fpRegs[i];
}
#elif defined(TARGET_AMD64)
#ifdef TARGET_UNIX
pContext->R12 = pRegs->R12;
pContext->R13 = pRegs->R13;
pContext->R14 = pRegs->R14;
pContext->R15 = pRegs->R15;
pContext->Rbx = pRegs->Rbx;
pContext->Rbp = pRegs->Rbp;
#else // TARGET_WINDOWS
pContext->Rdi = pRegs->Rdi;
pContext->Rsi = pRegs->Rsi;
pContext->Rbx = pRegs->Rbx;
pContext->Rbp = pRegs->Rbp;
pContext->R12 = pRegs->R12;
pContext->R13 = pRegs->R13;
pContext->R14 = pRegs->R14;
pContext->R15 = pRegs->R15;
// Restore FP callee-saved xmm6-xmm15
M128A* fpRegs = (M128A*)pInterpreterFrame->GetFPCalleeSavedRegisters();
pContext->Xmm6 = fpRegs[0];
pContext->Xmm7 = fpRegs[1];
pContext->Xmm8 = fpRegs[2];
pContext->Xmm9 = fpRegs[3];
pContext->Xmm10 = fpRegs[4];
pContext->Xmm11 = fpRegs[5];
pContext->Xmm12 = fpRegs[6];
pContext->Xmm13 = fpRegs[7];
pContext->Xmm14 = fpRegs[8];
pContext->Xmm15 = fpRegs[9];

// ClrRestoreNonvolatileContextWorker uses fxrstor on this CONTEXT. Keep the
// FP control state at runtime defaults so resume-after-catch does not enable
// spurious FP traps (e.g. STATUS_FLOAT_INEXACT_RESULT).
pContext->FltSave.ControlWord = 0x27F;
pContext->FltSave.MxCsr = 0x1F80;
pContext->FltSave.MxCsr_Mask = 0x1FFF;
pContext->MxCsr = 0x1F80;
#endif // TARGET_UNIX
#elif defined(TARGET_X86)
pContext->Edi = pRegs->Edi;
pContext->Esi = pRegs->Esi;
pContext->Ebx = pRegs->Ebx;
pContext->Ebp = pRegs->Ebp;
#elif defined(TARGET_LOONGARCH64)
DWORD loongArchContextFlags = preservedContext.ContextFlags & (CONTEXT_LSX | CONTEXT_LASX);
pContext->ContextFlags |= loongArchContextFlags;

pContext->Fp = pRegs->fp;
pContext->Ra = pRegs->ra;
pContext->S0 = pRegs->s0;
pContext->S1 = pRegs->s1;
pContext->S2 = pRegs->s2;
pContext->S3 = pRegs->s3;
pContext->S4 = pRegs->s4;
pContext->S5 = pRegs->s5;
pContext->S6 = pRegs->s6;
pContext->S7 = pRegs->s7;
pContext->S8 = pRegs->s8;

// Preserve floating-point control state and vector mode from the incoming context.
pContext->Fcc = preservedContext.Fcc;
pContext->Fcsr = preservedContext.Fcsr;

// Restore FP callee-saved f24-f31.
// SaveInterpCalleeSavedRegisters captures only the low 64-bit lane, so for LSX/LASX
// we preserve upper lanes from the incoming context and overwrite lane 0.
UINT64* fpRegs = (UINT64*)pInterpreterFrame->GetFPCalleeSavedRegisters();
for (int i = 0; i < 8; i++)
{
int regIndex = 24 + i;
if ((loongArchContextFlags & CONTEXT_LASX) != 0)
{
// JIT / AOT generated managed code
Thread::VirtualUnwindCallFrame(pContext);
// LASX stores each FP register in 4x UINT64 slots.
memcpy(&pContext->F[regIndex * 4], &preservedContext.F[regIndex * 4], sizeof(UINT64) * 4);
pContext->F[regIndex * 4] = fpRegs[i];
}
else if ((loongArchContextFlags & CONTEXT_LSX) != 0)
{
// LSX stores each FP register in 2x UINT64 slots.
memcpy(&pContext->F[regIndex * 2], &preservedContext.F[regIndex * 2], sizeof(UINT64) * 2);
pContext->F[regIndex * 2] = fpRegs[i];
}
else
{
#ifdef TARGET_UNIX
PAL_VirtualUnwind(pContext, NULL);
#else
Thread::VirtualUnwindCallFrame(pContext);
#endif
// Scalar FPU context stores each FP register in a single UINT64 slot.
pContext->F[regIndex] = fpRegs[i];
}
}
while (GetSP(pContext) != targetSP);
#elif defined(TARGET_RISCV64)
pContext->Fp = pRegs->fp;
pContext->Ra = pRegs->ra;
pContext->S1 = pRegs->s1;
pContext->S2 = pRegs->s2;
pContext->S3 = pRegs->s3;
pContext->S4 = pRegs->s4;
pContext->S5 = pRegs->s5;
pContext->S6 = pRegs->s6;
pContext->S7 = pRegs->s7;
pContext->S8 = pRegs->s8;
pContext->S9 = pRegs->s9;
pContext->S10 = pRegs->s10;
pContext->S11 = pRegs->s11;
pContext->Tp = pRegs->tp;
pContext->Gp = pRegs->gp;
// Restore FP callee-saved fs0-fs11 (f8-f9, f18-f27)
UINT64* fpRegs = (UINT64*)pInterpreterFrame->GetFPCalleeSavedRegisters();
pContext->F[8] = fpRegs[0]; // fs0
pContext->F[9] = fpRegs[1]; // fs1
for (int i = 0; i < 10; i++)
{
pContext->F[18 + i] = fpRegs[2 + i]; // fs2-fs11
}
#endif

#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
targetSSP = pInterpreterFrame->GetInterpExecMethodSSP();
#endif
#endif
ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, targetSSP, resumeSP, resumeIP);
#endif // TARGET_WASM
}
Expand Down
48 changes: 48 additions & 0 deletions src/coreclr/vm/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -2482,6 +2482,9 @@ class InterpreterFrame : public FramedMethodFrame
m_isFaulting(false)
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
, m_SSP(0)
#endif
#ifndef TARGET_WASM
, m_nativeIP(0)
#endif
, m_continuation(NULL)
{
Expand Down Expand Up @@ -2552,6 +2555,32 @@ class InterpreterFrame : public FramedMethodFrame
LIMITED_METHOD_CONTRACT;
return m_SP;
}

void SetInterpExecMethodIP(TADDR ip)
{
LIMITED_METHOD_CONTRACT;
m_nativeIP = ip;
}

TADDR GetInterpExecMethodIP()
{
LIMITED_METHOD_CONTRACT;
return m_nativeIP;
}

CalleeSavedRegisters* GetCalleeSavedRegisters()
{
LIMITED_METHOD_CONTRACT;
return &m_calleeSavedRegisters;
}

#if !defined(UNIX_AMD64_ABI) && !defined(UNIX_X86_ABI)
void* GetFPCalleeSavedRegisters()
{
LIMITED_METHOD_CONTRACT;
return &m_fpCalleeSavedRegisters;
}
#endif
#endif // TARGET_WASM

void SetIsFaulting(bool isFaulting)
Expand Down Expand Up @@ -2594,6 +2623,25 @@ class InterpreterFrame : public FramedMethodFrame
#endif // HOST_AMD64 && HOST_WINDOWS
#ifndef TARGET_WASM
TADDR m_SP;
// Saved IP inside InterpExecMethod's try block for deterministic resume-after-catch.
// The C++ exception handler uses this to route ThrowResumeAfterCatchException to the
// correct catch clause without requiring OS-based unwinding.
TADDR m_nativeIP;
// Lightweight buffer for callee-saved registers captured from within InterpExecMethod.
// Filled by SaveInterpCalleeSavedRegisters (asm helper) on each try-block entry.
// Used to build the CONTEXT in ResumeAfterCatch without OS-based unwinding.
CalleeSavedRegisters m_calleeSavedRegisters;
#if defined(TARGET_ARM64)
UINT64 m_fpCalleeSavedRegisters[8]; // d8-d15
#elif defined(TARGET_ARM)
UINT64 m_fpCalleeSavedRegisters[8]; // d8-d15
#elif defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
M128A m_fpCalleeSavedRegisters[10]; // xmm6-xmm15
#elif defined(TARGET_LOONGARCH64)
UINT64 m_fpCalleeSavedRegisters[8]; // f24-f31
#elif defined(TARGET_RISCV64)
UINT64 m_fpCalleeSavedRegisters[12]; // fs0-fs11
#endif
#endif // TARGET_WASM
PTR_Object m_continuation;
};
Expand Down
Loading
Loading