; ; Licensed to the Apache Software Foundation (ASF) under one ; or more contributor license agreements. See the NOTICE file ; distributed with this work for additional information ; regarding copyright ownership. The ASF licenses this file ; to you under the Apache License, Version 2.0 (the ; "License"); you may not use this file except in compliance ; with the License. You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, ; software distributed under the License is distributed on an ; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ; KIND, either express or implied. See the License for the ; specific language governing permissions and limitations ; under the License. ; typelib_TypeClass_VOID equ 0 typelib_TypeClass_CHAR equ 1 typelib_TypeClass_BOOLEAN equ 2 typelib_TypeClass_BYTE equ 3 typelib_TypeClass_SHORT equ 4 typelib_TypeClass_UNSIGNED_SHORT equ 5 typelib_TypeClass_LONG equ 6 typelib_TypeClass_UNSIGNED_LONG equ 7 typelib_TypeClass_HYPER equ 8 typelib_TypeClass_UNSIGNED_HYPER equ 9 typelib_TypeClass_FLOAT equ 10 typelib_TypeClass_DOUBLE equ 11 typelib_TypeClass_ENUM equ 15 EXTERN cpp_vtable_call: PROC .CODE ; ; | ... | ; +----------------------------+ ; | argument 4 | ; rbp+48 rsp+40 +----------------------------+ ------- ; | argument 3, r9/xmm3 home | ^ shadow ; rbp+40 rsp+32 +----------------------------+ | space, ; | argument 2, r8/xmm2 home | | guaranteed to be present but uninitialized, ; rbp+32 rsp+24 +----------------------------+ | we have to copy ; | argument 1, rdx/xmm1 home | | the first 4 parameters there from the registers, ; rbp+24 rsp+16 +----------------------------+ | to form the continuous array of arguments. ; | argument 0, rcx/xmm0 home | v ; rbp+16 rsp+08 +----------------------------+ ------- ; | return address | ; rbp+08 rsp--> +----------------------------+ ; | caller's rbp | ; rbp---------> +----------------------------+ <------ 16 byte boundary ; | pRegisterReturn memory | ; rbp-08 -----> +----------------------------+ ; | | ; rbp-16 -----> +----------------------------+ ------- ; | | ^ ; rbp-24 -----> +----------------------------+ | shadow space ; | | | for cpp_vtable_call ; rbp-32 -----> +----------------------------+ | ; | | | ; rbp-40 -----> +----------------------------+ | ; | | v ; rbp-48 -----> +----------------------------+ ------- ; ; rax = functionIndex ; r10 = vtableOffset ; r11 = &privateSnippetExecutor ; privateSnippetExecutor PROC FRAME push rbp .PUSHREG rbp mov rbp, rsp .SETFRAME rbp, 0 sub rsp, 48 .ENDPROLOG ; 4th param: sal_uInt64 *pRegisterReturn lea r9, -8[rbp] ; 3rd param: sal_Int32 nVtableOffset mov r8, r10 ; 2nd param: sal_Int32 nFunctionIndex mov rdx, rax ; 1st param: void ** pCallStack lea rcx, 8[rbp] call cpp_vtable_call ; Integers would return in RAX and floats in XMM0, but both are free for us to clobber, ; and the caller knows where to look: mov rax, -8[rbp] movsd xmm0, qword ptr -8[rbp] add rsp, 48 pop rbp ret privateSnippetExecutor ENDP ; ; | ... | ; rbp+64 -----> +---------------------------------------------+ ; | sal_uInt32 nStack | ; rbp+56 -----> +---------------------------------------------+ ; | sal_uInt64 *pStack | ; rbp+48 -----> +---------------------------------------------+ ------- ; | typelib_TypeClass eReturnTypeClass, r9 home | ^ shadow ; rbp+40 -----> +---------------------------------------------+ | space, ; | void *pRegisterReturn, r8 home | | guaranteed to be present but uninitialized, ; rbp+32 -----> +---------------------------------------------+ | we have to copy ; | sal_Int32 nVtableIndex, rdx home | | the first 4 parameters there from the registers, ; rbp+24 -----> +---------------------------------------------+ | to form the continuous array of arguments. ; | void* pAdjustedThisPtr, rcx home | v ; rbp+16 -----> +---------------------------------------------+ ------- ; | return address | ; rbp+08 -----> +---------------------------------------------+ ; | caller's rbp | ; rbp --------> +---------------------------------------------+ <---- 16 byte boundary ; | (possible 16 byte alignment placeholder) | ; rbp-08 -----> +---------------------------------------------+ ; | (stack for virtual method) | ; | ... | ; | (shadow space for virtual method) | ; rsp --------> +---------------------------------------------+ <---- 16 byte boundary callVirtualMethod PROC FRAME push rbp .PUSHREG rbp mov rbp, rsp .SETFRAME rbp, 0 ; Save our register arguments to the shadow space: mov 16[rbp], rcx mov 24[rbp], rdx mov 32[rbp], r8 mov 40[rbp], r9 ; nStack rounded to multiple of 2, so the stack is aligned to a 16 byte boundary: mov eax, 56[rbp] inc eax shr eax, 1 shl eax, 1 ; if nStack < 4, add (4 - nStack) empty slots to the stack: mov r10d, 4 sub r10d, eax js copyStack shl r10, 3 sub rsp, r10 copyStack: mov r10, rax shl rax, 3 add rax, 48[rbp] copyStackLoop: sub rax, 8 push [rax] dec r10 jne copyStackLoop populateArgumentRegisters: ; First 4 args are passed in registers. Floating point args needs to be ; in floating point registers, but those are volatile anyway, ; and the callee knows where to look, so put each arg in both ; its general purpose and its floating point register: mov rcx, [rsp] movsd xmm0, qword ptr [rsp] mov rdx, 8[rsp] movsd xmm1, qword ptr 8[rsp] mov r8, 16[rsp] movsd xmm2, qword ptr 16[rsp] mov r9, 24[rsp] movsd xmm3, qword ptr 24[rsp] .ENDPROLOG callMethod: ; Find the method pointer mov rax, 16[rbp] mov r10, [rax] ; pointer to vtable mov r11d, 24[rbp] shl r11, 3 ; sizeof(void*) == 8 add r10, r11 call qword ptr [r10] mov r10d, 40[rbp] mov r11, 32[rbp] cmp r10, typelib_TypeClass_VOID je cleanup cmp r10, typelib_TypeClass_LONG je Lint32 cmp r10, typelib_TypeClass_UNSIGNED_LONG je Lint32 cmp r10, typelib_TypeClass_ENUM je Lint32 cmp r10, typelib_TypeClass_BOOLEAN je Lint8 cmp r10, typelib_TypeClass_BYTE je Lint8 cmp r10, typelib_TypeClass_CHAR je Lint16 cmp r10, typelib_TypeClass_SHORT je Lint16 cmp r10, typelib_TypeClass_UNSIGNED_SHORT je Lint16 cmp r10, typelib_TypeClass_FLOAT je Lfloat cmp r10, typelib_TypeClass_DOUBLE je Lfloat cmp r10, typelib_TypeClass_HYPER je Lint64 cmp r10, typelib_TypeClass_UNSIGNED_HYPER je Lint64 ; https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017 ; "The same pointer must be returned by the callee in RAX." jmp cleanup Lint64: mov qword ptr [r11], rax jmp cleanup Lint32: mov dword ptr [r11], eax jmp cleanup Lint16: mov word ptr [r11], ax jmp cleanup Lint8: mov byte ptr [r11], al jmp cleanup Lfloat: movsd qword ptr [r11], xmm0 jmp cleanup cleanup: lea rsp, 0[rbp] pop rbp ret callVirtualMethod ENDP END