diff --git a/src/coreclr/pal/inc/unixasmmacrosamd64.inc b/src/coreclr/pal/inc/unixasmmacrosamd64.inc index 90c8947e754297..8f6ec93097a7a0 100644 --- a/src/coreclr/pal/inc/unixasmmacrosamd64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosamd64.inc @@ -147,6 +147,12 @@ C_FUNC(\Name\()_End): // the xmm registers are not supported by the libunwind .endm +// Unaligned version for use when stack alignment cannot be guaranteed +.macro save_xmm128_postrsp_unaligned Reg, Offset + __Offset = \Offset + movdqu xmmword ptr [rsp + __Offset], \Reg +.endm + .macro restore_xmm128 Reg, ofs __Offset = \ofs movdqa \Reg, xmmword ptr [rsp + __Offset] @@ -246,6 +252,20 @@ C_FUNC(\Name\()_End): .endm +// Unaligned version for cases where 16-byte stack alignment cannot be guaranteed +.macro SAVE_FLOAT_ARGUMENT_REGISTERS_UNALIGNED ofs + + save_xmm128_postrsp_unaligned xmm0, \ofs + save_xmm128_postrsp_unaligned xmm1, \ofs + 0x10 + save_xmm128_postrsp_unaligned xmm2, \ofs + 0x20 + save_xmm128_postrsp_unaligned xmm3, \ofs + 0x30 + save_xmm128_postrsp_unaligned xmm4, \ofs + 0x40 + save_xmm128_postrsp_unaligned xmm5, \ofs + 0x50 + save_xmm128_postrsp_unaligned xmm6, \ofs + 0x60 + save_xmm128_postrsp_unaligned xmm7, \ofs + 0x70 + +.endm + .macro RESTORE_FLOAT_ARGUMENT_REGISTERS ofs restore_xmm128 xmm0, \ofs @@ -428,6 +448,72 @@ C_FUNC(\Name\()_End): POP_CALLEE_SAVED_REGISTERS .endm +// Pushes a full TransitionBlock on the stack including argument registers and +// floating point argument registers. Used for exception throw helpers where we +// need to capture the complete register state. +// +// Stack layout (from high to low address after prologue): +// Return address (8 bytes) +// CalleeSavedRegisters (rbp, rbx, r15, r14, r13, r12 - 48 bytes) +// ArgumentRegisters (r9, r8, rcx, rdx, rsi, rdi - 48 bytes) <- TransitionBlock pointer +// FloatArgumentRegisters (xmm0-xmm7, 128 bytes) at rsp+8 +// 8-byte alignment padding at rsp+0 +// sp points here +// +// Stack alignment calculation: +// Before call to IL_Throw: rsp is 16-byte aligned (ABI requirement before call) +// After call (return addr pushed): rsp % 16 = 8 +// After PUSH_CALLEE_SAVED_REGISTERS (48 bytes): rsp % 16 = 8 +// After PUSH_ARGUMENT_REGISTERS (48 bytes): rsp % 16 = 8 +// After alloc_stack 136: rsp % 16 = (8 - 136 % 16) = (8 - 8) = 0 <- aligned! +// +// Stack layout for IL_Throw helpers using TransitionBlock with float registers. +// +// Stack alignment calculation: +// Before call to IL_Throw: rsp is 16-byte aligned (ABI requirement before call) +// After call (return addr pushed): rsp % 16 = 8 +// After PUSH_CALLEE_SAVED_REGISTERS (48 bytes): rsp % 16 = 8 +// After PUSH_ARGUMENT_REGISTERS (48 bytes): rsp % 16 = 8 +// After alloc_stack 136: rsp % 16 = (8 - 136 % 16) = (8 - 8) = 0 <- aligned! +// +// Stack layout (low to high addresses): +// rsp+0: 8 bytes padding (for alignment) +// rsp+8: FloatArgumentRegisters (xmm0-xmm7, 128 bytes) +// rsp+136: TransitionBlock start +// - ArgumentRegisters (rdi, rsi, rdx, rcx, r8, r9: 48 bytes) +// - CalleeSavedRegisters (r12, r13, r14, r15, rbx, rbp: 48 bytes) +// - Return address (8 bytes) +// +// TransitionBlock at rsp+136, floats at rsp+8 = TransitionBlock - 128 +// (matches GetOffsetOfFloatArgumentRegisters which returns -128) +// +// NOTE: We use SAVE_FLOAT_ARGUMENT_REGISTERS_UNALIGNED because rsp+8 is not +// 16-byte aligned (rsp is aligned, rsp+8 is not). +// +// On exit, \target contains the TransitionBlock pointer. +.macro PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS target + set_cfa_register rsp, 8 + + PUSH_CALLEE_SAVED_REGISTERS + + // Set RBP as frame pointer to facilitate stack walking for 3rd party tools. + // After PUSH_CALLEE_SAVED_REGISTERS, saved RBP is at rsp+40 (5 regs * 8 bytes above current rsp) + lea rbp, [rsp + 40] + + PUSH_ARGUMENT_REGISTERS + + // Allocate 128 bytes for floats + 8 bytes padding = 136 bytes + alloc_stack 136 + // Save float argument registers at offset 8 (TransitionBlock - 128) + // Using unaligned stores because rsp+8 is not 16-byte aligned + SAVE_FLOAT_ARGUMENT_REGISTERS_UNALIGNED 8 + + END_PROLOGUE + + // TransitionBlock starts at rsp+136 (where ArgumentRegisters are) + lea \target, [rsp + 136] +.endm + .macro INLINE_GETTHREAD // Inlined version of call C_FUNC(RhpGetThread) INLINE_GET_TLS_VAR t_CurrentThreadInfo diff --git a/src/coreclr/pal/inc/unixasmmacrosarm.inc b/src/coreclr/pal/inc/unixasmmacrosarm.inc index 54a6f7d4dc3b19..4ea9aa35236eb8 100644 --- a/src/coreclr/pal/inc/unixasmmacrosarm.inc +++ b/src/coreclr/pal/inc/unixasmmacrosarm.inc @@ -288,6 +288,43 @@ C_FUNC(\Name): add \target, sp, 4 .endm +// Pushes a full TransitionBlock on the stack including float argument registers. +// On exit, \target contains the TransitionBlock pointer. +// +// Stack layout (from sp going up): +// sp+0: padding (4 bytes) - for 8-byte alignment +// sp+4: d8-d15 (64 bytes) - FP callee-saved +// sp+68: padding (4 bytes) - to make d0-d7 8-byte aligned at TransitionBlock-68 +// sp+72: d0-d7 (64 bytes) - float argument registers (at TransitionBlock - 68) +// sp+136: padding (4 bytes) - to keep total allocation 8-byte aligned +// sp+140: TransitionBlock starts here (CalleeSavedRegisters + ArgumentRegisters pushed above) +// +// GetNegSpaceSize() for ARM32 = 64 (FloatArgumentRegisters) + 4 (padding) = 68 +// GetOffsetOfFloatArgumentRegisters() = -68 +// +// Total stack alloc: 4 + 64 + 4 + 64 + 4 = 140 bytes +// Stack: Arguments(16) + callee-saved(36) + alloc(140) = 192 bytes +// 192 % 8 = 0, properly aligned for ARM32 (8-byte alignment required) +.macro PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS target + // Push argument registers (r0-r3) - these will be at highest address in TransitionBlock + PUSH_ARGUMENT_REGISTERS + PUSH_CALLEE_SAVED_REGISTERS + PROLOG_STACK_SAVE_OFFSET r7, #12 + // let r7 point the saved r7 in the stack (clang FP style) + // Allocate space for: padding (4) + d8-d15 (64) + padding (4) + d0-d7 (64) + padding (4) = 140 bytes + alloc_stack 140 + // Save floating point argument registers (d0-d7) at sp+72 (TransitionBlock - 68) + add r12, sp, #72 + vstm r12, {d0-d7} + // Save FP callee-saved registers (d8-d15) at sp+4 + add r12, sp, #4 + vstm r12, {d8-d15} + CHECK_STACK_ALIGNMENT + END_PROLOGUE + // TransitionBlock is at sp + 140 + add \target, sp, #140 +.endm + .macro POP_COOP_PINVOKE_FRAME free_stack 4 POP_CALLEE_SAVED_REGISTERS diff --git a/src/coreclr/pal/inc/unixasmmacrosarm64.inc b/src/coreclr/pal/inc/unixasmmacrosarm64.inc index fa4265ab3fc9d2..2d27459372b561 100644 --- a/src/coreclr/pal/inc/unixasmmacrosarm64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosarm64.inc @@ -419,7 +419,54 @@ C_FUNC(\Name\()_End): EPILOG_RESTORE_REG_PAIR x25, x26, 64 EPILOG_RESTORE_REG_PAIR x27, x28, 80 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 176 -.endm +.endm + +// Pushes a full TransitionBlock on the stack including argument registers and +// floating point argument registers. Used for exception throw helpers where we +// need to capture the complete register state including FP callee-saved registers. +// +// Stack layout (from low to high address): +// sp+0: FP callee-saved registers (d8-d15, 64 bytes) +// sp+64: FloatArgumentRegisters (q0-q7, 128 bytes) +// sp+192: TransitionBlock start (176 bytes) +// - CalleeSavedRegisters (fp, lr, x19-x28 - 96 bytes) +// - padding (8 bytes) +// - x8 (8 bytes) +// - ArgumentRegisters (x0-x7, 64 bytes) +// +// On exit, \target contains the TransitionBlock pointer (sp+192). +.macro PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS target + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -176 + + // Spill callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, 16 + PROLOG_SAVE_REG_PAIR x21, x22, 32 + PROLOG_SAVE_REG_PAIR x23, x24, 48 + PROLOG_SAVE_REG_PAIR x25, x26, 64 + PROLOG_SAVE_REG_PAIR x27, x28, 80 + + // Allocate space for FloatArgumentRegisters (128) + FP callee-saved (64) = 192 bytes + PROLOG_STACK_ALLOC 192 + + // Save argument registers (x8, x0-x7) at offset 296 from sp (192 + 104) + SAVE_ARGUMENT_REGISTERS sp, 296 + + // Save floating point argument registers (q0-q7) at sp+64 + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 64 + + // Save FP callee-saved registers (d8-d15) at sp+0 + str d8, [sp, #0] + str d9, [sp, #8] + str d10, [sp, #16] + str d11, [sp, #24] + str d12, [sp, #32] + str d13, [sp, #40] + str d14, [sp, #48] + str d15, [sp, #56] + + // Set target to TransitionBlock pointer + add \target, sp, #192 +.endm // ------------------------------------------------------------------ // Macro to generate Redirection Stubs diff --git a/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc b/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc index 66ebdd147535e9..92d701598f933e 100644 --- a/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosloongarch64.inc @@ -404,6 +404,45 @@ C_FUNC(\Name\()_End): EPILOG_STACK_FREE 160 .endm +// Pushes a full TransitionBlock on the stack including argument registers and +// floating point argument registers. Used for exception throw helpers where we +// need to capture the complete register state. +// +// Stack layout (from low to high address): +// sp+0: FloatArgumentRegisters (fa0-fa7, 64 bytes) +// sp+64: TransitionBlock start +// - CalleeSavedRegisters (fp, ra, s0-s8 - 96 bytes) +// - ArgumentRegisters (a0-a7, 64 bytes) +// +// On exit, \target contains the TransitionBlock pointer (sp+128). +.macro PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS target + // Stack: FPCalleeSaved(64) + FloatArgs(64) + CalleeSaved(96) + Args(64) = 288 bytes + PROLOG_STACK_ALLOC 288 + PROLOG_SAVE_REG_PAIR 22, 1, 128, 1 + + // Save callee-saved registers at offset 128 (after FP callee-saved and FloatArgumentRegisters) + SAVE_CALLEESAVED_REGISTERS $sp, 128 + + // Save argument registers (a0-a7) at offset 224 + SAVE_ARGUMENT_REGISTERS $sp, 224 + + // Save floating-point argument registers (fa0-fa7) at offset 64 + SAVE_FLOAT_ARGUMENT_REGISTERS $sp, 64 + + // Save FP callee-saved registers (f24-f31) at offset 0 + fst.d $f24, $sp, 0 + fst.d $f25, $sp, 8 + fst.d $f26, $sp, 16 + fst.d $f27, $sp, 24 + fst.d $f28, $sp, 32 + fst.d $f29, $sp, 40 + fst.d $f30, $sp, 48 + fst.d $f31, $sp, 56 + + // Set target to TransitionBlock pointer + addi.d \target, $sp, 128 +.endm + // ------------------------------------------------------------------ // Macro to generate Redirection Stubs // diff --git a/src/coreclr/pal/inc/unixasmmacrosriscv64.inc b/src/coreclr/pal/inc/unixasmmacrosriscv64.inc index ead0d6b550d232..d244756c304eb9 100644 --- a/src/coreclr/pal/inc/unixasmmacrosriscv64.inc +++ b/src/coreclr/pal/inc/unixasmmacrosriscv64.inc @@ -349,6 +349,51 @@ C_FUNC(\Name): EPILOG_STACK_FREE 192 .endm +// Pushes a full TransitionBlock on the stack including argument registers and +// floating point argument registers. Used for exception throw helpers where we +// need to capture the complete register state. +// +// Stack layout (from low to high address): +// sp+0: FloatArgumentRegisters (fa0-fa7, 64 bytes) +// sp+64: TransitionBlock start +// - CalleeSavedRegisters (fp, ra, s1-s11, tp, gp - 120 bytes) +// - padding (8 bytes) +// - ArgumentRegisters (a0-a7, 64 bytes) +// +// On exit, \target contains the TransitionBlock pointer (sp+160). +.macro PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS target + // Stack: FPCalleeSaved(96) + FloatArgs(64) + CalleeSaved(120) + pad(8) + Args(64) = 352 bytes + PROLOG_STACK_ALLOC 352 + PROLOG_SAVE_REG_PAIR fp, ra, 160, 1 + + // Save callee-saved registers at offset 160 (after FP callee-saved and FloatArgumentRegisters) + SAVE_CALLEESAVED_REGISTERS sp, 160 + + // Save argument registers (a0-a7) at offset 288 + SAVE_ARGUMENT_REGISTERS sp, 288 + + // Save floating-point argument registers (fa0-fa7) at offset 96 + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 96 + + // Save FP callee-saved registers (fs0-fs11 = f8,f9,f18-f27) at offset 0 + // RISC-V FP callee-saved: fs0=f8, fs1=f9, fs2-fs11=f18-f27 + fsd fs0, 0(sp) // f8 + fsd fs1, 8(sp) // f9 + fsd fs2, 16(sp) // f18 + fsd fs3, 24(sp) // f19 + fsd fs4, 32(sp) // f20 + fsd fs5, 40(sp) // f21 + fsd fs6, 48(sp) // f22 + fsd fs7, 56(sp) // f23 + fsd fs8, 64(sp) // f24 + fsd fs9, 72(sp) // f25 + fsd fs10, 80(sp) // f26 + fsd fs11, 88(sp) // f27 + + // Set target to TransitionBlock pointer + addi \target, sp, 160 +.endm + // ------------------------------------------------------------------ // Macro to generate Redirection Stubs // diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index 83e53a633ded54..4725a0cf8eefbb 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -11,6 +11,9 @@ extern ProfileLeave:proc extern ProfileTailcall:proc extern OnHijackWorker:proc extern JIT_RareDisableHelperWorker:proc +extern IL_Throw_Impl:proc +extern IL_ThrowExact_Impl:proc +extern IL_Rethrow_Impl:proc ifdef FEATURE_INTERPRETER extern ExecuteInterpretedMethod:proc extern GetInterpThreadContextWithPossiblyMissingThreadOrCallStub:proc @@ -1200,4 +1203,51 @@ NESTED_END CallJittedMethodRetI8, _TEXT endif ; FEATURE_INTERPRETER +;========================================================================== +; Capture a transition block with register values and call the IL_Throw_Impl +; implementation written in C. +; +; Input state: +; RCX = Pointer to exception object +;========================================================================== +NESTED_ENTRY IL_Throw, _TEXT + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rdx + + ; RCX already contains exception object + ; RDX contains pointer to TransitionBlock + call IL_Throw_Impl + ; Should never return + int 3 +NESTED_END IL_Throw, _TEXT + +;========================================================================== +; Capture a transition block with register values and call the IL_ThrowExact_Impl +; implementation written in C. +; +; Input state: +; RCX = Pointer to exception object +;========================================================================== +NESTED_ENTRY IL_ThrowExact, _TEXT + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rdx + + ; RCX already contains exception object + ; RDX contains pointer to TransitionBlock + call IL_ThrowExact_Impl + ; Should never return + int 3 +NESTED_END IL_ThrowExact, _TEXT + +;========================================================================== +; Capture a transition block with register values and call the IL_Rethrow_Impl +; implementation written in C. +;========================================================================== +NESTED_ENTRY IL_Rethrow, _TEXT + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rcx + + ; RCX contains pointer to TransitionBlock + call IL_Rethrow_Impl + ; Should never return + int 3 +NESTED_END IL_Rethrow, _TEXT + end diff --git a/src/coreclr/vm/amd64/AsmMacros.inc b/src/coreclr/vm/amd64/AsmMacros.inc index c6966135fc7ef4..65d0f3a359392f 100644 --- a/src/coreclr/vm/amd64/AsmMacros.inc +++ b/src/coreclr/vm/amd64/AsmMacros.inc @@ -485,5 +485,73 @@ POP_COOP_PINVOKE_FRAME macro endm +; Pushes a full TransitionBlock on the stack including argument registers and +; floating point argument registers. Used for exception throw helpers where we +; need to capture the complete register state including FP callee-saved registers. +; +; Stack layout (from high to low address after prologue): +; Return address (m_ReturnAddress) +; CalleeSavedRegisters (r15, r14, r13, r12, rbp, rbx, rsi, rdi - 64 bytes) <- TransitionBlock starts here +; Outgoing argument homes (32 bytes) +; FloatArgumentRegisters (xmm0-xmm3, 64 bytes) +; FP Callee-saved registers (xmm6-xmm15, 160 bytes) +; sp points here +; +; Stack alignment: After call (8) + callee-saved pushes (64) + alloc (256) = 328 bytes +; 328 mod 16 = 8, so RSP mod 16 = 8 after alloc - NOT 16-byte aligned. +; We need RSP to be 16-byte aligned for movaps AND unwind offsets must be multiples of 16. +; +; Solution: Use alloc_stack 272 (adds 16 bytes padding). 8 + 64 + 272 = 344, 344 mod 16 = 8. +; Wait, that's still not right. Let me recalculate: +; After call: RSP mod 16 = 8 (return addr pushed from 16-byte aligned stack) +; After 8 pushes (64 bytes): RSP mod 16 = (8 + 64) mod 16 = 72 mod 16 = 8 +; After alloc N: RSP mod 16 = (8 + N) mod 16 +; For RSP mod 16 = 0, need N mod 16 = 8 +; 256 mod 16 = 0, so RSP mod 16 = 8 (NOT aligned) +; 264 mod 16 = 8, so RSP mod 16 = 0 (aligned!) but offsets 0,16,32... work +; +; With alloc_stack 264: RSP is 16-byte aligned, XMM saves at 0, 16, 32, ... (multiples of 16) +; +; On exit, target contains the TransitionBlock pointer (CalleeSavedRegisters). +PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS macro target + + PUSH_CALLEE_SAVED_REGISTERS + + ; Set RBP as frame pointer to facilitate stack walking for 3rd party tools. + ; After PUSH_CALLEE_SAVED_REGISTERS, saved RBP is at rsp+24 (3 regs * 8 bytes above current rsp) + ; Push order: r15, r14, r13, r12, rbp, rbx, rsi, rdi - so rbp is at offset 24 from rsp + lea rbp, [rsp + 24] + + ; Allocate space for: FP callee-saved (160) + float args (64) + shadow (32) + 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 + + ; Save argument registers to shadow space area at offset 224 + SAVE_ARGUMENT_REGISTERS 224 + + ; Save float argument registers at offset 160 + SAVE_FLOAT_ARGUMENT_REGISTERS 160 + + ; Save FP callee-saved registers (xmm6-xmm15) at offset 0 + ; RSP is 16-byte aligned, so offset 0, 16, 32, ... are all 16-byte aligned + ; AND these offsets are multiples of 16 as required by unwind codes + save_xmm128_postrsp xmm6, 0h + save_xmm128_postrsp xmm7, 10h + save_xmm128_postrsp xmm8, 20h + save_xmm128_postrsp xmm9, 30h + save_xmm128_postrsp xmm10, 40h + save_xmm128_postrsp xmm11, 50h + save_xmm128_postrsp xmm12, 60h + save_xmm128_postrsp xmm13, 70h + save_xmm128_postrsp xmm14, 80h + save_xmm128_postrsp xmm15, 90h + + END_PROLOGUE + + ; TransitionBlock pointer points to CalleeSavedRegisters at rsp + 264 + lea target, [rsp + 264] + + endm + ;; GC type flags GC_ALLOC_FINALIZE equ 1 diff --git a/src/coreclr/vm/amd64/asmhelpers.S b/src/coreclr/vm/amd64/asmhelpers.S index 9f5fd30792dcf4..38b3c87166d0be 100644 --- a/src/coreclr/vm/amd64/asmhelpers.S +++ b/src/coreclr/vm/amd64/asmhelpers.S @@ -1913,3 +1913,48 @@ END_PROLOGUE NESTED_END CallJittedMethodRetDoubleDouble, _TEXT #endif // FEATURE_INTERPRETER + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Throw_Impl +// implementation written in C. +// +// Input state: +// rdi = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Throw, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rsi + // rdi already contains exception object + // rsi contains pointer to TransitionBlock + call C_FUNC(IL_Throw_Impl) + // Should never return + int3 +NESTED_END IL_Throw, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_ThrowExact_Impl +// implementation written in C. +// +// Input state: +// rdi = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_ThrowExact, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rsi + // rdi already contains exception object + // rsi contains pointer to TransitionBlock + call C_FUNC(IL_ThrowExact_Impl) + // Should never return + int3 +NESTED_END IL_ThrowExact, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Rethrow_Impl +// implementation written in C. +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS rdi + // rdi contains pointer to TransitionBlock + call C_FUNC(IL_Rethrow_Impl) + // Should never return + int3 +NESTED_END IL_Rethrow, _TEXT + diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 74cf86fd5f31b1..5065a6d15c4cb4 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -897,3 +897,48 @@ LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT eor r0, r0, r1 EPILOG_BRANCH_REG r12 LEAF_END ThisPtrRetBufPrecodeWorker, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Throw_Impl +// implementation written in C. +// +// Input state: +// r0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Throw, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS r1 + // r0 already contains exception object + // r1 contains pointer to TransitionBlock + bl C_FUNC(IL_Throw_Impl) + // Should never return + EMIT_BREAKPOINT +NESTED_END IL_Throw, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_ThrowExact_Impl +// implementation written in C. +// +// Input state: +// r0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_ThrowExact, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS r1 + // r0 already contains exception object + // r1 contains pointer to TransitionBlock + bl C_FUNC(IL_ThrowExact_Impl) + // Should never return + EMIT_BREAKPOINT +NESTED_END IL_ThrowExact, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Rethrow_Impl +// implementation written in C. +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS r0 + // r0 contains pointer to TransitionBlock + bl C_FUNC(IL_Rethrow_Impl) + // Should never return + EMIT_BREAKPOINT +NESTED_END IL_Rethrow, _TEXT + diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 046482ea6e996c..127a5c0a118245 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -2752,3 +2752,56 @@ NESTED_END CallJittedMethodRet4Vector128, _TEXT #endif // FEATURE_INTERPRETER + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Throw_Impl +// implementation written in C. +// +// Stack layout (from low to high address): +// sp+0: FloatArgumentRegisters (q0-q7, 128 bytes) +// sp+128: TransitionBlock start (176 bytes) +// - CalleeSavedRegisters (fp, lr, x19-x28 - 96 bytes) +// - padding (8 bytes) +// - x8 (8 bytes) +// - ArgumentRegisters (x0-x7, 64 bytes) +// +// Input state: +// x0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Throw, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x1 + // x0 already contains exception object + // x1 contains pointer to TransitionBlock + bl C_FUNC(IL_Throw_Impl) + // Should never return + brk #0 +NESTED_END IL_Throw, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_ThrowExact_Impl +// implementation written in C. +// +// Input state: +// x0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_ThrowExact, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x1 + // x0 already contains exception object + // x1 contains pointer to TransitionBlock + bl C_FUNC(IL_ThrowExact_Impl) + // Should never return + brk #0 +NESTED_END IL_ThrowExact, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Rethrow_Impl +// implementation written in C. +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x0 + // x0 contains pointer to TransitionBlock + bl C_FUNC(IL_Rethrow_Impl) + // Should never return + brk #0 +NESTED_END IL_Rethrow, _TEXT + diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 281ca3bd0e85bc..7f510cd8ed62a5 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -23,6 +23,9 @@ #endif IMPORT HijackHandler IMPORT ThrowControlForThread + IMPORT IL_Throw_Impl + IMPORT IL_ThrowExact_Impl + IMPORT IL_Rethrow_Impl #ifdef FEATURE_INTERPRETER IMPORT GetInterpThreadContextWithPossiblyMissingThreadOrCallStub IMPORT ExecuteInterpretedMethod @@ -2981,5 +2984,49 @@ CopyLoop #endif // FEATURE_INTERPRETER +; ------------------------------------------------------------------ +; Capture a transition block with register values and call the IL_Throw_Impl +; implementation written in C. +; +; Input state: +; x0 = Pointer to exception object +; ------------------------------------------------------------------ + NESTED_ENTRY IL_Throw + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x1 + ; x0 already contains exception object + ; x1 contains pointer to TransitionBlock + bl IL_Throw_Impl + ; Should never return + brk #0 + NESTED_END IL_Throw + +; ------------------------------------------------------------------ +; Capture a transition block with register values and call the IL_ThrowExact_Impl +; implementation written in C. +; +; Input state: +; x0 = Pointer to exception object +; ------------------------------------------------------------------ + NESTED_ENTRY IL_ThrowExact + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x1 + ; x0 already contains exception object + ; x1 contains pointer to TransitionBlock + bl IL_ThrowExact_Impl + ; Should never return + brk #0 + NESTED_END IL_ThrowExact + +; ------------------------------------------------------------------ +; Capture a transition block with register values and call the IL_Rethrow_Impl +; implementation written in C. +; ------------------------------------------------------------------ + NESTED_ENTRY IL_Rethrow + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS x0 + ; x0 contains pointer to TransitionBlock + bl IL_Rethrow_Impl + ; Should never return + brk #0 + NESTED_END IL_Rethrow + ; 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 a11067633ab82e..93778d775f87c9 100644 --- a/src/coreclr/vm/arm64/asmmacros.h +++ b/src/coreclr/vm/arm64/asmmacros.h @@ -204,6 +204,55 @@ OFFSETOF__ee_alloc_context EQU OFFSETOF__RuntimeThreadLocals__ee_alloc_context EPILOG_RESTORE_REG_PAIR fp, lr, #176! MEND +; Pushes a full TransitionBlock on the stack including argument registers and +; floating point argument registers. Used for exception throw helpers where we +; need to capture the complete register state including FP callee-saved registers. +; +; Stack layout (from low to high address): +; sp+0: FP callee-saved registers (d8-d15, 64 bytes) +; sp+64: FloatArgumentRegisters (q0-q7, 128 bytes) +; sp+192: TransitionBlock start (176 bytes) +; - CalleeSavedRegisters (fp, lr, x19-x28 - 96 bytes) +; - padding (8 bytes) +; - x8 (8 bytes) +; - ArgumentRegisters (x0-x7, 64 bytes) +; +; On exit, $Target contains the TransitionBlock pointer (sp+192). + MACRO + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS $Target + + PROLOG_SAVE_REG_PAIR fp, lr, #-176! + + ; Spill callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, #16 + PROLOG_SAVE_REG_PAIR x21, x22, #32 + PROLOG_SAVE_REG_PAIR x23, x24, #48 + PROLOG_SAVE_REG_PAIR x25, x26, #64 + PROLOG_SAVE_REG_PAIR x27, x28, #80 + + ; Allocate space for FloatArgumentRegisters (128) + FP callee-saved (64) = 192 bytes + PROLOG_STACK_ALLOC 192 + + ; Save argument registers (x8, x0-x7) at offset 296 from sp (192 + 104) + SAVE_ARGUMENT_REGISTERS sp, 296 + + ; Save floating point argument registers (q0-q7) at sp+64 + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 64 + + ; Save FP callee-saved registers (d8-d15) at sp+0 + str d8, [sp, #0] + str d9, [sp, #8] + str d10, [sp, #16] + str d11, [sp, #24] + str d12, [sp, #32] + str d13, [sp, #40] + str d14, [sp, #48] + str d15, [sp, #56] + + ; Set target to TransitionBlock pointer + add $Target, sp, #192 + MEND + #define GC_ALLOC_FINALIZE 1 ;----------------------------------------------------------------------------- diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index fcc8835a49f1e4..2ad497051f0ad7 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -10871,43 +10871,428 @@ void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *p m_ReturnAddress = pTransitionBlock->m_ReturnAddress; } -#endif // TARGET_X86 +#elif defined(TARGET_AMD64) -// -// Init a new frame -// -void SoftwareExceptionFrame::Init() +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) { - WRAPPER_NO_CONTRACT; + LIMITED_METHOD_CONTRACT; - // On x86 we initialize the context state from transition block in - // UpdateContextFromTransitionBlock method. -#ifndef TARGET_X86 -#define CALLEE_SAVED_REGISTER(regname) m_ContextPointers.regname = NULL; + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + m_Context.SegCs = 0; + m_Context.SegSs = 0; + m_Context.EFlags = 0; + +#ifdef UNIX_AMD64_ABI + // On Unix AMD64, argument registers are saved in the transition block + m_Context.Rax = 0; + m_Context.Rdi = pTransitionBlock->m_argumentRegisters.RDI; + m_Context.Rsi = pTransitionBlock->m_argumentRegisters.RSI; + m_Context.Rdx = pTransitionBlock->m_argumentRegisters.RDX; + m_Context.Rcx = pTransitionBlock->m_argumentRegisters.RCX; + m_Context.R8 = pTransitionBlock->m_argumentRegisters.R8; + m_Context.R9 = pTransitionBlock->m_argumentRegisters.R9; + + m_ContextPointers.Rdi = &m_Context.Rdi; + m_ContextPointers.Rsi = &m_Context.Rsi; + m_ContextPointers.Rdx = &m_Context.Rdx; + m_ContextPointers.Rcx = &m_Context.Rcx; + m_ContextPointers.R8 = &m_Context.R8; + m_ContextPointers.R9 = &m_Context.R9; + + // Copy floating point argument registers (xmm0-xmm7) + // Use memcpy to avoid alignment issues - the source may not be 16-byte aligned + // depending on stack layout in the assembly helpers + BYTE *pFloatArgs = (BYTE*)pTransitionBlock + TransitionBlock::GetOffsetOfFloatArgumentRegisters(); + memcpy(&m_Context.Xmm0, pFloatArgs + 0x00, sizeof(m_Context.Xmm0)); + memcpy(&m_Context.Xmm1, pFloatArgs + 0x10, sizeof(m_Context.Xmm1)); + memcpy(&m_Context.Xmm2, pFloatArgs + 0x20, sizeof(m_Context.Xmm2)); + memcpy(&m_Context.Xmm3, pFloatArgs + 0x30, sizeof(m_Context.Xmm3)); + memcpy(&m_Context.Xmm4, pFloatArgs + 0x40, sizeof(m_Context.Xmm4)); + memcpy(&m_Context.Xmm5, pFloatArgs + 0x50, sizeof(m_Context.Xmm5)); + memcpy(&m_Context.Xmm6, pFloatArgs + 0x60, sizeof(m_Context.Xmm6)); + memcpy(&m_Context.Xmm7, pFloatArgs + 0x70, sizeof(m_Context.Xmm7)); + // Initialize remaining XMM registers to zero + memset(&m_Context.Xmm8, 0, sizeof(m_Context.Xmm8)); + memset(&m_Context.Xmm9, 0, sizeof(m_Context.Xmm9)); + memset(&m_Context.Xmm10, 0, sizeof(m_Context.Xmm10)); + memset(&m_Context.Xmm11, 0, sizeof(m_Context.Xmm11)); + memset(&m_Context.Xmm12, 0, sizeof(m_Context.Xmm12)); + memset(&m_Context.Xmm13, 0, sizeof(m_Context.Xmm13)); + memset(&m_Context.Xmm14, 0, sizeof(m_Context.Xmm14)); + memset(&m_Context.Xmm15, 0, sizeof(m_Context.Xmm15)); + // Initialize FP control/status + m_Context.MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) +#else + // On Windows AMD64, argument registers are not saved in the transition block + m_Context.Rax = 0; + m_Context.Rcx = 0; + m_Context.Rdx = 0; + m_Context.R8 = 0; + m_Context.R9 = 0; + + // Read FP callee-saved registers (xmm6-xmm15) from the stack + // They are stored at negative offsets from TransitionBlock: + // Layout: [xmm6-xmm15 (160 bytes)] [xmm0-xmm3 (64 bytes)] [shadow (32 bytes)] [padding (8 bytes)] [CalleeSavedRegs] [RetAddr] + // xmm6 is at sp+0, TransitionBlock is at sp+264, so xmm6 is at TransitionBlock - 264 + M128A *pFpCalleeSaved = (M128A*)((BYTE*)pTransitionBlock - 264); + m_Context.Xmm6 = pFpCalleeSaved[0]; + m_Context.Xmm7 = pFpCalleeSaved[1]; + m_Context.Xmm8 = pFpCalleeSaved[2]; + m_Context.Xmm9 = pFpCalleeSaved[3]; + m_Context.Xmm10 = pFpCalleeSaved[4]; + m_Context.Xmm11 = pFpCalleeSaved[5]; + m_Context.Xmm12 = pFpCalleeSaved[6]; + m_Context.Xmm13 = pFpCalleeSaved[7]; + m_Context.Xmm14 = pFpCalleeSaved[8]; + m_Context.Xmm15 = pFpCalleeSaved[9]; + + // Initialize FP control/status + m_Context.MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) +#endif + +#define CALLEE_SAVED_REGISTER(reg) \ + m_Context.reg = pTransitionBlock->m_calleeSavedRegisters.reg; \ + m_ContextPointers.reg = &m_Context.reg; ENUM_CALLEE_SAVED_REGISTERS(); #undef CALLEE_SAVED_REGISTER -#ifndef TARGET_UNIX - Thread::VirtualUnwindCallFrame(&m_Context, &m_ContextPointers); -#else // !TARGET_UNIX - BOOL success = PAL_VirtualUnwind(&m_Context, &m_ContextPointers); - if (!success) + m_Context.Rsp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Rip = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; +} + +#elif defined(TARGET_ARM) + +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) +{ + LIMITED_METHOD_CONTRACT; + + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + + // Copy argument registers (R0-R3) + m_Context.R0 = pTransitionBlock->m_argumentRegisters.r[0]; + m_Context.R1 = pTransitionBlock->m_argumentRegisters.r[1]; + m_Context.R2 = pTransitionBlock->m_argumentRegisters.r[2]; + m_Context.R3 = pTransitionBlock->m_argumentRegisters.r[3]; + + // Copy callee-saved registers (R4-R11, Lr) + m_Context.R4 = pTransitionBlock->m_calleeSavedRegisters.r4; + m_Context.R5 = pTransitionBlock->m_calleeSavedRegisters.r5; + m_Context.R6 = pTransitionBlock->m_calleeSavedRegisters.r6; + m_Context.R7 = pTransitionBlock->m_calleeSavedRegisters.r7; + m_Context.R8 = pTransitionBlock->m_calleeSavedRegisters.r8; + m_Context.R9 = pTransitionBlock->m_calleeSavedRegisters.r9; + m_Context.R10 = pTransitionBlock->m_calleeSavedRegisters.r10; + m_Context.R11 = pTransitionBlock->m_calleeSavedRegisters.r11; + m_Context.Lr = pTransitionBlock->m_calleeSavedRegisters.r14; // r14 is link register + + // Copy floating point argument registers (d0-d7 / s0-s15) + FloatArgumentRegisters *pFloatArgs = (FloatArgumentRegisters*)((BYTE*)pTransitionBlock + TransitionBlock::GetOffsetOfFloatArgumentRegisters()); + for (int i = 0; i < 8; i++) { - _ASSERTE(!"SoftwareExceptionFrame::Init failed"); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + m_Context.D[i] = pFloatArgs->d[i]; } -#endif // !TARGET_UNIX -#define CALLEE_SAVED_REGISTER(regname) if (m_ContextPointers.regname == NULL) m_ContextPointers.regname = &m_Context.regname; - ENUM_CALLEE_SAVED_REGISTERS(); -#undef CALLEE_SAVED_REGISTER + // Read FP callee-saved registers (d8-d15) from the stack + // They are stored at negative offset from TransitionBlock: + // Layout: [d8-d15 (64 bytes)] [padding (4)] [d0-d7 (64 bytes)] [padding (4)] [TransitionBlock] + // FP callee-saved are at TransitionBlock - 136 (64 + 4 + 64 + 4) + UINT64 *pFpCalleeSaved = (UINT64*)((BYTE*)pTransitionBlock - 136); + for (int i = 0; i < 8; i++) + { + m_Context.D[8 + i] = pFpCalleeSaved[i]; + } + + // Initialize remaining D registers (D16-D31) to zero - these are caller-saved + for (int i = 16; i < 32; i++) + { + m_Context.D[i] = 0; + } + // Initialize FP status/control register + m_Context.Fpscr = 0; + + // Set up context pointers for callee-saved registers + m_ContextPointers.R4 = &m_Context.R4; + m_ContextPointers.R5 = &m_Context.R5; + m_ContextPointers.R6 = &m_Context.R6; + m_ContextPointers.R7 = &m_Context.R7; + m_ContextPointers.R8 = &m_Context.R8; + m_ContextPointers.R9 = &m_Context.R9; + m_ContextPointers.R10 = &m_Context.R10; + m_ContextPointers.R11 = &m_Context.R11; + m_ContextPointers.Lr = &m_Context.Lr; + + m_Context.Sp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Pc = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; +} + +#elif defined(TARGET_ARM64) + +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) +{ + LIMITED_METHOD_CONTRACT; + + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + + // Copy argument registers (X0-X7) + for (int i = 0; i < 8; i++) + { + m_Context.X[i] = pTransitionBlock->m_argumentRegisters.x[i]; + } + + // Copy return buffer register (X8) + m_Context.X8 = pTransitionBlock->m_x8RetBuffReg; + + // Copy callee-saved registers (X19-X28) + m_Context.X19 = pTransitionBlock->m_calleeSavedRegisters.x19; + m_Context.X20 = pTransitionBlock->m_calleeSavedRegisters.x20; + m_Context.X21 = pTransitionBlock->m_calleeSavedRegisters.x21; + m_Context.X22 = pTransitionBlock->m_calleeSavedRegisters.x22; + m_Context.X23 = pTransitionBlock->m_calleeSavedRegisters.x23; + m_Context.X24 = pTransitionBlock->m_calleeSavedRegisters.x24; + m_Context.X25 = pTransitionBlock->m_calleeSavedRegisters.x25; + m_Context.X26 = pTransitionBlock->m_calleeSavedRegisters.x26; + m_Context.X27 = pTransitionBlock->m_calleeSavedRegisters.x27; + m_Context.X28 = pTransitionBlock->m_calleeSavedRegisters.x28; + + // Copy frame pointer and link register + m_Context.Fp = pTransitionBlock->m_calleeSavedRegisters.x29; + m_Context.Lr = pTransitionBlock->m_calleeSavedRegisters.x30; + + // Copy floating point argument registers (V0-V7) + FloatArgumentRegisters *pFloatArgs = (FloatArgumentRegisters*)((BYTE*)pTransitionBlock + TransitionBlock::GetOffsetOfFloatArgumentRegisters()); + for (int i = 0; i < 8; i++) + { + m_Context.V[i] = pFloatArgs->q[i]; + } + + // Read FP callee-saved registers (d8-d15) from the stack + // They are stored at negative offset from TransitionBlock: + // Layout: [d8-d15 (64 bytes)] [q0-q7 (128 bytes)] [TransitionBlock] + // FP callee-saved are at TransitionBlock - 192 (64 + 128) + UINT64 *pFpCalleeSaved = (UINT64*)((BYTE*)pTransitionBlock - 192); + m_Context.V[8].Low = pFpCalleeSaved[0]; + m_Context.V[8].High = 0; + m_Context.V[9].Low = pFpCalleeSaved[1]; + m_Context.V[9].High = 0; + m_Context.V[10].Low = pFpCalleeSaved[2]; + m_Context.V[10].High = 0; + m_Context.V[11].Low = pFpCalleeSaved[3]; + m_Context.V[11].High = 0; + m_Context.V[12].Low = pFpCalleeSaved[4]; + m_Context.V[12].High = 0; + m_Context.V[13].Low = pFpCalleeSaved[5]; + m_Context.V[13].High = 0; + m_Context.V[14].Low = pFpCalleeSaved[6]; + m_Context.V[14].High = 0; + m_Context.V[15].Low = pFpCalleeSaved[7]; + m_Context.V[15].High = 0; + + // Initialize remaining V registers (V16-V31) to zero - these are caller-saved + for (int i = 16; i < 32; i++) + { + m_Context.V[i].Low = 0; + m_Context.V[i].High = 0; + } + // Initialize FP control/status registers + m_Context.Fpcr = 0; + m_Context.Fpsr = 0; + + // Set up context pointers for callee-saved registers + m_ContextPointers.X19 = &m_Context.X19; + m_ContextPointers.X20 = &m_Context.X20; + m_ContextPointers.X21 = &m_Context.X21; + m_ContextPointers.X22 = &m_Context.X22; + m_ContextPointers.X23 = &m_Context.X23; + m_ContextPointers.X24 = &m_Context.X24; + m_ContextPointers.X25 = &m_Context.X25; + m_ContextPointers.X26 = &m_Context.X26; + m_ContextPointers.X27 = &m_Context.X27; + m_ContextPointers.X28 = &m_Context.X28; + m_ContextPointers.Fp = &m_Context.Fp; + m_ContextPointers.Lr = &m_Context.Lr; + + m_Context.Sp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Pc = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; +} + +#elif defined(TARGET_LOONGARCH64) + +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) +{ + LIMITED_METHOD_CONTRACT; - _ASSERTE(ExecutionManager::IsManagedCode(::GetIP(&m_Context))); + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + + // Copy argument registers (A0-A7) + m_Context.A0 = pTransitionBlock->m_argumentRegisters.a[0]; + m_Context.A1 = pTransitionBlock->m_argumentRegisters.a[1]; + m_Context.A2 = pTransitionBlock->m_argumentRegisters.a[2]; + m_Context.A3 = pTransitionBlock->m_argumentRegisters.a[3]; + m_Context.A4 = pTransitionBlock->m_argumentRegisters.a[4]; + m_Context.A5 = pTransitionBlock->m_argumentRegisters.a[5]; + m_Context.A6 = pTransitionBlock->m_argumentRegisters.a[6]; + m_Context.A7 = pTransitionBlock->m_argumentRegisters.a[7]; + + // Copy callee-saved registers (Fp, Ra, S0-S8) + m_Context.Fp = pTransitionBlock->m_calleeSavedRegisters.fp; + m_Context.Ra = pTransitionBlock->m_calleeSavedRegisters.ra; + m_Context.S0 = pTransitionBlock->m_calleeSavedRegisters.s0; + m_Context.S1 = pTransitionBlock->m_calleeSavedRegisters.s1; + m_Context.S2 = pTransitionBlock->m_calleeSavedRegisters.s2; + m_Context.S3 = pTransitionBlock->m_calleeSavedRegisters.s3; + m_Context.S4 = pTransitionBlock->m_calleeSavedRegisters.s4; + m_Context.S5 = pTransitionBlock->m_calleeSavedRegisters.s5; + m_Context.S6 = pTransitionBlock->m_calleeSavedRegisters.s6; + m_Context.S7 = pTransitionBlock->m_calleeSavedRegisters.s7; + m_Context.S8 = pTransitionBlock->m_calleeSavedRegisters.s8; + + // Copy floating point argument registers (fa0-fa7) + // F[] array in CONTEXT is 4*32 elements for LSX/LASX support. + // Each FP register takes 4 slots (for 256-bit LASX vectors). + // For 64-bit doubles, we only use the first slot of each register. + FloatArgumentRegisters *pFloatArgs = (FloatArgumentRegisters*)((BYTE*)pTransitionBlock + TransitionBlock::GetOffsetOfFloatArgumentRegisters()); + for (int i = 0; i < 8; i++) + { + memcpy(&m_Context.F[i * 4], &pFloatArgs->f[i], sizeof(double)); + } + + // Read FP callee-saved registers (f24-f31) from the stack + // They are stored at negative offset from TransitionBlock: + // Layout: [f24-f31 (64 bytes)] [fa0-fa7 (64 bytes)] [TransitionBlock] + // FP callee-saved are at TransitionBlock - 128 (64 + 64) + UINT64 *pFpCalleeSaved = (UINT64*)((BYTE*)pTransitionBlock - 128); + for (int i = 0; i < 8; i++) + { + // f24-f31 map to indices 24-31 in the F array, each taking 4 slots + memcpy(&m_Context.F[(24 + i) * 4], &pFpCalleeSaved[i], sizeof(double)); + } + + // Initialize remaining F registers (f8-f23) to zero + for (int i = 8; i < 24; i++) + { + memset(&m_Context.F[i * 4], 0, sizeof(double) * 4); + } + // Initialize FP control/status register + m_Context.Fcsr = 0; + + // Set up context pointers for callee-saved registers + m_ContextPointers.S0 = &m_Context.S0; + m_ContextPointers.S1 = &m_Context.S1; + m_ContextPointers.S2 = &m_Context.S2; + m_ContextPointers.S3 = &m_Context.S3; + m_ContextPointers.S4 = &m_Context.S4; + m_ContextPointers.S5 = &m_Context.S5; + m_ContextPointers.S6 = &m_Context.S6; + m_ContextPointers.S7 = &m_Context.S7; + m_ContextPointers.S8 = &m_Context.S8; + m_ContextPointers.Fp = &m_Context.Fp; + m_ContextPointers.Ra = &m_Context.Ra; + + m_Context.Sp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Pc = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; +} + +#elif defined(TARGET_RISCV64) + +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) +{ + LIMITED_METHOD_CONTRACT; - m_ReturnAddress = ::GetIP(&m_Context); -#endif // !TARGET_X86 + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + + // Copy argument registers (A0-A7) + m_Context.A0 = pTransitionBlock->m_argumentRegisters.a[0]; + m_Context.A1 = pTransitionBlock->m_argumentRegisters.a[1]; + m_Context.A2 = pTransitionBlock->m_argumentRegisters.a[2]; + m_Context.A3 = pTransitionBlock->m_argumentRegisters.a[3]; + m_Context.A4 = pTransitionBlock->m_argumentRegisters.a[4]; + m_Context.A5 = pTransitionBlock->m_argumentRegisters.a[5]; + m_Context.A6 = pTransitionBlock->m_argumentRegisters.a[6]; + m_Context.A7 = pTransitionBlock->m_argumentRegisters.a[7]; + + // Copy callee-saved registers (Fp, Ra, S1-S11, Tp, Gp) + m_Context.Fp = pTransitionBlock->m_calleeSavedRegisters.fp; + m_Context.Ra = pTransitionBlock->m_calleeSavedRegisters.ra; + m_Context.S1 = pTransitionBlock->m_calleeSavedRegisters.s1; + m_Context.S2 = pTransitionBlock->m_calleeSavedRegisters.s2; + m_Context.S3 = pTransitionBlock->m_calleeSavedRegisters.s3; + m_Context.S4 = pTransitionBlock->m_calleeSavedRegisters.s4; + m_Context.S5 = pTransitionBlock->m_calleeSavedRegisters.s5; + m_Context.S6 = pTransitionBlock->m_calleeSavedRegisters.s6; + m_Context.S7 = pTransitionBlock->m_calleeSavedRegisters.s7; + m_Context.S8 = pTransitionBlock->m_calleeSavedRegisters.s8; + m_Context.S9 = pTransitionBlock->m_calleeSavedRegisters.s9; + m_Context.S10 = pTransitionBlock->m_calleeSavedRegisters.s10; + m_Context.S11 = pTransitionBlock->m_calleeSavedRegisters.s11; + m_Context.Tp = pTransitionBlock->m_calleeSavedRegisters.tp; + m_Context.Gp = pTransitionBlock->m_calleeSavedRegisters.gp; + + // Initialize all F registers to zero first + memset(m_Context.F, 0, sizeof(m_Context.F)); + // Copy floating point argument registers (fa0-fa7) + FloatArgumentRegisters *pFloatArgs = (FloatArgumentRegisters*)((BYTE*)pTransitionBlock + TransitionBlock::GetOffsetOfFloatArgumentRegisters()); + for (int i = 0; i < 8; i++) + { + // F[10-17] are fa0-fa7 in RISC-V register naming + memcpy(&m_Context.F[10 + i], &pFloatArgs->f[i], sizeof(double)); + } + + // Read FP callee-saved registers (fs0-fs11) from the stack + // They are stored at negative offset from TransitionBlock: + // Layout: [fs0-fs11 (96 bytes)] [fa0-fa7 (64 bytes)] [TransitionBlock] + // FP callee-saved are at TransitionBlock - 160 (96 + 64) + // RISC-V FP callee-saved: fs0=f8, fs1=f9, fs2-fs11=f18-f27 + UINT64 *pFpCalleeSaved = (UINT64*)((BYTE*)pTransitionBlock - 160); + memcpy(&m_Context.F[8], &pFpCalleeSaved[0], sizeof(double)); // fs0 = f8 + memcpy(&m_Context.F[9], &pFpCalleeSaved[1], sizeof(double)); // fs1 = f9 + for (int i = 0; i < 10; i++) + { + memcpy(&m_Context.F[18 + i], &pFpCalleeSaved[2 + i], sizeof(double)); // fs2-fs11 = f18-f27 + } + + // Initialize FP control/status register + m_Context.Fcsr = 0; + + // Set up context pointers for callee-saved registers + m_ContextPointers.S1 = &m_Context.S1; + m_ContextPointers.S2 = &m_Context.S2; + m_ContextPointers.S3 = &m_Context.S3; + m_ContextPointers.S4 = &m_Context.S4; + m_ContextPointers.S5 = &m_Context.S5; + m_ContextPointers.S6 = &m_Context.S6; + m_ContextPointers.S7 = &m_Context.S7; + m_ContextPointers.S8 = &m_Context.S8; + m_ContextPointers.S9 = &m_Context.S9; + m_ContextPointers.S10 = &m_Context.S10; + m_ContextPointers.S11 = &m_Context.S11; + m_ContextPointers.Fp = &m_Context.Fp; + m_ContextPointers.Gp = &m_Context.Gp; + m_ContextPointers.Tp = &m_Context.Tp; + m_ContextPointers.Ra = &m_Context.Ra; + + m_Context.Sp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Pc = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; } +#elif defined(TARGET_WASM) + +void SoftwareExceptionFrame::UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock) +{ + LIMITED_METHOD_CONTRACT; + + // WASM cannot capture execution context, so just zero everything + memset(&m_Context, 0, sizeof(m_Context)); + memset(&m_ContextPointers, 0, sizeof(m_ContextPointers)); + m_ReturnAddress = 0; +} + +#endif // TARGET_X86 + // // Init and Link in a new frame // @@ -10915,8 +11300,6 @@ void SoftwareExceptionFrame::InitAndLink(Thread *pThread) { WRAPPER_NO_CONTRACT; - Init(); - Push(pThread); } diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 47e1175a7d157a..cfcea97ab1bc15 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -985,13 +985,11 @@ class SoftwareExceptionFrame : public Frame public: #ifndef DACCESS_COMPILE - SoftwareExceptionFrame() : Frame(FrameIdentifier::SoftwareExceptionFrame) { + SoftwareExceptionFrame() : Frame(FrameIdentifier::SoftwareExceptionFrame), m_ReturnAddress(0) { LIMITED_METHOD_CONTRACT; } -#ifdef TARGET_X86 void UpdateContextFromTransitionBlock(TransitionBlock *pTransitionBlock); -#endif #endif TADDR GetReturnAddressPtr_Impl() @@ -1001,7 +999,6 @@ class SoftwareExceptionFrame : public Frame } #ifndef DACCESS_COMPILE - void Init(); void InitAndLink(Thread *pThread); #endif diff --git a/src/coreclr/vm/i386/asmhelpers.S b/src/coreclr/vm/i386/asmhelpers.S index 62578e542c87c2..29b76b1f220ffb 100644 --- a/src/coreclr/vm/i386/asmhelpers.S +++ b/src/coreclr/vm/i386/asmhelpers.S @@ -1032,7 +1032,7 @@ LEAF_ENTRY IL_Throw, _TEXT CHECK_STACK_ALIGNMENT - call C_FUNC(IL_Throw_x86) + call C_FUNC(IL_Throw_Impl) add esp, STACK_ALIGN_PADDING #undef STACK_ALIGN_PADDING @@ -1058,7 +1058,7 @@ LEAF_ENTRY IL_ThrowExact, _TEXT CHECK_STACK_ALIGNMENT - call C_FUNC(IL_ThrowExact_x86) + call C_FUNC(IL_ThrowExact_Impl) add esp, STACK_ALIGN_PADDING #undef STACK_ALIGN_PADDING @@ -1081,7 +1081,7 @@ LEAF_ENTRY IL_Rethrow, _TEXT CHECK_STACK_ALIGNMENT - call C_FUNC(IL_Rethrow_x86) + call C_FUNC(IL_Rethrow_Impl) add esp, STACK_ALIGN_PADDING #undef STACK_ALIGN_PADDING diff --git a/src/coreclr/vm/i386/asmhelpers.asm b/src/coreclr/vm/i386/asmhelpers.asm index 2b74e0ac6d9b51..71e6edfeb3a443 100644 --- a/src/coreclr/vm/i386/asmhelpers.asm +++ b/src/coreclr/vm/i386/asmhelpers.asm @@ -82,9 +82,9 @@ EXTERN g_chained_lookup_miss_counter:DWORD EXTERN g_dispatch_cache_chain_success_counter:DWORD endif -EXTERN @IL_Throw_x86@8:PROC -EXTERN @IL_ThrowExact_x86@8:PROC -EXTERN @IL_Rethrow_x86@4:PROC +EXTERN @IL_Throw_Impl@8:PROC +EXTERN @IL_ThrowExact_Impl@8:PROC +EXTERN @IL_Rethrow_Impl@4:PROC UNREFERENCED macro arg local unref @@ -1635,7 +1635,7 @@ FASTCALL_FUNC IL_Throw, 4 STUB_PROLOG mov edx, esp - call @IL_Throw_x86@8 + call @IL_Throw_Impl@8 STUB_EPILOG ret 4 @@ -1652,7 +1652,7 @@ FASTCALL_FUNC IL_ThrowExact, 4 STUB_PROLOG mov edx, esp - call @IL_ThrowExact_x86@8 + call @IL_ThrowExact_Impl@8 STUB_EPILOG ret 4 @@ -1666,7 +1666,7 @@ FASTCALL_FUNC IL_Rethrow, 0 STUB_PROLOG mov ecx, esp - call @IL_Rethrow_x86@4 + call @IL_Rethrow_Impl@4 STUB_EPILOG ret 4 diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index cd97c0fe3d26df..1c6fbd901c46e5 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -745,9 +745,6 @@ HCIMPL1(EnregisteredTypeHandle, JIT_GetClassFromMethodParam, MethodDesc* pMD) HCIMPLEND #include - - - //======================================================================== // // EXCEPTION HELPERS @@ -765,12 +762,8 @@ HCIMPLEND /*************************************************************/ -#if defined(TARGET_X86) EXTERN_C FCDECL1(void, IL_Throw, Object* obj); -EXTERN_C HCIMPL2(void, IL_Throw_x86, Object* obj, TransitionBlock* transitionBlock) -#else -HCIMPL1(void, IL_Throw, Object* obj) -#endif +EXTERN_C HCIMPL2(void, IL_Throw_Impl, Object* obj, TransitionBlock* transitionBlock) { FCALL_CONTRACT; @@ -782,11 +775,7 @@ HCIMPL1(void, IL_Throw, Object* obj) Thread *pThread = GetThread(); SoftwareExceptionFrame exceptionFrame; -#ifdef TARGET_X86 exceptionFrame.UpdateContextFromTransitionBlock(transitionBlock); -#else - RtlCaptureContext(exceptionFrame.GetContext()); -#endif exceptionFrame.InitAndLink(pThread); FC_CAN_TRIGGER_GC(); @@ -804,23 +793,15 @@ HCIMPLEND /*************************************************************/ -#if defined(TARGET_X86) EXTERN_C FCDECL0(void, IL_Rethrow); -EXTERN_C HCIMPL1(void, IL_Rethrow_x86, TransitionBlock* transitionBlock) -#else -HCIMPL0(void, IL_Rethrow) -#endif +EXTERN_C HCIMPL1(void, IL_Rethrow_Impl, TransitionBlock* transitionBlock) { FCALL_CONTRACT; Thread *pThread = GetThread(); SoftwareExceptionFrame exceptionFrame; -#ifdef TARGET_X86 exceptionFrame.UpdateContextFromTransitionBlock(transitionBlock); -#else - RtlCaptureContext(exceptionFrame.GetContext()); -#endif exceptionFrame.InitAndLink(pThread); FC_CAN_TRIGGER_GC(); @@ -832,12 +813,8 @@ HCIMPL0(void, IL_Rethrow) } HCIMPLEND -#if defined(TARGET_X86) EXTERN_C FCDECL1(void, IL_ThrowExact, Object* obj); -EXTERN_C HCIMPL2(void, IL_ThrowExact_x86, Object* obj, TransitionBlock* transitionBlock) -#else -HCIMPL1(void, IL_ThrowExact, Object* obj) -#endif +EXTERN_C HCIMPL2(void, IL_ThrowExact_Impl, Object* obj, TransitionBlock* transitionBlock) { FCALL_CONTRACT; @@ -850,11 +827,7 @@ HCIMPL1(void, IL_ThrowExact, Object* obj) Thread *pThread = GetThread(); SoftwareExceptionFrame exceptionFrame; -#ifdef TARGET_X86 exceptionFrame.UpdateContextFromTransitionBlock(transitionBlock); -#else - RtlCaptureContext(exceptionFrame.GetContext()); -#endif exceptionFrame.InitAndLink(pThread); FC_CAN_TRIGGER_GC(); @@ -866,6 +839,31 @@ HCIMPL1(void, IL_ThrowExact, Object* obj) } HCIMPLEND +#ifdef TARGET_WASM +// WASM doesn't have assembly stubs, so provide thin wrapper entry points +// that call the _Impl functions with NULL (which zeros the context) +HCIMPL1(void, IL_Throw, Object* obj) +{ + FCALL_CONTRACT; + IL_Throw_Impl(obj, NULL); +} +HCIMPLEND + +HCIMPL0(void, IL_Rethrow) +{ + FCALL_CONTRACT; + IL_Rethrow_Impl(NULL); +} +HCIMPLEND + +HCIMPL1(void, IL_ThrowExact, Object* obj) +{ + FCALL_CONTRACT; + IL_ThrowExact_Impl(obj, NULL); +} +HCIMPLEND +#endif // TARGET_WASM + #ifndef STATUS_STACK_BUFFER_OVERRUN // Not defined yet in CESDK includes # define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS)0xC0000409L) #endif diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S index 457f21b34ef022..9f424c39dd30f5 100644 --- a/src/coreclr/vm/loongarch64/asmhelpers.S +++ b/src/coreclr/vm/loongarch64/asmhelpers.S @@ -1020,3 +1020,48 @@ LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT move $a1, $t0 // Move temp register to first arg register for static method with return buffer EPILOG_BRANCH_REG $METHODDESC_REGISTER LEAF_END ThisPtrRetBufPrecodeWorker, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Throw_Impl +// implementation written in C. +// +// Input state: +// $a0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Throw, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS $a1 + // $a0 already contains exception object + // $a1 contains pointer to TransitionBlock + bl C_FUNC(IL_Throw_Impl) + // Should never return + break 0 +NESTED_END IL_Throw, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_ThrowExact_Impl +// implementation written in C. +// +// Input state: +// $a0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_ThrowExact, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS $a1 + // $a0 already contains exception object + // $a1 contains pointer to TransitionBlock + bl C_FUNC(IL_ThrowExact_Impl) + // Should never return + break 0 +NESTED_END IL_ThrowExact, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Rethrow_Impl +// implementation written in C. +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS $a0 + // $a0 contains pointer to TransitionBlock + bl C_FUNC(IL_Rethrow_Impl) + // Should never return + break 0 +NESTED_END IL_Rethrow, _TEXT + diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index db92be31a188f3..3804dc264cdb17 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -43,6 +43,10 @@ #include "gdbjit.h" #endif // FEATURE_GDBJIT +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) +#include +#endif + #ifndef DACCESS_COMPILE EXTERN_C void STDCALL ThePreStubPatch(); @@ -1805,6 +1809,15 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method { PCODE pbRetVal = (PCODE)NULL; +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) + // Reset MXCSR to default value to prevent FP exceptions during JIT compilation. + // During exception handling, MXCSR can become corrupted (e.g., when RtlRestoreContext + // is called with a context that doesn't include CONTEXT_FLOATING_POINT). If the JIT + // is invoked during exception handling (e.g., to compile a catch handler), it may + // crash with an FP exception due to unmasked floating point exceptions. + _mm_setcsr(0x1F80); +#endif + PreserveLastErrorHolder preserveLastError; STATIC_CONTRACT_THROWS; diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 64b24957211ec8..0d26ef514d4f58 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -878,6 +878,50 @@ LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT EPILOG_BRANCH_REG t2 LEAF_END ThisPtrRetBufPrecodeWorker, _TEXT +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Throw_Impl +// implementation written in C. +// +// Input state: +// a0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Throw, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS a1 + // a0 already contains exception object + // a1 contains pointer to TransitionBlock + call C_FUNC(IL_Throw_Impl) + // Should never return + ebreak +NESTED_END IL_Throw, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_ThrowExact_Impl +// implementation written in C. +// +// Input state: +// a0 = Pointer to exception object +// ------------------------------------------------------------------ +NESTED_ENTRY IL_ThrowExact, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS a1 + // a0 already contains exception object + // a1 contains pointer to TransitionBlock + call C_FUNC(IL_ThrowExact_Impl) + // Should never return + ebreak +NESTED_END IL_ThrowExact, _TEXT + +// ------------------------------------------------------------------ +// Capture a transition block with register values and call the IL_Rethrow_Impl +// implementation written in C. +// ------------------------------------------------------------------ +NESTED_ENTRY IL_Rethrow, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS a0 + // a0 contains pointer to TransitionBlock + call C_FUNC(IL_Rethrow_Impl) + // Should never return + ebreak +NESTED_END IL_Rethrow, _TEXT + #ifdef FEATURE_INTERPRETER // Align interpreter stack by adjusting it by 8 bytes