diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index c3eb6e1c8bb778..54c8ebd5dcb1bc 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -304,6 +304,7 @@ set(VM_SOURCES_WKS callconvbuilder.cpp callhelpers.cpp callsiteinspect.cpp + callstubgenerator.cpp clrconfignative.cpp clrex.cpp clrvarargs.cpp @@ -449,6 +450,7 @@ set(VM_HEADERS_WKS interoputil.inl interpexec.h interpframeallocator.h + callstubgenerator.h invokeutil.h managedmdimport.hpp marshalnative.h diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index b80a2e51c69809..d621ce65cb97a7 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -482,23 +482,6 @@ JIT_PollGCRarePath: TAILJMP_RAX LEAF_END JIT_PollGC, _TEXT -ifdef FEATURE_INTERPRETER -NESTED_ENTRY InterpreterStub, _TEXT - - PROLOG_WITH_TRANSITION_BLOCK - - ; - ; call ExecuteInterpretedMethod - ; - lea rcx, [rsp + __PWTB_TransitionBlock] ; pTransitionBlock* - mov rdx, METHODDESC_REGISTER - call ExecuteInterpretedMethod - - EPILOG_WITH_TRANSITION_BLOCK_RETURN - -NESTED_END InterpreterStub, _TEXT -endif ; FEATURE_INTERPRETER - ; rcx -This pointer ; rdx -ReturnBuffer LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT @@ -629,4 +612,305 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT ret NESTED_END CallEHFilterFunclet, _TEXT +ifdef FEATURE_INTERPRETER + +NESTED_ENTRY InterpreterStub, _TEXT + + PROLOG_WITH_TRANSITION_BLOCK + + ; + ; call ExecuteInterpretedMethod + ; + lea rcx, [rsp + __PWTB_TransitionBlock] ; pTransitionBlock* + mov rdx, METHODDESC_REGISTER + call ExecuteInterpretedMethod + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + +NESTED_END InterpreterStub, _TEXT + +; Copy arguments from the interpreter stack to the processor stack. +; The CPU stack slots are aligned to pointer size. +LEAF_ENTRY Load_Stack, _TEXT + push rdi + push rsi + push rcx + mov edi, dword ptr [r11 + 8] ; SP offset + mov ecx, dword ptr [r11 + 12] ; number of stack slots + add edi, 20h ; the 3 pushes above plus return address + add rdi, rsp + mov rsi, r10 + shr rcx, 3 + rep movsq + mov r10, rsi + pop rcx + pop rsi + pop rdi + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Stack, _TEXT + +; Routines for passing value type arguments by reference in general purpose registers RCX, RDX, R8, R9 + +LEAF_ENTRY Load_Ref_RCX, _TEXT + mov rcx, r10 + add r10, [r11 + 8] ; size of the value type + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Ref_RCX, _TEXT + +LEAF_ENTRY Load_Ref_RDX, _TEXT + mov rdx, r10 + add r10, [r11 + 8] ; size of the value type + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Ref_RDX, _TEXT + +LEAF_ENTRY Load_Ref_R8, _TEXT + mov r8, r10 + add r10, [r11 + 8] ; size of the value type + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Ref_R8, _TEXT + +LEAF_ENTRY Load_Ref_R9, _TEXT + mov r9, r10 + add r10, [r11 + 8] ; size of the value type + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Ref_R9, _TEXT + +; Routines for passing arguments by value in general purpose registers RCX, RDX, R8, R9 + +LEAF_ENTRY Load_RCX, _TEXT + mov rcx, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX, _TEXT + +LEAF_ENTRY Load_RCX_RDX, _TEXT + mov rcx, [r10] + mov rdx, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX_RDX, _TEXT + +LEAF_ENTRY Load_RCX_RDX_R8, _TEXT + mov rcx, [r10] + mov rdx, [r10 + 8] + mov r8, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX_RDX_R8, _TEXT + +LEAF_ENTRY Load_RCX_RDX_R8_R9, _TEXT + mov rcx, [r10] + mov rdx, [r10 + 8] + mov r8, [r10 + 16] + mov r9, [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX_RDX_R8_R9, _TEXT + +LEAF_ENTRY Load_RDX, _TEXT + mov rdx, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX, _TEXT + +LEAF_ENTRY Load_RDX_R8, _TEXT + mov rdx, [r10] + mov r8, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_R8, _TEXT + +LEAF_ENTRY Load_RDX_R8_R9, _TEXT + mov rdx, [r10] + mov r8, [r10 + 8] + mov r9, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_R8_R9, _TEXT + +LEAF_ENTRY Load_R8, _TEXT + mov r8, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R8, _TEXT + +LEAF_ENTRY Load_R8_R9, _TEXT + mov r8, [r10] + mov r9, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R8_R9, _TEXT + +LEAF_ENTRY Load_R9, _TEXT + mov r9, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R9, _TEXT + +; Routines for passing arguments in floating point registers XMM0..XMM3 + +LEAF_ENTRY Load_XMM0, _TEXT + movsd xmm0, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + add r10, 10h + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM1, _TEXT + movsd xmm1, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM2, _TEXT + movsd xmm2, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM3, _TEXT + movsd xmm3, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3, _TEXT + +NESTED_ENTRY CallJittedMethodRetVoid, _TEXT + push_vol_reg rbp + mov rbp, rsp +END_PROLOGUE + add r9, 20h ; argument save area + alignment + sub rsp, r9 ; total stack space + mov r11, rcx ; The routines list + mov r10, rdx ; interpreter stack args + call qword ptr [r11] + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetVoid, _TEXT + +NESTED_ENTRY CallJittedMethodRetBuff, _TEXT + push_vol_reg rbp + mov rbp, rsp +END_PROLOGUE + add r9, 20h ; argument save area + alignment + sub rsp, r9 ; total stack space + mov r11, rcx ; The routines list + mov r10, rdx ; interpreter stack args + mov rcx, r8 ; return buffer + call qword ptr [r11] + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetBuff, _TEXT + +NESTED_ENTRY CallJittedMethodRetDouble, _TEXT + push_nonvol_reg rbp + mov rbp, rsp + push_vol_reg r8 + push_vol_reg rax ; align +END_PROLOGUE + add r9, 20h ; argument save area + alignment + sub rsp, r9 ; total stack space + mov r11, rcx ; The routines list + mov r10, rdx ; interpreter stack args + call qword ptr [r11] + add rsp, 20h + mov r8, [rbp - 8] + movsd real8 ptr [r8], xmm0 + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetDouble, _TEXT + +NESTED_ENTRY CallJittedMethodRetI8, _TEXT + push_nonvol_reg rbp + mov rbp, rsp + push_vol_reg r8 + push_vol_reg rax ; align +END_PROLOGUE + add r9, 20h ; argument save area + alignment + sub rsp, r9 ; total stack space + mov r11, rcx ; The routines list + mov r10, rdx ; interpreter stack args + call qword ptr [r11] + add rsp, 20h + mov r8, [rbp - 8] + mov qword ptr [r8], rax + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetI8, _TEXT + +endif ; FEATURE_INTERPRETER + end diff --git a/src/coreclr/vm/amd64/asmhelpers.S b/src/coreclr/vm/amd64/asmhelpers.S index c90c535a493840..24d303da7622da 100644 --- a/src/coreclr/vm/amd64/asmhelpers.S +++ b/src/coreclr/vm/amd64/asmhelpers.S @@ -334,25 +334,6 @@ LOCAL_LABEL(JIT_PollGCRarePath): jmp rax LEAF_END JIT_PollGC, _TEXT -#ifdef FEATURE_INTERPRETER -NESTED_ENTRY InterpreterStub, _TEXT, NoHandler - - PROLOG_WITH_TRANSITION_BLOCK 8, 0, 0, 0, 0 - mov [rsp], rax // Return buffer in Swift calling convention - - # - # call ExecuteInterpretedMethod - # - lea rdi, [rsp + __PWTB_TransitionBlock] // pTransitionBlock* - mov rsi, METHODDESC_REGISTER - call C_FUNC(ExecuteInterpretedMethod) - - mov rax, [rsp] - EPILOG_WITH_TRANSITION_BLOCK_RETURN - -NESTED_END InterpreterStub, _TEXT -#endif // FEATURE_INTERPRETER - //rdi -This pointer //rsi -ReturnBuffer LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT @@ -452,3 +433,725 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler FUNCLET_CALL_EPILOGUE ret NESTED_END CallEHFilterFunclet, _TEXT + +#ifdef FEATURE_INTERPRETER + +NESTED_ENTRY InterpreterStub, _TEXT, NoHandler + + PROLOG_WITH_TRANSITION_BLOCK 8, 0, 0, 0, 0 + mov [rsp], rax // Return buffer in Swift calling convention + + # + # call ExecuteInterpretedMethod + # + lea rdi, [rsp + __PWTB_TransitionBlock] // pTransitionBlock* + mov rsi, METHODDESC_REGISTER + call C_FUNC(ExecuteInterpretedMethod) + + mov rax, [rsp] + EPILOG_WITH_TRANSITION_BLOCK_RETURN + +NESTED_END InterpreterStub, _TEXT + +// Copy arguments from the interpreter stack to the processor stack. +// The CPU stack slots are aligned to pointer size. +LEAF_ENTRY Load_Stack, _TEXT + push rdi + push rsi + push rcx + mov edi, dword ptr [r11 + 8] // SP offset + mov ecx, dword ptr [r11 + 12] // number of stack slots + add edi, 0x20 // the 3 pushes above plus return address + add rdi, rsp + mov rsi, r10 + shr rcx, 3 + rep movsq + mov r10, rsi + pop rcx + pop rsi + pop rdi + add r11, 16 + jmp qword ptr [r11] +LEAF_END Load_Stack, _TEXT + +// Routines for passing arguments by value in general purpose registers RDI, RSI, RDX, RCX, R8, R9 + +LEAF_ENTRY Load_RDI, _TEXT + mov rdi, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI, _TEXT + +LEAF_ENTRY Load_RDI_RSI, _TEXT + mov rdi, [r10] + mov rsi, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI_RSI, _TEXT + +LEAF_ENTRY Load_RDI_RSI_RDX, _TEXT + mov rdi, [r10] + mov rsi, [r10 + 8] + mov rdx, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI_RSI_RDX, _TEXT + +LEAF_ENTRY Load_RDI_RSI_RDX_RCX, _TEXT + mov rdi, [r10] + mov rsi, [r10 + 8] + mov rdx, [r10 + 16] + mov rcx, [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI_RSI_RDX_RCX, _TEXT + +LEAF_ENTRY Load_RDI_RSI_RDX_RCX_R8, _TEXT + mov rdi, [r10] + mov rsi, [r10 + 8] + mov rdx, [r10 + 16] + mov rcx, [r10 + 24] + mov r8, [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI_RSI_RDX_RCX_R8, _TEXT + +LEAF_ENTRY Load_RDI_RSI_RDX_RCX_R8_R9, _TEXT + mov rdi, [r10] + mov rsi, [r10 + 8] + mov rdx, [r10 + 16] + mov rcx, [r10 + 24] + mov r8, [r10 + 32] + mov r9, [r10 + 40] + add r10, 48 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDI_RSI_RDX_RCX_R8_R9, _TEXT + +LEAF_ENTRY Load_RSI, _TEXT + mov rsi, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RSI, _TEXT + +LEAF_ENTRY Load_RSI_RDX, _TEXT + mov rsi, [r10] + mov rdx, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RSI_RDX, _TEXT + +LEAF_ENTRY Load_RSI_RDX_RCX, _TEXT + mov rsi, [r10] + mov rdx, [r10 + 8] + mov rcx, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RSI_RDX_RCX, _TEXT + +LEAF_ENTRY Load_RSI_RDX_RCX_R8, _TEXT + mov rsi, [r10] + mov rdx, [r10 + 8] + mov rcx, [r10 + 16] + mov r8, [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RSI_RDX_RCX_R8, _TEXT + +LEAF_ENTRY Load_RSI_RDX_RCX_R8_R9, _TEXT + mov rsi, [r10] + mov rdx, [r10 + 8] + mov rcx, [r10 + 16] + mov r8, [r10 + 24] + mov r9, [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RSI_RDX_RCX_R8_R9, _TEXT + +LEAF_ENTRY Load_RDX, _TEXT + mov rdx, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX, _TEXT + +LEAF_ENTRY Load_RDX_RCX, _TEXT + mov rdx, [r10] + mov rcx, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_RCX, _TEXT + +LEAF_ENTRY Load_RDX_RCX_R8, _TEXT + mov rdx, [r10] + mov rcx, [r10 + 8] + mov r8, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_RCX_R8, _TEXT + +LEAF_ENTRY Load_RDX_RCX_R8_R9, _TEXT + mov rdx, [r10] + mov rcx, [r10 + 8] + mov r8, [r10 + 16] + mov r9, [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_RCX_R8_R9, _TEXT + +LEAF_ENTRY Load_RCX, _TEXT + mov rcx, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX, _TEXT + +LEAF_ENTRY Load_RCX_R8, _TEXT + mov rcx, [r10] + mov r8, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX_R8, _TEXT + +LEAF_ENTRY Load_RCX_R8_R9, _TEXT + mov rcx, [r10] + mov r8, [r10 + 8] + mov r9, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RCX_R8_R9, _TEXT + +LEAF_ENTRY Load_RDX_R8, _TEXT + mov rdx, [r10] + mov r8, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_R8, _TEXT + +LEAF_ENTRY Load_RDX_R8_R9, _TEXT + mov rdx, [r10] + mov r8, [r10 + 8] + mov r9, [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_RDX_R8_R9, _TEXT + +LEAF_ENTRY Load_R8, _TEXT + mov r8, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R8, _TEXT + +LEAF_ENTRY Load_R8_R9, _TEXT + mov r8, [r10] + mov r9, [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R8_R9, _TEXT + +LEAF_ENTRY Load_R9, _TEXT + mov r9, [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_R9, _TEXT + +// Routines for passing arguments in floating point registers XMM0..XMM7 + +LEAF_ENTRY Load_XMM0, _TEXT + movsd xmm0, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3_XMM4, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + movsd xmm4, real8 ptr [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3_XMM4, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + movsd xmm4, real8 ptr [r10 + 32] + movsd xmm5, real8 ptr [r10 + 40] + add r10, 48 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + movsd xmm4, real8 ptr [r10 + 32] + movsd xmm5, real8 ptr [r10 + 40] + movsd xmm6, real8 ptr [r10 + 48] + add r10, 56 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + movsd xmm0, real8 ptr [r10] + movsd xmm1, real8 ptr [r10 + 8] + movsd xmm2, real8 ptr [r10 + 16] + movsd xmm3, real8 ptr [r10 + 24] + movsd xmm4, real8 ptr [r10 + 32] + movsd xmm5, real8 ptr [r10 + 40] + movsd xmm6, real8 ptr [r10 + 48] + movsd xmm7, real8 ptr [r10 + 56] + add r10, 64 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM1, _TEXT + movsd xmm1, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3_XMM4, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + movsd xmm4, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3_XMM4, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3_XMM4_XMM5, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + movsd xmm4, real8 ptr [r10 + 24] + movsd xmm5, real8 ptr [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3_XMM4_XMM5, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + movsd xmm4, real8 ptr [r10 + 24] + movsd xmm5, real8 ptr [r10 + 32] + movsd xmm6, real8 ptr [r10 + 40] + add r10, 48 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + movsd xmm1, real8 ptr [r10] + movsd xmm2, real8 ptr [r10 + 8] + movsd xmm3, real8 ptr [r10 + 16] + movsd xmm4, real8 ptr [r10 + 24] + movsd xmm5, real8 ptr [r10 + 32] + movsd xmm6, real8 ptr [r10 + 40] + movsd xmm7, real8 ptr [r10 + 48] + add r10, 56 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM2, _TEXT + movsd xmm2, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3_XMM4, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + movsd xmm4, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3_XMM4, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3_XMM4_XMM5, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + movsd xmm4, real8 ptr [r10 + 16] + movsd xmm5, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3_XMM4_XMM5, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + movsd xmm4, real8 ptr [r10 + 16] + movsd xmm5, real8 ptr [r10 + 24] + movsd xmm6, real8 ptr [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3_XMM4_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + movsd xmm2, real8 ptr [r10] + movsd xmm3, real8 ptr [r10 + 8] + movsd xmm4, real8 ptr [r10 + 16] + movsd xmm5, real8 ptr [r10 + 24] + movsd xmm6, real8 ptr [r10 + 32] + movsd xmm7, real8 ptr [r10 + 40] + add r10, 48 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM3, _TEXT + movsd xmm3, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3, _TEXT + +LEAF_ENTRY Load_XMM3_XMM4, _TEXT + movsd xmm3, real8 ptr [r10] + movsd xmm4, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3_XMM4, _TEXT + +LEAF_ENTRY Load_XMM3_XMM4_XMM5, _TEXT + movsd xmm3, real8 ptr [r10] + movsd xmm4, real8 ptr [r10 + 8] + movsd xmm5, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3_XMM4_XMM5, _TEXT + +LEAF_ENTRY Load_XMM3_XMM4_XMM5_XMM6, _TEXT + movsd xmm3, real8 ptr [r10] + movsd xmm4, real8 ptr [r10 + 8] + movsd xmm5, real8 ptr [r10 + 16] + movsd xmm6, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3_XMM4_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + movsd xmm3, real8 ptr [r10] + movsd xmm4, real8 ptr [r10 + 8] + movsd xmm5, real8 ptr [r10 + 16] + movsd xmm6, real8 ptr [r10 + 24] + movsd xmm7, real8 ptr [r10 + 32] + add r10, 40 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM3_XMM4_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM4, _TEXT + movsd xmm4, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM4, _TEXT + +LEAF_ENTRY Load_XMM4_XMM5, _TEXT + movsd xmm4, real8 ptr [r10] + movsd xmm5, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM4_XMM5, _TEXT + +LEAF_ENTRY Load_XMM4_XMM5_XMM6, _TEXT + movsd xmm4, real8 ptr [r10] + movsd xmm5, real8 ptr [r10 + 8] + movsd xmm6, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM4_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM4_XMM5_XMM6_XMM7, _TEXT + movsd xmm4, real8 ptr [r10] + movsd xmm5, real8 ptr [r10 + 8] + movsd xmm6, real8 ptr [r10 + 16] + movsd xmm7, real8 ptr [r10 + 24] + add r10, 32 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM4_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM5, _TEXT + movsd xmm5, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM5, _TEXT + +LEAF_ENTRY Load_XMM5_XMM6, _TEXT + movsd xmm5, real8 ptr [r10] + movsd xmm6, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM5_XMM6, _TEXT + +LEAF_ENTRY Load_XMM5_XMM6_XMM7, _TEXT + movsd xmm5, real8 ptr [r10] + movsd xmm6, real8 ptr [r10 + 8] + movsd xmm7, real8 ptr [r10 + 16] + add r10, 24 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM5_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM6, _TEXT + movsd xmm6, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM6, _TEXT + +LEAF_ENTRY Load_XMM6_XMM7, _TEXT + movsd xmm6, real8 ptr [r10] + movsd xmm7, real8 ptr [r10 + 8] + add r10, 16 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM6_XMM7, _TEXT + +LEAF_ENTRY Load_XMM7, _TEXT + movsd xmm7, real8 ptr [r10] + add r10, 8 + add r11, 8 + jmp qword ptr [r11] +LEAF_END Load_XMM7, _TEXT + + +NESTED_ENTRY CallJittedMethodRetVoid, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + alloc_stack 0x10 + save_reg_postrsp r10, 0 +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov r10, [rsp] + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetVoid, _TEXT + +NESTED_ENTRY CallJittedMethodRetBuff, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + alloc_stack 0x10 + save_reg_postrsp r10, 0 +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + mov rdi, rdx // return buffer + call qword ptr [r11] + mov r10, [rsp] + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetBuff, _TEXT + +NESTED_ENTRY CallJittedMethodRetDouble, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + //pop rdx + mov rdx, [rbp - 8] + movsd real8 ptr [rdx], xmm0 + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetDouble, _TEXT + +NESTED_ENTRY CallJittedMethodRetI8, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov rdx, [rbp - 8] + mov qword ptr [rdx], rax + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetI8, _TEXT + +NESTED_ENTRY CallJittedMethodRetI8I8, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov rcx, [rbp - 8] + mov qword ptr [rcx], rax + mov qword ptr [rcx + 8], rdx + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetI8I8, _TEXT + +NESTED_ENTRY CallJittedMethodRetI8Double, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov rcx, [rbp - 8] + mov qword ptr [rcx], rax + movsd real8 ptr [rcx + 8], xmm0 + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetI8Double, _TEXT + +NESTED_ENTRY CallJittedMethodRetDoubleI8, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov rcx, [rbp - 8] + movsd real8 ptr [rcx], xmm0 + mov qword ptr [rcx + 8], rax + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetDoubleI8, _TEXT + +NESTED_ENTRY CallJittedMethodRetDoubleDouble, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + push_register rdx + push_register rax // align +END_PROLOGUE + sub rsp, rcx // total stack space + mov r11, rdi // The routines list + mov r10, rsi // interpreter stack args + call qword ptr [r11] + mov rcx, [rbp - 8] + movsd real8 ptr [rcx], xmm0 + movsd real8 ptr [rcx + 8], xmm1 + mov rsp, rbp + pop rbp + ret +NESTED_END CallJittedMethodRetDoubleDouble, _TEXT + +#endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index f974a29352bbf7..f5df2e4520487c 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -845,6 +845,16 @@ LOCAL_LABEL(JIT_PollGCRarePath): br x9 LEAF_END JIT_PollGC, _TEXT +//x0 -This pointer +//x1 -ReturnBuffer +LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT + ldr x12, [METHODDESC_REGISTER, #ThisPtrRetBufPrecodeData__Target] + mov x11, x0 // Move first arg pointer to temp register + mov x0, x1 // Move ret buf arg pointer from location in ABI for return buffer for instance method to location in ABI for return buffer for static method + mov x1, x11 // Move temp register to first arg register for static method with return buffer + EPILOG_BRANCH_REG x12 +LEAF_END ThisPtrRetBufPrecodeWorker, _TEXT + #ifdef FEATURE_INTERPRETER NESTED_ENTRY InterpreterStub, _TEXT, NoHandler @@ -858,14 +868,599 @@ NESTED_ENTRY InterpreterStub, _TEXT, NoHandler EPILOG_WITH_TRANSITION_BLOCK_RETURN NESTED_END InterpreterStub, _TEXT -#endif // FEATURE_INTERPRETER -//x0 -This pointer -//x1 -ReturnBuffer -LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT - ldr x12, [METHODDESC_REGISTER, #ThisPtrRetBufPrecodeData__Target] - mov x11, x0 // Move first arg pointer to temp register - mov x0, x1 // Move ret buf arg pointer from location in ABI for return buffer for instance method to location in ABI for return buffer for static method - mov x1, x11 // Move temp register to first arg register for static method with return buffer - EPILOG_BRANCH_REG x12 -LEAF_END ThisPtrRetBufPrecodeWorker, _TEXT +// Copy arguments from the interpreter stack to the processor stack +// The CPU stack slots are aligned to pointer size. +LEAF_ENTRY Load_Stack + ldr w14, [x10], #4 // SP offset + ldr w12, [x10], #4 // number of stack slots + add x14, sp, x14 +LOCAL_LABEL(CopyLoop): + ldr x13, [x9], #8 + str x13, [x14], #8 + subs x12, x12, #8 + bne LOCAL_LABEL(CopyLoop) + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Stack + +#ifdef TARGET_APPLE + +// Copy single byte argument from the interpreter stack to the processor stack +LEAF_ENTRY Load_Stack_1B + ldr x14, [x10], #8 // SP offset + add x14, sp, x14 + ldrb w13, [x9], #8 + strb w13, [x14] + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Stack_1B + +// Copy two byte argument from the interpreter stack to the processor stack +LEAF_ENTRY Load_Stack_2B + ldr x14, [x10], #8 // SP offset + add x14, sp, x14 + ldrh w13, [x9], #8 + strh w13, [x14] + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Stack_2B + +// Copy four byte argument from the interpreter stack to the processor stack +LEAF_ENTRY Load_Stack_4B + ldr x14, [x10], #8 // SP offset + add x14, sp, x14 + ldr w13, [x9], #8 + str w13, [x14] + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Stack_4B + +#endif // TARGET_APPLE + +// Routines for passing value type arguments by reference in general purpose registers X0..X7 + +LEAF_ENTRY Load_Ref_X0 + mov x0, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0 + +LEAF_ENTRY Load_Ref_X1 + mov x1, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1 + +LEAF_ENTRY Load_Ref_X2 + mov x2, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X2 + +LEAF_ENTRY Load_Ref_X3 + mov x3, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X3 + +LEAF_ENTRY Load_Ref_X4 + mov x4, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X4 + +LEAF_ENTRY Load_Ref_X5 + mov x5, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X5 + +LEAF_ENTRY Load_Ref_X6 + mov x6, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X6 + +LEAF_ENTRY Load_Ref_X7 + mov x7, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_Ref_X7 + +// Routines for passing arguments by value in general purpose registers X0..X7 + +LEAF_ENTRY Load_X0 + ldr x0, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0 + +LEAF_ENTRY Load_X0_X1 + ldp x0, x1, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1 + +LEAF_ENTRY Load_X0_X1_X2 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2 + ldr x2, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2 + +LEAF_ENTRY Load_X0_X1_X2_X3 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2_X3 + ldp x2, x3, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2_X3 + +LEAF_ENTRY Load_X0_X1_X2_X3_X4 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2_X3_X4 + ldp x2, x3, [x9], #16 +ALTERNATE_ENTRY Load_X4 + ldr x4, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2_X3_X4 + +LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2_X3_X4_X5 + ldp x2, x3, [x9], #16 +ALTERNATE_ENTRY Load_X4_X5 + ldp x4, x5, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2_X3_X4_X5 + +LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5_X6 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2_X3_X4_X5_X6 + ldp x2, x3, [x9], #16 +ALTERNATE_ENTRY Load_X4_X5_X6 + ldp x4, x5, [x9], #16 +ALTERNATE_ENTRY Load_X6 + ldr x6, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2_X3_X4_X5_X6 + +LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5_X6_X7 + ldp x0, x1, [x9], #16 +ALTERNATE_ENTRY Load_X2_X3_X4_X5_X6_X7 + ldp x2, x3, [x9], #16 +ALTERNATE_ENTRY Load_X4_X5_X6_X7 + ldp x4, x5, [x9], #16 +ALTERNATE_ENTRY Load_X6_X7 + ldp x6, x7, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X0_X1_X2_X3_X4_X5_X6_X7 + +LEAF_ENTRY Load_X1 + ldr x1, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1 + +LEAF_ENTRY Load_X1_X2 + ldp x1, x2, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2 + +LEAF_ENTRY Load_X1_X2_X3 + ldp x1, x2, [x9], #16 +ALTERNATE_ENTRY Load_X3 + ldr x3, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2_X3 + +LEAF_ENTRY Load_X1_X2_X3_X4 + ldp x1, x2, [x9], #16 +ALTERNATE_ENTRY Load_X3_X4 + ldp x3, x4, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2_X3_X4 + +LEAF_ENTRY Load_X1_X2_X3_X4_X5 + ldp x1, x2, [x9], #16 +ALTERNATE_ENTRY Load_X3_X4_X5 + ldp x3, x4, [x9], #16 +ALTERNATE_ENTRY Load_X5 + ldr x5, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2_X3_X4_X5 + +LEAF_ENTRY Load_X1_X2_X3_X4_X5_X6 + ldp x1, x2, [x9], #16 +ALTERNATE_ENTRY Load_X3_X4_X5_X6 + ldp x3, x4, [x9], #16 +ALTERNATE_ENTRY Load_X5_X6 + ldp x5, x6, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2_X3_X4_X5_X6 + +LEAF_ENTRY Load_X1_X2_X3_X4_X5_X6_X7 + ldp x1, x2, [x9], #16 +ALTERNATE_ENTRY Load_X3_X4_X5_X6_X7 + ldp x3, x4, [x9], #16 +ALTERNATE_ENTRY Load_X5_X6_X7 + ldp x5, x6, [x9], #16 +ALTERNATE_ENTRY Load_X7 + ldr x7, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_X1_X2_X3_X4_X5_X6_X7 + +// Routines for passing arguments in floating point registers D0..D7 + +LEAF_ENTRY Load_D0 + ldr d0, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0 + +LEAF_ENTRY Load_D1 + ldr d1, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D1 + +LEAF_ENTRY Load_D0_D1 + ldp d0, d1, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1 + +LEAF_ENTRY Load_D0_D1_D2 + ldr d0, [x9], #8 +ALTERNATE_ENTRY Load_D1_D2 + ldr d1, [x9], #8 +ALTERNATE_ENTRY Load_D2 + ldr d2, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2 + +LEAF_ENTRY Load_D0_D1_D2_D3 + ldr d0, [x9], #8 +ALTERNATE_ENTRY Load_D1_D2_D3 + ldr d1, [x9], #8 +ALTERNATE_ENTRY Load_D2_D3 + ldr d2, [x9], #8 +ALTERNATE_ENTRY Load_D3 + ldr d3, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2_D3 + +LEAF_ENTRY Load_D0_D1_D2_D3_D4 + ldr d0, [x9], #8 +ALTERNATE_ENTRY Load_D1_D2_D3_D4 + ldr d1, [x9], #8 +ALTERNATE_ENTRY Load_D2_D3_D4 + ldr d2, [x9], #8 +ALTERNATE_ENTRY Load_D3_D4 + ldr d3, [x9], #8 +ALTERNATE_ENTRY Load_D4 + ldr d4, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2_D3_D4 + +LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5 + ldr d0, [x9], #8 +ALTERNATE_ENTRY Load_D1_D2_D3_D4_D5 + ldr d1, [x9], #8 +ALTERNATE_ENTRY Load_D2_D3_D4_D5 + ldr d2, [x9], #8 +ALTERNATE_ENTRY Load_D3_D4_D5 + ldr d3, [x9], #8 +ALTERNATE_ENTRY Load_D4_D5 + ldr d4, [x9], #8 +ALTERNATE_ENTRY Load_D5 + ldr d5, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2_D3_D4_D5 + +LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5_D6 + ldr d0, [x9], #8 +ALTERNATE_ENTRY Load_D1_D2_D3_D4_D5_D6 + ldr d1, [x9], #8 +ALTERNATE_ENTRY Load_D2_D3_D4_D5_D6 + ldr d2, [x9], #8 +ALTERNATE_ENTRY Load_D3_D4_D5_D6 + ldr d3, [x9], #8 +ALTERNATE_ENTRY Load_D4_D5_D6 + ldr d4, [x9], #8 +ALTERNATE_ENTRY Load_D5_D6 + ldr d5, [x9], #8 +ALTERNATE_ENTRY Load_D6 + ldr d6, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2_D3_D4_D5_D6 + +LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5_D6_D7 + ldp d0, d1, [x9], #16 +ALTERNATE_ENTRY Load_D2_D3_D4_D5_D6_D7 + ldp d2, d3, [x9], #16 +ALTERNATE_ENTRY Load_D4_D5_D6_D7 + ldp d4, d5, [x9], #16 +ALTERNATE_ENTRY Load_D6_D7 + ldp d6, d7, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D0_D1_D2_D3_D4_D5_D6_D7 + +LEAF_ENTRY Load_D1_D2_D3_D4_D5_D6_D7 + ldp d1, d2, [x9], #16 +ALTERNATE_ENTRY Load_D3_D4_D5_D6_D7 + ldp d3, d4, [x9], #16 +ALTERNATE_ENTRY Load_D5_D6_D7 + ldp d5, d6, [x9], #16 +ALTERNATE_ENTRY Load_D7 + ldr d7, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 +LEAF_END Load_D1_D2_D3_D4_D5_D6_D7 + +// Functions to invoke a sequence of routines to: +// 1. load arguments from the interpreter stack to registers / stack based on the calling convention +// 2. call the target method +// 3. put the return value of the target method to the interpreter stack + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRetVoid, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16 + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16 + EPILOG_RETURN +NESTED_END CallJittedMethodRetVoid, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRetBuff, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16 + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + mov x8, x2 + ldr x11, [x10], #8 + blr x11 + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16 + EPILOG_RETURN +NESTED_END CallJittedMethodRetBuff, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRetI8, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str x0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRetI8, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet2I8, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp x0, x1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet2I8, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRetDouble, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str d0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRetDouble, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet2Double, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet2Double, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet3Double, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2], #16 + str d2, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet3Double, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet4Double, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2], #16 + stp d2, d3, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet4Double, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRetFloat, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str s0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRetFloat, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet2Float, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet2Float, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet3Float, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2], #8 + str s2, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet3Float, _TEXT + +// X0 - routines array +// X1 - interpreter stack args location +// X2 - interpreter stack return value location +// X3 - stack arguments size (properly aligned) +NESTED_ENTRY CallJittedMethodRet4Float, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2], #8 + stp s2, s3, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN +NESTED_END CallJittedMethodRet4Float, _TEXT + +#endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 9d57250de9953c..e6483dd1a8ff02 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -1228,7 +1228,18 @@ JIT_PollGCRarePath br x9 LEAF_END +;x0 -This pointer +;x1 -ReturnBuffer + LEAF_ENTRY ThisPtrRetBufPrecodeWorker + ldr x12, [METHODDESC_REGISTER, #ThisPtrRetBufPrecodeData__Target] + mov x11, x0 ; Move first arg pointer to temp register + mov x0, x1 ; Move ret buf arg pointer from location in ABI for return buffer for instance method to location in ABI for return buffer for static method + mov x1, x11 ; Move temp register to first arg register for static method with return buffer + EPILOG_BRANCH_REG x12 + LEAF_END + #ifdef FEATURE_INTERPRETER + NESTED_ENTRY InterpreterStub PROLOG_WITH_TRANSITION_BLOCK @@ -1241,17 +1252,563 @@ JIT_PollGCRarePath EPILOG_WITH_TRANSITION_BLOCK_RETURN NESTED_END -#endif // FEATURE_INTERPRETER -;x0 -This pointer -;x1 -ReturnBuffer - LEAF_ENTRY ThisPtrRetBufPrecodeWorker - ldr x12, [METHODDESC_REGISTER, #ThisPtrRetBufPrecodeData__Target] - mov x11, x0 ; Move first arg pointer to temp register - mov x0, x1 ; Move ret buf arg pointer from location in ABI for return buffer for instance method to location in ABI for return buffer for static method - mov x1, x11 ; Move temp register to first arg register for static method with return buffer - EPILOG_BRANCH_REG x12 - LEAF_END + ; Copy arguments from the interpreter stack to the processor stack + ; The CPU stack slots are aligned to pointer size. + LEAF_ENTRY Load_Stack + ldr w14, [x10], #4 ; SP offset + ldr w12, [x10], #4 ; number of stack slots + add x14, sp, x14 +CopyLoop + ldr x13, [x9], #8 + str x13, [x14], #8 + subs x12, x12, #8 + bne CopyLoop + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Stack + + ; Routines for passing value type arguments by reference in general purpose registers X0..X7 + + LEAF_ENTRY Load_Ref_X0 + mov x0, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0 + + LEAF_ENTRY Load_Ref_X1 + mov x1, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1 + + LEAF_ENTRY Load_Ref_X2 + mov x2, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X2 + + LEAF_ENTRY Load_Ref_X3 + mov x3, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X3 + + LEAF_ENTRY Load_Ref_X4 + mov x4, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X4 + + LEAF_ENTRY Load_Ref_X5 + mov x5, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X5 + + LEAF_ENTRY Load_Ref_X6 + mov x6, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X6 + + LEAF_ENTRY Load_Ref_X7 + mov x7, x9 + ldr x12, [x10], #8 + add x9, x9, x12 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_Ref_X7 + + ; Routines for passing arguments by value in general purpose registers X0..X7 + + LEAF_ENTRY Load_X0 + ldr x0, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0 + + LEAF_ENTRY Load_X0_X1 + ldp x0, x1, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1 + + LEAF_ENTRY Load_X0_X1_X2 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2 + ldr x2, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2 + + LEAF_ENTRY Load_X0_X1_X2_X3 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2_X3 + ldp x2, x3, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2_X3 + + LEAF_ENTRY Load_X0_X1_X2_X3_X4 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2_X3_X4 + ldp x2, x3, [x9], #16 + ALTERNATE_ENTRY Load_X4 + ldr x4, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2_X3_X4 + + LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2_X3_X4_X5 + ldp x2, x3, [x9], #16 + ALTERNATE_ENTRY Load_X4_X5 + ldp x4, x5, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2_X3_X4_X5 + + LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5_X6 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2_X3_X4_X5_X6 + ldp x2, x3, [x9], #16 + ALTERNATE_ENTRY Load_X4_X5_X6 + ldp x4, x5, [x9], #16 + ALTERNATE_ENTRY Load_X6 + ldr x6, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2_X3_X4_X5_X6 + + LEAF_ENTRY Load_X0_X1_X2_X3_X4_X5_X6_X7 + ldp x0, x1, [x9], #16 + ALTERNATE_ENTRY Load_X2_X3_X4_X5_X6_X7 + ldp x2, x3, [x9], #16 + ALTERNATE_ENTRY Load_X4_X5_X6_X7 + ldp x4, x5, [x9], #16 + ALTERNATE_ENTRY Load_X6_X7 + ldp x6, x7, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X0_X1_X2_X3_X4_X5_X6_X7 + + LEAF_ENTRY Load_X1 + ldr x1, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1 + + LEAF_ENTRY Load_X1_X2 + ldp x1, x2, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2 + + LEAF_ENTRY Load_X1_X2_X3 + ldp x1, x2, [x9], #16 + ALTERNATE_ENTRY Load_X3 + ldr x3, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2_X3 + + LEAF_ENTRY Load_X1_X2_X3_X4 + ldp x1, x2, [x9], #16 + ALTERNATE_ENTRY Load_X3_X4 + ldp x3, x4, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2_X3_X4 + + LEAF_ENTRY Load_X1_X2_X3_X4_X5 + ldp x1, x2, [x9], #16 + ALTERNATE_ENTRY Load_X3_X4_X5 + ldp x3, x4, [x9], #16 + ALTERNATE_ENTRY Load_X5 + ldr x5, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2_X3_X4_X5 + + LEAF_ENTRY Load_X1_X2_X3_X4_X5_X6 + ldp x1, x2, [x9], #16 + ALTERNATE_ENTRY Load_X3_X4_X5_X6 + ldp x3, x4, [x9], #16 + ALTERNATE_ENTRY Load_X5_X6 + ldp x5, x6, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2_X3_X4_X5_X6 + + LEAF_ENTRY Load_X1_X2_X3_X4_X5_X6_X7 + ldp x1, x2, [x9], #16 + ALTERNATE_ENTRY Load_X3_X4_X5_X6_X7 + ldp x3, x4, [x9], #16 + ALTERNATE_ENTRY Load_X5_X6_X7 + ldp x5, x6, [x9], #16 + ALTERNATE_ENTRY Load_X7 + ldr x7, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_X1_X2_X3_X4_X5_X6_X7 + + ; Routines for passing arguments in floating point registers D0..D7 + + LEAF_ENTRY Load_D0 + ldr d0, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0 + + LEAF_ENTRY Load_D1 + ldr d1, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D1 + + LEAF_ENTRY Load_D0_D1 + ldp d0, d1, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1 + + LEAF_ENTRY Load_D0_D1_D2 + ldr d0, [x9], #8 + ALTERNATE_ENTRY Load_D1_D2 + ldr d1, [x9], #8 + ALTERNATE_ENTRY Load_D2 + ldr d2, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2 + + LEAF_ENTRY Load_D0_D1_D2_D3 + ldr d0, [x9], #8 + ALTERNATE_ENTRY Load_D1_D2_D3 + ldr d1, [x9], #8 + ALTERNATE_ENTRY Load_D2_D3 + ldr d2, [x9], #8 + ALTERNATE_ENTRY Load_D3 + ldr d3, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3 + + LEAF_ENTRY Load_D0_D1_D2_D3_D4 + ldr d0, [x9], #8 + ALTERNATE_ENTRY Load_D1_D2_D3_D4 + ldr d1, [x9], #8 + ALTERNATE_ENTRY Load_D2_D3_D4 + ldr d2, [x9], #8 + ALTERNATE_ENTRY Load_D3_D4 + ldr d3, [x9], #8 + ALTERNATE_ENTRY Load_D4 + ldr d4, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3_D4 + + LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5 + ldr d0, [x9], #8 + ALTERNATE_ENTRY Load_D1_D2_D3_D4_D5 + ldr d1, [x9], #8 + ALTERNATE_ENTRY Load_D2_D3_D4_D5 + ldr d2, [x9], #8 + ALTERNATE_ENTRY Load_D3_D4_D5 + ldr d3, [x9], #8 + ALTERNATE_ENTRY Load_D4_D5 + ldr d4, [x9], #8 + ALTERNATE_ENTRY Load_D5 + ldr d5, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3_D4_D5 + + LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5_D6 + ldr d0, [x9], #8 + ALTERNATE_ENTRY Load_D1_D2_D3_D4_D5_D6 + ldr d1, [x9], #8 + ALTERNATE_ENTRY Load_D2_D3_D4_D5_D6 + ldr d2, [x9], #8 + ALTERNATE_ENTRY Load_D3_D4_D5_D6 + ldr d3, [x9], #8 + ALTERNATE_ENTRY Load_D4_D5_D6 + ldr d4, [x9], #8 + ALTERNATE_ENTRY Load_D5_D6 + ldr d5, [x9], #8 + ALTERNATE_ENTRY Load_D6 + ldr d6, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3_D4_D5_D6 + + LEAF_ENTRY Load_D0_D1_D2_D3_D4_D5_D6_D7 + ldp d0, d1, [x9], #16 + ALTERNATE_ENTRY Load_D2_D3_D4_D5_D6_D7 + ldp d2, d3, [x9], #16 + ALTERNATE_ENTRY Load_D4_D5_D6_D7 + ldp d4, d5, [x9], #16 + ALTERNATE_ENTRY Load_D6_D7 + ldp d6, d7, [x9], #16 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3_D4_D5_D6_D7 + + LEAF_ENTRY Load_D1_D2_D3_D4_D5_D6_D7 + ldp d1, d2, [x9], #16 + ALTERNATE_ENTRY Load_D3_D4_D5_D6_D7 + ldp d3, d4, [x9], #16 + ALTERNATE_ENTRY Load_D5_D6_D7 + ldp d5, d6, [x9], #16 + ALTERNATE_ENTRY Load_D7 + ldr d7, [x9], #8 + ldr x11, [x10], #8 + EPILOG_BRANCH_REG x11 + LEAF_END Load_D0_D1_D2_D3_D4_D5_D6_D7 + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRetVoid + PROLOG_SAVE_REG_PAIR fp, lr, #-16! + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #16! + ret lr + NESTED_END CallJittedMethodRetVoid + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRetBuff + PROLOG_SAVE_REG_PAIR fp, lr, #-16! + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + mov x8, x2 + ldr x11, [x10], #8 + blr x11 + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #16! + EPILOG_RETURN + NESTED_END CallJittedMethodRetBuff + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRetI8 + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str x0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRetI8 + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet2I8 + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp x0, x1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet2I8 + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRetDouble + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str d0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRetDouble + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet2Double + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet2Double + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet3Double + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2], #16 + str d2, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet3Double + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet4Double + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp d0, d1, [x2], #16 + stp d2, d3, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet4Double + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRetFloat + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + str s0, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRetFloat + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet2Float + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet2Float + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet3Float + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2], #8 + str s2, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet3Float + + ; X0 - routines array + ; X1 - interpreter stack args location + ; X2 - interpreter stack return value location + ; X3 - stack arguments size (properly aligned) + NESTED_ENTRY CallJittedMethodRet4Float + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + str x2, [sp, #16] + sub sp, sp, x3 + mov x10, x0 + mov x9, x1 + ldr x11, [x10], #8 + blr x11 + ldr x2, [sp, #16] + stp s0, s1, [x2], #8 + stp s2, s3, [x2] + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END CallJittedMethodRet4Float + +#endif // FEATURE_INTERPRETER ; Must be at very end of file END diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp new file mode 100644 index 00000000000000..2394c5bacea3f1 --- /dev/null +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -0,0 +1,990 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef FEATURE_INTERPRETER + +#include "callstubgenerator.h" + +extern "C" void Load_Stack(); + +#if defined(TARGET_APPLE) && defined(TARGET_ARM64) +extern "C" void Load_Stack_1B(); +extern "C" void Load_Stack_2B(); +extern "C" void Load_Stack_4B(); +#endif // TARGET_APPLE && TARGET_ARM64 + +#ifdef TARGET_AMD64 + +#ifdef TARGET_WINDOWS +extern "C" void Load_RCX(); +extern "C" void Load_RCX_RDX(); +extern "C" void Load_RCX_RDX_R8(); +extern "C" void Load_RCX_RDX_R8_R9(); +extern "C" void Load_RDX(); +extern "C" void Load_RDX_R8(); +extern "C" void Load_RDX_R8_R9(); +extern "C" void Load_R8(); +extern "C" void Load_R8_R9(); +extern "C" void Load_R9(); +extern "C" void Load_XMM0(); +extern "C" void Load_XMM0_XMM1(); +extern "C" void Load_XMM0_XMM1_XMM2(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3(); +extern "C" void Load_XMM1(); +extern "C" void Load_XMM1_XMM2(); +extern "C" void Load_XMM1_XMM2_XMM3(); +extern "C" void Load_XMM2(); +extern "C" void Load_XMM2_XMM3(); +extern "C" void Load_XMM3(); +extern "C" void Load_Ref_RCX(); +extern "C" void Load_Ref_RDX(); +extern "C" void Load_Ref_R8(); +extern "C" void Load_Ref_R9(); + +PCODE GPRegsRoutines[] = +{ + (PCODE)Load_RCX, // 00 + (PCODE)Load_RCX_RDX, // 01 + (PCODE)Load_RCX_RDX_R8, // 02 + (PCODE)Load_RCX_RDX_R8_R9, // 03 + (PCODE)0, // 10 + (PCODE)Load_RDX, // 11 + (PCODE)Load_RDX_R8, // 12 + (PCODE)Load_RDX_R8_R9, // 13 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_R8, // 22 + (PCODE)Load_R8_R9, // 23 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_R9 // 33 +}; + +PCODE GPRegsRefRoutines[] = +{ + (PCODE)Load_Ref_RCX, // 0 + (PCODE)Load_Ref_RDX, // 1 + (PCODE)Load_Ref_R8, // 2 + (PCODE)Load_Ref_R9, // 3 +}; + +PCODE FPRegsRoutines[] = +{ + (PCODE)Load_XMM0, // 00 + (PCODE)Load_XMM0_XMM1, // 01 + (PCODE)Load_XMM0_XMM1_XMM2, // 02 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3, // 03 + (PCODE)0, // 10 + (PCODE)Load_XMM1, // 11 + (PCODE)Load_XMM1_XMM2, // 12 + (PCODE)Load_XMM1_XMM2_XMM3, // 13 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_XMM2, // 22 + (PCODE)Load_XMM2_XMM3, // 23 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_XMM3 // 33 +}; + +#else // TARGET_WINDOWS + +extern "C" void Load_RDI(); +extern "C" void Load_RDI_RSI(); +extern "C" void Load_RDI_RSI_RDX(); +extern "C" void Load_RDI_RSI_RDX_RCX(); +extern "C" void Load_RDI_RSI_RDX_RCX_R8(); +extern "C" void Load_RDI_RSI_RDX_RCX_R8_R9(); +extern "C" void Load_RSI(); +extern "C" void Load_RSI_RDX(); +extern "C" void Load_RSI_RDX_RCX(); +extern "C" void Load_RSI_RDX_RCX_R8(); +extern "C" void Load_RSI_RDX_RCX_R8_R9(); +extern "C" void Load_RDX(); +extern "C" void Load_RDX_RCX(); +extern "C" void Load_RDX_RCX_R8(); +extern "C" void Load_RDX_RCX_R8_R9(); +extern "C" void Load_RCX(); +extern "C" void Load_RCX_R8(); +extern "C" void Load_RCX_R8_R9(); +extern "C" void Load_R8(); +extern "C" void Load_R8_R9(); +extern "C" void Load_R9(); + +PCODE GPRegsRoutines[] = +{ + (PCODE)Load_RDI, // 00 + (PCODE)Load_RDI_RSI, // 01 + (PCODE)Load_RDI_RSI_RDX, // 02 + (PCODE)Load_RDI_RSI_RDX_RCX, // 03 + (PCODE)Load_RDI_RSI_RDX_RCX_R8, // 04 + (PCODE)Load_RDI_RSI_RDX_RCX_R8_R9, // 05 + (PCODE)0, // 10 + (PCODE)Load_RSI, // 11 + (PCODE)Load_RSI_RDX, // 12 + (PCODE)Load_RSI_RDX_RCX, // 13 + (PCODE)Load_RSI_RDX_RCX_R8, // 14 + (PCODE)Load_RSI_RDX_RCX_R8_R9, // 15 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_RDX, // 22 + (PCODE)Load_RDX_RCX, // 23 + (PCODE)Load_RDX_RCX_R8, // 24 + (PCODE)Load_RDX_RCX_R8_R9, // 25 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_RCX, // 33 + (PCODE)Load_RCX_R8, // 34 + (PCODE)Load_RCX_R8_R9, // 35 + (PCODE)0, // 40 + (PCODE)0, // 41 + (PCODE)0, // 42 + (PCODE)0, // 43 + (PCODE)Load_R8, // 44 + (PCODE)Load_R8_R9, // 45 + (PCODE)0, // 50 + (PCODE)0, // 51 + (PCODE)0, // 52 + (PCODE)0, // 53 + (PCODE)0, // 54 + (PCODE)Load_R9 // 55 +}; + +extern "C" void Load_XMM0(); +extern "C" void Load_XMM0_XMM1(); +extern "C" void Load_XMM0_XMM1_XMM2(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3_XMM4(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6(); +extern "C" void Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM1(); +extern "C" void Load_XMM1_XMM2(); +extern "C" void Load_XMM1_XMM2_XMM3(); +extern "C" void Load_XMM1_XMM2_XMM3_XMM4(); +extern "C" void Load_XMM1_XMM2_XMM3_XMM4_XMM5(); +extern "C" void Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6(); +extern "C" void Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM2(); +extern "C" void Load_XMM2_XMM3(); +extern "C" void Load_XMM2_XMM3_XMM4(); +extern "C" void Load_XMM2_XMM3_XMM4_XMM5(); +extern "C" void Load_XMM2_XMM3_XMM4_XMM5_XMM6(); +extern "C" void Load_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM3(); +extern "C" void Load_XMM3_XMM4(); +extern "C" void Load_XMM3_XMM4_XMM5(); +extern "C" void Load_XMM3_XMM4_XMM5_XMM6(); +extern "C" void Load_XMM3_XMM4_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM4(); +extern "C" void Load_XMM4_XMM5(); +extern "C" void Load_XMM4_XMM5_XMM6(); +extern "C" void Load_XMM4_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM5(); +extern "C" void Load_XMM5_XMM6(); +extern "C" void Load_XMM5_XMM6_XMM7(); +extern "C" void Load_XMM6(); +extern "C" void Load_XMM6_XMM7(); +extern "C" void Load_XMM7(); + +PCODE FPRegsRoutines[] = +{ + (PCODE)Load_XMM0, // 00 + (PCODE)Load_XMM0_XMM1, // 01 + (PCODE)Load_XMM0_XMM1_XMM2, // 02 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3, // 03 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4, // 04 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5, // 05 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, // 06 + (PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7,// 07 + (PCODE)0, // 10 + (PCODE)Load_XMM1, // 11 + (PCODE)Load_XMM1_XMM2, // 12 + (PCODE)Load_XMM1_XMM2_XMM3, // 13 + (PCODE)Load_XMM1_XMM2_XMM3_XMM4, // 14 + (PCODE)Load_XMM1_XMM2_XMM3_XMM4_XMM5, // 15 + (PCODE)Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, // 16 + (PCODE)Load_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, // 17 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_XMM2, // 22 + (PCODE)Load_XMM2_XMM3, // 23 + (PCODE)Load_XMM2_XMM3_XMM4, // 24 + (PCODE)Load_XMM2_XMM3_XMM4_XMM5, // 25 + (PCODE)Load_XMM2_XMM3_XMM4_XMM5_XMM6, // 26 + (PCODE)Load_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7, // 27 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_XMM3, // 33 + (PCODE)Load_XMM3_XMM4, // 34 + (PCODE)Load_XMM3_XMM4_XMM5, // 35 + (PCODE)Load_XMM3_XMM4_XMM5_XMM6, // 36 + (PCODE)Load_XMM3_XMM4_XMM5_XMM6_XMM7, // 37 + (PCODE)0, // 40 + (PCODE)0, // 41 + (PCODE)0, // 42 + (PCODE)0, // 43 + (PCODE)Load_XMM4, // 44 + (PCODE)Load_XMM4_XMM5, // 45 + (PCODE)Load_XMM4_XMM5_XMM6, // 46 + (PCODE)Load_XMM4_XMM5_XMM6_XMM7, // 47 + (PCODE)0, // 50 + (PCODE)0, // 51 + (PCODE)0, // 52 + (PCODE)0, // 53 + (PCODE)0, // 54 + (PCODE)Load_XMM5, // 55 + (PCODE)Load_XMM5_XMM6, // 56 + (PCODE)Load_XMM5_XMM6_XMM7, // 57 + (PCODE)0, // 60 + (PCODE)0, // 61 + (PCODE)0, // 62 + (PCODE)0, // 63 + (PCODE)0, // 64 + (PCODE)0, // 65 + (PCODE)Load_XMM6, // 66 + (PCODE)Load_XMM6_XMM7, // 67 + (PCODE)0, // 70 + (PCODE)0, // 71 + (PCODE)0, // 72 + (PCODE)0, // 73 + (PCODE)0, // 74 + (PCODE)0, // 75 + (PCODE)0, // 76 + (PCODE)Load_XMM7 // 77 +}; + +#endif // TARGET_WINDOWS + +#endif // TARGET_AMD64 + +#ifdef TARGET_ARM64 + +extern "C" void Load_X0(); +extern "C" void Load_X0_X1(); +extern "C" void Load_X0_X1_X2(); +extern "C" void Load_X0_X1_X2_X3(); +extern "C" void Load_X0_X1_X2_X3_X4(); +extern "C" void Load_X0_X1_X2_X3_X4_X5(); +extern "C" void Load_X0_X1_X2_X3_X4_X5_X6(); +extern "C" void Load_X0_X1_X2_X3_X4_X5_X6_X7(); +extern "C" void Load_X1(); +extern "C" void Load_X1_X2(); +extern "C" void Load_X1_X2_X3(); +extern "C" void Load_X1_X2_X3_X4(); +extern "C" void Load_X1_X2_X3_X4_X5(); +extern "C" void Load_X1_X2_X3_X4_X5_X6(); +extern "C" void Load_X1_X2_X3_X4_X5_X6_X7(); +extern "C" void Load_X2(); +extern "C" void Load_X2_X3(); +extern "C" void Load_X2_X3_X4(); +extern "C" void Load_X2_X3_X4_X5(); +extern "C" void Load_X2_X3_X4_X5_X6(); +extern "C" void Load_X2_X3_X4_X5_X6_X7(); +extern "C" void Load_X3(); +extern "C" void Load_X3_X4(); +extern "C" void Load_X3_X4_X5(); +extern "C" void Load_X3_X4_X5_X6(); +extern "C" void Load_X3_X4_X5_X6_X7(); +extern "C" void Load_X4(); +extern "C" void Load_X4_X5(); +extern "C" void Load_X4_X5_X6(); +extern "C" void Load_X4_X5_X6_X7(); +extern "C" void Load_X5(); +extern "C" void Load_X5_X6(); +extern "C" void Load_X5_X6_X7(); +extern "C" void Load_X6(); +extern "C" void Load_X6_X7(); +extern "C" void Load_X7(); + +extern "C" void Load_Ref_X0(); +extern "C" void Load_Ref_X1(); +extern "C" void Load_Ref_X2(); +extern "C" void Load_Ref_X3(); +extern "C" void Load_Ref_X4(); +extern "C" void Load_Ref_X5(); +extern "C" void Load_Ref_X6(); +extern "C" void Load_Ref_X7(); + + +PCODE GPRegsRoutines[] = +{ + (PCODE)Load_X0, // 00 + (PCODE)Load_X0_X1, // 01 + (PCODE)Load_X0_X1_X2, // 02 + (PCODE)Load_X0_X1_X2_X3, // 03 + (PCODE)Load_X0_X1_X2_X3_X4, // 04 + (PCODE)Load_X0_X1_X2_X3_X4_X5, // 05 + (PCODE)Load_X0_X1_X2_X3_X4_X5_X6, // 06 + (PCODE)Load_X0_X1_X2_X3_X4_X5_X6_X7, // 07 + (PCODE)0, // 10 + (PCODE)Load_X1, // 11 + (PCODE)Load_X1_X2, // 12 + (PCODE)Load_X1_X2_X3, // 13 + (PCODE)Load_X1_X2_X3_X4, // 14 + (PCODE)Load_X1_X2_X3_X4_X5, // 15 + (PCODE)Load_X1_X2_X3_X4_X5_X6, // 16 + (PCODE)Load_X1_X2_X3_X4_X5_X6_X7, // 17 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_X2, // 22 + (PCODE)Load_X2_X3, // 23 + (PCODE)Load_X2_X3_X4, // 24 + (PCODE)Load_X2_X3_X4_X5, // 25 + (PCODE)Load_X2_X3_X4_X5_X6, // 26 + (PCODE)Load_X2_X3_X4_X5_X6_X7, // 27 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_X3, // 33 + (PCODE)Load_X3_X4, // 34 + (PCODE)Load_X3_X4_X5, // 35 + (PCODE)Load_X3_X4_X5_X6, // 36 + (PCODE)Load_X3_X4_X5_X6_X7, // 37 + (PCODE)0, // 40 + (PCODE)0, // 41 + (PCODE)0, // 42 + (PCODE)0, // 43 + (PCODE)Load_X4, // 44 + (PCODE)Load_X4_X5, // 45 + (PCODE)Load_X4_X5_X6, // 46 + (PCODE)Load_X4_X5_X6_X7, // 47 + (PCODE)0, // 50 + (PCODE)0, // 51 + (PCODE)0, // 52 + (PCODE)0, // 53 + (PCODE)0, // 54 + (PCODE)Load_X5, // 55 + (PCODE)Load_X5_X6, // 56 + (PCODE)Load_X5_X6_X7, // 57 + (PCODE)0, // 60 + (PCODE)0, // 61 + (PCODE)0, // 62 + (PCODE)0, // 63 + (PCODE)0, // 64 + (PCODE)0, // 65 + (PCODE)Load_X6, // 66 + (PCODE)Load_X6_X7, // 67 + (PCODE)0, // 70 + (PCODE)0, // 71 + (PCODE)0, // 72 + (PCODE)0, // 73 + (PCODE)0, // 74 + (PCODE)0, // 75 + (PCODE)0, // 76 + (PCODE)Load_X7 // 77 +}; + +PCODE GPRegsRefRoutines[] = +{ + (PCODE)Load_Ref_X0, // 0 + (PCODE)Load_Ref_X1, // 1 + (PCODE)Load_Ref_X2, // 2 + (PCODE)Load_Ref_X3, // 3 + (PCODE)Load_Ref_X4, // 4 + (PCODE)Load_Ref_X5, // 5 + (PCODE)Load_Ref_X6, // 6 + (PCODE)Load_Ref_X7 // 7 +}; + +extern "C" void Load_D0(); +extern "C" void Load_D0_D1(); +extern "C" void Load_D0_D1_D2(); +extern "C" void Load_D0_D1_D2_D3(); +extern "C" void Load_D0_D1_D2_D3_D4(); +extern "C" void Load_D0_D1_D2_D3_D4_D5(); +extern "C" void Load_D0_D1_D2_D3_D4_D5_D6(); +extern "C" void Load_D0_D1_D2_D3_D4_D5_D6_D7(); +extern "C" void Load_D1(); +extern "C" void Load_D1_D2(); +extern "C" void Load_D1_D2_D3(); +extern "C" void Load_D1_D2_D3_D4(); +extern "C" void Load_D1_D2_D3_D4_D5(); +extern "C" void Load_D1_D2_D3_D4_D5_D6(); +extern "C" void Load_D1_D2_D3_D4_D5_D6_D7(); +extern "C" void Load_D2(); +extern "C" void Load_D2_D3(); +extern "C" void Load_D2_D3_D4(); +extern "C" void Load_D2_D3_D4_D5(); +extern "C" void Load_D2_D3_D4_D5_D6(); +extern "C" void Load_D2_D3_D4_D5_D6_D7(); +extern "C" void Load_D3(); +extern "C" void Load_D3_D4(); +extern "C" void Load_D3_D4_D5(); +extern "C" void Load_D3_D4_D5_D6(); +extern "C" void Load_D3_D4_D5_D6_D7(); +extern "C" void Load_D4(); +extern "C" void Load_D4_D5(); +extern "C" void Load_D4_D5_D6(); +extern "C" void Load_D4_D5_D6_D7(); +extern "C" void Load_D5(); +extern "C" void Load_D5_D6(); +extern "C" void Load_D5_D6_D7(); +extern "C" void Load_D6(); +extern "C" void Load_D6_D7(); +extern "C" void Load_D7(); + +PCODE FPRegsRoutines[] = +{ + (PCODE)Load_D0, // 00 + (PCODE)Load_D0_D1, // 01 + (PCODE)Load_D0_D1_D2, // 02 + (PCODE)Load_D0_D1_D2_D3, // 03 + (PCODE)Load_D0_D1_D2_D3_D4, // 04 + (PCODE)Load_D0_D1_D2_D3_D4_D5, // 05 + (PCODE)Load_D0_D1_D2_D3_D4_D5_D6, // 06 + (PCODE)Load_D0_D1_D2_D3_D4_D5_D6_D7, // 07 + (PCODE)0, // 10 + (PCODE)Load_D1, // 11 + (PCODE)Load_D1_D2, // 12 + (PCODE)Load_D1_D2_D3, // 13 + (PCODE)Load_D1_D2_D3_D4, // 14 + (PCODE)Load_D1_D2_D3_D4_D5, // 15 + (PCODE)Load_D1_D2_D3_D4_D5_D6, // 16 + (PCODE)Load_D1_D2_D3_D4_D5_D6_D7, // 17 + (PCODE)0, // 20 + (PCODE)0, // 21 + (PCODE)Load_D2, // 22 + (PCODE)Load_D2_D3, // 23 + (PCODE)Load_D2_D3_D4, // 24 + (PCODE)Load_D2_D3_D4_D5, // 25 + (PCODE)Load_D2_D3_D4_D5_D6, // 26 + (PCODE)Load_D2_D3_D4_D5_D6_D7, // 27 + (PCODE)0, // 30 + (PCODE)0, // 31 + (PCODE)0, // 32 + (PCODE)Load_D3, // 33 + (PCODE)Load_D3_D4, // 34 + (PCODE)Load_D3_D4_D5, // 35 + (PCODE)Load_D3_D4_D5_D6, // 36 + (PCODE)Load_D3_D4_D5_D6_D7, // 37 + (PCODE)0, // 40 + (PCODE)0, // 41 + (PCODE)0, // 42 + (PCODE)0, // 43 + (PCODE)Load_D4, // 44 + (PCODE)Load_D4_D5, // 45 + (PCODE)Load_D4_D5_D6, // 46 + (PCODE)Load_D4_D5_D6_D7, // 47 + (PCODE)0, // 50 + (PCODE)0, // 51 + (PCODE)0, // 52 + (PCODE)0, // 53 + (PCODE)0, // 54 + (PCODE)Load_D5, // 55 + (PCODE)Load_D5_D6, // 56 + (PCODE)Load_D5_D6_D7, // 57 + (PCODE)0, // 60 + (PCODE)0, // 61 + (PCODE)0, // 62 + (PCODE)0, // 63 + (PCODE)0, // 64 + (PCODE)0, // 65 + (PCODE)Load_D6, // 66 + (PCODE)Load_D6_D7, // 67 + (PCODE)0, // 70 + (PCODE)0, // 71 + (PCODE)0, // 72 + (PCODE)0, // 73 + (PCODE)0, // 74 + (PCODE)0, // 75 + (PCODE)0, // 76 + (PCODE)Load_D7 // 77 +}; +#endif // TARGET_ARM64 + +PCODE GetGPRegRangeLoadRoutine(int r1, int r2) +{ + int index = r1 * NUM_ARGUMENT_REGISTERS + r2; + return GPRegsRoutines[index]; +} + +#ifndef UNIX_AMD64_ABI +PCODE GetGPRegRefLoadRoutine(int r) +{ + return GPRegsRefRoutines[r]; +} +#endif // UNIX_AMD64_ABI + +PCODE GetFPRegRangeLoadRoutine(int x1, int x2) +{ + int index = x1 * NUM_FLOAT_ARGUMENT_REGISTERS + x2; + return FPRegsRoutines[index]; +} + +extern "C" void CallJittedMethodRetVoid(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetDouble(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetI8(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetBuff(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); + +#ifdef UNIX_AMD64_ABI +extern "C" void CallJittedMethodRetI8I8(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetI8Double(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetDoubleI8(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetDoubleDouble(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +#endif + +#ifdef TARGET_ARM64 +extern "C" void CallJittedMethodRet2I8(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet2Double(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet3Double(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet4Double(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRetFloat(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet2Float(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet3Float(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +extern "C" void CallJittedMethodRet4Float(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); +#endif // TARGET_ARM64 + +// Generate the call stub for the given method. +// The returned call stub header must be freed by the caller using FreeCallStub. +CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTracker *pamTracker) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pMD != NULL); + + MetaSig sig(pMD); + ArgIterator argIt(&sig); + + m_r1 = NoRange; // indicates that there is no active range of general purpose registers + m_r2 = 0; + m_x1 = NoRange; // indicates that there is no active range of FP registers + m_x2 = 0; + m_s1 = NoRange; // indicates that there is no active range of stack arguments + m_s2 = 0; + m_routineIndex = 0; + m_totalStackSize = 0; + + int numArgs = sig.NumFixedArgs() + (sig.HasThis() ? 1 : 0); + + if (argIt.HasThis()) + { + // The "this" argument register is not enumerated by the arg iterator, so + // we need to "inject" it here. +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + if (argIt.HasRetBuffArg()) + { + // The return buffer on Windows AMD64 is passed in the first argument register, so the + // "this" argument is be passed in the second argument register. + m_r1 = 1; + } + else +#endif // TARGET_WINDOWS && TARGET_AMD64 + { + // The "this" pointer is passed in the first argument register. + m_r1 = 0; + } + } + + // Allocate space for the routines. The size of the array is conservatively set to twice the number of arguments + // plus one slot for the target pointer and reallocated to the real size at the end. + PCODE *pRoutines = (PCODE*)alloca(sizeof(CallStubHeader) + (numArgs * 2 + 1) * sizeof(PCODE)); + + int ofs; + while ((ofs = argIt.GetNextOffset()) != TransitionBlock::InvalidOffset) + { + ArgLocDesc argLocDesc; + argIt.GetArgLoc(ofs, &argLocDesc); + +#ifdef UNIX_AMD64_ABI + if (argIt.GetArgLocDescForStructInRegs() != NULL) + { + TypeHandle argTypeHandle; + CorElementType corType = argIt.GetArgType(&argTypeHandle); + _ASSERTE(corType == ELEMENT_TYPE_VALUETYPE); + + MethodTable *pMT = argTypeHandle.AsMethodTable(); + EEClass *pEEClass = pMT->GetClass(); + int numEightBytes = pEEClass->GetNumberEightBytes(); + for (int i = 0; i < numEightBytes; i++) + { + ArgLocDesc argLocDescEightByte = {}; + SystemVClassificationType eightByteType = pEEClass->GetEightByteClassification(i); + if (eightByteType == SystemVClassificationTypeInteger) + { + if (argLocDesc.m_cGenReg != 0) + { + argLocDescEightByte.m_cGenReg = 1; + argLocDescEightByte.m_idxGenReg = argLocDesc.m_idxGenReg++; + } + else + { + argLocDescEightByte.m_byteStackSize = 8; + argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex; + argLocDesc.m_byteStackIndex += 8; + } + } + else if (eightByteType == SystemVClassificationTypeSSE) + { + if (argLocDesc.m_cFloatReg != 0) + { + argLocDescEightByte.m_cFloatReg = 1; + argLocDescEightByte.m_idxFloatReg = argLocDesc.m_idxFloatReg++; + } + else + { + argLocDescEightByte.m_byteStackSize = 8; + argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex; + argLocDesc.m_byteStackIndex += 8; + } + } + ProcessArgument(argIt, argLocDescEightByte, pRoutines); + } + } + else +#endif // UNIX_AMD64_ABI + { + ProcessArgument(argIt, argLocDesc, pRoutines); + } + } + + // All arguments were processed, but there is likely a pending ranges to store. + // Process such a range if any. + if (m_r1 != NoRange) + { + pRoutines[m_routineIndex++] = GetGPRegRangeLoadRoutine(m_r1, m_r2); + } + else if (m_x1 != NoRange) + { + pRoutines[m_routineIndex++] = GetFPRegRangeLoadRoutine(m_x1, m_x2); + } + else if (m_s1 != NoRange) + { + m_totalStackSize += m_s2 - m_s1 + 1; + pRoutines[m_routineIndex++] = (PCODE)Load_Stack; + pRoutines[m_routineIndex++] = ((int64_t)(m_s2 - m_s1 + 1) << 32) | m_s1; + } + + CallStubHeader::InvokeFunctionPtr pInvokeFunction = NULL; + + if (argIt.HasRetBuffArg()) + { + pInvokeFunction = CallJittedMethodRetBuff; + } + else + { + TypeHandle thReturnValueType; + CorElementType thReturnType = sig.GetReturnTypeNormalized(&thReturnValueType); + + switch (thReturnType) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_FNPTR: + pInvokeFunction = CallJittedMethodRetI8; + break; + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + pInvokeFunction = CallJittedMethodRetDouble; + break; + case ELEMENT_TYPE_VOID: + pInvokeFunction = CallJittedMethodRetVoid; + break; + case ELEMENT_TYPE_VALUETYPE: +#ifdef TARGET_AMD64 +#ifdef TARGET_WINDOWS + if (thReturnValueType.AsMethodTable()->IsIntrinsicType()) + { + // E.g. Vector2 + pInvokeFunction = CallJittedMethodRetDouble; + } + else + { + // POD structs smaller than 64 bits are returned in rax + pInvokeFunction = CallJittedMethodRetI8; + } +#else // TARGET_WINDOWS + if (thReturnValueType.AsMethodTable()->IsRegPassedStruct()) + { + UINT fpReturnSize = argIt.GetFPReturnSize(); + if (fpReturnSize == 0) + { + pInvokeFunction = CallJittedMethodRetI8; + } + else if (fpReturnSize == 8) + { + pInvokeFunction = CallJittedMethodRetDouble; + } + else + { + _ASSERTE((fpReturnSize & 16) != 0); + // The fpReturnSize bits 0..1 have the following meaning: + // Bit 0 - the first 8 bytes of the struct is integer (0) or floating point (1) + // Bit 1 - the second 8 bytes of the struct is integer (0) or floating point (1) + switch (fpReturnSize & 0x3) + { + case 0: + pInvokeFunction = CallJittedMethodRetI8I8; + break; + case 1: + pInvokeFunction = CallJittedMethodRetDoubleI8; + break; + case 2: + pInvokeFunction = CallJittedMethodRetI8Double; + break; + case 3: + pInvokeFunction = CallJittedMethodRetDoubleDouble; + break; + } + } + } + else + { + _ASSERTE(!"All value types that are not returnable structs in registers should be returned using return buffer"); + } +#endif // TARGET_WINDOWS +#elif TARGET_ARM64 + // HFA, HVA, POD structs smaller than 128 bits + if (thReturnValueType.IsHFA()) + { + switch (thReturnValueType.GetHFAType()) + { + case CORINFO_HFA_ELEM_FLOAT: + switch (thReturnValueType.GetSize()) + { + case 4: + pInvokeFunction = CallJittedMethodRetFloat; + break; + case 8: + pInvokeFunction = CallJittedMethodRet2Float; + break; + case 12: + pInvokeFunction = CallJittedMethodRet3Float; + break; + case 16: + pInvokeFunction = CallJittedMethodRet4Float; + break; + default: + _ASSERTE(!"Should not get here"); + break; + } + break; + case CORINFO_HFA_ELEM_DOUBLE: + switch (thReturnValueType.GetSize()) + { + case 8: + pInvokeFunction = CallJittedMethodRetDouble; + break; + case 16: + pInvokeFunction = CallJittedMethodRet2Double; + break; + case 24: + pInvokeFunction = CallJittedMethodRet3Double; + break; + case 32: + pInvokeFunction = CallJittedMethodRet4Double; + break; + default: + _ASSERTE(!"Should not get here"); + break; + } + break; + default: + _ASSERTE(!"HFA types other than float and double are not supported yet"); + break; + } + } + else + { + switch (thReturnValueType.GetSize()) + { + case 1: + case 2: + case 4: + case 8: + pInvokeFunction = CallJittedMethodRetI8; + break; + case 16: + pInvokeFunction = CallJittedMethodRet2I8; + break; + default: + _ASSERTE(!"The return types that are not HFA should be <= 16 bytes in size"); + break; + } + } +#else + _ASSERTE(!"Struct returns by value are not supported yet"); +#endif + break; + default: + _ASSERTE(!"Unexpected return type"); + break; + } + } + + m_routineIndex++; // Reserve one extra slot for the target method pointer + + LoaderAllocator *pLoaderAllocator = pMD->GetLoaderAllocator(); + S_SIZE_T finalStubSize(sizeof(CallStubHeader) + m_routineIndex * sizeof(PCODE)); + void *pHeaderStorage = pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(finalStubSize)); + + CallStubHeader *pHeader = new (pHeaderStorage) CallStubHeader(m_routineIndex, pRoutines, ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), pInvokeFunction); + + return pHeader; +} + +// Process the argument described by argLocDesc. This function is called for each argument in the method signature. +// It updates the ranges of registers and emits entries into the routines array at discontinuities. +void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDesc, PCODE *pRoutines) +{ + LIMITED_METHOD_CONTRACT; + + // Check if we have a range of registers or stack arguments that we need to store because the current argument + // terminates it. + if ((argLocDesc.m_cGenReg == 0) && (m_r1 != NoRange)) + { + // No GP register is used to pass the current argument, but we already have a range of GP registers, + // store the routine for the range + pRoutines[m_routineIndex++] = GetGPRegRangeLoadRoutine(m_r1, m_r2); + m_r1 = NoRange; + } + else if (((argLocDesc.m_cFloatReg == 0)) && (m_x1 != NoRange)) + { + // No floating point register is used to pass the current argument, but we already have a range of FP registers, + // store the routine for the range + pRoutines[m_routineIndex++] = GetFPRegRangeLoadRoutine(m_x1, m_x2); + m_x1 = NoRange; + } + else if ((argLocDesc.m_byteStackSize == 0) && (m_s1 != NoRange)) + { + // No stack argument is used to pass the current argument, but we already have a range of stack arguments, + // store the routine for the range + m_totalStackSize += m_s2 - m_s1 + 1; + pRoutines[m_routineIndex++] = (PCODE)Load_Stack; + pRoutines[m_routineIndex++] = ((int64_t)(m_s2 - m_s1 + 1) << 32) | m_s1; + m_s1 = NoRange; + } + + if (argLocDesc.m_cGenReg != 0) + { + if (m_r1 == NoRange) // No active range yet + { + // Start a new range + m_r1 = argLocDesc.m_idxGenReg; + m_r2 = m_r1 + argLocDesc.m_cGenReg - 1; + } + else if (argLocDesc.m_idxGenReg == m_r2 + 1 && !argIt.IsArgPassedByRef()) + { + // Extend an existing range, but only if the argument is not passed by reference. + // Arguments passed by reference are handled separately, because the interpreter stores the value types on its stack by value. + m_r2 += argLocDesc.m_cGenReg; + } + else + { + // Discontinuous range - store a routine for the current and start a new one + pRoutines[m_routineIndex++] = GetGPRegRangeLoadRoutine(m_r1, m_r2); + m_r1 = argLocDesc.m_idxGenReg; + m_r2 = m_r1 + argLocDesc.m_cGenReg - 1; + } + } + + if (argLocDesc.m_cFloatReg != 0) + { + if (m_x1 == NoRange) // No active range yet + { + // Start a new range + m_x1 = argLocDesc.m_idxFloatReg; + m_x2 = m_x1 + argLocDesc.m_cFloatReg - 1; + } + else if (argLocDesc.m_idxFloatReg == m_x2 + 1) + { + // Extend an existing range + m_x2 += argLocDesc.m_cFloatReg; + } + else + { + // Discontinuous range - store a routine for the current and start a new one + pRoutines[m_routineIndex++] = GetFPRegRangeLoadRoutine(m_x1, m_x2); + m_x1 = argLocDesc.m_idxFloatReg; + m_x2 = m_x1 + argLocDesc.m_cFloatReg - 1; + } + } + + if (argLocDesc.m_byteStackSize != 0) + { + if (m_s1 == NoRange) // No active range yet + { + // Start a new range + m_s1 = argLocDesc.m_byteStackIndex; + m_s2 = m_s1 + argLocDesc.m_byteStackSize - 1; + } + else if ((argLocDesc.m_byteStackIndex == m_s2 + 1) && (argLocDesc.m_byteStackSize >= 8)) + { + // Extend an existing range, but only if the argument is at least pointer size large. + // The only case when this is not true is on Apple ARM64 OSes where primitive type smaller + // than 8 bytes are passed on the stack in a packed manner. We process such arguments one by + // one to avoid explosion of the number of pRoutines. + m_s2 += argLocDesc.m_byteStackSize; + } + else + { + // Discontinuous range - store a routine for the current and start a new one + m_totalStackSize += m_s2 - m_s1 + 1; + pRoutines[m_routineIndex++] = (PCODE)Load_Stack; + pRoutines[m_routineIndex++] = ((int64_t)(m_s2 - m_s1 + 1) << 32) | m_s1; + m_s1 = argLocDesc.m_byteStackIndex; + m_s2 = m_s1 + argLocDesc.m_byteStackSize - 1; + } + +#if defined(TARGET_APPLE) && defined(TARGET_ARM64) + // Process primitive types smaller than 8 bytes separately on Apple ARM64 + if (argLocDesc.m_byteStackSize < 8) + { + switch (argLocDesc.m_byteStackSize) + { + case 1: + pRoutines[m_routineIndex++] = (PCODE)Load_Stack_1B; + break; + case 2: + pRoutines[m_routineIndex++] = (PCODE)Load_Stack_2B; + break; + case 4: + pRoutines[m_routineIndex++] = (PCODE)Load_Stack_4B; + break; + default: + _ASSERTE(!"Unexpected stack argument size"); + break; + } + pRoutines[m_routineIndex++] = m_s1; + m_s1 = NoRange; + } +#endif // TARGET_APPLE && TARGET_ARM64 + } + +#ifndef UNIX_AMD64_ABI + // Arguments passed by reference are handled separately, because the interpreter stores the value types on its stack by value. + // So the argument loading routine needs to load the address of the argument. To avoid explosion of number of the routines, + // we always process single argument passed by reference using single routine. + if (argIt.IsArgPassedByRef()) + { + _ASSERTE(argLocDesc.m_cGenReg == 1); + pRoutines[m_routineIndex++] = GetGPRegRefLoadRoutine(argLocDesc.m_idxGenReg); + pRoutines[m_routineIndex++] = argIt.GetArgSize(); + m_r1 = NoRange; + } +#endif // UNIX_AMD64_ABI +} + +#endif // FEATURE_INTERPRETER \ No newline at end of file diff --git a/src/coreclr/vm/callstubgenerator.h b/src/coreclr/vm/callstubgenerator.h new file mode 100644 index 00000000000000..476bb2f4c9da91 --- /dev/null +++ b/src/coreclr/vm/callstubgenerator.h @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef CALLSTUBGENERATOR_H +#define CALLSTUBGENERATOR_H + +#include "callingconvention.h" + +class MethodDesc; +class AllocMemTracker; + +// This is a header for a call stub that translates arguments from the interpreter stack to the CPU registers and native +// stack, invokes the target method, and translates the return value back to the interpreter stack. +struct CallStubHeader +{ + typedef void (*InvokeFunctionPtr)(PCODE *routines, int8_t*pArgs, int8_t*pRet, int totalStackSize); + + // Number of routines in the Routines array. The last one is the target method to call. + int NumRoutines; + // Total stack size used for the arguments. + int TotalStackSize; + // This is a pointer to a helper function that invokes the target method. There are several + // versions of this function, depending on the return type of the target method. + InvokeFunctionPtr Invoke; + // This is an array of routines that translate the arguments from the interpreter stack to the CPU registers and native stack. + PCODE Routines[0]; + + CallStubHeader(int numRoutines, PCODE *pRoutines, int totalStackSize, InvokeFunctionPtr pInvokeFunction) + { + LIMITED_METHOD_CONTRACT; + + NumRoutines = numRoutines; + TotalStackSize = totalStackSize; + Invoke = pInvokeFunction; + + memcpy(Routines, pRoutines, NumRoutines * sizeof(PCODE)); + } + + // Set the address of the target method to call. + void SetTarget(PCODE target) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(target != 0); + Routines[NumRoutines - 1] = target; + } +}; + +// This class generates the call stub for a given method. It uses the calling convention of the target CPU to determine +// how to translate the arguments from the interpreter stack to the CPU registers and native stack. +class CallStubGenerator +{ + // When the m_r1, m_x1 or m_s1 are set to NoRange, it means that there is no active range of registers or stack arguments. + static const int NoRange = -1; + + // Current sequential range of general purpose registers used to pass arguments. + int m_r1; + int m_r2; + // Current sequential range of floating point registers used to pass arguments. + int m_x1; + int m_x2; + // Current sequential range of offsets of stack arguments used to pass arguments. + int m_s1; + int m_s2; + // The index of the next routine to store in the Routines array. + int m_routineIndex; + // The total stack size used for the arguments. + int m_totalStackSize; + + // Process the argument described by argLocDesc. This function is called for each argument in the method signature. + void ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDesc, PCODE *pRoutines); +public: + // Generate the call stub for the given method. + CallStubHeader *GenerateCallStub(MethodDesc *pMD, AllocMemTracker *pamTracker); +}; + +#endif // CALLSTUBGENERATOR_H diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 883d12e336e22c..79ce13d2ce0cbe 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -6,6 +6,45 @@ #include "threads.h" #include "gcenv.h" #include "interpexec.h" +#include "callstubgenerator.h" + +void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet) +{ + CONTRACTL + { + THROWS; + MODE_ANY; + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(CheckPointer(pArgs)); + PRECONDITION(CheckPointer(pRet)); + } + CONTRACTL_END + + CallStubGenerator callStubGenerator; + CallStubHeader *pHeader = pMD->GetCallStub(); + if (pHeader == NULL) + { + GCX_PREEMP(); + + AllocMemTracker amTracker; + pHeader = callStubGenerator.GenerateCallStub(pMD, &amTracker); + + if (pMD->SetCallStub(pHeader)) + { + amTracker.SuppressRelease(); + } + else + { + // We have lost the race for generating the header, use the one that was generated by another thread + // and let the amTracker release the memory of the one we generated. + pHeader = pMD->GetCallStub(); + } + } + + pHeader->SetTarget(pMD->GetNativeCode()); // The method to call + + pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize); +} typedef void* (*HELPER_FTN_PP)(void*); @@ -1081,13 +1120,24 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } else { - // At this stage in the implementation, we assume this is pointer to - // interpreter code. In the future, this should probably be tagged pointer - // for interpreter call or normal pointer for JIT/R2R call. targetIp = (const int32_t*)targetMethod; } } CALL_TARGET_IP: + // Interpreter-TODO: we need a fast way to check of the targetIp is an interpreter code or not. + // Probably use a tagged pointer for interpreter code and a normal pointer for JIT/R2R code. + EECodeInfo codeInfo((PCODE)targetIp); + if (!codeInfo.IsValid()) + { + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Attempted to execute native code from interpreter")); + } + else if (codeInfo.GetCodeManager() != ExecutionManager::GetInterpreterCodeManager()) + { + MethodDesc *pMD = codeInfo.GetMethodDesc(); + InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset); + break; + } + // Save current execution state for when we return from called method pFrame->ip = ip; diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index b4b6c5185a0941..07023802238c80 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -253,6 +253,30 @@ HRESULT MethodDesc::SetMethodDescVersionState(PTR_MethodDescVersioningState stat return S_OK; } +#ifdef FEATURE_INTERPRETER +// Set the call stub for the interpreter to JIT/AOT calls +// Returns true if the current call set the stub, false if it was already set +bool MethodDesc::SetCallStub(CallStubHeader *pHeader) +{ + STANDARD_VM_CONTRACT; + + IfFailThrow(EnsureCodeDataExists(NULL)); + + _ASSERTE(m_codeData != NULL); + return InterlockedCompareExchangeT(&m_codeData->CallStub, pHeader, NULL) == NULL; +} + +CallStubHeader *MethodDesc::GetCallStub() +{ + LIMITED_METHOD_CONTRACT; + + PTR_MethodDescCodeData codeData = VolatileLoadWithoutBarrier(&m_codeData); + if (codeData == NULL) + return NULL; + return VolatileLoadWithoutBarrier(&codeData->CallStub); +} +#endif // FEATURE_INTERPRETER + #endif //!DACCESS_COMPILE PTR_MethodDescVersioningState MethodDesc::GetMethodDescVersionState() diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index de0f28698f1f4b..5bf221d2498cdd 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -22,7 +22,9 @@ #include #include "eeconfig.h" #include "precode.h" - +#ifdef FEATURE_INTERPRETER +#include "callstubgenerator.h" +#endif // FEATURE_INTERPRETER class Stub; class FCallMethodDesc; class FieldDesc; @@ -231,6 +233,9 @@ struct MethodDescCodeData final { PTR_MethodDescVersioningState VersioningState; PCODE TemporaryEntryPoint; +#ifdef FEATURE_INTERPRETER + CallStubHeader *CallStub; +#endif // FEATURE_INTERPRETER }; using PTR_MethodDescCodeData = DPTR(MethodDescCodeData); @@ -1815,6 +1820,11 @@ class MethodDesc HRESULT EnsureCodeDataExists(AllocMemTracker *pamTracker); HRESULT SetMethodDescVersionState(PTR_MethodDescVersioningState state); +#ifdef FEATURE_INTERPRETER + bool SetCallStub(CallStubHeader *pHeader); + CallStubHeader *GetCallStub(); +#endif // FEATURE_INTERPRETER + #endif //!DACCESS_COMPILE PTR_MethodDescVersioningState GetMethodDescVersionState(); diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index a0a5cd621353ec..dcd5c910dd7a98 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Numerics; using System.Runtime.CompilerServices; public interface ITest @@ -76,12 +77,223 @@ public StructWithRefs(int val1, int val2) } } +public struct TestStruct +{ + public int a; + public int b; + public int c; + public int d; + public int e; + public int f; +} + +public struct TestStruct2 +{ + public int a; + public int b; +} + +public struct TestStruct4ii +{ + public int a; + public int b; + public int c; + public int d; +} + +public struct TestStruct4if +{ + public int a; + public int b; + public float c; + public float d; +} + +public struct TestStruct4fi +{ + public float a; + public float b; + public int c; + public int d; +} + +public struct TestStruct4ff +{ + public float a; + public float b; + public float c; + public float d; +} + + +public struct TestStruct3d +{ + public double a; + public double b; + public double c; +} + public class InterpreterTest { + static void TestCallingConvention0(int a, float b, int c, double d, int e, double f) + { + Console.WriteLine("TestCallingConvention0: a = {0}, b = {1}, c = {2}, d = {3}, e = {4}, f = {5}", a, b, c, d, e, f); + } + + static void TestCallingConvention1(TestStruct s) + { + Console.WriteLine("TestCallingConvention1: a = {0}, b = {1}, c = {2}, d = {3}, e = {4}, f = {5}", s.a, s.b, s.c, s.d, s.e, s.f); + } + + static TestStruct2 TestCallingConvention2() + { + TestStruct2 s; + s.a = 1; + s.b = 2; + return s; + } + + static Vector2 TestCallingConvention3() + { + Vector2 v = new Vector2(1, 2); + return v; + } + + static TestStruct TestCallingConvention4() + { + TestStruct s; + s.a = 1; + s.b = 2; + s.c = 3; + s.d = 4; + s.e = 5; + s.f = 6; + return s; + } + + static TestStruct4ii TestCallingConvention5() + { + TestStruct4ii s; + s.a = 1; + s.b = 2; + s.c = 3; + s.d = 4; + return s; + } + + static TestStruct4if TestCallingConvention6() + { + TestStruct4if s; + s.a = 1; + s.b = 2; + s.c = 3.0f; + s.d = 4.0f; + return s; + } + + static TestStruct4fi TestCallingConvention7() + { + TestStruct4fi s; + s.a = 1.0f; + s.b = 2.0f; + s.c = 3; + s.d = 4; + return s; + } + + static TestStruct4ff TestCallingConvention8() + { + TestStruct4ff s; + s.a = 1.0f; + s.b = 2.0f; + s.c = 3.0f; + s.d = 4.0f; + return s; + } + + static void TestCallingConvention9(TestStruct4fi s) + { + Console.WriteLine("TestCallingConvention9: a = {0}, b = {1}, c = {2}, d = {3}", s.a, s.b, s.c, s.d); + } + + static void TestCallingConvention10(TestStruct3d s) + { + Console.WriteLine("TestCallingConvention10: a = {0}, b = {1}, c = {2}", s.a, s.b, s.c); + } + + static TestStruct3d TestCallingConvention11() + { + TestStruct3d s; + s.a = 1.0f; + s.b = 2.0f; + s.c = 3.0f; + return s; + } + + static void TestCallingConvention12(byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte h, byte i, char j, int k, int l, long m) + { + Console.WriteLine("TestCallingConvention12: a = {0}, b = {1}, c = {2}, d = {3}, e = {4}, f = {5}, g = {6}, h = {7}, i = {8}, j = {9}, k = {10}, l = {11}, m = {12}", a, b, c, d, e, f, g, h, i, j, k, l, m); + } + + // This method is invoked before we start interpretting anything, so the methods invoked in it will be jitted. + // This is necessary for the calling convention tests that test calls from the interpreter to the JITted code + // to actually test things. + static void EnsureCallingConventionTestTargetMethodsAreJitted() + { + TestCallingConvention0(1, 2.0f, 3, 4.0, 5, 6.0); + + TestStruct s = new TestStruct(); + s.a = 1; + s.b = 2; + s.c = 3; + s.d = 4; + s.e = 5; + s.f = 6; + TestCallingConvention1(s); + + TestStruct2 s2 = TestCallingConvention2(); + + Vector2 v = TestCallingConvention3(); + + TestStruct s4 = TestCallingConvention4(); + + TestStruct4ii s5 = TestCallingConvention5(); + + TestStruct4if s6 = TestCallingConvention6(); + + TestStruct4fi s7 = TestCallingConvention7(); + + TestStruct4ff s8 = TestCallingConvention8(); + + TestStruct4fi s9 = new TestStruct4fi(); + s9.a = 1.0f; + s9.b = 2.0f; + s9.c = 3; + s9.d = 4; + TestCallingConvention9(s9); + + TestStruct3d s10 = new TestStruct3d(); + s10.a = 1.0f; + s10.b = 2.0f; + s10.c = 3.0f; + TestCallingConvention10(s10); + + TestStruct3d s11 = TestCallingConvention11(); + Console.WriteLine("TestCallingConvention11: s = "); + Console.WriteLine(s11.a); + Console.WriteLine(s11.b); + Console.WriteLine(s11.c); + + TestCallingConvention12(1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 10, 11, 12); + } + static int Main(string[] args) { jitField1 = 42; jitField2 = 43; + + EnsureCallingConventionTestTargetMethodsAreJitted(); + RunInterpreterTests(); return 100; } @@ -89,6 +301,87 @@ static int Main(string[] args) [MethodImpl(MethodImplOptions.NoInlining)] public static void RunInterpreterTests() { + TestCallingConvention0(1, 2.0f, 3, 4.0, 5, 6.0); + + TestStruct s = new TestStruct(); + s.a = 1; + s.b = 2; + s.c = 3; + s.d = 4; + s.e = 5; + s.f = 6; + TestCallingConvention1(s); + + TestStruct2 s2 = TestCallingConvention2(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s2.a); + Console.WriteLine(s2.b); + +#if VECTOR_ALIGNMENT_WORKS + // Interpreter-TODO: enable this again after fixing the alignment for the Vector2 struct and similar ones + Vector2 v = TestCallingConvention3(); + Console.WriteLine("TestCallingConvention: v = "); + Console.WriteLine(v[0]); + Console.WriteLine(v[1]); +#endif + TestStruct s4 = TestCallingConvention4(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s4.a); + Console.WriteLine(s4.b); + Console.WriteLine(s4.c); + Console.WriteLine(s4.d); + Console.WriteLine(s4.e); + Console.WriteLine(s4.f); + + TestStruct4ii s5 = TestCallingConvention5(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s5.a); + Console.WriteLine(s5.b); + Console.WriteLine(s5.c); + Console.WriteLine(s5.d); + + TestStruct4if s6 = TestCallingConvention6(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s6.a); + Console.WriteLine(s6.b); + Console.WriteLine(s6.c); + Console.WriteLine(s6.d); + + TestStruct4fi s7 = TestCallingConvention7(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s7.a); + Console.WriteLine(s7.b); + Console.WriteLine(s7.c); + Console.WriteLine(s7.d); + + TestStruct4ff s8 = TestCallingConvention8(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s8.a); + Console.WriteLine(s8.b); + Console.WriteLine(s8.c); + Console.WriteLine(s8.d); + + TestStruct4fi s9 = new TestStruct4fi(); + s9.a = 1.0f; + s9.b = 2.0f; + s9.c = 3; + s9.d = 4; + TestCallingConvention9(s9); + + TestStruct3d s10 = new TestStruct3d(); + s10.a = 1.0f; + s10.b = 2.0f; + s10.c = 3.0f; + TestCallingConvention10(s10); + + TestStruct3d s11 = TestCallingConvention11(); + Console.WriteLine("TestCallingConvention: s = "); + Console.WriteLine(s11.a); + Console.WriteLine(s11.b); + Console.WriteLine(s11.c); + + TestCallingConvention12(1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 10, 11, 12); + // Console.WriteLine("Run interp tests"); if (SumN(50) != 1275) Environment.FailFast(null);