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
|
;; 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.
.586
.model flat
option casemap:none
.code
include AsmMacros.inc
ifdef FEATURE_CACHED_INTERFACE_DISPATCH
EXTERN RhpCidResolve : PROC
EXTERN _RhpUniversalTransition_DebugStepTailCall@0 : PROC
EXTERN RhpCastableObjectResolve : PROC
EXTERN _t_TLS_DispatchCell:DWORD
EXTERN __tls_index:DWORD
GET_TLS_DISPATCH_CELL macro
ASSUME fs : NOTHING
mov eax, [__tls_index]
add eax, eax
add eax, eax
add eax, fs:[__tls_array]
mov eax, [eax]
mov eax, [eax + SECTIONREL _t_TLS_DispatchCell]
endm
SET_TLS_DISPATCH_CELL macro
;; ecx: value to assign to the TLS variable
ASSUME fs : NOTHING
push eax
mov eax, [__tls_index]
add eax, eax
add eax, eax
add eax, fs:[__tls_array]
mov eax, [eax]
lea eax, [eax + SECTIONREL _t_TLS_DispatchCell]
mov [eax],ecx
pop eax
endm
_RhpCastableObjectDispatch_CommonStub proc public
;; There are arbitrary callers passing arguments with arbitrary signatures.
;; Custom calling convention:
;; eax: pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers)
;; make some scratch regs
push ecx
;; store dispatch cell address into the TLS variable
mov ecx, [eax]
SET_TLS_DISPATCH_CELL
;; restore the regs we used
pop ecx
;; jump to the target
mov eax, [eax + 4] ;; eax <- target slot data
jmp eax
_RhpCastableObjectDispatch_CommonStub endp
_RhpTailCallTLSDispatchCell proc public
;; Load the dispatch cell out of the TLS variable
GET_TLS_DISPATCH_CELL
;; Tail call to the target of the dispatch cell
jmp dword ptr [eax]
_RhpTailCallTLSDispatchCell endp
_RhpCastableObjectDispatchHelper_TailCalled proc public
push ebp
mov ebp, esp
;; Load the dispatch cell out of the TLS variable
GET_TLS_DISPATCH_CELL
jmp _RhpCastableObjectDispatchHelper2
_RhpCastableObjectDispatchHelper_TailCalled endp
_RhpCastableObjectDispatchHelper proc public
push ebp
mov ebp, esp
;; TODO! Implement fast lookup helper to avoid the universal transition each time we
;; hit a CastableObject
;; If the initial lookup fails, call into managed under the universal thunk
;; to run the full lookup routine
;; EAX currently contains the cache block. We need to point it to the
;; indirection cell using the back pointer in the cache block
mov eax, [eax + OFFSETOF__InterfaceDispatchCache__m_pCell]
ALTERNATE_ENTRY RhpCastableObjectDispatchHelper2
;; indirection cell address is in EAX, it will be passed by
;; the universal transition thunk as an argument to RhpCastableObjectResolve
push eax
lea eax, RhpCastableObjectResolve
push eax
jmp _RhpUniversalTransition_DebugStepTailCall@0
_RhpCastableObjectDispatchHelper endp
;; Macro that generates code to check a single cache entry.
CHECK_CACHE_ENTRY macro entry
NextLabel textequ @CatStr( Attempt, %entry+1 )
cmp ebx, [eax + (OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 8))]
jne @F
pop ebx
jmp dword ptr [eax + (OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 8) + 4)]
@@:
endm
;; Macro that generates a stub consuming a cache with the given number of entries.
DEFINE_INTERFACE_DISPATCH_STUB macro entries
StubName textequ @CatStr( _RhpInterfaceDispatch, entries )
StubName proc public
;; Check the instance here to catch null references. We're going to touch it again below (to cache
;; the EEType pointer), but that's after we've pushed ebx below, and taking an A/V there will
;; mess up the stack trace for debugging. We also don't have a spare scratch register (eax holds
;; the cache pointer and the push of ebx below is precisely so we can access a second register
;; to hold the EEType pointer).
test ecx, ecx
je RhpInterfaceDispatchNullReference
;; eax currently contains the indirection cell address. We need to update it to point to the cache
;; block instead.
mov eax, [eax + OFFSETOF__InterfaceDispatchCell__m_pCache]
;; Cache pointer is already loaded in the only scratch register we have so far, eax. We need
;; another scratch register to hold the instance type so save the value of ebx and use that.
push ebx
;; Load the EEType from the object instance in ebx.
mov ebx, [ecx]
CurrentEntry = 0
while CurrentEntry lt entries
CHECK_CACHE_ENTRY %CurrentEntry
CurrentEntry = CurrentEntry + 1
endm
;; eax currently contains the cache block. We need to point it back to the
;; indirection cell using the back pointer in the cache block
mov eax, [eax + OFFSETOF__InterfaceDispatchCache__m_pCell]
pop ebx
jmp RhpInterfaceDispatchSlow
StubName endp
endm ;; DEFINE_INTERFACE_DISPATCH_STUB
;; Define all the stub routines we currently need.
DEFINE_INTERFACE_DISPATCH_STUB 1
DEFINE_INTERFACE_DISPATCH_STUB 2
DEFINE_INTERFACE_DISPATCH_STUB 4
DEFINE_INTERFACE_DISPATCH_STUB 8
DEFINE_INTERFACE_DISPATCH_STUB 16
DEFINE_INTERFACE_DISPATCH_STUB 32
DEFINE_INTERFACE_DISPATCH_STUB 64
;; Shared out of line helper used on cache misses.
RhpInterfaceDispatchSlow proc
;; eax points at InterfaceDispatchCell
;; Setup call to Universal Transition thunk
push ebp
mov ebp, esp
push eax ; First argument (Interface Dispatch Cell)
lea eax, [RhpCidResolve]
push eax ; Second argument (RhpCidResolve)
;; Jump to Universal Transition
jmp _RhpUniversalTransition_DebugStepTailCall@0
RhpInterfaceDispatchSlow endp
;; Out of line helper used when we try to interface dispatch on a null pointer. Sets up the stack so the
;; debugger gives a reasonable stack trace.
RhpInterfaceDispatchNullReference proc public
push ebp
mov ebp, esp
mov ebx, [ecx] ;; This should A/V
int 3
RhpInterfaceDispatchNullReference endp
;; Stub dispatch routine for dispatch to a vtable slot
_RhpVTableOffsetDispatch proc public
;; eax currently contains the indirection cell address. We need to update it to point to the vtable offset (which is in the m_pCache field)
mov eax, [eax + OFFSETOF__InterfaceDispatchCell__m_pCache]
;; add the vtable offset to the EEType pointer
add eax, [ecx]
;; Load the target address of the vtable into eax
mov eax, [eax]
;; tail-jump to the target
jmp eax
_RhpVTableOffsetDispatch endp
;; Initial dispatch on an interface when we don't have a cache yet.
_RhpInitialInterfaceDispatch proc public
ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch
jmp RhpInterfaceDispatchSlow
_RhpInitialInterfaceDispatch endp
endif ;; FEATURE_CACHED_INTERFACE_DISPATCH
end
|