-
Notifications
You must be signed in to change notification settings - Fork 0
/
objc_msgSend.x86-64.S
279 lines (247 loc) · 8.96 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
#define DTABLE_OFFSET 64
#define SMALLOBJ_MASK 7
#define SHIFT_OFFSET 4
#define DATA_OFFSET 16
#define SLOT_OFFSET 32
.macro MSGSEND receiver, sel
.cfi_startproc # 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
push %r12
push %r13
mov (\sel), %r11 # Load the selector index
mov SHIFT_OFFSET(%r10), %r13 # Load the shift (dtable size)
mov DATA_OFFSET(%r10), %r12 # load the address of the start of the array
cmpl $8, %r13d # If this is a small dtable, jump to the small dtable handlers
je 2f
cmpl $0, %r13d
je 3f
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
2: # dtable16:
mov %r11, %r13
and $0xff00, %r13
shrl $5, %r13d
add %r13, %r12
mov (%r12), %r12
mov DATA_OFFSET(%r12), %r12
3: # dtable8:
mov %r11, %r13
and $0xff, %r13
shll $3, %r13d
add %r13, %r12
mov (%r12), %r10
pop %r13
pop %r12
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
jmp *%r10
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", "%rdi"
push %rdi
mov %rsp, %rdi
push %rsi # Save _cmd (not preserved across calls)
push %rdx
.else
push %rdi # Save the sret pointer
push %rsi # Save self where it can be modified
mov %rsp, %rdi
push %rdx
mov %rdx, %rsi # move _cmd to where the callee expects it to be
.endif
.cfi_adjust_cfa_offset 0xD8
call CDECL(slowMsgLookup) # Call the slow lookup function
mov %rax, %r10 # Load the returned IMP
pop %rdx
pop %rsi
pop %rdi
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
shll $3, %r10d
lea CDECL(SmallObjectClasses)(%rip), %r11
add %r11, %r10
mov (%r10), %r10
jmp 1b
.cfi_endproc
.endm
.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 %rdi, %rsi
.globl CDECL(objc_msgSend_stret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function)
CDECL(objc_msgSend_stret):
MSGSEND %rsi, %rdx