From bf780e608423d72f4c6b78f45a15d2b160368550 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 28 Jan 2026 06:06:08 -0800 Subject: [PATCH] [release/11.0-preview1] Revert "Bring a few jithelpers to new unwind plan (#123307)" This reverts commit 18acc2d8f708b72420de1410c954d2984312faa2. --- src/coreclr/pal/inc/unixasmmacrosarm64.inc | 23 -- .../pal/inc/unixasmmacrosloongarch64.inc | 25 -- src/coreclr/pal/inc/unixasmmacrosriscv64.inc | 29 -- src/coreclr/vm/amd64/AsmHelpers.asm | 9 +- src/coreclr/vm/amd64/AsmMacros.inc | 40 +- src/coreclr/vm/arm64/asmconstants.h | 26 -- src/coreclr/vm/arm64/asmhelpers.S | 63 +-- src/coreclr/vm/arm64/asmhelpers.asm | 62 +-- src/coreclr/vm/arm64/asmmacros.h | 23 -- src/coreclr/vm/excep.cpp | 371 ++++-------------- src/coreclr/vm/exceptmacros.h | 4 + src/coreclr/vm/frames.h | 5 - src/coreclr/vm/jithelpers.cpp | 108 +++-- src/coreclr/vm/loongarch64/asmconstants.h | 30 +- src/coreclr/vm/loongarch64/asmhelpers.S | 79 +--- src/coreclr/vm/riscv64/asmconstants.h | 26 -- src/coreclr/vm/riscv64/asmhelpers.S | 86 +--- src/coreclr/vm/threads.cpp | 4 +- 18 files changed, 165 insertions(+), 848 deletions(-) diff --git a/src/coreclr/pal/inc/unixasmmacrosarm64.inc b/src/coreclr/pal/inc/unixasmmacrosarm64.inc index 258d3e8c4c28c5..2d27459372b561 100644 --- a/src/coreclr/pal/inc/unixasmmacrosarm64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosarm64.inc @@ -468,29 +468,6 @@ C_FUNC(\Name\()_End): add \target, sp, #192 .endm -// Epilog for PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS - restores FP callee-saved and returns -.macro POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN - - // Restore FP callee-saved registers (d8-d15) from sp+0 - ldp d8, d9, [sp, #0] - ldp d10, d11, [sp, #16] - ldp d12, d13, [sp, #32] - ldp d14, d15, [sp, #48] - - // Deallocate space for FloatArgumentRegisters + FP callee-saved - EPILOG_STACK_FREE 192 - - // Restore callee-saved registers - EPILOG_RESTORE_REG_PAIR x27, x28, 80 - EPILOG_RESTORE_REG_PAIR x25, x26, 64 - EPILOG_RESTORE_REG_PAIR x23, x24, 48 - EPILOG_RESTORE_REG_PAIR x21, x22, 32 - EPILOG_RESTORE_REG_PAIR x19, x20, 16 - - EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 176 - ret -.endm - // ------------------------------------------------------------------ // Macro to generate Redirection Stubs // diff --git a/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc b/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc index b33d7377c327dc..92d701598f933e 100644 --- a/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc @@ -443,31 +443,6 @@ C_FUNC(\Name\()_End): addi.d \target, $sp, 128 .endm -// ------------------------------------------------------------------ -// Epilog macro for PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS that restores all registers and returns. -// Use this for JIT helpers that may return normally (unlike IL_Throw which never returns). -.macro POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN - // Restore FP callee-saved registers (f24-f31) from offset 0 - fld.d $f24, $sp, 0 - fld.d $f25, $sp, 8 - fld.d $f26, $sp, 16 - fld.d $f27, $sp, 24 - fld.d $f28, $sp, 32 - fld.d $f29, $sp, 40 - fld.d $f30, $sp, 48 - fld.d $f31, $sp, 56 - - // Restore callee-saved registers from offset 128 - RESTORE_CALLEESAVED_REGISTERS $sp, 128 - - // Restore fp, ra - EPILOG_RESTORE_REG_PAIR 22, 1, 128 - - // Deallocate stack and return - EPILOG_STACK_FREE 288 - EPILOG_RETURN -.endm - // ------------------------------------------------------------------ // Macro to generate Redirection Stubs // diff --git a/src/coreclr/pal/inc/unixasmmacrosriscv64.inc b/src/coreclr/pal/inc/unixasmmacrosriscv64.inc index 5e0f6d27261f72..d244756c304eb9 100644 --- a/src/coreclr/pal/inc/unixasmmacrosriscv64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosriscv64.inc @@ -394,35 +394,6 @@ C_FUNC(\Name): addi \target, sp, 160 .endm -// ------------------------------------------------------------------ -// Epilog macro for PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS that restores all registers and returns. -// Use this for JIT helpers that may return normally (unlike IL_Throw which never returns). -.macro POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN - // Restore FP callee-saved registers (fs0-fs11 = f8,f9,f18-f27) from offset 0 - fld fs0, 0(sp) // f8 - fld fs1, 8(sp) // f9 - fld fs2, 16(sp) // f18 - fld fs3, 24(sp) // f19 - fld fs4, 32(sp) // f20 - fld fs5, 40(sp) // f21 - fld fs6, 48(sp) // f22 - fld fs7, 56(sp) // f23 - fld fs8, 64(sp) // f24 - fld fs9, 72(sp) // f25 - fld fs10, 80(sp) // f26 - fld fs11, 88(sp) // f27 - - // Restore callee-saved registers from offset 160 - RESTORE_CALLEESAVED_REGISTERS sp, 160 - - // Restore fp, ra - EPILOG_RESTORE_REG_PAIR fp, ra, 160 - - // Deallocate stack and return - EPILOG_STACK_FREE 352 - EPILOG_RETURN -.endm - // ------------------------------------------------------------------ // Macro to generate Redirection Stubs // diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index a2468353221f06..7805d686dad781 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -402,15 +402,12 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT extern JIT_PatchpointWorkerWorkerWithPolicy:proc NESTED_ENTRY JIT_Patchpoint, _TEXT - ; Use PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS to save all registers including FP callee-saved - ; This allows us to build a complete CONTEXT from TransitionBlock without RtlCaptureContext - PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rcx + PROLOG_WITH_TRANSITION_BLOCK - ; RCX contains pointer to TransitionBlock + lea rcx, [rsp + __PWTB_TransitionBlock] ; TransitionBlock * call JIT_PatchpointWorkerWorkerWithPolicy - ; If we return, restore all registers and return to caller - POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN + EPILOG_WITH_TRANSITION_BLOCK_RETURN NESTED_END JIT_Patchpoint, _TEXT ; first arg register holds iloffset, which needs to be moved to the second register, and the first register filled with NULL diff --git a/src/coreclr/vm/amd64/AsmMacros.inc b/src/coreclr/vm/amd64/AsmMacros.inc index 3baf14dcfaef4a..07531371d6627d 100644 --- a/src/coreclr/vm/amd64/AsmMacros.inc +++ b/src/coreclr/vm/amd64/AsmMacros.inc @@ -503,14 +503,13 @@ PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS macro target PUSH_CALLEE_SAVED_REGISTERS - ; Allocate space for: shadow (32) + FP callee-saved (160) + float args (64) + padding (8) = 264 bytes - ; This makes RSP 16-byte aligned (8 + 64 + 264 = 336, and original RSP - 336 is 16-byte aligned) - alloc_stack 264 + ; Allocate space for: shadow for call (32) + FP callee-saved (160) + float args (64) + arg regs (32) + padding (8) = 296 bytes + ; Shadow space at offset 0 is reserved for the call to IL_Throw_Impl etc. + ; This makes RSP 16-byte aligned (8 + 64 + 296 = 368, and original RSP - 368 is 16-byte aligned) + alloc_stack 296 - ; Save argument registers into caller's shadow space - ; TransitionBlock is at rsp + 264, sizeof(TransitionBlock) = 72 - ; So argument registers go at rsp + 264 + 72 = rsp + 336 - SAVE_ARGUMENT_REGISTERS 336 + ; Save argument registers at offset 256 (32 + 160 + 64) + SAVE_ARGUMENT_REGISTERS 256 ; Save float argument registers at offset 192 (32 + 160) SAVE_FLOAT_ARGUMENT_REGISTERS 192 @@ -531,31 +530,8 @@ PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS macro target END_PROLOGUE - ; TransitionBlock pointer points to CalleeSavedRegisters at rsp + 264 - lea target, [rsp + 264] - - endm - -; Epilog for PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS - restores XMM callee-saved and returns -POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN macro - - ; Restore FP callee-saved registers (xmm6-xmm15) from offset 32 (after shadow space) - movdqa xmm6, [rsp + 20h] - movdqa xmm7, [rsp + 30h] - movdqa xmm8, [rsp + 40h] - movdqa xmm9, [rsp + 50h] - movdqa xmm10, [rsp + 60h] - movdqa xmm11, [rsp + 70h] - movdqa xmm12, [rsp + 80h] - movdqa xmm13, [rsp + 90h] - movdqa xmm14, [rsp + 0A0h] - movdqa xmm15, [rsp + 0B0h] - - ; Deallocate stack space (264 bytes) - add rsp, 264 - - POP_CALLEE_SAVED_REGISTERS - ret + ; TransitionBlock pointer points to CalleeSavedRegisters at rsp + 296 + lea target, [rsp + 296] endm diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index 1575d54928342d..9f78786c3da059 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -101,38 +101,12 @@ ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); #endif ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); -#define OFFSETOF__CONTEXT__ContextFlags 0x0 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__ContextFlags == offsetof(T_CONTEXT, ContextFlags)); - -// CONTEXT_INTEGER_BIT is bit 1 in ContextFlags (from pal.h CONTEXT_INTEGER definition) -#define CONTEXT_INTEGER_BIT 1 - -#define OFFSETOF__CONTEXT__X0 0x08 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__X0 == offsetof(T_CONTEXT, X0)); - #define OFFSETOF__CONTEXT__X19 0xA0 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__X19 == offsetof(T_CONTEXT, X19)); #define OFFSETOF__CONTEXT__Fp 0xF0 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Fp == offsetof(T_CONTEXT, Fp)); -#define OFFSETOF__CONTEXT__Lr 0xF8 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Lr == offsetof(T_CONTEXT, Lr)); - -#define OFFSETOF__CONTEXT__Sp 0x100 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Sp == offsetof(T_CONTEXT, Sp)); - -#define OFFSETOF__CONTEXT__Pc 0x108 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Pc == offsetof(T_CONTEXT, Pc)); - -// Floating point registers V[0..31] start at 0x110, each is 16 bytes (NEON128) -// Non-volatile FP registers are V8-V15 (d8-d15) -#define OFFSETOF__CONTEXT__V0 0x110 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__V0 == offsetof(T_CONTEXT, V)); - -// CONTEXT_FLOATING_POINT_BIT is bit 2 in ContextFlags -#define CONTEXT_FLOATING_POINT_BIT 2 - #define OFFSETOF__DynamicHelperStubArgs__Constant1 0x0 ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicHelperStubArgs__Constant1 == offsetof(DynamicHelperStubArgs, Constant1)); diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 359bb876a7dc60..6e2c5bf3adbfe0 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -586,11 +586,9 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK - // x0 = pointer to TransitionBlock - add x0, sp, #__PWTB_TransitionBlock + add x0, sp, #__PWTB_TransitionBlock // TransitionBlock * bl C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) - // If we return, restore all registers and return to caller EPILOG_WITH_TRANSITION_BLOCK_RETURN NESTED_END JIT_Patchpoint, _TEXT @@ -2899,62 +2897,3 @@ NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler brk #0 NESTED_END IL_Rethrow, _TEXT -// ------------------------------------------------------------------ -// ClrRestoreNonvolatileContextWorker -// -// Restores registers based on ContextFlags and jumps to the target IP. -// When CONTEXT_INTEGER is set, restores ALL integer registers (x0-x28) -// because exception handling needs x0 (exception object). -// When CONTEXT_FLOATING_POINT is set, restores non-volatile FP regs (d8-d15). -// -// Arguments: -// x0 - pointer to CONTEXT structure -// -// Does not return - jumps directly to the Pc stored in the CONTEXT -// ------------------------------------------------------------------ -LEAF_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT - - // Save context pointer in x16 since we'll overwrite x0 - mov x16, x0 - - // Check ContextFlags to see if we should restore floating point registers - ldr w17, [x16, #OFFSETOF__CONTEXT__ContextFlags] - tbz w17, #CONTEXT_FLOATING_POINT_BIT, LOCAL_LABEL(SkipFloatingPointRestore) - - // Restore non-volatile FP registers d8-d15 (lower 64 bits of v8-v15) - // V8 is at OFFSETOF__CONTEXT__V0 + 8*16 = 0x110 + 0x80 = 0x190 - ldp q8, q9, [x16, #(OFFSETOF__CONTEXT__V0 + 8*16)] - ldp q10, q11, [x16, #(OFFSETOF__CONTEXT__V0 + 10*16)] - ldp q12, q13, [x16, #(OFFSETOF__CONTEXT__V0 + 12*16)] - ldp q14, q15, [x16, #(OFFSETOF__CONTEXT__V0 + 14*16)] - -LOCAL_LABEL(SkipFloatingPointRestore): - // Check ContextFlags to see if we should restore integer registers - tbz w17, #CONTEXT_INTEGER_BIT, LOCAL_LABEL(SkipIntegerRestore) - - // Restore argument registers x0-x7 (exception handling needs x0 for exception object) - // and non-volatile registers x19-x28 - ldp x0, x1, [x16, #OFFSETOF__CONTEXT__X0] - ldp x2, x3, [x16, #(OFFSETOF__CONTEXT__X0 + 16)] - ldp x4, x5, [x16, #(OFFSETOF__CONTEXT__X0 + 32)] - ldp x6, x7, [x16, #(OFFSETOF__CONTEXT__X0 + 48)] - // Skip x8-x18: x8-x15 are scratch, x16-x17 we're using, x18 is platform reserved - ldp x19, x20, [x16, #OFFSETOF__CONTEXT__X19] - ldp x21, x22, [x16, #(OFFSETOF__CONTEXT__X19 + 16)] - ldp x23, x24, [x16, #(OFFSETOF__CONTEXT__X19 + 32)] - ldp x25, x26, [x16, #(OFFSETOF__CONTEXT__X19 + 48)] - ldp x27, x28, [x16, #(OFFSETOF__CONTEXT__X19 + 64)] - -LOCAL_LABEL(SkipIntegerRestore): - // Restore fp (x29) and lr (x30) - ldp fp, lr, [x16, #OFFSETOF__CONTEXT__Fp] - - // Load Sp and Pc into scratch registers (after all other loads) - ldr x17, [x16, #OFFSETOF__CONTEXT__Sp] - ldr x16, [x16, #OFFSETOF__CONTEXT__Pc] - - // Set sp and jump to target - mov sp, x17 - br x16 - -LEAF_END ClrRestoreNonvolatileContextWorker, _TEXT diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 13a93f9340b62e..7d051454457471 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -1013,11 +1013,9 @@ __HelperNakedFuncName SETS "$helper":CC:"Naked" NESTED_ENTRY JIT_Patchpoint PROLOG_WITH_TRANSITION_BLOCK - ; x0 = pointer to TransitionBlock - add x0, sp, #__PWTB_TransitionBlock + add x0, sp, #__PWTB_TransitionBlock ; TransitionBlock * bl JIT_PatchpointWorkerWorkerWithPolicy - ; If we return, restore all registers and return to caller EPILOG_WITH_TRANSITION_BLOCK_RETURN NESTED_END @@ -3185,63 +3183,5 @@ CopyLoop brk #0 NESTED_END IL_Rethrow -; ------------------------------------------------------------------ -; ClrRestoreNonvolatileContextWorker -; -; Restores registers based on ContextFlags and jumps to PC. -; When CONTEXT_INTEGER is set, restores ALL integer registers (x0-x28) -; because exception handling needs x0 (exception object). -; When CONTEXT_FLOATING_POINT is set, restores non-volatile FP regs (d8-d15). -; -; x0 - pointer to CONTEXT structure -; x1 - unused (SSP, not used on ARM64) -; ------------------------------------------------------------------ - LEAF_ENTRY ClrRestoreNonvolatileContextWorker - - ; Save CONTEXT pointer in x16 before we potentially clobber x0 - mov x16, x0 - - ; Check if CONTEXT_FLOATING_POINT bit is set (bit 2) - ldr w17, [x16, #OFFSETOF__CONTEXT__ContextFlags] - tbz w17, #2, SkipFloatingPointRestore - - ; Restore non-volatile FP registers d8-d15 (full q8-q15) - ; V8 is at OFFSETOF__CONTEXT__V0 + 8*16 = 0x110 + 0x80 = 0x190 - ldp q8, q9, [x16, #(OFFSETOF__CONTEXT__V0 + 128)] - ldp q10, q11, [x16, #(OFFSETOF__CONTEXT__V0 + 160)] - ldp q12, q13, [x16, #(OFFSETOF__CONTEXT__V0 + 192)] - ldp q14, q15, [x16, #(OFFSETOF__CONTEXT__V0 + 224)] - -SkipFloatingPointRestore - ; Check if CONTEXT_INTEGER bit is set - tbz w17, #1, SkipIntegerRestore ; CONTEXT_INTEGER_BIT = 1 - - ; Restore argument registers x0-x7 (exception handling needs x0 for exception object) - ; and non-volatile registers x19-x28 - ldp x0, x1, [x16, #OFFSETOF__CONTEXT__X0] - ldp x2, x3, [x16, #(OFFSETOF__CONTEXT__X0 + 16)] - ldp x4, x5, [x16, #(OFFSETOF__CONTEXT__X0 + 32)] - ldp x6, x7, [x16, #(OFFSETOF__CONTEXT__X0 + 48)] - ; Skip x8-x18: x8-x15 are scratch, x16-x17 we're using, x18 is platform reserved - ldp x19, x20, [x16, #OFFSETOF__CONTEXT__X19] - ldp x21, x22, [x16, #(OFFSETOF__CONTEXT__X19 + 16)] - ldp x23, x24, [x16, #(OFFSETOF__CONTEXT__X19 + 32)] - ldp x25, x26, [x16, #(OFFSETOF__CONTEXT__X19 + 48)] - ldp x27, x28, [x16, #(OFFSETOF__CONTEXT__X19 + 64)] - -SkipIntegerRestore - ; Restore fp and lr - ldp fp, lr, [x16, #OFFSETOF__CONTEXT__Fp] - - ; Load Sp and Pc (x16 will be overwritten) - ldr x17, [x16, #OFFSETOF__CONTEXT__Pc] - ldr x16, [x16, #OFFSETOF__CONTEXT__Sp] - - ; Set sp and jump - mov sp, x16 - br x17 - - LEAF_END ClrRestoreNonvolatileContextWorker - ; Must be at very end of file END diff --git a/src/coreclr/vm/arm64/asmmacros.h b/src/coreclr/vm/arm64/asmmacros.h index fc144fb9842f94..93778d775f87c9 100644 --- a/src/coreclr/vm/arm64/asmmacros.h +++ b/src/coreclr/vm/arm64/asmmacros.h @@ -253,29 +253,6 @@ OFFSETOF__ee_alloc_context EQU OFFSETOF__RuntimeThreadLocals__ee_alloc_context add $Target, sp, #192 MEND -; Epilog for PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS - restores FP callee-saved and returns - MACRO - POP_COOP_PINVOKE_FRAME_WITH_FLOATS_RETURN - - ; Restore FP callee-saved registers (d8-d15) from sp+0 - ldp d8, d9, [sp, #0] - ldp d10, d11, [sp, #16] - ldp d12, d13, [sp, #32] - ldp d14, d15, [sp, #48] - - ; Deallocate space for FloatArgumentRegisters + FP callee-saved - EPILOG_STACK_FREE 192 - - ; Restore callee-saved registers - EPILOG_RESTORE_REG_PAIR x27, x28, #80 - EPILOG_RESTORE_REG_PAIR x25, x26, #64 - EPILOG_RESTORE_REG_PAIR x23, x24, #48 - EPILOG_RESTORE_REG_PAIR x21, x22, #32 - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - - EPILOG_RESTORE_REG_PAIR_RET fp, lr, #176! - MEND - #define GC_ALLOC_FINALIZE 1 ;----------------------------------------------------------------------------- diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a324e4d83041e8..8b670c6ea4911d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -5463,9 +5463,8 @@ static uintptr_t writeBarrierAVLocations[] = }; // Check if the passed in instruction pointer is in one of the -// write barrier helper functions. These are leaf functions that do not -// set up a frame, so we can unwind them with a simple LR/RA extraction. -static bool IsIPInWriteBarrierHelper(PCODE uControlPc) +// JIT helper functions. +bool IsIPInMarkedJitHelper(PCODE uControlPc) { LIMITED_METHOD_CONTRACT; @@ -5483,138 +5482,29 @@ static bool IsIPInWriteBarrierHelper(PCODE uControlPc) return true; } -#define CHECK_WRITE_BARRIER_RANGE(name) \ +#define CHECK_RANGE(name) \ if (GetEEFuncEntryPoint(name) <= uControlPc && uControlPc < GetEEFuncEntryPoint(name##_End)) return true; #ifndef TARGET_X86 - CHECK_WRITE_BARRIER_RANGE(JIT_WriteBarrier) - CHECK_WRITE_BARRIER_RANGE(JIT_CheckedWriteBarrier) - CHECK_WRITE_BARRIER_RANGE(JIT_ByRefWriteBarrier) + CHECK_RANGE(JIT_WriteBarrier) + CHECK_RANGE(JIT_CheckedWriteBarrier) + CHECK_RANGE(JIT_ByRefWriteBarrier) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) + CHECK_RANGE(JIT_StackProbe) +#endif // !TARGET_ARM64 && !TARGET_LOONGARCH64 && !TARGET_RISCV64 #else - CHECK_WRITE_BARRIER_RANGE(JIT_WriteBarrierGroup) - CHECK_WRITE_BARRIER_RANGE(JIT_PatchedWriteBarrierGroup) + CHECK_RANGE(JIT_WriteBarrierGroup) + CHECK_RANGE(JIT_PatchedWriteBarrierGroup) #endif // TARGET_X86 #if defined(TARGET_AMD64) && defined(_DEBUG) - CHECK_WRITE_BARRIER_RANGE(JIT_WriteBarrier_Debug) + CHECK_RANGE(JIT_WriteBarrier_Debug) #endif - -#undef CHECK_WRITE_BARRIER_RANGE -#endif // !FEATURE_PORTABLE_HELPERS - - return false; -} - -// Check if the passed in instruction pointer is in JIT_StackProbe. -// JIT_StackProbe exists on AMD64 and ARM only. -static bool IsIPInJITStackProbe(PCODE uControlPc) -{ - LIMITED_METHOD_CONTRACT; - -#ifndef FEATURE_PORTABLE_HELPERS -#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) - if (GetEEFuncEntryPoint(JIT_StackProbe) <= uControlPc && uControlPc < GetEEFuncEntryPoint(JIT_StackProbe_End)) - return true; -#endif // !TARGET_X86 && !TARGET_ARM64 && !TARGET_LOONGARCH64 && !TARGET_RISCV64 #endif // !FEATURE_PORTABLE_HELPERS return false; } -// Check if the passed in instruction pointer is in one of the -// JIT helper functions. -bool IsIPInMarkedJitHelper(PCODE uControlPc) -{ - LIMITED_METHOD_CONTRACT; - - if (IsIPInWriteBarrierHelper(uControlPc)) - return true; - - if (IsIPInJITStackProbe(uControlPc)) - return true; - - return false; -} - -// Unwind JIT_StackProbe to its caller. JIT_StackProbe has different frame layouts -// depending on the platform: -// - AMD64 Windows: leaf function (no frame), return address at [RSP] -// - AMD64 Unix: RBP frame (push rbp; mov rbp, rsp), return address at [RBP+8] -// - ARM: R7 frame (push {r7}; mov r7, sp), return address in LR (already saved by caller) -static void UnwindJITStackProbeToCaller(CONTEXT* pContext) -{ - LIMITED_METHOD_CONTRACT; - -#if defined(TARGET_AMD64) -#ifdef TARGET_UNIX - // AMD64 Unix: JIT_StackProbe has an RBP frame (push rbp; mov rbp, rsp) - // Return address is at [RBP + 8], saved RBP is at [RBP] - TADDR rbp = GetFP(pContext); - PCODE returnAddress = *dac_cast(rbp + 8); - SetIP(pContext, returnAddress); - SetFP(pContext, *dac_cast(rbp)); // restore RBP - SetSP(pContext, rbp + 16); // RSP after ret = RBP + 16 -#else // TARGET_WINDOWS - // AMD64 Windows: JIT_StackProbe is a leaf function (no frame) - // Return address is at [RSP], simulate a ret instruction - PCODE returnAddress = *dac_cast(GetSP(pContext)); - SetIP(pContext, returnAddress); - SetSP(pContext, GetSP(pContext) + sizeof(void*)); // pop the stack -#endif // TARGET_UNIX -#elif defined(TARGET_ARM) - // ARM: JIT_StackProbe has an R7 frame (push {r7}; mov r7, sp) - // At the point of AV, R7 contains the frame pointer. - // Return address is in LR (ARM calling convention, caller saved LR before call). - // Saved R7 is at [R7], we need to restore R7 and SP. - TADDR r7 = pContext->R7; - SetIP(pContext, pContext->Lr); - pContext->R7 = *dac_cast(r7); // restore R7 - SetSP(pContext, r7 + 4); // SP after pop {r7} and ret -#else - // JIT_StackProbe doesn't exist on other architectures - UNREACHABLE(); -#endif -} - -// Unwind a write barrier helper to its caller. Write barriers are leaf functions -// that do not set up a frame, so we can unwind them by simply extracting the -// return address from LR/RA (on ARM/RISC-V) or from the stack (on x86/x64). -// -// Similar to NativeAOT's UnwindSimpleHelperToCaller in EHHelpers.cpp. -static void UnwindWriteBarrierToCaller(CONTEXT* pContext) -{ - LIMITED_METHOD_CONTRACT; - -#if defined(TARGET_AMD64) - // On x64, return address is at [RSP], simulate a ret instruction - PCODE returnAddress = *dac_cast(GetSP(pContext)); - SetIP(pContext, returnAddress); - SetSP(pContext, GetSP(pContext) + sizeof(void*)); // pop the stack -#elif defined(TARGET_X86) - // On x86, return address is at [ESP], simulate a ret instruction - PCODE returnAddress = *dac_cast(GetSP(pContext)); - SetIP(pContext, returnAddress); - SetSP(pContext, GetSP(pContext) + sizeof(void*)); // pop the stack -#elif defined(TARGET_ARM64) - // On ARM64, return address is in LR, no stack adjustment needed for leaf - SetIP(pContext, pContext->Lr); -#elif defined(TARGET_ARM) - // On ARM, return address is in LR - SetIP(pContext, pContext->Lr); -#elif defined(TARGET_LOONGARCH64) - // On LoongArch64, return address is in RA - SetIP(pContext, pContext->Ra); -#elif defined(TARGET_RISCV64) - // On RISC-V64, return address is in RA - SetIP(pContext, pContext->Ra); -#elif defined(TARGET_WASM) - // WASM uses interpreter, write barriers don't fault - UNREACHABLE(); -#else -#error "UnwindWriteBarrierToCaller not implemented for this architecture" -#endif -} - // Returns TRUE if caller should resume execution. BOOL AdjustContextForJITHelpers( @@ -5721,32 +5611,33 @@ AdjustContextForJITHelpers( pContext = &tempContext; } - if (IsIPInWriteBarrierHelper(f_IP)) - { - // Write barriers are leaf functions that do not set up a frame. - // We can unwind them with a simple LR/RA/stack-pop extraction. - UnwindWriteBarrierToCaller(pContext); + Thread::VirtualUnwindToFirstManagedCallFrame(pContext); #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // On ARM/ARM64/LoongArch64/RISC-V, adjust IP to point to the call instruction - // rather than the return address, so the exception appears to originate - // from the managed code that called the write barrier. - PCODE ControlPCPostAdjustment = GetIP(pContext) - STACKWALK_CONTROLPC_ADJUST_OFFSET; - SetIP(pContext, ControlPCPostAdjustment); + // We had an AV in the writebarrier that needs to be treated + // as originating in managed code. At this point, the stack (growing + // from left->right) looks like this: + // + // ManagedFunc -> Native_WriteBarrierInVM -> AV + // + // We just performed an unwind from the write-barrier + // and now have the context in ManagedFunc. Since + // ManagedFunc called into the write-barrier, the return + // address in the unwound context corresponds to the + // instruction where the call will return. + // + // On ARM, just like we perform ControlPC adjustment + // during exception dispatch (refer to ExInfo::InitializeCrawlFrame), + // we will need to perform the corresponding adjustment of IP + // we got from unwind above, so as to indicate that the AV + // happened "before" the call to the writebarrier and not at + // the instruction at which the control will return. + PCODE ControlPCPostAdjustment = GetIP(pContext) - STACKWALK_CONTROLPC_ADJUST_OFFSET; + + // Now we save the address back into the context so that it gets used + // as the faulting address. + SetIP(pContext, ControlPCPostAdjustment); #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 - } - else - { - // JIT_StackProbe has a known frame layout on each platform. - UnwindJITStackProbeToCaller(pContext); - -#if defined(TARGET_ARM) - // On ARM, adjust IP to point to the call instruction - // rather than the return address. - PCODE ControlPCPostAdjustment = GetIP(pContext) - STACKWALK_CONTROLPC_ADJUST_OFFSET; - SetIP(pContext, ControlPCPostAdjustment); -#endif // TARGET_ARM - } // Unwind the frame chain - On Win64, this is required since we may handle the managed fault and to do so, // we will replace the exception context with the managed context and "continue execution" there. Thus, we do not @@ -6907,6 +6798,39 @@ void ThrowResumeAfterCatchException(TADDR resumeSP, TADDR resumeIP) { throw ResumeAfterCatchException(resumeSP, resumeIP); } + +VOID DECLSPEC_NORETURN UnwindAndContinueResumeAfterCatch(TADDR resumeSP, TADDR resumeIP) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_ANY; + + CONTEXT context; + ClrCaptureContext(&context); + + // Unwind to the caller of the Ex.RhThrowEx / Ex.RhThrowHwEx + Thread::VirtualUnwindToFirstManagedCallFrame(&context); + +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + size_t targetSSP = GetSSPForFrameOnCurrentStack(GetIP(&context)); +#else + size_t targetSSP = 0; +#endif + + // Skip all managed frames upto a native frame + while (ExecutionManager::IsManagedCode(GetIP(&context))) + { + Thread::VirtualUnwindCallFrame(&context); +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + if (targetSSP != 0) + { + targetSSP += sizeof(size_t); + } +#endif + } + + ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, &context, targetSSP, resumeSP, resumeIP); +} #endif // FEATURE_INTERPRETER thread_local DWORD t_dwCurrentExceptionCode; @@ -10846,9 +10770,9 @@ void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *p // Read FP callee-saved registers (xmm6-xmm15) from the stack // They are stored at negative offsets from TransitionBlock: - // Layout: [shadow (32)] [xmm6-xmm15 (160)] [xmm0-xmm3 (64)] [padding (8)] [CalleeSavedRegs] [RetAddr] [ArgRegs] - // xmm6 is at sp+32, TransitionBlock is at sp+264, so xmm6 is at TransitionBlock - 232 - M128A *pFpCalleeSaved = (M128A*)((BYTE*)pTransitionBlock - 232); + // Layout: [shadow (32)] [xmm6-xmm15 (160)] [xmm0-xmm3 (64)] [arg regs (32)] [padding (8)] [CalleeSavedRegs] [RetAddr] + // xmm6 is at sp+32, TransitionBlock is at sp+296, so xmm6 is at TransitionBlock - 264 + M128A *pFpCalleeSaved = (M128A*)((BYTE*)pTransitionBlock - 264); m_Context.Xmm6 = pFpCalleeSaved[0]; m_Context.Xmm7 = pFpCalleeSaved[1]; @@ -11216,155 +11140,4 @@ void SoftwareExceptionFrame::InitAndLink(Thread *pThread) Push(pThread); } -// Static helper to populate a CONTEXT from a TransitionBlock for OSR transitions. -// This shares similar logic with UpdateContextFromTransitionBlock but also handles -// platform-specific adjustments needed for OSR (like simulating the call stack alignment). -// -// Returns the adjusted SP and FP values that the OSR method should use. -#ifdef FEATURE_ON_STACK_REPLACEMENT -void SoftwareExceptionFrame::UpdateContextForOSRTransition(TransitionBlock* pTransitionBlock, CONTEXT* pContext, - UINT_PTR* pCurrentSP, UINT_PTR* pCurrentFP) -{ - LIMITED_METHOD_CONTRACT; - -#if defined(TARGET_AMD64) -#if defined(TARGET_WINDOWS) - pContext->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; - - // Read FP callee-saved registers (xmm6-xmm15) from the stack - // Layout: [shadow (32)] [xmm6-xmm15 (160)] [xmm0-xmm3 (64)] [padding (8)] [CalleeSavedRegs] [RetAddr] [ArgRegs] - // xmm6 is at sp+32, TransitionBlock is at sp+264, so xmm6 is at TransitionBlock - 232 - M128A *pFpCalleeSaved = (M128A*)((BYTE*)pTransitionBlock - 232); - - pContext->Xmm6 = pFpCalleeSaved[0]; - pContext->Xmm7 = pFpCalleeSaved[1]; - pContext->Xmm8 = pFpCalleeSaved[2]; - pContext->Xmm9 = pFpCalleeSaved[3]; - pContext->Xmm10 = pFpCalleeSaved[4]; - pContext->Xmm11 = pFpCalleeSaved[5]; - pContext->Xmm12 = pFpCalleeSaved[6]; - pContext->Xmm13 = pFpCalleeSaved[7]; - pContext->Xmm14 = pFpCalleeSaved[8]; - pContext->Xmm15 = pFpCalleeSaved[9]; - - // Initialize FP control/status - pContext->FltSave.ControlWord = 0x27F; - pContext->FltSave.MxCsr = 0x1F80; - pContext->FltSave.MxCsr_Mask = 0x1FFF; - pContext->MxCsr = 0x1F80; -#else // UNIX_AMD64_ABI - pContext->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; -#endif - - // Copy integer callee-saved registers from TransitionBlock - pContext->Rbx = pTransitionBlock->m_calleeSavedRegisters.Rbx; - pContext->Rbp = pTransitionBlock->m_calleeSavedRegisters.Rbp; - pContext->R12 = pTransitionBlock->m_calleeSavedRegisters.R12; - pContext->R13 = pTransitionBlock->m_calleeSavedRegisters.R13; - pContext->R14 = pTransitionBlock->m_calleeSavedRegisters.R14; - pContext->R15 = pTransitionBlock->m_calleeSavedRegisters.R15; -#if defined(TARGET_WINDOWS) - pContext->Rdi = pTransitionBlock->m_calleeSavedRegisters.Rdi; - pContext->Rsi = pTransitionBlock->m_calleeSavedRegisters.Rsi; -#endif - - // SP points just past the TransitionBlock (after return address) - // Adjust for call simulation: OSR method expects SP as if a call just happened - *pCurrentSP = (UINT_PTR)(pTransitionBlock + 1); - _ASSERTE(*pCurrentSP % 16 == 0); - *pCurrentSP -= 8; // Simulate the call pushing return address - *pCurrentFP = pTransitionBlock->m_calleeSavedRegisters.Rbp; - -#elif defined(TARGET_ARM64) - // Restore control and integer registers, matching the x64 approach - pContext->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - - // Copy callee-saved registers x19-x28 from TransitionBlock - // These are the values F() had when it called JIT_Patchpoint - pContext->X19 = pTransitionBlock->m_calleeSavedRegisters.x19; - pContext->X20 = pTransitionBlock->m_calleeSavedRegisters.x20; - pContext->X21 = pTransitionBlock->m_calleeSavedRegisters.x21; - pContext->X22 = pTransitionBlock->m_calleeSavedRegisters.x22; - pContext->X23 = pTransitionBlock->m_calleeSavedRegisters.x23; - pContext->X24 = pTransitionBlock->m_calleeSavedRegisters.x24; - pContext->X25 = pTransitionBlock->m_calleeSavedRegisters.x25; - pContext->X26 = pTransitionBlock->m_calleeSavedRegisters.x26; - pContext->X27 = pTransitionBlock->m_calleeSavedRegisters.x27; - pContext->X28 = pTransitionBlock->m_calleeSavedRegisters.x28; - - // F()'s FP points to where F() saved [caller_fp, caller_lr] - UINT_PTR managedFrameFP = pTransitionBlock->m_calleeSavedRegisters.x29; - // Read Test()'s FP and LR from F()'s stack frame - TADDR callerFP = *((TADDR*)managedFrameFP); // Test()'s FP at [F's FP + 0] - TADDR callerLR = *((TADDR*)(managedFrameFP + 8)); // LR to Test() at [F's FP + 8] - - // CRITICAL: Use Test()'s FP (callerFP), not F()'s FP (managedFrameFP)! - pContext->Fp = callerFP; - pContext->Lr = callerLR; - - // SP = F()'s SP when it called JIT_Patchpoint - *pCurrentSP = (UINT_PTR)(pTransitionBlock + 1); - // FP output should also be caller's FP - *pCurrentFP = callerFP; - -#elif defined(TARGET_LOONGARCH64) - // Restore control and integer registers, matching the ARM64 approach - pContext->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - - // Copy callee-saved registers s0-s8 from TransitionBlock - pContext->S0 = pTransitionBlock->m_calleeSavedRegisters.s0; - pContext->S1 = pTransitionBlock->m_calleeSavedRegisters.s1; - pContext->S2 = pTransitionBlock->m_calleeSavedRegisters.s2; - pContext->S3 = pTransitionBlock->m_calleeSavedRegisters.s3; - pContext->S4 = pTransitionBlock->m_calleeSavedRegisters.s4; - pContext->S5 = pTransitionBlock->m_calleeSavedRegisters.s5; - pContext->S6 = pTransitionBlock->m_calleeSavedRegisters.s6; - pContext->S7 = pTransitionBlock->m_calleeSavedRegisters.s7; - pContext->S8 = pTransitionBlock->m_calleeSavedRegisters.s8; - - UINT_PTR managedFrameFP = pTransitionBlock->m_calleeSavedRegisters.fp; - TADDR callerFP = *((TADDR*)managedFrameFP); - TADDR callerRA = *((TADDR*)(managedFrameFP + 8)); - - pContext->Fp = callerFP; - pContext->Ra = callerRA; - - *pCurrentSP = (UINT_PTR)(pTransitionBlock + 1); - *pCurrentFP = callerFP; - -#elif defined(TARGET_RISCV64) - // Restore control and integer registers, matching the ARM64 approach - pContext->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - - // Copy callee-saved registers s1-s11 from TransitionBlock - pContext->S1 = pTransitionBlock->m_calleeSavedRegisters.s1; - pContext->S2 = pTransitionBlock->m_calleeSavedRegisters.s2; - pContext->S3 = pTransitionBlock->m_calleeSavedRegisters.s3; - pContext->S4 = pTransitionBlock->m_calleeSavedRegisters.s4; - pContext->S5 = pTransitionBlock->m_calleeSavedRegisters.s5; - pContext->S6 = pTransitionBlock->m_calleeSavedRegisters.s6; - pContext->S7 = pTransitionBlock->m_calleeSavedRegisters.s7; - pContext->S8 = pTransitionBlock->m_calleeSavedRegisters.s8; - pContext->S9 = pTransitionBlock->m_calleeSavedRegisters.s9; - pContext->S10 = pTransitionBlock->m_calleeSavedRegisters.s10; - pContext->S11 = pTransitionBlock->m_calleeSavedRegisters.s11; - pContext->Tp = pTransitionBlock->m_calleeSavedRegisters.tp; - pContext->Gp = pTransitionBlock->m_calleeSavedRegisters.gp; - - UINT_PTR managedFrameFP = pTransitionBlock->m_calleeSavedRegisters.fp; - TADDR callerFP = *((TADDR*)managedFrameFP); - TADDR callerRA = *((TADDR*)(managedFrameFP + 8)); - - pContext->Fp = callerFP; - pContext->Ra = callerRA; - - *pCurrentSP = (UINT_PTR)(pTransitionBlock + 1); - *pCurrentFP = callerFP; - -#else -#error "Unsupported platform for OSR TransitionBlock-based context capture" -#endif -} -#endif // FEATURE_ON_STACK_REPLACEMENT - #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 7ddccf1d06bc5f..c72b673c898ad5 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -205,6 +205,10 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable); void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pException); VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException, bool nativeRethrow); +#ifdef FEATURE_INTERPRETER +VOID DECLSPEC_NORETURN UnwindAndContinueResumeAfterCatch(TADDR resumeSP, TADDR resumeIP); +#endif // FEATURE_INTERPRETER + #ifdef TARGET_UNIX VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException); diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 30e53602bc162f..8ec28c47132c6e 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -1033,11 +1033,6 @@ class SoftwareExceptionFrame : public Frame } void UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock); - - // Static helper to populate a CONTEXT from a TransitionBlock for OSR transitions. - // Returns the adjusted SP and FP values that the OSR method should use. - static void UpdateContextForOSRTransition(TransitionBlock* pTransitionBlock, CONTEXT* pContext, - UINT_PTR* pCurrentSP, UINT_PTR* pCurrentFP); #endif TADDR GetReturnAddressPtr_Impl() diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 6861a44f10f366..4a63050a9493b5 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1624,61 +1624,95 @@ extern "C" void JIT_PatchpointWorkerWorkerWithPolicy(TransitionBlock * pTransiti pThread->UnhijackThread(); #endif + // Find context for the original method CONTEXT *pFrameContext = NULL; - UINT_PTR currentSP; - UINT_PTR currentFP; - - // Build CONTEXT directly from TransitionBlock - CONTEXT frameContext; - memset(&frameContext, 0, sizeof(frameContext)); - pFrameContext = &frameContext; - #if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) - // Handle extended context for shadow stacks if enabled - CONTEXT* pAllocatedContext = NULL; + DWORD contextSize = 0; + ULONG64 xStateCompactionMask = 0; + DWORD contextFlags = CONTEXT_FULL; if (Thread::AreShadowStacksEnabled()) { - DWORD contextSize = 0; - ULONG64 xStateCompactionMask = XSTATE_MASK_CET_U; - DWORD contextFlags = CONTEXT_FULL | CONTEXT_XSTATE; - - // The initialize call should fail but return contextSize - BOOL success = g_pfnInitializeContext2 ? - g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) : - InitializeContext(NULL, contextFlags, NULL, &contextSize); + xStateCompactionMask = XSTATE_MASK_CET_U; + contextFlags |= CONTEXT_XSTATE; + } - _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)); + // The initialize call should fail but return contextSize + BOOL success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) : + InitializeContext(NULL, contextFlags, NULL, &contextSize); - PVOID pBuffer = _alloca(contextSize); - success = g_pfnInitializeContext2 ? - g_pfnInitializeContext2(pBuffer, contextFlags, &pAllocatedContext, &contextSize, xStateCompactionMask) : - InitializeContext(pBuffer, contextFlags, &pAllocatedContext, &contextSize); - _ASSERTE(success); + _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)); - pFrameContext = pAllocatedContext; - } -#endif + PVOID pBuffer = _alloca(contextSize); + success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) : + InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize); + _ASSERTE(success); +#else // TARGET_WINDOWS && TARGET_AMD64 + CONTEXT frameContext; + frameContext.ContextFlags = CONTEXT_FULL; + pFrameContext = &frameContext; +#endif // TARGET_WINDOWS && TARGET_AMD64 - // Use the shared helper to populate the CONTEXT from TransitionBlock - SoftwareExceptionFrame::UpdateContextForOSRTransition(pTransitionBlock, pFrameContext, ¤tSP, ¤tFP); + // Find context for the original method + RtlCaptureContext(pFrameContext); #if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) if (Thread::AreShadowStacksEnabled()) { pFrameContext->ContextFlags |= CONTEXT_XSTATE; - SetXStateFeaturesMask(pFrameContext, XSTATE_MASK_CET_U); + SetXStateFeaturesMask(pFrameContext, xStateCompactionMask); SetSSP(pFrameContext, _rdsspq()); + } +#endif // TARGET_WINDOWS && TARGET_AMD64 - DWORD64 ssp = GetSSP(pFrameContext); - if (ssp != 0) - { - SetSSP(pFrameContext, ssp - 8); // Simulate call pushing shadow stack - } + // Walk back to the original method frame + pThread->VirtualUnwindToFirstManagedCallFrame(pFrameContext); + + // Remember original method FP and SP because new method will inherit them. + UINT_PTR currentSP = GetSP(pFrameContext); + UINT_PTR currentFP = GetFP(pFrameContext); + + // We expect to be back at the right IP + if ((UINT_PTR)ip != GetIP(pFrameContext)) + { + // Should be fatal + STRESS_LOG2(LF_TIEREDCOMPILATION, LL_FATALERROR, "Jit_Patchpoint: patchpoint (0x%p) TRANSITION" + " unexpected context IP 0x%p\n", ip, GetIP(pFrameContext)); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } -#endif + + // Now unwind back to the original method caller frame. + EECodeInfo callerCodeInfo(GetIP(pFrameContext)); + ULONG_PTR establisherFrame = 0; + PVOID handlerData = NULL; + RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(), + pFrameContext, &handlerData, &establisherFrame, NULL); + + // Now, set FP and SP back to the values they had just before this helper was called, + // since the new method must have access to the original method frame. + // + // TODO: if we access the patchpointInfo here, we can read out the FP-SP delta from there and + // use that to adjust the stack, likely saving some stack space. + +#if defined(TARGET_AMD64) + // If calls push the return address, we need to simulate that here, so the OSR + // method sees the "expected" SP misalgnment on entry. + _ASSERTE(currentSP % 16 == 0); + currentSP -= 8; + +#if defined(TARGET_WINDOWS) + DWORD64 ssp = GetSSP(pFrameContext); + if (ssp != 0) + { + SetSSP(pFrameContext, ssp - 8); + } +#endif // TARGET_WINDOWS + + pFrameContext->Rbp = currentFP; +#endif // TARGET_AMD64 SetSP(pFrameContext, currentSP); - SetFP(pFrameContext, currentFP); // Note we can get here w/o triggering, if there is an existing OSR method and // we hit the patchpoint. diff --git a/src/coreclr/vm/loongarch64/asmconstants.h b/src/coreclr/vm/loongarch64/asmconstants.h index 67e45a5cceb247..77404df969160f 100644 --- a/src/coreclr/vm/loongarch64/asmconstants.h +++ b/src/coreclr/vm/loongarch64/asmconstants.h @@ -99,37 +99,11 @@ ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); #define SIZEOF__CONTEXT 0x520 ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); -#define OFFSETOF__CONTEXT__ContextFlags 0x0 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__ContextFlags == offsetof(T_CONTEXT, ContextFlags)); - -#define OFFSETOF__CONTEXT__Ra 0x10 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Ra == offsetof(T_CONTEXT, Ra)); - -#define OFFSETOF__CONTEXT__Sp 0x20 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Sp == offsetof(T_CONTEXT, Sp)); - -#define OFFSETOF__CONTEXT__A0 0x28 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__A0 == offsetof(T_CONTEXT, A0)); - -#define OFFSETOF__CONTEXT__Fp 0xB8 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Fp == offsetof(T_CONTEXT, Fp)); - #define OFFSETOF__CONTEXT__S0 0xC0 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__S0 == offsetof(T_CONTEXT, S0)); -#define OFFSETOF__CONTEXT__Pc 0x108 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Pc == offsetof(T_CONTEXT, Pc)); - -// Floating point registers F[0..127] start after Pc -// Non-volatile FP registers are F24-F31 -// Each F entry is 8 bytes (ULONGLONG), but stored as 4*32 for LASX support -#define OFFSETOF__CONTEXT__F 0x110 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__F == offsetof(T_CONTEXT, F)); - -// CONTEXT_FLOATING_POINT_BIT is bit 2 in ContextFlags -#define CONTEXT_FLOATING_POINT_BIT 2 - -#define CONTEXT_INTEGER_BIT 1 +#define OFFSETOF__CONTEXT__Fp 0xB8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Fp == offsetof(T_CONTEXT, Fp)); //========================================= #define OFFSETOF__MethodTable__m_dwFlags 0x0 diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S index 74893d04fdcf22..9f424c39dd30f5 100644 --- a/src/coreclr/vm/loongarch64/asmhelpers.S +++ b/src/coreclr/vm/loongarch64/asmhelpers.S @@ -956,8 +956,7 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK - // $a0 = pointer to TransitionBlock - addi.d $a0, $sp, __PWTB_TransitionBlock + addi.d $a0, $sp, __PWTB_TransitionBlock // TransitionBlock * bl C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) EPILOG_WITH_TRANSITION_BLOCK_RETURN @@ -1066,79 +1065,3 @@ NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler break 0 NESTED_END IL_Rethrow, _TEXT -// ------------------------------------------------------------------ -// ClrRestoreNonvolatileContextWorker -// -// Restores registers based on ContextFlags and jumps to target PC. -// When CONTEXT_INTEGER is set, restores ALL integer registers -// because exception handling needs $a0 (exception object). -// When CONTEXT_FLOATING_POINT is set, restores non-volatile FP regs (f24-f31). -// -// Arguments: -// $a0 - pointer to CONTEXT structure -// $a1 - unused (SSP, not supported on LoongArch64) -// ------------------------------------------------------------------ -LEAF_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT - - // Save CONTEXT pointer in $t0 before we potentially clobber $a0 - move $t0, $a0 - - // Check if CONTEXT_FLOATING_POINT is set - ld.w $t1, $t0, OFFSETOF__CONTEXT__ContextFlags - andi $t2, $t1, (1 << CONTEXT_FLOATING_POINT_BIT) - beqz $t2, LOCAL_LABEL(SkipFloatingPointRestore) - - // Restore non-volatile FP registers f24-f31 - // F24 is at OFFSETOF__CONTEXT__F + 24*8 = 0x110 + 0xC0 = 0x1D0 - fld.d $f24, $t0, (OFFSETOF__CONTEXT__F + 24*8) - fld.d $f25, $t0, (OFFSETOF__CONTEXT__F + 25*8) - fld.d $f26, $t0, (OFFSETOF__CONTEXT__F + 26*8) - fld.d $f27, $t0, (OFFSETOF__CONTEXT__F + 27*8) - fld.d $f28, $t0, (OFFSETOF__CONTEXT__F + 28*8) - fld.d $f29, $t0, (OFFSETOF__CONTEXT__F + 29*8) - fld.d $f30, $t0, (OFFSETOF__CONTEXT__F + 30*8) - fld.d $f31, $t0, (OFFSETOF__CONTEXT__F + 31*8) - -LOCAL_LABEL(SkipFloatingPointRestore): - // Check if CONTEXT_INTEGER is set - andi $t2, $t1, (1 << CONTEXT_INTEGER_BIT) - beqz $t2, LOCAL_LABEL(SkipIntegerRestore) - - // Restore ALL integer registers (needed by exception handling) - // Argument registers a1-a7 (a0 last since we use it) - ld.d $a1, $t0, (OFFSETOF__CONTEXT__A0 + 8) - ld.d $a2, $t0, (OFFSETOF__CONTEXT__A0 + 16) - ld.d $a3, $t0, (OFFSETOF__CONTEXT__A0 + 24) - ld.d $a4, $t0, (OFFSETOF__CONTEXT__A0 + 32) - ld.d $a5, $t0, (OFFSETOF__CONTEXT__A0 + 40) - ld.d $a6, $t0, (OFFSETOF__CONTEXT__A0 + 48) - ld.d $a7, $t0, (OFFSETOF__CONTEXT__A0 + 56) - - // Callee-saved registers s0-s8 - ld.d $s0, $t0, OFFSETOF__CONTEXT__S0 - ld.d $s1, $t0, (OFFSETOF__CONTEXT__S0 + 8) - ld.d $s2, $t0, (OFFSETOF__CONTEXT__S0 + 16) - ld.d $s3, $t0, (OFFSETOF__CONTEXT__S0 + 24) - ld.d $s4, $t0, (OFFSETOF__CONTEXT__S0 + 32) - ld.d $s5, $t0, (OFFSETOF__CONTEXT__S0 + 40) - ld.d $s6, $t0, (OFFSETOF__CONTEXT__S0 + 48) - ld.d $s7, $t0, (OFFSETOF__CONTEXT__S0 + 56) - ld.d $s8, $t0, (OFFSETOF__CONTEXT__S0 + 64) - - // Restore $a0 last (exception object for exception handling) - ld.d $a0, $t0, OFFSETOF__CONTEXT__A0 - -LOCAL_LABEL(SkipIntegerRestore): - // Restore fp, ra - ld.d $fp, $t0, OFFSETOF__CONTEXT__Fp - ld.d $ra, $t0, OFFSETOF__CONTEXT__Ra - - // Load sp and pc ($t0 will be overwritten) - ld.d $t1, $t0, OFFSETOF__CONTEXT__Pc - ld.d $t0, $t0, OFFSETOF__CONTEXT__Sp - - // Set sp and jump - move $sp, $t0 - jr $t1 - -LEAF_END ClrRestoreNonvolatileContextWorker, _TEXT diff --git a/src/coreclr/vm/riscv64/asmconstants.h b/src/coreclr/vm/riscv64/asmconstants.h index 0ad2c2934fb7da..12efe007f07c4f 100644 --- a/src/coreclr/vm/riscv64/asmconstants.h +++ b/src/coreclr/vm/riscv64/asmconstants.h @@ -94,15 +94,6 @@ ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); #define SIZEOF__CONTEXT 0x220 ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); -#define OFFSETOF__CONTEXT__ContextFlags 0x0 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__ContextFlags == offsetof(T_CONTEXT, ContextFlags)); - -#define OFFSETOF__CONTEXT__Ra 0x10 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Ra == offsetof(T_CONTEXT, Ra)); - -#define OFFSETOF__CONTEXT__Sp 0x18 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Sp == offsetof(T_CONTEXT, Sp)); - #define OFFSETOF__CONTEXT__Gp 0x20 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Gp == offsetof(T_CONTEXT, Gp)); @@ -115,26 +106,9 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Fp == offsetof(T_CONTEXT, Fp)); #define OFFSETOF__CONTEXT__S1 0x50 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__S1 == offsetof(T_CONTEXT, S1)); -#define OFFSETOF__CONTEXT__A0 0x58 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__A0 == offsetof(T_CONTEXT, A0)); - #define OFFSETOF__CONTEXT__S2 0x98 ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__S2 == offsetof(T_CONTEXT, S2)); -#define OFFSETOF__CONTEXT__Pc 0x108 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Pc == offsetof(T_CONTEXT, Pc)); - -// Floating point registers F[0..31] start after Pc -// Non-volatile FP registers are F8-F9, F18-F27 -// Each F entry is 8 bytes (ULONGLONG) -#define OFFSETOF__CONTEXT__F 0x110 -ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__F == offsetof(T_CONTEXT, F)); - -// CONTEXT_FLOATING_POINT_BIT is bit 2 in ContextFlags -#define CONTEXT_FLOATING_POINT_BIT 2 - -#define CONTEXT_INTEGER_BIT 1 - //========================================= #define OFFSETOF__MethodTable__m_dwFlags 0x0 ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags)); diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 90f009ccb2e3c0..e0a0af3c9b84bb 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -829,8 +829,7 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT NESTED_ENTRY JIT_Patchpoint, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK - // a0 = pointer to TransitionBlock - addi a0, sp, __PWTB_TransitionBlock + addi a0, sp, __PWTB_TransitionBlock // TransitionBlock * call C_FUNC(JIT_PatchpointWorkerWorkerWithPolicy) EPILOG_WITH_TRANSITION_BLOCK_RETURN @@ -923,89 +922,6 @@ NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler ebreak NESTED_END IL_Rethrow, _TEXT -// ------------------------------------------------------------------ -// ClrRestoreNonvolatileContextWorker -// -// Restores registers based on ContextFlags and jumps to target PC. -// When CONTEXT_INTEGER is set, restores ALL integer registers -// because exception handling needs a0 (exception object). -// When CONTEXT_FLOATING_POINT is set, restores non-volatile FP regs (f8-f9, f18-f27). -// -// Arguments: -// a0 - pointer to CONTEXT structure -// a1 - unused (SSP, not supported on RISC-V64) -// ------------------------------------------------------------------ -LEAF_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT - - // Save CONTEXT pointer in t0 before we potentially clobber a0 - mv t0, a0 - - // Check if CONTEXT_FLOATING_POINT is set - lw t1, OFFSETOF__CONTEXT__ContextFlags(t0) - andi t2, t1, (1 << CONTEXT_FLOATING_POINT_BIT) - beqz t2, LOCAL_LABEL(SkipFloatingPointRestore) - - // Restore non-volatile FP registers f8-f9, f18-f27 - // Each F entry is 8 bytes - fld f8, (OFFSETOF__CONTEXT__F + 8*8)(t0) - fld f9, (OFFSETOF__CONTEXT__F + 9*8)(t0) - fld f18, (OFFSETOF__CONTEXT__F + 18*8)(t0) - fld f19, (OFFSETOF__CONTEXT__F + 19*8)(t0) - fld f20, (OFFSETOF__CONTEXT__F + 20*8)(t0) - fld f21, (OFFSETOF__CONTEXT__F + 21*8)(t0) - fld f22, (OFFSETOF__CONTEXT__F + 22*8)(t0) - fld f23, (OFFSETOF__CONTEXT__F + 23*8)(t0) - fld f24, (OFFSETOF__CONTEXT__F + 24*8)(t0) - fld f25, (OFFSETOF__CONTEXT__F + 25*8)(t0) - fld f26, (OFFSETOF__CONTEXT__F + 26*8)(t0) - fld f27, (OFFSETOF__CONTEXT__F + 27*8)(t0) - -LOCAL_LABEL(SkipFloatingPointRestore): - // Check if CONTEXT_INTEGER is set - andi t2, t1, (1 << CONTEXT_INTEGER_BIT) - beqz t2, LOCAL_LABEL(SkipIntegerRestore) - - // Restore ALL integer registers (needed by exception handling) - // Argument registers a1-a7 (a0 last since we use it) - ld a1, (OFFSETOF__CONTEXT__A0 + 8)(t0) - ld a2, (OFFSETOF__CONTEXT__A0 + 16)(t0) - ld a3, (OFFSETOF__CONTEXT__A0 + 24)(t0) - ld a4, (OFFSETOF__CONTEXT__A0 + 32)(t0) - ld a5, (OFFSETOF__CONTEXT__A0 + 40)(t0) - ld a6, (OFFSETOF__CONTEXT__A0 + 48)(t0) - ld a7, (OFFSETOF__CONTEXT__A0 + 56)(t0) - - // Callee-saved registers s1-s11 - ld s1, OFFSETOF__CONTEXT__S1(t0) - ld s2, OFFSETOF__CONTEXT__S2(t0) - ld s3, (OFFSETOF__CONTEXT__S2 + 8)(t0) - ld s4, (OFFSETOF__CONTEXT__S2 + 16)(t0) - ld s5, (OFFSETOF__CONTEXT__S2 + 24)(t0) - ld s6, (OFFSETOF__CONTEXT__S2 + 32)(t0) - ld s7, (OFFSETOF__CONTEXT__S2 + 40)(t0) - ld s8, (OFFSETOF__CONTEXT__S2 + 48)(t0) - ld s9, (OFFSETOF__CONTEXT__S2 + 56)(t0) - ld s10, (OFFSETOF__CONTEXT__S2 + 64)(t0) - ld s11, (OFFSETOF__CONTEXT__S2 + 72)(t0) - - // Restore a0 last (exception object for exception handling) - ld a0, OFFSETOF__CONTEXT__A0(t0) - -LOCAL_LABEL(SkipIntegerRestore): - // Restore fp, ra - ld fp, OFFSETOF__CONTEXT__Fp(t0) - ld ra, OFFSETOF__CONTEXT__Ra(t0) - - // Load sp and pc (t0 will be overwritten) - ld t1, OFFSETOF__CONTEXT__Pc(t0) - ld t0, OFFSETOF__CONTEXT__Sp(t0) - - // Set sp and jump - mv sp, t0 - jr t1 - -LEAF_END ClrRestoreNonvolatileContextWorker, _TEXT - #ifdef FEATURE_INTERPRETER // Align interpreter stack by adjusting it by 8 bytes diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 3ddbbb3f08cbcc..00f17de38ab91c 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -6634,7 +6634,7 @@ void Thread::InitializeSpecialUserModeApc() #endif // FEATURE_SPECIAL_USER_MODE_APC -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) +#if defined(TARGET_AMD64) EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); #endif @@ -6647,8 +6647,6 @@ void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord, size_t targetSSP) targetSSP = GetSSP(ContextRecord); } ClrRestoreNonvolatileContextWorker(ContextRecord, targetSSP); -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - ClrRestoreNonvolatileContextWorker(ContextRecord, 0); #elif defined(TARGET_X86) && defined(TARGET_WINDOWS) // need to pop the SEH records before write over the stack LPVOID oldSP = (LPVOID)GetSP(ContextRecord);