-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
UniversalTransition.S
162 lines (129 loc) · 7.44 KB
/
UniversalTransition.S
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
.intel_syntax noprefix
#include <unixasmmacros.inc>
#ifdef FEATURE_DYNAMIC_CODE
#ifdef _DEBUG
#define TRASH_SAVED_ARGUMENT_REGISTERS 1
#endif
#define SIZEOF_RETADDR 8
#define SIZEOF_RETURN_BLOCK 0x10 // for 16 bytes of conservatively reported space that the callee can
// use to manage the return value that the call eventually generates
#define SIZEOF_ARGUMENT_REGISTERS 0x30 // Callee register spill
//
// From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions:
//
// SIZEOF_RETADDR
// SIZEOF_ARGUMENT_REGISTERS
// SIZEOF_RETURN_BLOCK
// SIZEOF_FP_REGS
//
#define DISTANCE_FROM_CHILDSP_TO_FP_REGS 0
#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK SIZEOF_FP_REGS
#define DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS (SIZEOF_FP_REGS + SIZEOF_RETURN_BLOCK)
#define DISTANCE_FROM_CHILDSP_TO_RETADDR (SIZEOF_FP_REGS + SIZEOF_RETURN_BLOCK + SIZEOF_ARGUMENT_REGISTERS + 8)
//
// Defines an assembly thunk used to make a transition from managed code to a callee,
// then (based on the return value from the callee), either returning or jumping to
// a new location while preserving the input arguments. The usage of this thunk also
// ensures arguments passed are properly reported.
//
// TODO: This code currently only tailcalls, and does not return.
//
// Inputs:
// rdi, esi, rcx, rdx, r8, r9, stack space: arguments as normal
// r10: The location of the target code the UniversalTransition thunk will call
// r11: The only parameter to the target function (passed in rdx to callee)
//
//
// Frame layout is:
//
// {StackPassedArgs} ChildSP+0D0 CallerSP+000
// {CallerRetaddr} ChildSP+0C8 CallerSP-008
// {AlignmentPad (0x8 bytes)} ChildSP+0C0 CallerSP-010
// {IntArgRegs (0x30 bytes)} ChildSP+090 CallerSP-040
// {ReturnBlock (0x10 bytes)} ChildSP+080 CallerSP-050
// {FpArgRegs (xmm0-xmm7) (0x80 bytes)} ChildSP+000 CallerSP-0D0
// {CalleeRetaddr} ChildSP-008 CallerSP-0D8
//
// NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure
// must be updated as well.
//
// NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has
// knowledge of the exact layout of all pieces of the frame that lie at or above the pushed
// FpArgRegs.
//
// NOTE: The stack walker guarantees that conservative GC reporting will be applied to
// everything between the base of the ReturnBlock and the top of the StackPassedArgs.
//
.macro UNIVERSAL_TRANSITION FunctionName
NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler
alloc_stack DISTANCE_FROM_CHILDSP_TO_RETADDR
// save integer argument registers
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x00], rdi
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x08], rsi
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x10], rcx
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x18], rdx
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x20], r8
mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x28], r9
// save fp argument registers
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x00], xmm0
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x10], xmm1
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x20], xmm2
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x30], xmm3
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x40], xmm4
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x50], xmm5
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x60], xmm6
movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x70], xmm7
#ifdef TRASH_SAVED_ARGUMENT_REGISTERS
// Before calling out, trash all of the argument registers except the ones (rdi, rsi) that
// hold outgoing arguments. All of these registers have been saved to the transition
// frame, and the code at the call target is required to use only the transition frame
// copies when dispatching this call to the eventual callee.
movsd xmm0, [C_VAR(RhpFpTrashValues) + 0x0]
movsd xmm1, [C_VAR(RhpFpTrashValues) + 0x8]
movsd xmm2, [C_VAR(RhpFpTrashValues) + 0x10]
movsd xmm3, [C_VAR(RhpFpTrashValues) + 0x18]
movsd xmm4, [C_VAR(RhpFpTrashValues) + 0x20]
movsd xmm5, [C_VAR(RhpFpTrashValues) + 0x28]
movsd xmm6, [C_VAR(RhpFpTrashValues) + 0x30]
movsd xmm7, [C_VAR(RhpFpTrashValues) + 0x38]
mov rcx, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x10]
mov rdx, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x18]
mov r8, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x20]
mov r9, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x28]
#endif // TRASH_SAVED_ARGUMENT_REGISTERS
//
// Call out to the target, while storing and reporting arguments to the GC.
//
mov rsi, r11
lea rdi, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK]
call r10
ALTERNATE_ENTRY ReturnFrom\FunctionName
// restore fp argument registers
movdqa xmm0, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x00]
movdqa xmm1, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x10]
movdqa xmm2, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x20]
movdqa xmm3, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x30]
movdqa xmm4, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x40]
movdqa xmm5, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x50]
movdqa xmm6, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x60]
movdqa xmm7, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x70]
// restore integer argument registers
mov rdi, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x00]
mov rsi, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x08]
mov rcx, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x10]
mov rdx, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x18]
mov r8, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x20]
mov r9, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x28]
// Pop the space that was allocated between the ChildSP and the caller return address.
free_stack DISTANCE_FROM_CHILDSP_TO_RETADDR
jmp rax
NESTED_END Rhp\FunctionName, _TEXT
.endm // UNIVERSAL_TRANSITION
// To enable proper step-in behavior in the debugger, we need to have two instances
// of the thunk. For the first one, the debugger steps into the call in the function,
// for the other, it steps over it.
UNIVERSAL_TRANSITION UniversalTransition
UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall
#endif // FEATURE_DYNAMIC_CODE