]> git.saurik.com Git - apple/libc.git/blame - i386/sys/OSAtomic.s
Libc-763.11.tar.gz
[apple/libc.git] / i386 / sys / OSAtomic.s
CommitLineData
3d9156a7 1/*
224c7076
A
2 * Copyright (c) 2007 Apple Inc. All rights reserved.
3 * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
3d9156a7
A
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include <machine/cpu_capabilities.h>
1f2f436a 26#include <platfunc.h>
3d9156a7 27
1f2f436a
A
28#define ATOMIC_UP 0
29#define ATOMIC_MP 1
30#define ATOMIC_RET_ORIG 0
31#define ATOMIC_RET_NEW 1
32
33// compare and exchange 32-bit
34// xchg32 <new> <dst> <mp>
35.macro xchg32
36 .if $2 == ATOMIC_MP
37 lock
38 .endif
39 cmpxchgl $0, ($1)
40.endm
41
42// compare and exchange 64-bit
43// xchg64 <dst> <mp>
44.macro xchg64
45 .if $1 == ATOMIC_MP
46 lock
47 .endif
48 cmpxchg8b ($0)
49.endm
50
51
52// int32_t OSAtomicAdd32(int32_t theAmount, volatile int32_t *theValue);
53#define ATOMIC_ARITHMETIC(instr, orig, mp) \
54 movl 8(%esp), %ecx /* load 2nd arg ptr into ecx */ ;\
55 movl (%ecx), %eax /* load contents of ecx into eax */ ;\
561: movl 4(%esp), %edx /* load 1st arg into edx */ ;\
57 instr %eax, %edx /* do the operation */ ;\
58 xchg32 %edx, %ecx, mp /* old in %eax, new in %edx, exchange into %ecx */ ;\
59 jnz 1b /* go back if we failed to exchange */ ;\
60 .if orig == ATOMIC_RET_NEW ;\
61 movl %edx, %eax /* return new value */ ;\
62 .endif
63
64// bool OSAtomicTestAndSet(uint32_t n, volatile void *theAddress);
65#define ATOMIC_BIT_OP(instr, mp) \
66 movl 4(%esp), %eax ;\
67 movl 8(%esp), %edx ;\
68 shldl $3,%edx,%ecx /* save top 3 bits of address in %ecx */ ;\
69 shll $3,%edx ;\
70 xorl $7,%eax /* bit position is numbered big endian so convert to little endian */ ;\
71 addl %eax,%edx /* generate bit address */ ;\
72 adcl $0,%ecx /* handle carry out of lower half of address */ ;\
73 movl %edx,%eax /* copy lower half of bit address */ ;\
74 andl $31,%eax /* keep bit offset in range 0..31 */ ;\
75 xorl %eax,%edx /* 4-byte align address */ ;\
76 shrdl $3,%ecx,%edx /* restore 32-bit byte address in %edx */ ;\
77 .if mp == ATOMIC_MP ;\
78 lock ;\
79 .endif ;\
80 instr %eax, (%edx) ;\
81 setc %al ;\
82 movzbl %al,%eax // widen in case caller assumes we return an int
83
84// int64_t OSAtomicAdd64(int64_t theAmount, volatile int64_t *theValue);
85#define ATOMIC_ADD64(mp) \
86 pushl %ebx ;\
87 pushl %esi ;\
88 movl 20(%esp), %esi ;\
89 movl 0(%esi), %eax ;\
90 movl 4(%esi), %edx ;\
911: movl 12(%esp), %ebx ;\
92 movl 16(%esp), %ecx ;\
93 addl %eax, %ebx ;\
94 adcl %edx, %ecx ;\
95 xchg64 %esi, mp ;\
96 jnz 1b ;\
97 movl %ebx, %eax ;\
98 movl %ecx, %edx ;\
99 popl %esi ;\
100 popl %ebx
101
102 .text
103
104PLATFUNC_FUNCTION_START(OSAtomicAnd32, up, 32, 2)
105PLATFUNC_FUNCTION_START(OSAtomicAnd32Barrier, up, 32, 2)
106 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_UP)
107 ret
108
109PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32, mp, 32, 2)
110PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Barrier, mp, 32, 2)
111 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_MP)
112 ret
113
114PLATFUNC_FUNCTION_START(OSAtomicOr32, up, 32, 2)
115PLATFUNC_FUNCTION_START(OSAtomicOr32Barrier, up, 32, 2)
116 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_UP)
117 ret
118
119PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32, mp, 32, 2)
120PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Barrier, mp, 32, 2)
121 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_MP)
3d9156a7
A
122 ret
123
1f2f436a
A
124PLATFUNC_FUNCTION_START(OSAtomicXor32, up, 32, 2)
125PLATFUNC_FUNCTION_START(OSAtomicXor32Barrier, up, 32, 2)
126 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_UP)
3d9156a7
A
127 ret
128
1f2f436a
A
129PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32, mp, 32, 2)
130PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Barrier, mp, 32, 2)
131 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_MP)
3d9156a7
A
132 ret
133
1f2f436a
A
134PLATFUNC_FUNCTION_START(OSAtomicAnd32Orig, up, 32, 2)
135PLATFUNC_FUNCTION_START(OSAtomicAnd32OrigBarrier, up, 32, 2)
136 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_UP)
224c7076
A
137 ret
138
1f2f436a
A
139PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Orig, mp, 32, 2)
140PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32OrigBarrier, mp, 32, 2)
141 ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_MP)
224c7076
A
142 ret
143
1f2f436a
A
144PLATFUNC_FUNCTION_START(OSAtomicOr32Orig, up, 32, 2)
145PLATFUNC_FUNCTION_START(OSAtomicOr32OrigBarrier, up, 32, 2)
146 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_UP)
224c7076
A
147 ret
148
1f2f436a
A
149PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Orig, mp, 32, 2)
150PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32OrigBarrier, mp, 32, 2)
151 ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_MP)
152 ret
153
154PLATFUNC_FUNCTION_START(OSAtomicXor32Orig, up, 32, 2)
155PLATFUNC_FUNCTION_START(OSAtomicXor32OrigBarrier, up, 32, 2)
156 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_UP)
157 ret
158
159PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Orig, mp, 32, 2)
160PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32OrigBarrier, mp, 32, 2)
161 ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_MP)
162 ret
163
164// bool OSAtomicCompareAndSwapInt(int oldValue, int newValue, volatile int *theValue);
165PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtr, up, 32, 2)
166PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtrBarrier, up, 32, 2)
167PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapInt, up, 32, 2)
168PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapIntBarrier, up, 32, 2)
169PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLong, up, 32, 2)
170PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLongBarrier, up, 32, 2)
171PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32, up, 32, 2)
172PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32Barrier, up, 32, 2)
173 movl 4(%esp), %eax
174 movl 8(%esp), %edx
175 movl 12(%esp), %ecx
176 xchg32 %edx, %ecx, ATOMIC_UP
3d9156a7 177 sete %al
224c7076 178 movzbl %al,%eax // widen in case caller assumes we return an int
3d9156a7
A
179 ret
180
1f2f436a
A
181PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtr, mp, 32, 2)
182PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtrBarrier, mp, 32, 2)
183PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapInt, mp, 32, 2)
184PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapIntBarrier, mp, 32, 2)
185PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLong, mp, 32, 2)
186PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLongBarrier, mp, 32, 2)
187PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32, mp, 32, 2)
188PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32Barrier, mp, 32, 2)
189 movl 4(%esp), %eax
190 movl 8(%esp), %edx
191 movl 12(%esp), %ecx
192 xchg32 %edx, %ecx, ATOMIC_MP
193 sete %al
194 movzbl %al,%eax // widen in case caller assumes we return an int
195 ret
196
197// bool OSAtomicCompareAndSwap64(int64_t oldValue, int64_t newValue, volatile int64_t *theValue);
198PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64, up, 32, 2)
199PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64Barrier, up, 32, 2)
200 pushl %ebx // push out spare stuff for space
3d9156a7 201 pushl %esi
1f2f436a 202 movl 12(%esp), %eax // load in 1st 64-bit parameter
3d9156a7 203 movl 16(%esp), %edx
1f2f436a 204 movl 20(%esp), %ebx // load in 2nd 64-bit parameter
3d9156a7 205 movl 24(%esp), %ecx
1f2f436a
A
206 movl 28(%esp), %esi // laod in destination address
207 xchg64 %esi, ATOMIC_UP // compare and swap 64-bit
3d9156a7 208 sete %al
224c7076 209 movzbl %al,%eax // widen in case caller assumes we return an int
3d9156a7
A
210 popl %esi
211 popl %ebx
212 ret
213
1f2f436a
A
214PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64, mp, 32, 2)
215PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64Barrier, mp, 32, 2)
216 pushl %ebx // push out spare stuff for space
3d9156a7 217 pushl %esi
1f2f436a
A
218 movl 12(%esp), %eax // load in 1st 64-bit parameter
219 movl 16(%esp), %edx
220 movl 20(%esp), %ebx // load in 2nd 64-bit parameter
221 movl 24(%esp), %ecx
222 movl 28(%esp), %esi // laod in destination address
223 xchg64 %esi, ATOMIC_MP // compare and swap 64-bit
224 sete %al
225 movzbl %al,%eax // widen in case caller assumes we return an int
3d9156a7 226 popl %esi
1f2f436a 227 popl %ebx
3d9156a7
A
228 ret
229
1f2f436a
A
230PLATFUNC_FUNCTION_START(OSAtomicAdd32, up, 32, 2)
231PLATFUNC_FUNCTION_START(OSAtomicAdd32Barrier, up, 32, 2)
3d9156a7
A
232 movl 4(%esp), %eax
233 movl 8(%esp), %edx
eb1cde05 234 movl %eax, %ecx
1f2f436a
A
235 xaddl %eax, (%edx)
236 addl %ecx, %eax
3d9156a7
A
237 ret
238
1f2f436a
A
239PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32, mp, 32, 2)
240PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32Barrier, mp, 32, 2)
3d9156a7
A
241 movl 4(%esp), %eax
242 movl 8(%esp), %edx
eb1cde05 243 movl %eax, %ecx
1f2f436a
A
244 lock
245 xaddl %eax, (%edx)
246 addl %ecx, %eax
3d9156a7
A
247 ret
248
1f2f436a
A
249PLATFUNC_FUNCTION_START(OSAtomicAdd64, up, 32, 2)
250PLATFUNC_FUNCTION_START(OSAtomicAdd64Barrier, up, 32, 2)
251 ATOMIC_ADD64(ATOMIC_UP)
3d9156a7
A
252 ret
253
1f2f436a
A
254PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64, mp, 32, 2)
255PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64Barrier, mp, 32, 2)
256 ATOMIC_ADD64(ATOMIC_MP)
257 ret
224c7076 258
1f2f436a
A
259PLATFUNC_FUNCTION_START(OSAtomicTestAndSet, up, 32, 2)
260PLATFUNC_FUNCTION_START(OSAtomicTestAndSetBarrier, up, 32, 2)
261 ATOMIC_BIT_OP(btsl, ATOMIC_UP)
262 ret
263
264PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSet, mp, 32, 2)
265PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSetBarrier, mp, 32, 2)
266 ATOMIC_BIT_OP(btsl, ATOMIC_MP)
267 ret
268
269PLATFUNC_FUNCTION_START(OSAtomicTestAndClear, up, 32, 2)
270PLATFUNC_FUNCTION_START(OSAtomicTestAndClearBarrier, up, 32, 2)
271 ATOMIC_BIT_OP(btrl, ATOMIC_UP)
272 ret
273
274PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClear, mp, 32, 2)
275PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClearBarrier, mp, 32, 2)
276 ATOMIC_BIT_OP(btrl, ATOMIC_MP)
277 ret
278
279// OSMemoryBarrier()
280// These are used both in 32 and 64-bit mode. We use a fence even on UP
281// machines, so this function can be used with nontemporal stores.
282
283PLATFUNC_FUNCTION_START_GENERIC(OSMemoryBarrier, all, 32, 4)
284 lock
285 addl $0,(%esp)
286 ret
287PLATFUNC_DESCRIPTOR(OSMemoryBarrier,all,0,kHasSSE2);
288
289PLATFUNC_FUNCTION_START(OSMemoryBarrier, sse2, 32, 4)
290 mfence
291 ret
292PLATFUNC_DESCRIPTOR(OSMemoryBarrier,sse2,kHasSSE2,0);
293
294 /*
295 * typedef volatile struct {
296 * void *opaque1; <-- ptr to 1st queue element or null
297 * long opaque2; <-- generation count
298 * } OSQueueHead;
299 *
300 * void OSAtomicEnqueue( OSQueueHead *list, void *new, size_t offset);
301 */
302PLATFUNC_FUNCTION_START(OSAtomicEnqueue, up, 32, 2)
224c7076
A
303 pushl %edi
304 pushl %esi
305 pushl %ebx
306 movl 16(%esp),%edi // %edi == ptr to list head
307 movl 20(%esp),%ebx // %ebx == new
308 movl 24(%esp),%esi // %esi == offset
309 movl (%edi),%eax // %eax == ptr to 1st element in Q
310 movl 4(%edi),%edx // %edx == current generation count
1f2f436a 3111: movl %eax,(%ebx,%esi)// link to old list head from new element
224c7076
A
312 movl %edx,%ecx
313 incl %ecx // increment generation count
1f2f436a 314 xchg64 %edi, ATOMIC_UP // ...push on new element
224c7076
A
315 jnz 1b
316 popl %ebx
317 popl %esi
318 popl %edi
3d9156a7 319 ret
1f2f436a
A
320
321PLATFUNC_FUNCTION_START_GENERIC(OSAtomicEnqueue, mp, 32, 2)
322 pushl %edi
323 pushl %esi
324 pushl %ebx
325 movl 16(%esp),%edi // %edi == ptr to list head
326 movl 20(%esp),%ebx // %ebx == new
327 movl 24(%esp),%esi // %esi == offset
328 movl (%edi),%eax // %eax == ptr to 1st element in Q
329 movl 4(%edi),%edx // %edx == current generation count
3301: movl %eax,(%ebx,%esi)// link to old list head from new element
331 movl %edx,%ecx
332 incl %ecx // increment generation count
333 xchg64 %edi, ATOMIC_MP // ...push on new element
334 jnz 1b
335 popl %ebx
336 popl %esi
337 popl %edi
338 ret
339
224c7076 340/* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
1f2f436a 341PLATFUNC_FUNCTION_START(OSAtomicDequeue, up, 32, 2)
224c7076
A
342 pushl %edi
343 pushl %esi
344 pushl %ebx
345 movl 16(%esp),%edi // %edi == ptr to list head
346 movl 20(%esp),%esi // %esi == offset
347 movl (%edi),%eax // %eax == ptr to 1st element in Q
348 movl 4(%edi),%edx // %edx == current generation count
1f2f436a 3491: testl %eax,%eax // list empty?
224c7076
A
350 jz 2f // yes
351 movl (%eax,%esi),%ebx // point to 2nd in Q
352 movl %edx,%ecx
353 incl %ecx // increment generation count
1f2f436a 354 xchg64 %edi, ATOMIC_UP // ...pop off 1st element
224c7076 355 jnz 1b
1f2f436a
A
3562: popl %ebx
357 popl %esi
358 popl %edi
359 ret // ptr to 1st element in Q still in %eax
360
361PLATFUNC_FUNCTION_START_GENERIC(OSAtomicDequeue, mp, 32, 2)
362 pushl %edi
363 pushl %esi
364 pushl %ebx
365 movl 16(%esp),%edi // %edi == ptr to list head
366 movl 20(%esp),%esi // %esi == offset
367 movl (%edi),%eax // %eax == ptr to 1st element in Q
368 movl 4(%edi),%edx // %edx == current generation count
3691: testl %eax,%eax // list empty?
370 jz 2f // yes
371 movl (%eax,%esi),%ebx // point to 2nd in Q
372 movl %edx,%ecx
373 incl %ecx // increment generation count
374 xchg64 %edi, ATOMIC_MP // ...pop off 1st element
375 jnz 1b
3762: popl %ebx
377 popl %esi
378 popl %edi
379 ret // ptr to 1st element in Q still in %eax
380
381/*
382 * typedef volatile struct {
383 * void *opaque1; <-- ptr to first queue element or null
384 * void *opaque2; <-- ptr to last queue element or null
385 * int opaque3; <-- spinlock
386 * } OSFifoQueueHead;
387 *
388 * void OSAtomicFifoEnqueue( OSFifoQueueHead *list, void *new, size_t offset);
389 */
390 .align 2
391 .globl _OSAtomicFifoEnqueue
392_OSAtomicFifoEnqueue:
393 pushl %edi
394 pushl %esi
395 pushl %ebx
396 xorl %ebx,%ebx // clear "preemption pending" flag
397 movl 16(%esp),%edi // %edi == ptr to list head
398 movl 20(%esp),%esi // %esi == new
399 movl 24(%esp),%edx // %edx == offset
400 movl $(_COMM_PAGE_PFZ_ENQUEUE), %ecx
401 call *%ecx
402 testl %ebx,%ebx // pending preemption?
403 jz 1f
404 call _preempt
4051: popl %ebx
406 popl %esi
407 popl %edi
408 ret
409
410/* void* OSAtomicFifoDequeue( OSFifoQueueHead *list, size_t offset); */
411 .align 2
412 .globl _OSAtomicFifoDequeue
413_OSAtomicFifoDequeue:
414 pushl %edi
415 pushl %esi
416 pushl %ebx
417 xorl %ebx,%ebx // clear "preemption pending" flag
418 movl 16(%esp),%edi // %edi == ptr to list head
419 movl 20(%esp),%edx // %edx == offset
420 movl $(_COMM_PAGE_PFZ_DEQUEUE), %ecx
421 call *%ecx
422 testl %ebx,%ebx // pending preemption?
423 jz 1f
424 pushl %eax // save return value across sysenter
425 call _preempt
426 popl %eax
4271: popl %ebx
224c7076
A
428 popl %esi
429 popl %edi
430 ret // ptr to 1st element in Q still in %eax
431
1f2f436a
A
432// Local Variables:
433// tab-width: 8
434// End: