2 * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/appleapiopts.h>
30 #include <machine/cpu_capabilities.h>
31 #include <machine/commpage.h>
33 /* OSAtomic.h library native implementations. */
38 // This is a regparm(3) subroutine used by:
40 // bool OSAtomicCompareAndSwap32( int32_t old, int32_t new, int32_t *value);
41 // int32_t OSAtomicAnd32( int32_t mask, int32_t *value);
42 // int32_t OSAtomicOr32( int32_t mask, int32_t *value);
43 // int32_t OSAtomicXor32( int32_t mask, int32_t *value);
45 // It assumes old -> %eax, new -> %edx, value -> %ecx
46 // on success: returns with ZF set
47 // on failure: returns with *value in %eax, ZF clear
49 // The first word of the routine contains the address of the first instruction,
50 // so callers can pass parameters in registers by using the absolute:
52 // call *_COMPARE_AND_SWAP32
54 // TODO: move the .long onto a separate page to reduce icache pollution (?)
56 Lcompare_and_swap32_mp:
57 .long _COMM_PAGE_COMPARE_AND_SWAP32+4
62 COMMPAGE_DESCRIPTOR(compare_and_swap32_mp,_COMM_PAGE_COMPARE_AND_SWAP32,0,kUP)
64 Lcompare_and_swap32_up:
65 .long _COMM_PAGE_COMPARE_AND_SWAP32+4
69 COMMPAGE_DESCRIPTOR(compare_and_swap32_up,_COMM_PAGE_COMPARE_AND_SWAP32,kUP,0)
71 // This is a subroutine used by:
72 // bool OSAtomicCompareAndSwap64( int64_t old, int64_t new, int64_t *value);
74 // It assumes old -> %eax/%edx, new -> %ebx/%ecx, value -> %esi
75 // on success: returns with ZF set
76 // on failure: returns with *value in %eax/%edx, ZF clear
78 Lcompare_and_swap64_mp:
79 .long _COMM_PAGE_COMPARE_AND_SWAP64+4
84 COMMPAGE_DESCRIPTOR(compare_and_swap64_mp,_COMM_PAGE_COMPARE_AND_SWAP64,0,kUP)
86 Lcompare_and_swap64_up:
87 .long _COMM_PAGE_COMPARE_AND_SWAP64+4
91 COMMPAGE_DESCRIPTOR(compare_and_swap64_up,_COMM_PAGE_COMPARE_AND_SWAP64,kUP,0)
93 // This is a subroutine used by:
94 // bool OSAtomicTestAndSet( uint32_t n, void *value );
95 // It assumes n -> %eax, value -> %edx
97 // Returns: old value of bit in CF
100 .long _COMM_PAGE_BTS+4
105 COMMPAGE_DESCRIPTOR(bit_test_and_set_mp,_COMM_PAGE_BTS,0,kUP)
107 Lbit_test_and_set_up:
108 .long _COMM_PAGE_BTS+4
112 COMMPAGE_DESCRIPTOR(bit_test_and_set_up,_COMM_PAGE_BTS,kUP,0)
114 // This is a subroutine used by:
115 // bool OSAtomicTestAndClear( uint32_t n, void *value );
116 // It assumes n -> %eax, value -> %edx
118 // Returns: old value of bit in CF
120 Lbit_test_and_clear_mp:
121 .long _COMM_PAGE_BTC+4
126 COMMPAGE_DESCRIPTOR(bit_test_and_clear_mp,_COMM_PAGE_BTC,0,kUP)
128 Lbit_test_and_clear_up:
129 .long _COMM_PAGE_BTC+4
133 COMMPAGE_DESCRIPTOR(bit_test_and_clear_up,_COMM_PAGE_BTC,kUP,0)
135 // This is a subroutine used by:
136 // int32_t OSAtomicAdd32( int32_t amt, int32_t *value );
137 // It assumes amt -> %eax, value -> %edx
139 // Returns: old value in %eax
140 // NB: OSAtomicAdd32 returns the new value, so clients will add amt to %eax
143 .long _COMM_PAGE_ATOMIC_ADD32+4
148 COMMPAGE_DESCRIPTOR(atomic_add32_mp,_COMM_PAGE_ATOMIC_ADD32,0,kUP)
151 .long _COMM_PAGE_ATOMIC_ADD32+4
155 COMMPAGE_DESCRIPTOR(atomic_add32_up,_COMM_PAGE_ATOMIC_ADD32,kUP,0)
159 // These are used both in 32 and 64-bit mode. We use a fence even on UP
160 // machines, so this function can be used with nontemporal stores.
167 COMMPAGE_DESCRIPTOR(memory_barrier,_COMM_PAGE_MEMORY_BARRIER,0,kHasSSE2);
169 Lmemory_barrier_sse2:
173 COMMPAGE_DESCRIPTOR(memory_barrier_sse2,_COMM_PAGE_MEMORY_BARRIER,kHasSSE2,0);
177 * typedef volatile struct {
178 * void *opaque1; <-- ptr to 1st queue element or null
179 * long opaque2; <-- generation count
182 * void OSAtomicEnqueue( OSQueueHead *list, void *new, size_t offset);
189 movl 16(%esp),%edi // %edi == ptr to list head
190 movl 20(%esp),%ebx // %ebx == new
191 movl 24(%esp),%esi // %esi == offset
192 movl (%edi),%eax // %eax == ptr to 1st element in Q
193 movl 4(%edi),%edx // %edx == current generation count
195 movl %eax,(%ebx,%esi)// link to old list head from new element
197 incl %ecx // increment generation count
198 lock // always lock for now...
199 cmpxchg8b (%edi) // ...push on new element
206 COMMPAGE_DESCRIPTOR(AtomicEnqueue,_COMM_PAGE_ENQUEUE,0,0)
209 /* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
215 movl 16(%esp),%edi // %edi == ptr to list head
216 movl 20(%esp),%esi // %esi == offset
217 movl (%edi),%eax // %eax == ptr to 1st element in Q
218 movl 4(%edi),%edx // %edx == current generation count
220 testl %eax,%eax // list empty?
222 movl (%eax,%esi),%ebx // point to 2nd in Q
224 incl %ecx // increment generation count
225 lock // always lock for now...
226 cmpxchg8b (%edi) // ...pop off 1st element
232 ret // ptr to 1st element in Q still in %eax
234 COMMPAGE_DESCRIPTOR(AtomicDequeue,_COMM_PAGE_DEQUEUE,0,0)
238 /************************* x86_64 versions follow **************************/
241 // This is a subroutine used by:
243 // bool OSAtomicCompareAndSwap32( int32_t old, int32_t new, int32_t *value);
244 // int32_t OSAtomicAnd32( int32_t mask, int32_t *value);
245 // int32_t OSAtomicOr32( int32_t mask, int32_t *value);
246 // int32_t OSAtomicXor32( int32_t mask, int32_t *value);
248 // It assumes: old -> %rdi (ie, it follows the ABI parameter conventions)
251 // on success: returns with ZF set
252 // on failure: returns with *value in %eax, ZF clear
255 Lcompare_and_swap32_mp_64:
256 movl %edi,%eax // put old value where "cmpxchg" wants it
258 cmpxchgl %esi, (%rdx)
261 COMMPAGE_DESCRIPTOR(compare_and_swap32_mp_64,_COMM_PAGE_COMPARE_AND_SWAP32,0,kUP)
264 Lcompare_and_swap32_up_64:
265 movl %edi,%eax // put old value where "cmpxchg" wants it
266 cmpxchgl %esi, (%rdx)
269 COMMPAGE_DESCRIPTOR(compare_and_swap32_up_64,_COMM_PAGE_COMPARE_AND_SWAP32,kUP,0)
271 // This is a subroutine used by:
272 // bool OSAtomicCompareAndSwap64( int64_t old, int64_t new, int64_t *value);
274 // It assumes: old -> %rdi (ie, it follows the ABI parameter conventions)
277 // on success: returns with ZF set
278 // on failure: returns with *value in %rax, ZF clear
281 Lcompare_and_swap64_mp_64:
282 movq %rdi,%rax // put old value where "cmpxchg" wants it
284 cmpxchgq %rsi, (%rdx)
287 COMMPAGE_DESCRIPTOR(compare_and_swap64_mp_64,_COMM_PAGE_COMPARE_AND_SWAP64,0,kUP)
290 Lcompare_and_swap64_up_64:
291 movq %rdi,%rax // put old value where "cmpxchg" wants it
292 cmpxchgq %rsi, (%rdx)
295 COMMPAGE_DESCRIPTOR(compare_and_swap64_up_64,_COMM_PAGE_COMPARE_AND_SWAP64,kUP,0)
297 // This is a subroutine used by:
298 // bool OSAtomicTestAndSet( uint32_t n, void *value );
299 // It is called with standard register conventions:
302 // Returns: old value of bit in CF
305 Lbit_test_and_set_mp_64:
310 COMMPAGE_DESCRIPTOR(bit_test_and_set_mp_64,_COMM_PAGE_BTS,0,kUP)
313 Lbit_test_and_set_up_64:
317 COMMPAGE_DESCRIPTOR(bit_test_and_set_up_64,_COMM_PAGE_BTS,kUP,0)
319 // This is a subroutine used by:
320 // bool OSAtomicTestAndClear( uint32_t n, void *value );
321 // It is called with standard register conventions:
324 // Returns: old value of bit in CF
327 Lbit_test_and_clear_mp_64:
332 COMMPAGE_DESCRIPTOR(bit_test_and_clear_mp_64,_COMM_PAGE_BTC,0,kUP)
335 Lbit_test_and_clear_up_64:
339 COMMPAGE_DESCRIPTOR(bit_test_and_clear_up_64,_COMM_PAGE_BTC,kUP,0)
341 // This is a subroutine used by:
342 // int32_t OSAtomicAdd32( int32_t amt, int32_t *value );
343 // It is called with standard register conventions:
346 // Returns: old value in %edi
347 // NB: OSAtomicAdd32 returns the new value, so clients will add amt to %edi
355 COMMPAGE_DESCRIPTOR(atomic_add32_mp_64,_COMM_PAGE_ATOMIC_ADD32,0,kUP)
362 COMMPAGE_DESCRIPTOR(atomic_add32_up_64,_COMM_PAGE_ATOMIC_ADD32,kUP,0)
364 // This is a subroutine used by:
365 // int64_t OSAtomicAdd64( int64_t amt, int64_t *value );
366 // It is called with standard register conventions:
369 // Returns: old value in %rdi
370 // NB: OSAtomicAdd64 returns the new value, so clients will add amt to %rdi
378 COMMPAGE_DESCRIPTOR(atomic_add64_mp_64,_COMM_PAGE_ATOMIC_ADD64,0,kUP)
385 COMMPAGE_DESCRIPTOR(atomic_add64_up_64,_COMM_PAGE_ATOMIC_ADD64,kUP,0)
389 * typedef volatile struct {
390 * void *opaque1; <-- ptr to 1st queue element or null
391 * long opaque2; <-- generation count
394 * void OSAtomicEnqueue( OSQueueHead *list, void *new, size_t offset);
398 LAtomicEnqueue_64: // %rdi == list head, %rsi == new, %rdx == offset
400 movq %rsi,%rbx // %rbx == new
401 movq %rdx,%rsi // %rsi == offset
402 movq (%rdi),%rax // %rax == ptr to 1st element in Q
403 movq 8(%rdi),%rdx // %rdx == current generation count
405 movq %rax,(%rbx,%rsi)// link to old list head from new element
407 incq %rcx // increment generation count
408 lock // always lock for now...
409 cmpxchg16b (%rdi) // ...push on new element
414 COMMPAGE_DESCRIPTOR(AtomicEnqueue_64,_COMM_PAGE_ENQUEUE,0,0)
417 /* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
420 LAtomicDequeue_64: // %rdi == list head, %rsi == offset
422 movq (%rdi),%rax // %rax == ptr to 1st element in Q
423 movq 8(%rdi),%rdx // %rdx == current generation count
425 testq %rax,%rax // list empty?
427 movq (%rax,%rsi),%rbx // point to 2nd in Q
429 incq %rcx // increment generation count
430 lock // always lock for now...
431 cmpxchg16b (%rdi) // ...pop off 1st element
435 ret // ptr to 1st element in Q still in %rax
437 COMMPAGE_DESCRIPTOR(AtomicDequeue_64,_COMM_PAGE_DEQUEUE,0,0)