summaryrefslogtreecommitdiff
path: root/src/vm/amd64/GenericComCallStubs.asm
blob: b7e5e0f5ad61cd22dd4492e3f9ca277c4f059cd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
; Licensed to the .NET Foundation under one or more agreements.
; The .NET Foundation licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.

; ==++==
;

;
; ==--==

ifdef FEATURE_COMINTEROP

include AsmMacros.inc
include asmconstants.inc

extern CallDescrWorkerUnwindFrameChainHandler:proc
extern ReverseComUnwindFrameChainHandler:proc
extern COMToCLRWorker:proc
extern JIT_FailFast:proc
extern s_gsCookie:qword


NESTED_ENTRY GenericComCallStub, _TEXT, ReverseComUnwindFrameChainHandler

;
; Set up a ComMethodFrame and call COMToCLRWorker.
;
; Stack frame layout:
;
; (stack parameters)
; ...
; r9
; r8
; rdx
; rcx
; UnmanagedToManagedFrame::m_ReturnAddress
; UnmanagedToManagedFrame::m_Datum
; Frame::m_Next
; __VFN_table                                   <-- rsp + GenericComCallStub_ComMethodFrame_OFFSET
; GSCookie
; (optional padding to qword align xmm save area)
; xmm3
; xmm2
; xmm1
; xmm0                                          <-- rsp + GenericComCallStub_XMM_SAVE_OFFSET
; r12
; r13
; r14
; (optional padding to qword align rsp)
; callee's r9
; callee's r8
; callee's rdx
; callee's rcx

GenericComCallStub_STACK_FRAME_SIZE = 0

; ComMethodFrame MUST be the highest part of the stack frame, immediately
; below the return address and MethodDesc*, so that
; UnmanagedToManagedFrame::m_ReturnAddress and
; UnmanagedToManagedFrame::m_Datum are the right place.
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + (SIZEOF__ComMethodFrame - 8)
GenericComCallStub_ComMethodFrame_NEGOFFSET = GenericComCallStub_STACK_FRAME_SIZE

GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + SIZEOF_GSCookie

; Ensure that the offset of the XMM save area will be 16-byte aligned.
if ((GenericComCallStub_STACK_FRAME_SIZE + 8) MOD 16) ne 0
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + 8
endif

; XMM save area MUST be immediately below GenericComCallStub
; (w/ alignment padding)
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + 4*16
GenericComCallStub_XMM_SAVE_NEGOFFSET = GenericComCallStub_STACK_FRAME_SIZE

; Add in the callee scratch area size.
GenericComCallStub_CALLEE_SCRATCH_SIZE = 4*8
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + GenericComCallStub_CALLEE_SCRATCH_SIZE

; Now we have the full size of the stack frame.  The offsets have been computed relative to the
; top, so negate them to make them relative to the post-prologue rsp.
GenericComCallStub_ComMethodFrame_OFFSET = GenericComCallStub_STACK_FRAME_SIZE - GenericComCallStub_ComMethodFrame_NEGOFFSET
GenericComCallStub_XMM_SAVE_OFFSET = GenericComCallStub_STACK_FRAME_SIZE - GenericComCallStub_XMM_SAVE_NEGOFFSET
OFFSETOF_GSCookie = GenericComCallStub_ComMethodFrame_OFFSET - SIZEOF_GSCookie

        .allocstack     8                       ; UnmanagedToManagedFrame::m_Datum, pushed by prepad

        ;
        ; Allocate the remainder of the ComMethodFrame.  The fields
        ; will be filled in by COMToCLRWorker
        ;
        alloc_stack     SIZEOF__ComMethodFrame - 10h

        ;
        ; Save ComMethodFrame* to pass to COMToCLRWorker
        ;
        mov             r10, rsp

        alloc_stack     GenericComCallStub_ComMethodFrame_OFFSET
            
        ;
        ; Save argument registers
        ;
        SAVE_ARGUMENT_REGISTERS GenericComCallStub_STACK_FRAME_SIZE + 8h

        ;
        ; spill the fp args
        ;
        SAVE_FLOAT_ARGUMENT_REGISTERS GenericComCallStub_XMM_SAVE_OFFSET

        END_PROLOGUE

        mov            rcx, s_gsCookie
        mov            [rsp + OFFSETOF_GSCookie], rcx
        
        ;
        ; Call COMToCLRWorker.  Note that the first parameter (pThread) is
        ; filled in by callee.
        ;
        
ifdef _DEBUG
        mov             rcx, 0cccccccccccccccch
endif
        mov             rdx, r10
        call            COMToCLRWorker

ifdef _DEBUG
        mov             rcx, s_gsCookie
        cmp             [rsp + OFFSETOF_GSCookie], rcx
        je              GoodGSCookie
        call            JIT_FailFast
GoodGSCookie:
endif ; _DEBUG

        ;
        ; epilogue
        ;
        add             rsp, GenericComCallStub_STACK_FRAME_SIZE
        ret

NESTED_END GenericComCallStub, _TEXT


