-
Notifications
You must be signed in to change notification settings - Fork 117
/
objc_msgSend.x86-64.S
315 lines (282 loc) · 10.1 KB
/
objc_msgSend.x86-64.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
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
305
306
307
308
309
310
311
312
313
314
315
#ifdef _WIN64
# define START_PROC(x) .seh_proc x
# define END_PROC(x) .seh_endproc
# define FRAME_OFFSET(x) .seh_stackalloc x
# define FIRST_ARGUMENT_STR "%rcx"
# define FIRST_ARGUMENT %rcx
# define SECOND_ARGUMENT %rdx
# define THIRD_ARGUMENT %r8
#else
# define START_PROC(x) .cfi_startproc
# define END_PROC(x) .cfi_endproc
# define FRAME_OFFSET(x) .cfi_adjust_cfa_offset x
# define FIRST_ARGUMENT_STR "%rdi"
# define FIRST_ARGUMENT %rdi
# define SECOND_ARGUMENT %rsi
# define THIRD_ARGUMENT %rdx
#endif
.macro MSGSEND fnname receiver, sel
START_PROC(\fnname) # Start emitting unwind data. We
# don't actually care about any of
# the stuff except the slow call,
# because that's the only one that
# can throw.
test \receiver, \receiver # If the receiver is nil
jz 4f # return nil
movq $SMALLOBJ_MASK, %r10 # Load the small object mask
test \receiver, %r10 # Check if the receiver is a small object
jnz 6f # Get the small object class
mov (\receiver), %r10 # Load the dtable from the class
1: # classLoaded
mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class into r10
mov %rax, -8(%rsp) # %rax contains information for variadic calls
mov %rbx, -16(%rsp) # On the fast path, spill into the red zone
mov (\sel), %eax # Load the selector index into %eax
mov SHIFT_OFFSET(%r10), %r11d # Load the shift (dtable size) into r11
cmpl $8, %r11d # If this is a small dtable, jump to the small dtable handlers
je 2f
cmpl $0, %r11d
je 3f
movl %eax, %r11d
shrl $16, %r11d
movq DATA_OFFSET(%r10, %r11, 8), %r10
2: # dtable16:
movzbl %ah, %ebx
movq DATA_OFFSET(%r10, %rbx, 8), %r10
3: # dtable8:
movzbl %al, %ebx
mov -8(%rsp), %rax
movq DATA_OFFSET(%r10, %rbx, 8), %r10
mov -16(%rsp), %rbx
test %r10, %r10
jz 5f # Nil slot - invoke some kind of forwarding mechanism
mov SLOT_OFFSET(%r10), %r10
7:
#ifdef WITH_TRACING
push %r12
push %r13
push %r10
mov (\sel), %r11 # Load the selector index
lea tracing_dtable(%rip), %r10
mov (%r10), %r10
mov SHIFT_OFFSET(%r10), %r13 # Load the shift (dtable size)
mov DATA_OFFSET(%r10), %r12 # load the address of the start of the array
pop %r10
cmpl $8, %r13d # If this is a small dtable, jump to the small dtable handlers
je 10f
cmpl $0, %r13d
je 11f
mov %r11, %r13
and $0xff0000, %r13
shrl $13, %r13d # Right shift 16, but then left shift by 3 *sizeof(void*)
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
10: # dtable16:
mov %r11, %r13
and $0xff00, %r13
shrl $5, %r13d
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
11: # dtable8:
mov %r11, %r13
and $0xff, %r13
shll $3, %r13d
add %r13, %r12
mov (%r12), %r11
pop %r13
pop %r12
test %r11, %r11
jz 12f
push %rax # We need to preserve all registers that may contain arguments:
push %rdi
push %rsi
push %rdx
push %rcx
push %r8
push %r9
push %r10
push %r11
mov \receiver, %rdi # Arg 0 is receiver
mov \sel, %rsi # Arg 1 is selector
mov %r10, %rdx # Arg 2 is IMP
mov $0, %rcx # Arg 3 is entry / exit (0/1)
mov $0, %r8 # Arg 4 is return value (0 on entry)
call *%r11 # Call the tracing function
cmpq $0, %rax
jz 13f # If it returns 0, don't call the end-tracing function.
cmpq $1, %rax # If it returns 1, do call the tracing function
jne 14f # Any other value is an interposition
# function to call instead of the method
call pushTraceReturnStack # rax now contains a thread-local buffer for storing returns
pop %r11 # Restore all of the argument registers
pop %r10 # except rax, which we'll need before the call
pop %r9
pop %r8
pop %rcx
pop %rdx
pop %rsi
pop %rdi
mov \receiver, (%rax) # Store the receiver in TLS
mov \sel, 8(%rax) # Store the selector in TLS
mov %r10, 16(%rax) # Store the method in TLS
mov %r11, 24(%rax) # Store the tracing function in TLS
mov 8(%rsp), %r11 # r11 now contains the return address
mov %r11, 32(%rax) # Store the method-return address in TLS
pop %rax
pop %r11 # r11 now contains the return address, but we don't care
call *%r10 # Call the IMP. The stack should now be in the same state
# that it was on entry into this function
push %rax # Now we are free to clobber argument
push %rdx # registers, but we must preserve return registers...
call popTraceReturnStack # rax now contains a thread-local buffer for storing returns
push %rax # save the return value, because we'll need it after the tracing function call
mov (%rax), %rdi # Load the receiver into arg 0
mov 8(%rax), %rsi # Load the selector into arg 1
mov 16(%rax), %rdx # Load the IMP into arg 3
mov $1, %rcx # Arg 4 is 1 (tracing on exit)
mov %rax, %r8 # Arg 5 is the return result
mov 24(%rax), %r11 # Reload the address of the tracing function
call *%r11 # Call the tracing function
pop %rax # Reload the real return address
mov 32(%rax), %r11
pop %rdx # Reload saved values
pop %rax
jmp *%r11 # Simulate a return by jumping to the cached return address
13: # Skip tracing on exit and just tail-call the method
pop %r11
pop %r10
pop %r9
pop %r8
pop %rcx
pop %rdx
pop %rsi
pop %rdi
pop %rax
jmp *%r10
14:
mov %rax, %r10
pop %r9
pop %r9
pop %r9
pop %r8
pop %rcx
pop %rdx
pop %rsi
pop %rdi
pop %rax
12:
#endif // WITH_TRACING
#ifdef _MSC_VER
mov %r10, %rax
jmp *__guard_dispatch_icall_fptr(%rip)
#else
jmp *%r10
#endif
4: # returnNil:
# Both of the return registers are
# callee-save on x86-64, so we can
# return 0 in both in the same code:
xor %rax, %rax # Return 0 as an integer
pxor %xmm0, %xmm0 # Return 0 as a floating point value
ret
5: # slowSend:
push %rax # We need to preserve all registers that may contain arguments:
push %rbx
push %rcx
push %r8
push %r9
sub $0x98, %rsp
movups %xmm0, 0x80(%rsp)
movups %xmm1, 0x70(%rsp)
movups %xmm2, 0x60(%rsp)
movups %xmm3, 0x50(%rsp)
movups %xmm4, 0x40(%rsp)
movups %xmm5, 0x30(%rsp)
movups %xmm6, 0x20(%rsp)
movups %xmm7, 0x10(%rsp)
#rdi rsi rdx
# We're (potentially) modifying the self argument with the lookup, so we don't want to be
.ifc "\receiver", FIRST_ARGUMENT_STR
push FIRST_ARGUMENT
mov %rsp, FIRST_ARGUMENT
push SECOND_ARGUMENT # Save _cmd (not preserved across calls)
push THIRD_ARGUMENT
.else
push FIRST_ARGUMENT # Save the sret pointer
push SECOND_ARGUMENT # Save self where it can be modified
mov %rsp, FIRST_ARGUMENT
push THIRD_ARGUMENT
mov THIRD_ARGUMENT, SECOND_ARGUMENT # move _cmd to where the callee expects it to be
.endif
FRAME_OFFSET(0xD8)
call CDECL(slowMsgLookup) # Call the slow lookup function
mov %rax, %r10 # Load the returned IMP
pop THIRD_ARGUMENT
pop SECOND_ARGUMENT
pop FIRST_ARGUMENT
movups 0x80(%rsp), %xmm0
movups 0x70(%rsp), %xmm1
movups 0x60(%rsp), %xmm2
movups 0x50(%rsp), %xmm3
movups 0x40(%rsp), %xmm4
movups 0x30(%rsp), %xmm5
movups 0x20(%rsp), %xmm6
movups 0x10(%rsp), %xmm7
add $0x98, %rsp
pop %r9
pop %r8
pop %rcx
pop %rbx
pop %rax
jmp 7b
6: # smallObject:
and \receiver, %r10 # Find the small int type
lea CDECL(SmallObjectClasses)(%rip), %r11
mov (%r11, %r10, 8), %r10
jmp 1b
END_PROC(\fnname)
.endm
#ifdef _WIN64
.text
.def objc_msgSend;
.scl 2;
.type 32;
.endef
.def objc_msgSend_fpret;
.scl 2;
.type 32;
.endef
.def objc_msgSend_stret;
.scl 2;
.type 32;
.endef
.globl CDECL(objc_msgSend_fpret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function)
.globl CDECL(objc_msgSend)
TYPE_DIRECTIVE(CDECL(objc_msgSend), @function)
CDECL(objc_msgSend_fpret):
CDECL(objc_msgSend):
MSGSEND objc_msgSend, %rcx, %rdx
.globl CDECL(objc_msgSend_stret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function)
CDECL(objc_msgSend_stret):
MSGSEND objc_msgSend_stret, %rdx, %r8
.section .drectve,"yn"
EXPORT_SYMBOL(objc_msgSend)
EXPORT_SYMBOL(objc_msgSend_fpret)
EXPORT_SYMBOL(objc_msgSend_stret)
#else
.globl CDECL(objc_msgSend)
TYPE_DIRECTIVE(CDECL(objc_msgSend), @function)
.globl CDECL(objc_msgSend_fpret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function)
CDECL(objc_msgSend_fpret):
CDECL(objc_msgSend):
MSGSEND objc_msgSend, %rdi, %rsi
.globl CDECL(objc_msgSend_stret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function)
CDECL(objc_msgSend_stret):
MSGSEND objc_msgSend_stret, %rsi, %rdx
#endif