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 125143377f0ecf..dc8c5eeec42f91 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 @@ -517,8 +520,9 @@ NESTED_ENTRY CallEHFunclet, _TEXT movdqa xmm14, [r8 + OFFSETOF__CONTEXT__Xmm14] movdqa xmm15, [r8 + OFFSETOF__CONTEXT__Xmm15] - ; Save the SP of this function. + ; Save the SP of this function. mov [r9], rsp + ; Invoke the funclet call rdx @@ -543,6 +547,7 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT mov [r9], rsp ; Restore RBP to match main function RBP mov rbp, rdx + ; Invoke the filter funclet call r8 @@ -1199,4 +1204,54 @@ 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 + ; Shadow space for the call is included in PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS + 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 + ; Shadow space for the call is included in PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS + 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 + ; Shadow space for the call is included in PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS + 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..07531371d6627d 100644 --- a/src/coreclr/vm/amd64/AsmMacros.inc +++ b/src/coreclr/vm/amd64/AsmMacros.inc @@ -485,5 +485,55 @@ 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) +; Shadow space for call (32 bytes) +; sp points here +; +; On exit, target contains the TransitionBlock pointer (CalleeSavedRegisters). +PUSH_COOP_PINVOKE_FRAME_WITH_FLOATS macro target + + PUSH_CALLEE_SAVED_REGISTERS + + ; 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 at offset 256 (32 + 160 + 64) + SAVE_ARGUMENT_REGISTERS 256 + + ; Save float argument registers at offset 192 (32 + 160) + SAVE_FLOAT_ARGUMENT_REGISTERS 192 + + ; Save FP callee-saved registers (xmm6-xmm15) at offset 32 (after shadow space) + ; RSP is 16-byte aligned, so offset 32, 48, 64, ... are all 16-byte aligned + ; AND these offsets are multiples of 16 as required by unwind codes + save_xmm128_postrsp xmm6, 20h + save_xmm128_postrsp xmm7, 30h + save_xmm128_postrsp xmm8, 40h + save_xmm128_postrsp xmm9, 50h + save_xmm128_postrsp xmm10, 60h + save_xmm128_postrsp xmm11, 70h + save_xmm128_postrsp xmm12, 80h + save_xmm128_postrsp xmm13, 90h + save_xmm128_postrsp xmm14, 0A0h + save_xmm128_postrsp xmm15, 0B0h + + END_PROLOGUE + + ; TransitionBlock pointer points to CalleeSavedRegisters at rsp + 296 + lea target, [rsp + 296] + + 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..3141735397593b 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 + PROLOG_WITH_TRANSITION_BLOCK + // rdi already contains exception object + lea rsi, [rsp + __PWTB_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 + PROLOG_WITH_TRANSITION_BLOCK + // rdi already contains exception object + lea rsi, [rsp + __PWTB_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 + PROLOG_WITH_TRANSITION_BLOCK + lea rdi, [rsp + __PWTB_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 437ab1a527016d..71448ca3948d27 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -24,6 +24,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 @@ -3011,5 +3014,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 1df93c2061ca0c..8b670c6ea4911d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -10746,43 +10746,390 @@ 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; - ENUM_CALLEE_SAVED_REGISTERS(); -#undef CALLEE_SAVED_REGISTER +#ifdef UNIX_AMD64_ABI + // On Unix AMD64, there are no non-volatile FP registers, so we only need + // control registers and integer callee-saved registers. We don't need to + // capture argument registers or FP state for exception handling. + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + m_Context.SegCs = 0; + m_Context.SegSs = 0; + m_Context.EFlags = 0; + m_Context.Rax = 0; +#else + // On Windows AMD64, we need FP state because xmm6-xmm15 are non-volatile + m_Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + m_Context.SegCs = 0; + m_Context.SegSs = 0; + m_Context.EFlags = 0; -#ifndef TARGET_UNIX - Thread::VirtualUnwindCallFrame(&m_Context, &m_ContextPointers); -#else // !TARGET_UNIX - BOOL success = PAL_VirtualUnwind(&m_Context, &m_ContextPointers); - if (!success) - { - _ASSERTE(!"SoftwareExceptionFrame::Init failed"); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } -#endif // !TARGET_UNIX + // 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)] [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]; + 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 in FltSave - this is what fxrstor restores from + m_Context.FltSave.ControlWord = 0x27F; // Default x87 control word + m_Context.FltSave.MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) + m_Context.FltSave.MxCsr_Mask = 0x1FFF; // MXCSR mask + m_Context.MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) +#endif -#define CALLEE_SAVED_REGISTER(regname) if (m_ContextPointers.regname == NULL) m_ContextPointers.regname = &m_Context.regname; +#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 - _ASSERTE(ExecutionManager::IsManagedCode(::GetIP(&m_Context))); + m_Context.Rsp = (UINT_PTR)(pTransitionBlock + 1); + m_Context.Rip = pTransitionBlock->m_ReturnAddress; + m_ReturnAddress = pTransitionBlock->m_ReturnAddress; +} - m_ReturnAddress = ::GetIP(&m_Context); -#endif // !TARGET_X86 +#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++) + { + m_Context.D[i] = pFloatArgs->d[i]; + } + + // 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 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; + + 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_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 // @@ -10790,8 +11137,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 bbe17c2ef9e885..8ec28c47132c6e 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -1028,13 +1028,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() @@ -1044,7 +1042,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 fa2075019598ae..0520a652ff4ac1 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/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