1;; Licensed to the .NET Foundation under one or more agreements.
2;; The .NET Foundation licenses this file to you under the MIT license.
3;; See the LICENSE file in the project root for more information.
4
5include AsmMacros.inc
6
7ifdef FEATURE_DYNAMIC_CODE
8
9ifdef _DEBUG
10TRASH_SAVED_ARGUMENT_REGISTERS equ 1
11else
12TRASH_SAVED_ARGUMENT_REGISTERS equ 0
13endif
14
15if TRASH_SAVED_ARGUMENT_REGISTERS ne 0
16EXTERN RhpIntegerTrashValues    : QWORD
17EXTERN RhpFpTrashValues         : QWORD
18endif ;; TRASH_SAVED_ARGUMENT_REGISTERS
19
20SIZEOF_RETADDR                  equ 8h
21
22SIZEOF_ALIGNMENT_PADDING        equ 8h
23
24SIZEOF_RETURN_BLOCK             equ 10h    ; for 16 bytes of conservatively reported space that the callee can
25                                           ; use to manage the return value that the call eventually generates
26
27SIZEOF_FP_REGS                  equ 40h    ; xmm0-3
28
29SIZEOF_OUT_REG_HOMES            equ 20h    ; Callee register spill
30
31;
32; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions:
33;
34;       SIZEOF_RETADDR
35;       SIZEOF_ALIGNMENT_PADDING
36;       SIZEOF_RETURN_BLOCK
37;       SIZEOF_FP_REGS
38;       SIZEOF_OUT_REG_HOMES
39;
40
41DISTANCE_FROM_CHILDSP_TO_FP_REGS                equ SIZEOF_OUT_REG_HOMES
42
43DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK           equ DISTANCE_FROM_CHILDSP_TO_FP_REGS + SIZEOF_FP_REGS
44
45DISTANCE_FROM_CHILDSP_TO_RETADDR                equ DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK + SIZEOF_RETURN_BLOCK + SIZEOF_ALIGNMENT_PADDING
46
47DISTANCE_FROM_CHILDSP_TO_CALLERSP               equ DISTANCE_FROM_CHILDSP_TO_RETADDR + SIZEOF_RETADDR
48
49.errnz DISTANCE_FROM_CHILDSP_TO_CALLERSP mod 16
50
51;;
52;; Defines an assembly thunk used to make a transition from managed code to a callee,
53;; then (based on the return value from the callee), either returning or jumping to
54;; a new location while preserving the input arguments.  The usage of this thunk also
55;; ensures arguments passed are properly reported.
56;;
57;; TODO: This code currently only tailcalls, and does not return.
58;;
59;; Inputs:
60;;      rcx, rdx, r8, r9, stack space: arguments as normal
61;;      r10: The location of the target code the UniversalTransition thunk will call
62;;      r11: The only parameter to the target function (passed in rdx to callee)
63;;
64
65;
66; Frame layout is:
67;
68;   {StackPassedArgs}                           ChildSP+0a0     CallerSP+020
69;   {IntArgRegs (rcx,rdx,r8,r9) (0x20 bytes)}   ChildSP+080     CallerSP+000
70;   {CallerRetaddr}                             ChildSP+078     CallerSP-008
71;   {AlignmentPad (0x8 bytes)}                  ChildSP+070     CallerSP-010
72;   {ReturnBlock (0x10 bytes)}                  ChildSP+060     CallerSP-020
73;   {FpArgRegs (xmm0-xmm3) (0x40 bytes)}        ChildSP+020     CallerSP-060
74;   {CalleeArgumentHomes (0x20 bytes)}          ChildSP+000     CallerSP-080
75;   {CalleeRetaddr}                             ChildSP-008     CallerSP-088
76;
77; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure
78; must be updated as well.
79;
80; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has
81; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed
82; FpArgRegs.
83;
84; NOTE: The stack walker guarantees that conservative GC reporting will be applied to
85; everything between the base of the ReturnBlock and the top of the StackPassedArgs.
86;
87
88UNIVERSAL_TRANSITION macro FunctionName
89
90NESTED_ENTRY Rhp&FunctionName, _TEXT
91
92        alloc_stack DISTANCE_FROM_CHILDSP_TO_RETADDR
93
94        save_reg_postrsp    rcx,   0h + DISTANCE_FROM_CHILDSP_TO_CALLERSP
95        save_reg_postrsp    rdx,   8h + DISTANCE_FROM_CHILDSP_TO_CALLERSP
96        save_reg_postrsp    r8,   10h + DISTANCE_FROM_CHILDSP_TO_CALLERSP
97        save_reg_postrsp    r9,   18h + DISTANCE_FROM_CHILDSP_TO_CALLERSP
98
99        save_xmm128_postrsp xmm0, DISTANCE_FROM_CHILDSP_TO_FP_REGS
100        save_xmm128_postrsp xmm1, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 10h
101        save_xmm128_postrsp xmm2, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 20h
102        save_xmm128_postrsp xmm3, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 30h
103
104        END_PROLOGUE
105
106if TRASH_SAVED_ARGUMENT_REGISTERS ne 0
107
108        ; Before calling out, trash all of the argument registers except the ones (rcx, rdx) that
109        ; hold outgoing arguments.  All of these registers have been saved to the transition
110        ; frame, and the code at the call target is required to use only the transition frame
111        ; copies when dispatching this call to the eventual callee.
112
113        movsd           xmm0, mmword ptr [RhpFpTrashValues + 0h]
114        movsd           xmm1, mmword ptr [RhpFpTrashValues + 8h]
115        movsd           xmm2, mmword ptr [RhpFpTrashValues + 10h]
116        movsd           xmm3, mmword ptr [RhpFpTrashValues + 18h]
117
118        mov             r8, qword ptr [RhpIntegerTrashValues + 10h]
119        mov             r9, qword ptr [RhpIntegerTrashValues + 18h]
120
121endif ; TRASH_SAVED_ARGUMENT_REGISTERS
122
123        ;
124        ; Call out to the target, while storing and reporting arguments to the GC.
125        ;
126        mov  rdx, r11
127        lea  rcx, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK]
128        call r10
129
130        EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom&FunctionName
131
132        ; We cannot make the label public as that tricks DIA stackwalker into thinking
133        ; it's the beginning of a method. For this reason we export the address
134        ; by means of an auxiliary variable.
135
136        ; restore fp argument registers
137        movdqa          xmm0, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS      ]
138        movdqa          xmm1, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 10h]
139        movdqa          xmm2, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 20h]
140        movdqa          xmm3, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 30h]
141
142        ; restore integer argument registers
143        mov             rcx, [rsp +  0h + DISTANCE_FROM_CHILDSP_TO_CALLERSP]
144        mov             rdx, [rsp +  8h + DISTANCE_FROM_CHILDSP_TO_CALLERSP]
145        mov             r8,  [rsp + 10h + DISTANCE_FROM_CHILDSP_TO_CALLERSP]
146        mov             r9,  [rsp + 18h + DISTANCE_FROM_CHILDSP_TO_CALLERSP]
147
148        ; epilog
149        nop
150
151        ; Pop the space that was allocated between the ChildSP and the caller return address.
152        add             rsp, DISTANCE_FROM_CHILDSP_TO_RETADDR
153
154        TAILJMP_RAX
155
156NESTED_END Rhp&FunctionName, _TEXT
157
158        endm
159
160        ; To enable proper step-in behavior in the debugger, we need to have two instances
161        ; of the thunk. For the first one, the debugger steps into the call in the function,
162        ; for the other, it steps over it.
163        UNIVERSAL_TRANSITION UniversalTransition
164        UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall
165
166endif
167
168end
169