]>
Commit | Line | Data |
---|---|---|
13d88034 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.1 (the "License"). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /******************************************************************** | |
25 | ******************************************************************** | |
26 | ** | |
27 | ** objc-msg-i386.s - i386 code to support objc messaging. | |
28 | ** | |
29 | ******************************************************************** | |
30 | ********************************************************************/ | |
31 | ||
32 | // The assembler syntax for an immediate value is the same as the | |
33 | // syntax for a macro argument number (dollar sign followed by the | |
34 | // digits). Argument number wins in this ambiguity. Until the | |
35 | // assembler is fixed we have to find another way. | |
36 | #define NO_MACRO_CONSTS | |
37 | #ifdef NO_MACRO_CONSTS | |
38 | kTwo = 2 | |
39 | kEight = 8 | |
40 | #endif | |
41 | ||
42 | #if defined(OBJC_COLLECTING_CACHE) | |
43 | // _objc_entryPoints and _objc_exitPoints are used by objc | |
44 | // to get the critical regions for which method caches | |
45 | // cannot be garbage collected. | |
46 | ||
47 | .data | |
48 | .globl _objc_entryPoints | |
49 | _objc_entryPoints: | |
50 | .long _objc_msgSend | |
51 | .long _objc_msgSend_stret | |
52 | .long _objc_msgSendSuper | |
53 | .long _objc_msgSendSuper_stret | |
54 | .long 0 | |
55 | ||
56 | .globl _objc_exitPoints | |
57 | _objc_exitPoints: | |
58 | .long LMsgSendExit | |
59 | .long LMsgSendStretExit | |
60 | .long LMsgSendSuperExit | |
61 | .long LMsgSendSuperStretExit | |
62 | .long 0 | |
63 | #endif | |
64 | ||
65 | ||
66 | /******************************************************************** | |
67 | * | |
68 | * Constants. | |
69 | * | |
70 | ********************************************************************/ | |
71 | ||
72 | // In case the implementation is _objc_msgForward, indicate to it | |
73 | // whether the method was invoked as a word-return or struct-return. | |
74 | // This flag is passed in a register that is caller-saved, and is | |
75 | // not part of the parameter passing convention (i.e. it is "out of | |
76 | // band"). This works because _objc_msgForward is only entered | |
77 | // from here in the messenger. | |
78 | kFwdMsgSend = 0 | |
79 | kFwdMsgSendStret = 1 | |
80 | ||
81 | ||
82 | /******************************************************************** | |
83 | * | |
84 | * Common offsets. | |
85 | * | |
86 | ********************************************************************/ | |
87 | ||
88 | self = 4 | |
89 | super = 4 | |
90 | selector = 8 | |
91 | marg_size = 12 | |
92 | marg_list = 16 | |
93 | ||
94 | struct_addr = 4 | |
95 | ||
96 | self_stret = 8 | |
97 | super_stret = 8 | |
98 | selector_stret = 12 | |
99 | marg_size_stret = 16 | |
100 | marg_list_stret = 20 | |
101 | ||
102 | ||
103 | /******************************************************************** | |
104 | * | |
105 | * Structure definitions. | |
106 | * | |
107 | ********************************************************************/ | |
108 | ||
109 | // objc_super parameter to sendSuper | |
110 | receiver = 0 | |
111 | class = 4 | |
112 | ||
113 | // Selected field offsets in class structure | |
114 | isa = 0 | |
115 | cache = 32 | |
116 | ||
117 | // Method descriptor | |
118 | method_name = 0 | |
119 | method_imp = 8 | |
120 | ||
121 | // Cache header | |
122 | mask = 0 | |
123 | occupied = 4 | |
124 | buckets = 8 // variable length array | |
125 | ||
126 | #if defined(OBJC_INSTRUMENTED) | |
127 | // Cache instrumentation data, follows buckets | |
128 | hitCount = 0 | |
129 | hitProbes = hitCount + 4 | |
130 | maxHitProbes = hitProbes + 4 | |
131 | missCount = maxHitProbes + 4 | |
132 | missProbes = missCount + 4 | |
133 | maxMissProbes = missProbes + 4 | |
134 | flushCount = maxMissProbes + 4 | |
135 | flushedEntries = flushCount + 4 | |
136 | ||
137 | // Buckets in CacheHitHistogram and CacheMissHistogram | |
138 | CACHE_HISTOGRAM_SIZE = 512 | |
139 | #endif | |
140 | ||
141 | ||
142 | ////////////////////////////////////////////////////////////////////// | |
143 | // | |
144 | // LOAD_STATIC_WORD targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL | |
145 | // | |
146 | // Load the value of the named static data word. | |
147 | // | |
148 | // Takes: targetReg - the register, other than r0, to load | |
149 | // symbolName - the name of the symbol | |
150 | // LOCAL_SYMBOL - symbol name used as-is | |
151 | // EXTERNAL_SYMBOL - symbol name gets nonlazy treatment | |
152 | // | |
153 | // Eats: edx and targetReg | |
154 | ////////////////////////////////////////////////////////////////////// | |
155 | ||
156 | // Values to specify whether the symbol is plain or nonlazy | |
157 | LOCAL_SYMBOL = 0 | |
158 | EXTERNAL_SYMBOL = 1 | |
159 | ||
160 | .macro LOAD_STATIC_WORD | |
161 | ||
162 | #if defined(__DYNAMIC__) | |
163 | call 1f | |
164 | 1: popl %edx | |
165 | .if $2 == EXTERNAL_SYMBOL | |
166 | movl L$1-1b(%edx),$0 | |
167 | movl 0($0),$0 | |
168 | .elseif $2 == LOCAL_SYMBOL | |
169 | movl $1-1b(%edx),$0 | |
170 | .else | |
171 | !!! Unknown symbol type !!! | |
172 | .endif | |
173 | #else | |
174 | movl $1,$0 | |
175 | #endif | |
176 | ||
177 | .endmacro | |
178 | ||
179 | ////////////////////////////////////////////////////////////////////// | |
180 | // | |
181 | // LEA_STATIC_DATA targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL | |
182 | // | |
183 | // Load the address of the named static data. | |
184 | // | |
185 | // Takes: targetReg - the register, other than edx, to load | |
186 | // symbolName - the name of the symbol | |
187 | // LOCAL_SYMBOL - symbol is local to this module | |
188 | // EXTERNAL_SYMBOL - symbol is imported from another module | |
189 | // | |
190 | // Eats: edx and targetReg | |
191 | ////////////////////////////////////////////////////////////////////// | |
192 | ||
193 | .macro LEA_STATIC_DATA | |
194 | #if defined(__DYNAMIC__) | |
195 | call 1f | |
196 | 1: popl %edx | |
197 | .if $2 == EXTERNAL_SYMBOL | |
198 | movl L$1-1b(%edx),$0 | |
199 | .elseif $2 == LOCAL_SYMBOL | |
200 | leal $1-1b(%edx),$0 | |
201 | .else | |
202 | !!! Unknown symbol type !!! | |
203 | .endif | |
204 | #else | |
205 | leal $1,$0 | |
206 | #endif | |
207 | ||
208 | .endmacro | |
209 | ||
210 | ////////////////////////////////////////////////////////////////////// | |
211 | // | |
212 | // ENTRY functionName | |
213 | // | |
214 | // Assembly directives to begin an exported function. | |
215 | // | |
216 | // Takes: functionName - name of the exported function | |
217 | ////////////////////////////////////////////////////////////////////// | |
218 | ||
219 | .macro ENTRY | |
220 | .text | |
221 | .globl $0 | |
222 | .align 4, 0x90 | |
223 | $0: | |
224 | .endmacro | |
225 | ||
226 | ////////////////////////////////////////////////////////////////////// | |
227 | // | |
228 | // END_ENTRY functionName | |
229 | // | |
230 | // Assembly directives to end an exported function. Just a placeholder, | |
231 | // a close-parenthesis for ENTRY, until it is needed for something. | |
232 | // | |
233 | // Takes: functionName - name of the exported function | |
234 | ////////////////////////////////////////////////////////////////////// | |
235 | ||
236 | .macro END_ENTRY | |
237 | .endmacro | |
238 | ||
239 | ////////////////////////////////////////////////////////////////////// | |
240 | // | |
241 | // CALL_MCOUNTER counterName | |
242 | // | |
243 | // Allocate and maintain a counter for the call site. | |
244 | // | |
245 | // Takes: counterName - name of counter. | |
246 | ////////////////////////////////////////////////////////////////////// | |
247 | HAVE_CALL_EXTERN_mcount = 0 | |
248 | ||
249 | .macro CALL_MCOUNTER | |
250 | #ifdef PROFILE | |
251 | pushl %ebp | |
252 | movl %esp,%ebp | |
253 | LOAD_STATIC_WORD %eax, $0, LOCAL_SYMBOL | |
254 | .if HAVE_CALL_EXTERN_mcount == 0 | |
255 | HAVE_CALL_EXTERN_mcount = 1 | |
256 | CALL_EXTERN(mcount) | |
257 | .else | |
258 | CALL_EXTERN_AGAIN(mcount) | |
259 | .endif | |
260 | .data | |
261 | .align 2 | |
262 | $0: | |
263 | .long 0 | |
264 | .text | |
265 | movl %ebp,%esp | |
266 | popl %ebp | |
267 | #endif | |
268 | .endmacro | |
269 | ||
270 | ||
271 | ///////////////////////////////////////////////////////////////////// | |
272 | // | |
273 | // | |
274 | // CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER, cacheMissLabel | |
275 | // | |
276 | // Locate the implementation for a selector in a class method cache. | |
277 | // | |
278 | // Takes: WORD_RETURN (first parameter is at sp+4) | |
279 | // STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8) | |
280 | // MSG_SEND (first parameter is receiver) | |
281 | // MSG_SENDSUPER (first parameter is address of objc_super structure) | |
282 | // | |
283 | // cacheMissLabel = label to branch to iff method is not cached | |
284 | // | |
285 | // On exit: (found) imp in eax register | |
286 | // (not found) jumps to cacheMissLabel | |
287 | // | |
288 | ///////////////////////////////////////////////////////////////////// | |
289 | ||
290 | ||
291 | // Values to specify to method lookup macros whether the return type of | |
292 | // the method is word or structure. | |
293 | WORD_RETURN = 0 | |
294 | STRUCT_RETURN = 1 | |
295 | ||
296 | // Values to specify to method lookup macros whether the first argument | |
297 | // is an object/class reference or a 'objc_super' structure. | |
298 | MSG_SEND = 0 | |
299 | MSG_SENDSUPER = 1 | |
300 | ||
301 | .macro CacheLookup | |
302 | ||
303 | // load variables and save caller registers. | |
304 | // Overlapped to prevent AGI | |
305 | .if $0 == WORD_RETURN // Regular word return | |
306 | .if $1 == MSG_SEND // MSG_SEND | |
307 | movl isa(%eax), %eax // class = self->isa | |
308 | movl selector(%esp), %ecx // get selector | |
309 | .else // MSG_SENDSUPER | |
310 | movl super(%esp), %eax // get objc_super address | |
311 | movl class(%eax), %eax // class = caller->class | |
312 | movl selector(%esp), %ecx // get selector | |
313 | .endif | |
314 | .else // Struct return | |
315 | .if $1 == MSG_SEND // MSG_SEND (stret) | |
316 | movl isa(%eax), %eax // class = self->isa | |
317 | movl (selector_stret)(%esp), %ecx // get selector | |
318 | .else // MSG_SENDSUPER (stret) | |
319 | movl super_stret(%esp), %eax // get objc_super address | |
320 | movl class(%eax), %eax // class = caller->class | |
321 | movl (selector_stret)(%esp), %ecx // get selector | |
322 | .endif | |
323 | .endif | |
324 | ||
325 | pushl %edi // save scratch register | |
326 | movl cache(%eax), %eax // cache = class->cache | |
327 | pushl %esi // save scratch register | |
328 | ||
329 | #if defined(OBJC_INSTRUMENTED) | |
330 | pushl %ebx // save non-volatile register | |
331 | pushl %eax // save cache pointer | |
332 | xorl %ebx, %ebx // probeCount = 0 | |
333 | #endif | |
334 | leal buckets(%eax), %edi // buckets = &cache->buckets | |
335 | movl mask(%eax), %esi // mask = cache->mask | |
336 | movl %ecx, %edx // index = selector | |
337 | ||
338 | // search the receiver's cache | |
339 | LMsgSendProbeCache_$0_$1: | |
340 | #if defined(OBJC_INSTRUMENTED) | |
341 | inc %ebx // probeCount += 1 | |
342 | #endif | |
343 | andl %esi, %edx // index &= mask | |
344 | movl (%edi, %edx, 4), %eax // method = buckets[index] | |
345 | ||
346 | testl %eax, %eax // check for end of bucket | |
347 | je LMsgSendCacheMiss_$0_$1 // go to cache miss code | |
348 | cmpl method_name(%eax), %ecx // check for method name match | |
349 | je LMsgSendCacheHit_$0_$1 // go handle cache hit | |
350 | inc %edx // bump index ... | |
351 | jmp LMsgSendProbeCache_$0_$1// ... and loop | |
352 | ||
353 | // not found in cache: restore state and go to callers handler | |
354 | LMsgSendCacheMiss_$0_$1: | |
355 | #if defined(OBJC_INSTRUMENTED) | |
356 | popl %edx // retrieve cache pointer | |
357 | movl mask(%edx), %esi // mask = cache->mask | |
358 | testl %esi, %esi // a mask of zero is only for the... | |
359 | je LMsgSendMissInstrumentDone_$0 // ... emptyCache, do not record anything | |
360 | ||
361 | // locate and update the CacheInstrumentation structure | |
362 | inc %esi // entryCount = mask + 1 | |
363 | #ifdef NO_MACRO_CONSTS | |
364 | shll $kTwo, %esi // tableSize = entryCount * sizeof(entry) | |
365 | #else | |
366 | shll $2, %esi // tableSize = entryCount * sizeof(entry) | |
367 | #endif | |
368 | addl $buckets, %esi // offset = buckets + tableSize | |
369 | addl %edx, %esi // cacheData = &cache->buckets[mask+1] | |
370 | ||
371 | movl missCount(%esi), %edi // | |
372 | inc %edi // | |
373 | movl %edi, missCount(%esi) // cacheData->missCount += 1 | |
374 | movl missProbes(%esi), %edi // | |
375 | addl %ebx, %edi // | |
376 | movl %edi, missProbes(%esi) // cacheData->missProbes += probeCount | |
377 | movl maxMissProbes(%esi), %edi// if (cacheData->maxMissProbes < probeCount) | |
378 | cmpl %ebx, %edi // | |
379 | jge LMsgSendMaxMissProbeOK_$0 // | |
380 | movl %ebx, maxMissProbes(%esi)// cacheData->maxMissProbes = probeCount | |
381 | LMsgSendMaxMissProbeOK_$0: | |
382 | ||
383 | // update cache miss probe histogram | |
384 | cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index | |
385 | jl LMsgSendMissHistoIndexSet_$0 | |
386 | movl $(CACHE_HISTOGRAM_SIZE-1), %ebx | |
387 | LMsgSendMissHistoIndexSet_$0: | |
388 | LEA_STATIC_DATA %esi, _CacheMissHistogram, EXTERNAL_SYMBOL | |
389 | #ifdef NO_MACRO_CONSTS | |
390 | shll $kTwo, %ebx // convert probeCount to histogram index | |
391 | #else | |
392 | shll $2, %ebx // convert probeCount to histogram index | |
393 | #endif | |
394 | addl %ebx, %esi // calculate &CacheMissHistogram[probeCount<<2] | |
395 | movl 0(%esi), %edi // get current tally | |
396 | inc %edi // | |
397 | movl %edi, 0(%esi) // tally += 1 | |
398 | LMsgSendMissInstrumentDone_$0: | |
399 | popl %ebx // restore non-volatile register | |
400 | #endif | |
401 | ||
402 | .if $0 == WORD_RETURN // Regular word return | |
403 | .if $1 == MSG_SEND // MSG_SEND | |
404 | popl %esi // restore callers register | |
405 | popl %edi // restore callers register | |
406 | movl self(%esp), %eax // get messaged object | |
407 | movl isa(%eax), %eax // get objects class | |
408 | .else // MSG_SENDSUPER | |
409 | // replace "super" arg with "receiver" | |
410 | movl super+8(%esp), %edi // get super structure | |
411 | movl receiver(%edi), %esi // get messaged object | |
412 | movl %esi, super+8(%esp) // make it the first argument | |
413 | movl class(%edi), %eax // get messaged class | |
414 | popl %esi // restore callers register | |
415 | popl %edi // restore callers register | |
416 | .endif | |
417 | .else // Struct return | |
418 | .if $1 == MSG_SEND // MSG_SEND (stret) | |
419 | popl %esi // restore callers register | |
420 | popl %edi // restore callers register | |
421 | movl self_stret(%esp), %eax // get messaged object | |
422 | movl isa(%eax), %eax // get objects class | |
423 | .else // MSG_SENDSUPER (stret) | |
424 | // replace "super" arg with "receiver" | |
425 | movl super_stret+8(%esp), %edi// get super structure | |
426 | movl receiver(%edi), %esi // get messaged object | |
427 | movl %esi, super_stret+8(%esp)// make it the first argument | |
428 | movl class(%edi), %eax // get messaged class | |
429 | popl %esi // restore callers register | |
430 | popl %edi // restore callers register | |
431 | .endif | |
432 | .endif | |
433 | ||
434 | jmp $2 // go to callers handler | |
435 | ||
436 | // eax points to matching cache entry | |
437 | .align 4, 0x90 | |
438 | LMsgSendCacheHit_$0_$1: | |
439 | #if defined(OBJC_INSTRUMENTED) | |
440 | popl %edx // retrieve cache pointer | |
441 | movl mask(%edx), %esi // mask = cache->mask | |
442 | testl %esi, %esi // a mask of zero is only for the... | |
443 | je LMsgSendHitInstrumentDone_$0_$1 // ... emptyCache, do not record anything | |
444 | ||
445 | // locate and update the CacheInstrumentation structure | |
446 | inc %esi // entryCount = mask + 1 | |
447 | #ifdef NO_MACRO_CONSTS | |
448 | shll $kTwo, %esi // tableSize = entryCount * sizeof(entry) | |
449 | #else | |
450 | shll $2, %esi // tableSize = entryCount * sizeof(entry) | |
451 | #endif | |
452 | addl $buckets, %esi // offset = buckets + tableSize | |
453 | addl %edx, %esi // cacheData = &cache->buckets[mask+1] | |
454 | ||
455 | movl hitCount(%esi), %edi | |
456 | inc %edi | |
457 | movl %edi, hitCount(%esi) // cacheData->hitCount += 1 | |
458 | movl hitProbes(%esi), %edi | |
459 | addl %ebx, %edi | |
460 | movl %edi, hitProbes(%esi) // cacheData->hitProbes += probeCount | |
461 | movl maxHitProbes(%esi), %edi// if (cacheData->maxHitProbes < probeCount) | |
462 | cmpl %ebx, %edi | |
463 | jge LMsgSendMaxHitProbeOK_$0_$1 | |
464 | movl %ebx, maxHitProbes(%esi)// cacheData->maxHitProbes = probeCount | |
465 | LMsgSendMaxHitProbeOK_$0_$1: | |
466 | ||
467 | // update cache hit probe histogram | |
468 | cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index | |
469 | jl LMsgSendHitHistoIndexSet_$0_$1 | |
470 | movl $(CACHE_HISTOGRAM_SIZE-1), %ebx | |
471 | LMsgSendHitHistoIndexSet_$0_$1: | |
472 | LEA_STATIC_DATA %esi, _CacheHitHistogram, EXTERNAL_SYMBOL | |
473 | #ifdef NO_MACRO_CONSTS | |
474 | shll $kTwo, %ebx // convert probeCount to histogram index | |
475 | #else | |
476 | shll $2, %ebx // convert probeCount to histogram index | |
477 | #endif | |
478 | addl %ebx, %esi // calculate &CacheHitHistogram[probeCount<<2] | |
479 | movl 0(%esi), %edi // get current tally | |
480 | inc %edi // | |
481 | movl %edi, 0(%esi) // tally += 1 | |
482 | LMsgSendHitInstrumentDone_$0_$1: | |
483 | popl %ebx // restore non-volatile register | |
484 | #endif | |
485 | ||
486 | // load implementation address, restore state, and we're done | |
487 | movl method_imp(%eax), %eax // imp = method->method_imp | |
488 | ||
489 | .if $0 == WORD_RETURN // Regular word return | |
490 | .if $1 == MSG_SENDSUPER // MSG_SENDSUPER | |
491 | // replace "super" arg with "self" | |
492 | movl super+8(%esp), %edi | |
493 | movl receiver(%edi), %esi | |
494 | movl %esi, super+8(%esp) | |
495 | .endif | |
496 | .else // Struct return | |
497 | .if $1 == MSG_SENDSUPER // MSG_SENDSUPER (stret) | |
498 | // replace "super" arg with "self" | |
499 | movl super_stret+8(%esp), %edi | |
500 | movl receiver(%edi), %esi | |
501 | movl %esi, super_stret+8(%esp) | |
502 | .endif | |
503 | .endif | |
504 | ||
505 | // restore caller registers | |
506 | popl %esi | |
507 | popl %edi | |
508 | .endmacro | |
509 | ||
510 | ||
511 | ///////////////////////////////////////////////////////////////////// | |
512 | // | |
513 | // MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | |
514 | // | |
515 | // Takes: WORD_RETURN (first parameter is at sp+4) | |
516 | // STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8) | |
517 | // MSG_SEND (first parameter is receiver) | |
518 | // MSG_SENDSUPER (first parameter is address of objc_super structure) | |
519 | // | |
520 | // On exit: Register parameters restored from CacheLookup | |
521 | // imp in eax | |
522 | // | |
523 | ///////////////////////////////////////////////////////////////////// | |
524 | ||
525 | HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 0 | |
526 | ||
527 | .macro MethodTableLookup | |
528 | ||
529 | // push args (class, selector) | |
530 | pushl %ecx | |
531 | pushl %eax | |
532 | .if HAVE_CALL_EXTERN_lookupMethodAndLoadCache == 0 | |
533 | HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 1 | |
534 | CALL_EXTERN(__class_lookupMethodAndLoadCache) | |
535 | .else | |
536 | CALL_EXTERN_AGAIN(__class_lookupMethodAndLoadCache) | |
537 | .endif | |
538 | #ifdef NO_MACRO_CONSTS | |
539 | addl $kEight, %esp // pop parameters | |
540 | #else | |
541 | addl $8, %esp // pop parameters | |
542 | #endif | |
543 | .endmacro | |
544 | ||
545 | /************************** | |
546 | * This #defines is missing from asm_help.h | |
547 | ***************************/ | |
548 | #define EXTERN_TO_REG_AGAIN(var, reg) \ | |
549 | call 1f ; \ | |
550 | 1: ; \ | |
551 | popl %edx ; \ | |
552 | movl L ## var ##$non_lazy_ptr-1b(%edx),reg ; | |
553 | ||
554 | #define BRANCH_EXTERN_AGAIN(func) \ | |
555 | PICIFY(func) ; \ | |
556 | jmp %edx ; | |
557 | ||
558 | /******************************************************************** | |
559 | * | |
560 | * id objc_msgSend(id self, SEL _cmd,...); | |
561 | * | |
562 | ********************************************************************/ | |
563 | ||
564 | ENTRY _objc_msgSend | |
565 | CALL_MCOUNTER LP0 | |
566 | ||
567 | movl self(%esp), %eax | |
568 | ||
569 | // check whether receiver is nil | |
570 | testl %eax, %eax | |
571 | je LMsgSendNilSelf | |
572 | ||
573 | #if !defined(OBJC_COLLECTING_CACHE) | |
574 | // check whether context is multithreaded | |
575 | EXTERN_TO_REG(__objc_multithread_mask,%ecx) | |
576 | testl %ecx, %ecx | |
577 | je LMsgSendMT | |
578 | #endif | |
579 | ||
580 | // single threaded and receiver is non-nil: search the cache | |
581 | CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss | |
582 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
583 | jmp *%eax // goto *imp | |
584 | ||
585 | // cache miss: go search the method lists | |
586 | LMsgSendCacheMiss: | |
587 | MethodTableLookup WORD_RETURN, MSG_SEND | |
588 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
589 | jmp *%eax // goto *imp | |
590 | ||
591 | #if !defined(OBJC_COLLECTING_CACHE) | |
592 | // multithreaded: hold _messageLock while accessing cache | |
593 | LMsgSendMT: | |
594 | movl $1, %ecx // acquire _messageLock | |
595 | LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL | |
596 | LMsgSendLockSpin: | |
597 | xchgl %ecx, (%eax) | |
598 | cmpl $0, %ecx | |
599 | jne LMsgSendLockSpin | |
600 | movl self(%esp), %eax // restore eax | |
601 | ||
602 | CacheLookup WORD_RETURN, MSG_SEND, LMsgSendMTCacheMiss | |
603 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
604 | movl $0, (%ecx) // unlock | |
605 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
606 | jmp *%eax // goto *imp | |
607 | ||
608 | // cache miss: go search the method lists | |
609 | LMsgSendMTCacheMiss: | |
610 | MethodTableLookup WORD_RETURN, MSG_SEND | |
611 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
612 | movl $0, (%ecx) // unlock | |
613 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
614 | jmp *%eax // goto *imp | |
615 | #endif | |
616 | ||
617 | // message sent to nil object: call optional handler and return nil | |
618 | LMsgSendNilSelf: | |
619 | EXTERN_TO_REG(__objc_msgNil,%eax) | |
620 | movl 0(%eax), %eax // load nil message handler | |
621 | testl %eax, %eax | |
622 | je LMsgSendDone // if NULL just return and don't do anything | |
623 | call *%eax // call __objc_msgNil | |
624 | xorl %eax, %eax // Rezero $eax just in case | |
625 | LMsgSendDone: | |
626 | ret | |
627 | ||
628 | LMsgSendExit: | |
629 | END_ENTRY _objc_msgSend | |
630 | ||
631 | /******************************************************************** | |
632 | * | |
633 | * id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...); | |
634 | * | |
635 | * struct objc_super { | |
636 | * id receiver; | |
637 | * Class class; | |
638 | * }; | |
639 | ********************************************************************/ | |
640 | ||
641 | ENTRY _objc_msgSendSuper | |
642 | CALL_MCOUNTER LP1 | |
643 | ||
644 | movl super(%esp), %eax | |
645 | ||
646 | #if !defined(OBJC_COLLECTING_CACHE) | |
647 | // check whether context is multithreaded | |
648 | EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) | |
649 | testl %ecx, %ecx | |
650 | je LMsgSendSuperMT | |
651 | #endif | |
652 | ||
653 | // single threaded and receiver is non-nil: search the cache | |
654 | CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss | |
655 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
656 | jmp *%eax // goto *imp | |
657 | ||
658 | // cache miss: go search the method lists | |
659 | LMsgSendSuperCacheMiss: | |
660 | MethodTableLookup WORD_RETURN, MSG_SENDSUPER | |
661 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
662 | jmp *%eax // goto *imp | |
663 | ||
664 | #if !defined(OBJC_COLLECTING_CACHE) | |
665 | LMsgSendSuperMT: | |
666 | // multithreaded: hold _messageLock while accessing cache | |
667 | movl $1, %ecx // acquire _messageLock | |
668 | LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL | |
669 | LMsgSendSuperLockSpin: | |
670 | xchgl %ecx, (%eax) | |
671 | cmpl $0, %ecx | |
672 | jne LMsgSendSuperLockSpin | |
673 | movl super(%esp), %eax // restore eax | |
674 | ||
675 | CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperMTCacheMiss | |
676 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
677 | movl $0, (%ecx) // unlock | |
678 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
679 | jmp *%eax // goto *imp | |
680 | ||
681 | // cache miss: go search the method lists | |
682 | LMsgSendSuperMTCacheMiss: | |
683 | MethodTableLookup WORD_RETURN, MSG_SENDSUPER | |
684 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
685 | movl $0, (%ecx) // unlock | |
686 | movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward | |
687 | jmp *%eax // goto *imp | |
688 | #endif | |
689 | ||
690 | LMsgSendSuperExit: | |
691 | END_ENTRY _objc_msgSendSuper | |
692 | ||
693 | /******************************************************************** | |
694 | * id objc_msgSendv(id self, SEL _cmd, unsigned size, marg_list frame); | |
695 | * | |
696 | * On entry: | |
697 | * (sp+4) is the message receiver, | |
698 | * (sp+8) is the selector, | |
699 | * (sp+12) is the size of the marg_list, in bytes, | |
700 | * (sp+16) is the address of the marg_list | |
701 | * | |
702 | ********************************************************************/ | |
703 | ||
704 | ENTRY _objc_msgSendv | |
705 | ||
706 | #if defined(KERNEL) | |
707 | trap // _objc_msgSendv is not for the kernel | |
708 | #else | |
709 | pushl %ebp | |
710 | movl %esp, %ebp | |
711 | movl (marg_list+4)(%ebp), %edx | |
712 | addl $8, %edx // skip self & selector | |
713 | movl (marg_size+4)(%ebp), %ecx | |
714 | subl $5, %ecx // skip self & selector | |
715 | shrl $2, %ecx | |
716 | jle LMsgSendvArgsOK | |
717 | LMsgSendvArgLoop: | |
718 | decl %ecx | |
719 | movl 0(%edx, %ecx, 4), %eax | |
720 | pushl %eax | |
721 | jg LMsgSendvArgLoop | |
722 | ||
723 | LMsgSendvArgsOK: | |
724 | movl (selector+4)(%ebp), %ecx | |
725 | pushl %ecx | |
726 | movl (self+4)(%ebp),%ecx | |
727 | pushl %ecx | |
728 | call _objc_msgSend | |
729 | movl %ebp,%esp | |
730 | popl %ebp | |
731 | ||
732 | ret | |
733 | #endif | |
734 | END_ENTRY _objc_msgSendv | |
735 | ||
736 | ||
737 | /******************************************************************** | |
738 | * | |
739 | * void objc_msgSend_stret(void *st_addr , id self, SEL _cmd, ...); | |
740 | * | |
741 | * | |
742 | * objc_msgSend_stret is the struct-return form of msgSend. | |
743 | * The ABI calls for (sp+4) to be used as the address of the structure | |
744 | * being returned, with the parameters in the succeeding locations. | |
745 | * | |
746 | * On entry: (sp+4)is the address where the structure is returned, | |
747 | * (sp+8) is the message receiver, | |
748 | * (sp+12) is the selector | |
749 | ********************************************************************/ | |
750 | ||
751 | ENTRY _objc_msgSend_stret | |
752 | CALL_MCOUNTER LP2 | |
753 | ||
754 | movl self_stret(%esp), %eax | |
755 | ||
756 | // check whether receiver is nil | |
757 | testl %eax, %eax | |
758 | je LMsgSendStretNilSelf | |
759 | ||
760 | #if !defined(OBJC_COLLECTING_CACHE) | |
761 | // check whether context is multithreaded | |
762 | EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) | |
763 | testl %ecx, %ecx | |
764 | je LMsgSendStretMT | |
765 | #endif | |
766 | ||
767 | // single threaded and receiver is non-nil: search the cache | |
768 | CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss | |
769 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
770 | jmp *%eax // goto *imp | |
771 | ||
772 | // cache miss: go search the method lists | |
773 | LMsgSendStretCacheMiss: | |
774 | MethodTableLookup STRUCT_RETURN, MSG_SEND | |
775 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
776 | jmp *%eax // goto *imp | |
777 | ||
778 | #if !defined(OBJC_COLLECTING_CACHE) | |
779 | // multithreaded: hold _messageLock while accessing cache | |
780 | LMsgSendStretMT: | |
781 | movl $1, %ecx // acquire _messageLock | |
782 | LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL | |
783 | LMsgSendStretLockSpin: | |
784 | xchgl %ecx, (%eax) | |
785 | cmpl $0, %ecx | |
786 | jne LMsgSendStretLockSpin | |
787 | movl self_stret(%esp), %eax // restore eax | |
788 | ||
789 | CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretMTCacheMiss | |
790 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
791 | movl $0, (%ecx) // unlock | |
792 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
793 | jmp *%eax // goto *imp | |
794 | ||
795 | // cache miss: go search the method lists | |
796 | LMsgSendStretMTCacheMiss: | |
797 | MethodTableLookup STRUCT_RETURN, MSG_SEND | |
798 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
799 | movl $0, (%ecx) // unlock | |
800 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
801 | jmp *%eax // goto *imp | |
802 | #endif | |
803 | ||
804 | // message sent to nil object: call optional handler and return nil | |
805 | LMsgSendStretNilSelf: | |
806 | EXTERN_TO_REG_AGAIN(__objc_msgNil,%eax) | |
807 | movl 0(%eax), %eax // load nil message handler | |
808 | testl %eax, %eax | |
809 | je LMsgSendStretDone // if NULL just return and don't do anything | |
810 | ||
811 | call *%eax // call __objc_msgNil | |
812 | xorl %eax, %eax // Rezero $eax just in case | |
813 | LMsgSendStretDone: | |
814 | ret | |
815 | ||
816 | LMsgSendStretExit: | |
817 | END_ENTRY _objc_msgSend_stret | |
818 | ||
819 | /******************************************************************** | |
820 | * | |
821 | * void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...); | |
822 | * | |
823 | * struct objc_super { | |
824 | * id receiver; | |
825 | * Class class; | |
826 | * }; | |
827 | * | |
828 | * objc_msgSendSuper_stret is the struct-return form of msgSendSuper. | |
829 | * The ABI calls for (sp+4) to be used as the address of the structure | |
830 | * being returned, with the parameters in the succeeding registers. | |
831 | * | |
832 | * On entry: (sp+4)is the address where the structure is returned, | |
833 | * (sp+8) is the address of the objc_super structure, | |
834 | * (sp+12) is the selector | |
835 | * | |
836 | ********************************************************************/ | |
837 | ||
838 | ENTRY _objc_msgSendSuper_stret | |
839 | CALL_MCOUNTER LP3 | |
840 | ||
841 | movl super_stret(%esp), %eax | |
842 | ||
843 | #if !defined(OBJC_COLLECTING_CACHE) | |
844 | // check whether context is multithreaded | |
845 | EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) | |
846 | testl %ecx, %ecx | |
847 | je LMsgSendSuperStretMT | |
848 | #endif | |
849 | ||
850 | // single threaded and receiver is non-nil: search the cache | |
851 | CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss | |
852 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
853 | jmp *%eax // goto *imp | |
854 | ||
855 | // cache miss: go search the method lists | |
856 | LMsgSendSuperStretCacheMiss: | |
857 | MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER | |
858 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
859 | jmp *%eax // goto *imp | |
860 | ||
861 | #if !defined(OBJC_COLLECTING_CACHE) | |
862 | LMsgSendStretSuperMT: | |
863 | // multithreaded: hold _messageLock while accessing cache | |
864 | movl $1, %ecx // acquire _messageLock | |
865 | LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL | |
866 | LMsgSendSuperStretLockSpin: | |
867 | xchgl %ecx, (%eax) | |
868 | cmpl $0, %ecx | |
869 | jne LMsgSendSuperStretLockSpin | |
870 | movl super_stret(%esp), %eax // restore eax | |
871 | ||
872 | CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretMTCacheMiss | |
873 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
874 | movl $0, (%ecx) // unlock | |
875 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
876 | jmp *%eax // goto *imp | |
877 | ||
878 | // cache miss: go search the method lists | |
879 | LMsgSendSuperStretMTCacheMiss: | |
880 | MethodTableLookup MSG_SENDSUPER | |
881 | LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL | |
882 | movl $0, (%ecx) // unlock | |
883 | movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward | |
884 | jmp *%eax // goto *imp | |
885 | #endif | |
886 | ||
887 | LMsgSendSuperStretExit: | |
888 | END_ENTRY _objc_msgSendSuper_stret | |
889 | ||
890 | ||
891 | /******************************************************************** | |
892 | * id objc_msgSendv_stret(void *st_addr, id self, SEL _cmd, unsigned size, marg_list frame); | |
893 | * | |
894 | * objc_msgSendv_stret is the struct-return form of msgSendv. | |
895 | * The ABI calls for (sp+4) to be used as the address of the structure | |
896 | * being returned, with the parameters in the succeeding locations. | |
897 | * | |
898 | * On entry: (sp+4) is the address in which the returned struct is put, | |
899 | * (sp+8) is the message receiver, | |
900 | * (sp+12) is the selector, | |
901 | * (sp+16) is the size of the marg_list, in bytes, | |
902 | * (sp+20) is the address of the marg_list | |
903 | * | |
904 | ********************************************************************/ | |
905 | ||
906 | ENTRY _objc_msgSendv_stret | |
907 | ||
908 | #if defined(KERNEL) | |
909 | trap // _objc_msgSendv_stret is not for the kernel | |
910 | #else | |
911 | pushl %ebp | |
912 | movl %esp, %ebp | |
913 | movl (marg_list_stret+4)(%ebp), %edx | |
914 | addl $8, %edx // skip self & selector | |
915 | movl (marg_size_stret+4)(%ebp), %ecx | |
916 | subl $5, %ecx // skip self & selector | |
917 | shrl $2, %ecx | |
918 | jle LMsgSendvStretArgsOK | |
919 | LMsgSendvStretArgLoop: | |
920 | decl %ecx | |
921 | movl 0(%edx, %ecx, 4), %eax | |
922 | pushl %eax | |
923 | jg LMsgSendvStretArgLoop | |
924 | ||
925 | LMsgSendvStretArgsOK: | |
926 | movl (selector_stret+4)(%ebp), %ecx | |
927 | pushl %ecx | |
928 | movl (self_stret+4)(%ebp),%ecx | |
929 | pushl %ecx | |
930 | movl (struct_addr+4)(%ebp),%ecx | |
931 | pushl %ecx | |
932 | call _objc_msgSend_stret | |
933 | movl %ebp,%esp | |
934 | popl %ebp | |
935 | ||
936 | ret | |
937 | #endif | |
938 | END_ENTRY _objc_msgSendv_stret | |
939 | ||
940 | ||
941 | /******************************************************************** | |
942 | * | |
943 | * id _objc_msgForward(id self, SEL _cmd,...); | |
944 | * | |
945 | ********************************************************************/ | |
946 | ||
947 | // Location LFwdStr contains the string "forward::" | |
948 | // Location LFwdSel contains a pointer to LFwdStr, that can be changed | |
949 | // to point to another forward:: string for selector uniquing | |
950 | // purposes. ALWAYS dereference LFwdSel to get to "forward::" !! | |
951 | .objc_meth_var_names | |
952 | .align 2 | |
953 | LFwdStr:.ascii "forward::\0" | |
954 | ||
955 | .objc_message_refs | |
956 | .align 2 | |
957 | LFwdSel:.long LFwdStr | |
958 | ||
959 | .cstring | |
960 | .align 2 | |
961 | LUnkSelStr: .ascii "Does not recognize selector %s\0" | |
962 | ||
963 | ENTRY __objc_msgForward | |
964 | ||
965 | #if defined(KERNEL) | |
966 | trap // _objc_msgForward is not for the kernel | |
967 | #else | |
968 | cmpl $kFwdMsgSendStret, %edx // check secret flag for word vs struct return | |
969 | je LForwardStretVersion // jump to struct return version... | |
970 | ||
971 | // non-stret version ... | |
972 | pushl %ebp | |
973 | movl %esp,%ebp | |
974 | movl (selector+4)(%esp), %eax | |
975 | #if defined(__DYNAMIC__) | |
976 | call L__objc_msgForward$pic_base | |
977 | L__objc_msgForward$pic_base: | |
978 | popl %edx | |
979 | leal LFwdSel-L__objc_msgForward$pic_base(%edx),%ecx | |
980 | cmpl %ecx, %eax | |
981 | #else | |
982 | cmpl LFwdSel, %eax | |
983 | #endif | |
984 | je LMsgForwardError | |
985 | ||
986 | leal (self+4)(%esp), %ecx | |
987 | pushl %ecx | |
988 | pushl %eax | |
989 | #if defined(__DYNAMIC__) | |
990 | movl LFwdSel-L__objc_msgForward$pic_base(%edx),%ecx | |
991 | #else | |
992 | movl LFwdSel,%ecx | |
993 | #endif | |
994 | pushl %ecx | |
995 | pushl (self+16)(%esp) | |
996 | call _objc_msgSend | |
997 | movl %ebp,%esp | |
998 | popl %ebp | |
999 | ret | |
1000 | ||
1001 | // call error handler with unrecognized selector message | |
1002 | .align 4, 0x90 | |
1003 | LMsgForwardError: | |
1004 | #if defined(__DYNAMIC__) | |
1005 | leal LFwdSel-L__objc_msgForward$pic_base(%edx),%eax | |
1006 | pushl %eax | |
1007 | leal LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax | |
1008 | pushl %eax | |
1009 | #else | |
1010 | pushl $LFwdSel | |
1011 | pushl $LUnkSelStr | |
1012 | #endif | |
1013 | pushl (self+12)(%esp) | |
1014 | BRANCH_EXTERN(___objc_error) // volatile, will not return | |
1015 | ||
1016 | // ***** Stret version of function below | |
1017 | // ***** offsets have been changed (by adding a word to make room for the | |
1018 | // ***** structure, and labels have been changed to be unique. | |
1019 | ||
1020 | LForwardStretVersion: | |
1021 | pushl %ebp | |
1022 | movl %esp,%ebp | |
1023 | movl (selector_stret+4)(%esp), %eax | |
1024 | ||
1025 | #if defined(__DYNAMIC__) | |
1026 | call L__objc_msgForwardStret$pic_base | |
1027 | L__objc_msgForwardStret$pic_base: | |
1028 | popl %edx | |
1029 | leal LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx | |
1030 | cmpl %ecx, %eax | |
1031 | #else | |
1032 | cmpl LFwdSel, %eax | |
1033 | #endif | |
1034 | je LMsgForwardStretError | |
1035 | ||
1036 | leal (self_stret+4)(%esp), %ecx | |
1037 | pushl %ecx | |
1038 | pushl %eax | |
1039 | #if defined(__DYNAMIC__) | |
1040 | movl LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx | |
1041 | #else | |
1042 | movl LFwdSel,%ecx | |
1043 | #endif | |
1044 | pushl %ecx | |
1045 | pushl (self_stret+16)(%esp) | |
1046 | call _objc_msgSend_stret | |
1047 | movl %ebp,%esp | |
1048 | popl %ebp | |
1049 | ret | |
1050 | ||
1051 | // call error handler with unrecognized selector message | |
1052 | .align 4, 0x90 | |
1053 | LMsgForwardStretError: | |
1054 | #if defined(__DYNAMIC__) | |
1055 | leal LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%eax | |
1056 | pushl %eax | |
1057 | leal LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax | |
1058 | pushl %eax | |
1059 | #else | |
1060 | pushl $LFwdSel | |
1061 | pushl $LUnkSelStr | |
1062 | #endif | |
1063 | pushl (self_stret+12)(%esp) | |
1064 | BRANCH_EXTERN_AGAIN(___objc_error) // volatile, will not return | |
1065 | ||
1066 | #endif defined (KERNEL) | |
1067 | END_ENTRY __objc_msgForward | |
1068 |