; ARG_SLOT COMToCLRDispatchHelperWithStack(DWORD  dwStackSlots,    // rcx
;                                          ComMethodFrame *pFrame, // rdx
;                                          PCODE  pTarget,         // r8
;                                          PCODE  pSecretArg,      // r9
;                                          INT_PTR pDangerousThis  // rbp+40h
;                                          );
NESTED_ENTRY COMToCLRDispatchHelperWithStack, _TEXT, CallDescrWorkerUnwindFrameChainHandler

ComMethodFrame_Arguments_OFFSET = SIZEOF__ComMethodFrame
ComMethodFrame_XMM_SAVE_OFFSET = GenericComCallStub_XMM_SAVE_OFFSET - GenericComCallStub_ComMethodFrame_OFFSET

        push_nonvol_reg rdi             ; save nonvolatile registers
        push_nonvol_reg rsi             ;
        push_nonvol_reg rbp             ;
        set_frame rbp, 0                ; set frame pointer

        END_PROLOGUE


        ;
        ; copy stack
        ;
        lea     rsi, [rdx + ComMethodFrame_Arguments_OFFSET]
        add     ecx, 4                  ; outgoing argument homes
        mov     eax, ecx                ; number of stack slots
        shl     eax, 3                  ; compute number of argument bytes
        add     eax, 8h                 ; alignment padding
        and     rax, 0FFFFFFFFFFFFFFf0h ; for proper stack alignment, v-liti remove partial register stall
        sub     rsp, rax                ; allocate argument list
        mov     rdi, rsp                ; set destination argument list address
        rep movsq                       ; copy arguments to the stack
        

        ; Stack layout:
        ;
        ; callee's rcx (to be loaded into rcx) <- rbp+40h
        ; r9           (to be loaded into r10)
        ; r8           (IL stub entry point)
        ; rdx          (ComMethodFrame ptr)
        ; rcx          (number of stack slots to repush)
        ; return address
        ; saved rdi
        ; saved rsi
        ; saved rbp                            <- rbp
        ; alignment
        ; (stack parameters)
        ; callee's r9
        ; callee's r8
        ; callee's rdx
        ; callee's rcx (not loaded into rcx)   <- rsp

        ;
        ; load fp registers
        ;
        movdqa  xmm0, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 00h]
        movdqa  xmm1, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 10h]
        movdqa  xmm2, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 20h]
        movdqa  xmm3, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 30h]

        ;
        ; load secret arg and target
        ;
        mov     r10, r9
        mov     rax, r8
        
        ;
        ; load argument registers
        ;
        mov     rcx, [rbp + 40h]        ; ignoring the COM IP at [rsp]
        mov     rdx, [rsp + 08h]
        mov     r8,  [rsp + 10h]
        mov     r9,  [rsp + 18h]
        
        ;
        ; call the target
        ;
        call    rax

        ; It is important to have an instruction between the previous call and the epilog.
        ; If the return address is in epilog, OS won't call personality routine because
        ; it thinks personality routine does not help in this case.
        nop

        ;
        ; epilog
        ;
        lea     rsp, 0[rbp]             ; deallocate argument list
        pop     rbp                     ; restore nonvolatile register
        pop     rsi                     ;
        pop     rdi                     ;
        ret

NESTED_END COMToCLRDispatchHelperWithStack, _TEXT

; ARG_SLOT COMToCLRDispatchHelper(DWORD  dwStackSlots,    // rcx
;                                 ComMethodFrame *pFrame, // rdx
;                                 PCODE  pTarget,         // r8
;                                 PCODE  pSecretArg,      // r9
;                                 INT_PTR pDangerousThis  // rsp + 28h on entry
;                                 );
NESTED_ENTRY COMToCLRDispatchHelper, _TEXT, CallDescrWorkerUnwindFrameChainHandler

        ;
        ; Check to see if we have stack to copy and, if so, tail call to 
        ; the routine that can handle that.
        ;
        test    ecx, ecx
        jnz     COMToCLRDispatchHelperWithStack

        alloc_stack     28h     ; alloc scratch space + alignment,   pDangerousThis moves to [rsp+50]
        END_PROLOGUE


        ; get pointer to arguments
        lea     r11, [rdx + ComMethodFrame_Arguments_OFFSET]

        ;
        ; load fp registers
        ;
        movdqa  xmm0, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 00h]
        movdqa  xmm1, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 10h]
        movdqa  xmm2, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 20h]
        movdqa  xmm3, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 30h]

        ;
        ; load secret arg and target
        ;
        mov     r10, r9
        mov     rax, r8
        
        ;
        ; load argument registers
        ;
        mov     rcx, [rsp + 50h]        ; ignoring the COM IP at [r11 + 00h]
        mov     rdx, [r11 + 08h]
        mov     r8,  [r11 + 10h]
        mov     r9,  [r11 + 18h]
        
        ;
        ; call the target
        ;
        call    rax

        ; It is important to have an instruction between the previous call and the epilog.
        ; If the return address is in epilog, OS won't call personality routine because
        ; it thinks personality routine does not help in this case.
        nop

        ;
        ; epilog
        ;
        add     rsp, 28h
        ret
NESTED_END COMToCLRDispatchHelper, _TEXT



endif ; FEATURE_COMINTEROP

        end