2 * Copyright (c) 2007 Apple Inc. All rights reserved.
3 * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include <machine/cpu_capabilities.h>
27 #include <architecture/i386/asm_help.h>
31 #define ATOMIC_RET_ORIG 0
32 #define ATOMIC_RET_NEW 1
34 // compare and exchange 32-bit
35 // xchg32 <new> <dst> <mp>
43 // compare and exchange 64-bit
53 // int32_t OSAtomicAdd32(int32_t theAmount, volatile int32_t *theValue);
54 #define ATOMIC_ARITHMETIC(instr, orig, mp) \
55 movl 8(%esp), %ecx /* load 2nd arg ptr into ecx */ ;\
56 movl (%ecx), %eax /* load contents of ecx into eax */ ;\
57 1: movl 4(%esp), %edx /* load 1st arg into edx */ ;\
58 instr %eax, %edx /* do the operation */ ;\
59 xchg32 %edx, %ecx, mp /* old in %eax, new in %edx, exchange into %ecx */ ;\
60 jnz 1b /* go back if we failed to exchange */ ;\
61 .if orig == ATOMIC_RET_NEW ;\
62 movl %edx, %eax /* return new value */ ;\
65 // bool OSAtomicTestAndSet(uint32_t n, volatile void *theAddress);
66 #define ATOMIC_BIT_OP(instr, mp) \
69 shldl $3,%edx,%ecx /* save top 3 bits of address in %ecx */ ;\
71 xorl $7,%eax /* bit position is numbered big endian so convert to little endian */ ;\
72 addl %eax,%edx /* generate bit address */ ;\
73 adcl $0,%ecx /* handle carry out of lower half of address */ ;\
74 movl %edx,%eax /* copy lower half of bit address */ ;\
75 andl $31,%eax /* keep bit offset in range 0..31 */ ;\
76 xorl %eax,%edx /* 4-byte align address */ ;\
77 shrdl $3,%ecx,%edx /* restore 32-bit byte address in %edx */ ;\
78 .if mp == ATOMIC_MP ;\
83 movzbl %al,%eax // widen in case caller assumes we return an int
85 // int64_t OSAtomicAdd64(int64_t theAmount, volatile int64_t *theValue);
86 #define ATOMIC_ADD64(mp) \
89 movl 20(%esp), %esi ;\
92 1: movl 12(%esp), %ebx ;\
93 movl 16(%esp), %ecx ;\
105 PLATFUNC_FUNCTION_START(OSAtomicAnd32, up, 32, 2)
106 PLATFUNC_FUNCTION_START(OSAtomicAnd32Barrier, up, 32, 2)
107 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_UP)
110 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32, mp, 32, 2)
111 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Barrier, mp, 32, 2)
112 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_MP)
115 PLATFUNC_FUNCTION_START(OSAtomicOr32, up, 32, 2)
116 PLATFUNC_FUNCTION_START(OSAtomicOr32Barrier, up, 32, 2)
117 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_UP)
120 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32, mp, 32, 2)
121 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Barrier, mp, 32, 2)
122 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_MP)
125 PLATFUNC_FUNCTION_START(OSAtomicXor32, up, 32, 2)
126 PLATFUNC_FUNCTION_START(OSAtomicXor32Barrier, up, 32, 2)
127 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_UP)
130 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32, mp, 32, 2)
131 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Barrier, mp, 32, 2)
132 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_MP)
135 PLATFUNC_FUNCTION_START(OSAtomicAnd32Orig, up, 32, 2)
136 PLATFUNC_FUNCTION_START(OSAtomicAnd32OrigBarrier, up, 32, 2)
137 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_UP)
140 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Orig, mp, 32, 2)
141 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32OrigBarrier, mp, 32, 2)
142 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_MP)
145 PLATFUNC_FUNCTION_START(OSAtomicOr32Orig, up, 32, 2)
146 PLATFUNC_FUNCTION_START(OSAtomicOr32OrigBarrier, up, 32, 2)
147 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_UP)
150 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Orig, mp, 32, 2)
151 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32OrigBarrier, mp, 32, 2)
152 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_MP)
155 PLATFUNC_FUNCTION_START(OSAtomicXor32Orig, up, 32, 2)
156 PLATFUNC_FUNCTION_START(OSAtomicXor32OrigBarrier, up, 32, 2)
157 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_UP)
160 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Orig, mp, 32, 2)
161 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32OrigBarrier, mp, 32, 2)
162 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_MP)
165 // bool OSAtomicCompareAndSwapInt(int oldValue, int newValue, volatile int *theValue);
166 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtr, up, 32, 2)
167 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtrBarrier, up, 32, 2)
168 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapInt, up, 32, 2)
169 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapIntBarrier, up, 32, 2)
170 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLong, up, 32, 2)
171 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLongBarrier, up, 32, 2)
172 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32, up, 32, 2)
173 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32Barrier, up, 32, 2)
177 xchg32 %edx, %ecx, ATOMIC_UP
179 movzbl %al,%eax // widen in case caller assumes we return an int
182 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtr, mp, 32, 2)
183 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtrBarrier, mp, 32, 2)
184 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapInt, mp, 32, 2)
185 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapIntBarrier, mp, 32, 2)
186 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLong, mp, 32, 2)
187 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLongBarrier, mp, 32, 2)
188 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32, mp, 32, 2)
189 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32Barrier, mp, 32, 2)
193 xchg32 %edx, %ecx, ATOMIC_MP
195 movzbl %al,%eax // widen in case caller assumes we return an int
198 // bool OSAtomicCompareAndSwap64(int64_t oldValue, int64_t newValue, volatile int64_t *theValue);
199 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64, up, 32, 2)
200 PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64Barrier, up, 32, 2)
201 pushl %ebx // push out spare stuff for space
203 movl 12(%esp), %eax // load in 1st 64-bit parameter
205 movl 20(%esp), %ebx // load in 2nd 64-bit parameter
207 movl 28(%esp), %esi // laod in destination address
208 xchg64 %esi, ATOMIC_UP // compare and swap 64-bit
210 movzbl %al,%eax // widen in case caller assumes we return an int
215 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64, mp, 32, 2)
216 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64Barrier, mp, 32, 2)
217 pushl %ebx // push out spare stuff for space
219 movl 12(%esp), %eax // load in 1st 64-bit parameter
221 movl 20(%esp), %ebx // load in 2nd 64-bit parameter
223 movl 28(%esp), %esi // laod in destination address
224 xchg64 %esi, ATOMIC_MP // compare and swap 64-bit
226 movzbl %al,%eax // widen in case caller assumes we return an int
231 PLATFUNC_FUNCTION_START(OSAtomicAdd32, up, 32, 2)
232 PLATFUNC_FUNCTION_START(OSAtomicAdd32Barrier, up, 32, 2)
240 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32, mp, 32, 2)
241 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32Barrier, mp, 32, 2)
250 PLATFUNC_FUNCTION_START(OSAtomicAdd64, up, 32, 2)
251 PLATFUNC_FUNCTION_START(OSAtomicAdd64Barrier, up, 32, 2)
252 ATOMIC_ADD64(ATOMIC_UP)
255 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64, mp, 32, 2)
256 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64Barrier, mp, 32, 2)
257 ATOMIC_ADD64(ATOMIC_MP)
260 PLATFUNC_FUNCTION_START(OSAtomicTestAndSet, up, 32, 2)
261 PLATFUNC_FUNCTION_START(OSAtomicTestAndSetBarrier, up, 32, 2)
262 ATOMIC_BIT_OP(btsl, ATOMIC_UP)
265 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSet, mp, 32, 2)
266 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSetBarrier, mp, 32, 2)
267 ATOMIC_BIT_OP(btsl, ATOMIC_MP)
270 PLATFUNC_FUNCTION_START(OSAtomicTestAndClear, up, 32, 2)
271 PLATFUNC_FUNCTION_START(OSAtomicTestAndClearBarrier, up, 32, 2)
272 ATOMIC_BIT_OP(btrl, ATOMIC_UP)
275 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClear, mp, 32, 2)
276 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClearBarrier, mp, 32, 2)
277 ATOMIC_BIT_OP(btrl, ATOMIC_MP)
281 // These are used both in 32 and 64-bit mode. We use a fence even on UP
282 // machines, so this function can be used with nontemporal stores.
284 PLATFUNC_FUNCTION_START_GENERIC(OSMemoryBarrier, all, 32, 4)
288 PLATFUNC_DESCRIPTOR(OSMemoryBarrier,all,0,kHasSSE2);
290 PLATFUNC_FUNCTION_START(OSMemoryBarrier, sse2, 32, 4)
293 PLATFUNC_DESCRIPTOR(OSMemoryBarrier,sse2,kHasSSE2,0);
296 * typedef volatile struct {
297 * void *opaque1; <-- ptr to 1st queue element or null
298 * long opaque2; <-- generation count
301 * void OSAtomicEnqueue( OSQueueHead *list, void *new, size_t offset);
303 PLATFUNC_FUNCTION_START(OSAtomicEnqueue, up, 32, 2)
307 movl 16(%esp),%edi // %edi == ptr to list head
308 movl 20(%esp),%ebx // %ebx == new
309 movl 24(%esp),%esi // %esi == offset
310 movl (%edi),%eax // %eax == ptr to 1st element in Q
311 movl 4(%edi),%edx // %edx == current generation count
312 1: movl %eax,(%ebx,%esi)// link to old list head from new element
314 incl %ecx // increment generation count
315 xchg64 %edi, ATOMIC_UP // ...push on new element
322 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicEnqueue, mp, 32, 2)
326 movl 16(%esp),%edi // %edi == ptr to list head
327 movl 20(%esp),%ebx // %ebx == new
328 movl 24(%esp),%esi // %esi == offset
329 movl (%edi),%eax // %eax == ptr to 1st element in Q
330 movl 4(%edi),%edx // %edx == current generation count
331 1: movl %eax,(%ebx,%esi)// link to old list head from new element
333 incl %ecx // increment generation count
334 xchg64 %edi, ATOMIC_MP // ...push on new element
341 /* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
342 PLATFUNC_FUNCTION_START(OSAtomicDequeue, up, 32, 2)
346 movl 16(%esp),%edi // %edi == ptr to list head
347 movl 20(%esp),%esi // %esi == offset
348 movl (%edi),%eax // %eax == ptr to 1st element in Q
349 movl 4(%edi),%edx // %edx == current generation count
350 1: testl %eax,%eax // list empty?
352 movl (%eax,%esi),%ebx // point to 2nd in Q
354 incl %ecx // increment generation count
355 xchg64 %edi, ATOMIC_UP // ...pop off 1st element
360 ret // ptr to 1st element in Q still in %eax
362 PLATFUNC_FUNCTION_START_GENERIC(OSAtomicDequeue, mp, 32, 2)
366 movl 16(%esp),%edi // %edi == ptr to list head
367 movl 20(%esp),%esi // %esi == offset
368 movl (%edi),%eax // %eax == ptr to 1st element in Q
369 movl 4(%edi),%edx // %edx == current generation count
370 1: testl %eax,%eax // list empty?
372 movl (%eax,%esi),%ebx // point to 2nd in Q
374 incl %ecx // increment generation count
375 xchg64 %edi, ATOMIC_MP // ...pop off 1st element
380 ret // ptr to 1st element in Q still in %eax
383 * typedef volatile struct {
384 * void *opaque1; <-- ptr to first queue element or null
385 * void *opaque2; <-- ptr to last queue element or null
386 * int opaque3; <-- spinlock
389 * void OSAtomicFifoEnqueue( OSFifoQueueHead *list, void *new, size_t offset);
392 .globl _OSAtomicFifoEnqueue
393 _OSAtomicFifoEnqueue:
397 xorl %ebx,%ebx // clear "preemption pending" flag
398 movl 16(%esp),%edi // %edi == ptr to list head
399 movl 20(%esp),%esi // %esi == new
400 EXTERN_TO_REG(_commpage_pfz_base,%ecx)
402 addl $(_COMM_TEXT_PFZ_ENQUEUE_OFFSET), %ecx
403 movl 24(%esp),%edx // %edx == offset
405 testl %ebx,%ebx // pending preemption?
413 /* void* OSAtomicFifoDequeue( OSFifoQueueHead *list, size_t offset); */
415 .globl _OSAtomicFifoDequeue
416 _OSAtomicFifoDequeue:
420 xorl %ebx,%ebx // clear "preemption pending" flag
421 movl 16(%esp),%edi // %edi == ptr to list head
422 PICIFY(_commpage_pfz_base)
424 movl 20(%esp),%edx // %edx == offset
425 addl $(_COMM_TEXT_PFZ_DEQUEUE_OFFSET), %ecx
427 testl %ebx,%ebx // pending preemption?
429 pushl %eax // save return value across sysenter
435 ret // ptr to 1st element in Q still in %eax