1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27	.file	"atomic.s"
28
29#include <sys/asm_linkage.h>
30
31/*
32 * ATOMIC_BO_ENABLE_SHIFT can be selectively defined by processors
33 * to enable exponential backoff. No definition means backoff is
34 * not desired i.e. backoff should be disabled.
35 * By default, the shift value is used to generate a power of 2
36 * value for backoff limit. In the kernel, processors scale this
37 * shift value with the number of online cpus.
38 */
39
40#if defined(_KERNEL)
41	/*
42	 * Legacy kernel interfaces; they will go away the moment our closed
43	 * bins no longer require them.
44	 */
45	ANSI_PRAGMA_WEAK2(cas8,atomic_cas_8,function)
46	ANSI_PRAGMA_WEAK2(cas32,atomic_cas_32,function)
47	ANSI_PRAGMA_WEAK2(cas64,atomic_cas_64,function)
48	ANSI_PRAGMA_WEAK2(caslong,atomic_cas_ulong,function)
49	ANSI_PRAGMA_WEAK2(casptr,atomic_cas_ptr,function)
50	ANSI_PRAGMA_WEAK2(atomic_and_long,atomic_and_ulong,function)
51	ANSI_PRAGMA_WEAK2(atomic_or_long,atomic_or_ulong,function)
52	ANSI_PRAGMA_WEAK2(swapl,atomic_swap_32,function)
53
54#ifdef ATOMIC_BO_ENABLE_SHIFT
55
56#if !defined(lint)
57	.weak   cpu_atomic_delay
58	.type   cpu_atomic_delay, #function
59#endif  /* lint */
60
61/*
62 * For the kernel, invoke processor specific delay routine to perform
63 * low-impact spin delay. The value of ATOMIC_BO_ENABLE_SHIFT is tuned
64 * with respect to the specific spin delay implementation.
65 */
66#define	DELAY_SPIN(label, tmp1, tmp2)					\
67	/*								; \
68	 * Define a pragma weak reference to a cpu specific		; \
69	 * delay routine for atomic backoff. For CPUs that		; \
70	 * have no such delay routine defined, the delay becomes	; \
71	 * just a simple tight loop.					; \
72	 *								; \
73	 * tmp1 = holds CPU specific delay routine			; \
74	 * tmp2 = holds atomic routine's callee return address		; \
75	 */								; \
76	sethi	%hi(cpu_atomic_delay), tmp1				; \
77	or	tmp1, %lo(cpu_atomic_delay), tmp1			; \
78label##0:								; \
79	brz,pn	tmp1, label##1					; \
80	mov	%o7, tmp2						; \
81	jmpl	tmp1, %o7	/* call CPU specific delay routine */	; \
82	  nop			/* delay slot : do nothing */		; \
83	mov	tmp2, %o7	/* restore callee's return address */	; \
84label##1:
85
86/*
87 * For the kernel, we take into consideration of cas failures
88 * and also scale the backoff limit w.r.t. the number of cpus.
89 * For cas failures, we reset the backoff value to 1 if the cas
90 * failures exceed or equal to the number of online cpus. This
91 * will enforce some degree of fairness and prevent starvation.
92 * We also scale/normalize the processor provided specific
93 * ATOMIC_BO_ENABLE_SHIFT w.r.t. the number of online cpus to
94 * obtain the actual final limit to use.
95 */
96#define ATOMIC_BACKOFF_CPU(val, limit, ncpu, cas_cnt, label)		\
97	brnz,pt	ncpu, label##0					; \
98	  inc	cas_cnt							; \
99	sethi	%hi(ncpus_online), ncpu					; \
100	ld	[ncpu + %lo(ncpus_online)], ncpu			; \
101label##0:								; \
102	cmp	cas_cnt, ncpu						; \
103	blu,pt	%xcc, label##1					; \
104	  sllx	ncpu, ATOMIC_BO_ENABLE_SHIFT, limit			; \
105	mov	%g0, cas_cnt						; \
106	mov	1, val							; \
107label##1:
108#endif	/* ATOMIC_BO_ENABLE_SHIFT */
109
110#else	/* _KERNEL */
111
112/*
113 * ATOMIC_BO_ENABLE_SHIFT may be enabled/defined here for generic
114 * libc atomics. None for now.
115 */
116#ifdef ATOMIC_BO_ENABLE_SHIFT
117#define	DELAY_SPIN(label, tmp1, tmp2)	\
118label##0:
119
120#define ATOMIC_BACKOFF_CPU(val, limit, ncpu, cas_cnt, label)  \
121	set	1 << ATOMIC_BO_ENABLE_SHIFT, limit
122#endif	/* ATOMIC_BO_ENABLE_SHIFT */
123#endif	/* _KERNEL */
124
125#ifdef ATOMIC_BO_ENABLE_SHIFT
126/*
127 * ATOMIC_BACKOFF_INIT macro for initialization.
128 * backoff val is initialized to 1.
129 * ncpu is initialized to 0
130 * The cas_cnt counts the cas instruction failure and is
131 * initialized to 0.
132 */
133#define ATOMIC_BACKOFF_INIT(val, ncpu, cas_cnt)	\
134	mov	1, val				; \
135	mov	%g0, ncpu			; \
136	mov	%g0, cas_cnt
137
138#define ATOMIC_BACKOFF_BRANCH(cr, backoff, loop) \
139	bne,a,pn cr, backoff
140
141/*
142 * Main ATOMIC_BACKOFF_BACKOFF macro for backoff.
143 */
144#define ATOMIC_BACKOFF_BACKOFF(val, limit, ncpu, cas_cnt, label, retlabel) \
145	ATOMIC_BACKOFF_CPU(val, limit, ncpu, cas_cnt, label##_0)	; \
146	cmp	val, limit						; \
147	blu,a,pt %xcc, label##_1					; \
148	  mov	val, limit						; \
149label##_1:								; \
150	mov	limit, val						; \
151	DELAY_SPIN(label##_2, %g2, %g3)				; \
152	deccc	limit							; \
153	bgu,pn	%xcc, label##_20 /* branch to middle of DELAY_SPIN */	; \
154	  nop								; \
155	ba	retlabel						; \
156	sllx	val, 1, val
157
158#else	/* ATOMIC_BO_ENABLE_SHIFT */
159#define ATOMIC_BACKOFF_INIT(val, ncpu, cas_cnt)
160
161#define ATOMIC_BACKOFF_BRANCH(cr, backoff, loop) \
162	bne,a,pn cr, loop
163
164#define ATOMIC_BACKOFF_BACKOFF(val, limit, ncpu, cas_cnt, label, retlabel)
165#endif	/* ATOMIC_BO_ENABLE_SHIFT */
166
167	/*
168	 * NOTE: If atomic_inc_8 and atomic_inc_8_nv are ever
169	 * separated, you need to also edit the libc sparcv9 platform
170	 * specific mapfile and remove the NODYNSORT attribute
171	 * from atomic_inc_8_nv.
172	 */
173	ENTRY(atomic_inc_8)
174	ALTENTRY(atomic_inc_8_nv)
175	ALTENTRY(atomic_inc_uchar)
176	ALTENTRY(atomic_inc_uchar_nv)
177	ba	add_8
178	  add	%g0, 1, %o1
179	SET_SIZE(atomic_inc_uchar_nv)
180	SET_SIZE(atomic_inc_uchar)
181	SET_SIZE(atomic_inc_8_nv)
182	SET_SIZE(atomic_inc_8)
183
184	/*
185	 * NOTE: If atomic_dec_8 and atomic_dec_8_nv are ever
186	 * separated, you need to also edit the libc sparcv9 platform
187	 * specific mapfile and remove the NODYNSORT attribute
188	 * from atomic_dec_8_nv.
189	 */
190	ENTRY(atomic_dec_8)
191	ALTENTRY(atomic_dec_8_nv)
192	ALTENTRY(atomic_dec_uchar)
193	ALTENTRY(atomic_dec_uchar_nv)
194	ba	add_8
195	  sub	%g0, 1, %o1
196	SET_SIZE(atomic_dec_uchar_nv)
197	SET_SIZE(atomic_dec_uchar)
198	SET_SIZE(atomic_dec_8_nv)
199	SET_SIZE(atomic_dec_8)
200
201	/*
202	 * NOTE: If atomic_add_8 and atomic_add_8_nv are ever
203	 * separated, you need to also edit the libc sparcv9 platform
204	 * specific mapfile and remove the NODYNSORT attribute
205	 * from atomic_add_8_nv.
206	 */
207	ENTRY(atomic_add_8)
208	ALTENTRY(atomic_add_8_nv)
209	ALTENTRY(atomic_add_char)
210	ALTENTRY(atomic_add_char_nv)
211add_8:
212	and	%o0, 0x3, %o4		! %o4 = byte offset, left-to-right
213	xor	%o4, 0x3, %g1		! %g1 = byte offset, right-to-left
214	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
215	set	0xff, %o3		! %o3 = mask
216	sll	%o3, %g1, %o3		! %o3 = shifted to bit offset
217	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
218	and	%o1, %o3, %o1		! %o1 = single byte value
219	andn	%o0, 0x3, %o0		! %o0 = word address
220	ld	[%o0], %o2		! read old value
2211:
222	add	%o2, %o1, %o5		! add value to the old value
223	and	%o5, %o3, %o5		! clear other bits
224	andn	%o2, %o3, %o4		! clear target bits
225	or	%o4, %o5, %o5		! insert the new value
226	cas	[%o0], %o2, %o5
227	cmp	%o2, %o5
228	bne,a,pn %icc, 1b
229	  mov	%o5, %o2		! %o2 = old value
230	add	%o2, %o1, %o5
231	and	%o5, %o3, %o5
232	retl
233	srl	%o5, %g1, %o0		! %o0 = new value
234	SET_SIZE(atomic_add_char_nv)
235	SET_SIZE(atomic_add_char)
236	SET_SIZE(atomic_add_8_nv)
237	SET_SIZE(atomic_add_8)
238
239	/*
240	 * NOTE: If atomic_inc_16 and atomic_inc_16_nv are ever
241	 * separated, you need to also edit the libc sparcv9 platform
242	 * specific mapfile and remove the NODYNSORT attribute
243	 * from atomic_inc_16_nv.
244	 */
245	ENTRY(atomic_inc_16)
246	ALTENTRY(atomic_inc_16_nv)
247	ALTENTRY(atomic_inc_ushort)
248	ALTENTRY(atomic_inc_ushort_nv)
249	ba	add_16
250	  add	%g0, 1, %o1
251	SET_SIZE(atomic_inc_ushort_nv)
252	SET_SIZE(atomic_inc_ushort)
253	SET_SIZE(atomic_inc_16_nv)
254	SET_SIZE(atomic_inc_16)
255
256	/*
257	 * NOTE: If atomic_dec_16 and atomic_dec_16_nv are ever
258	 * separated, you need to also edit the libc sparcv9 platform
259	 * specific mapfile and remove the NODYNSORT attribute
260	 * from atomic_dec_16_nv.
261	 */
262	ENTRY(atomic_dec_16)
263	ALTENTRY(atomic_dec_16_nv)
264	ALTENTRY(atomic_dec_ushort)
265	ALTENTRY(atomic_dec_ushort_nv)
266	ba	add_16
267	  sub	%g0, 1, %o1
268	SET_SIZE(atomic_dec_ushort_nv)
269	SET_SIZE(atomic_dec_ushort)
270	SET_SIZE(atomic_dec_16_nv)
271	SET_SIZE(atomic_dec_16)
272
273	/*
274	 * NOTE: If atomic_add_16 and atomic_add_16_nv are ever
275	 * separated, you need to also edit the libc sparcv9 platform
276	 * specific mapfile and remove the NODYNSORT attribute
277	 * from atomic_add_16_nv.
278	 */
279	ENTRY(atomic_add_16)
280	ALTENTRY(atomic_add_16_nv)
281	ALTENTRY(atomic_add_short)
282	ALTENTRY(atomic_add_short_nv)
283add_16:
284	and	%o0, 0x2, %o4		! %o4 = byte offset, left-to-right
285	xor	%o4, 0x2, %g1		! %g1 = byte offset, right-to-left
286	sll	%o4, 3, %o4		! %o4 = bit offset, left-to-right
287	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
288	sethi	%hi(0xffff0000), %o3	! %o3 = mask
289	srl	%o3, %o4, %o3		! %o3 = shifted to bit offset
290	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
291	and	%o1, %o3, %o1		! %o1 = single short value
292	andn	%o0, 0x2, %o0		! %o0 = word address
293	! if low-order bit is 1, we will properly get an alignment fault here
294	ld	[%o0], %o2		! read old value
2951:
296	add	%o1, %o2, %o5		! add value to the old value
297	and	%o5, %o3, %o5		! clear other bits
298	andn	%o2, %o3, %o4		! clear target bits
299	or	%o4, %o5, %o5		! insert the new value
300	cas	[%o0], %o2, %o5
301	cmp	%o2, %o5
302	bne,a,pn %icc, 1b
303	  mov	%o5, %o2		! %o2 = old value
304	add	%o1, %o2, %o5
305	and	%o5, %o3, %o5
306	retl
307	srl	%o5, %g1, %o0		! %o0 = new value
308	SET_SIZE(atomic_add_short_nv)
309	SET_SIZE(atomic_add_short)
310	SET_SIZE(atomic_add_16_nv)
311	SET_SIZE(atomic_add_16)
312
313	/*
314	 * NOTE: If atomic_inc_32 and atomic_inc_32_nv are ever
315	 * separated, you need to also edit the libc sparcv9 platform
316	 * specific mapfile and remove the NODYNSORT attribute
317	 * from atomic_inc_32_nv.
318	 */
319	ENTRY(atomic_inc_32)
320	ALTENTRY(atomic_inc_32_nv)
321	ALTENTRY(atomic_inc_uint)
322	ALTENTRY(atomic_inc_uint_nv)
323	ba	add_32
324	  add	%g0, 1, %o1
325	SET_SIZE(atomic_inc_uint_nv)
326	SET_SIZE(atomic_inc_uint)
327	SET_SIZE(atomic_inc_32_nv)
328	SET_SIZE(atomic_inc_32)
329
330	/*
331	 * NOTE: If atomic_dec_32 and atomic_dec_32_nv are ever
332	 * separated, you need to also edit the libc sparcv9 platform
333	 * specific mapfile and remove the NODYNSORT attribute
334	 * from atomic_dec_32_nv.
335	 */
336	ENTRY(atomic_dec_32)
337	ALTENTRY(atomic_dec_32_nv)
338	ALTENTRY(atomic_dec_uint)
339	ALTENTRY(atomic_dec_uint_nv)
340	ba	add_32
341	  sub	%g0, 1, %o1
342	SET_SIZE(atomic_dec_uint_nv)
343	SET_SIZE(atomic_dec_uint)
344	SET_SIZE(atomic_dec_32_nv)
345	SET_SIZE(atomic_dec_32)
346
347	/*
348	 * NOTE: If atomic_add_32 and atomic_add_32_nv are ever
349	 * separated, you need to also edit the libc sparcv9 platform
350	 * specific mapfile and remove the NODYNSORT attribute
351	 * from atomic_add_32_nv.
352	 */
353	ENTRY(atomic_add_32)
354	ALTENTRY(atomic_add_32_nv)
355	ALTENTRY(atomic_add_int)
356	ALTENTRY(atomic_add_int_nv)
357add_32:
358	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
3590:
360	ld	[%o0], %o2
3611:
362	add	%o2, %o1, %o3
363	cas	[%o0], %o2, %o3
364	cmp	%o2, %o3
365	ATOMIC_BACKOFF_BRANCH(%icc, 2f, 1b)
366	  mov	%o3, %o2
367	retl
368	add	%o2, %o1, %o0		! return new value
3692:
370	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, add32, 0b)
371	SET_SIZE(atomic_add_int_nv)
372	SET_SIZE(atomic_add_int)
373	SET_SIZE(atomic_add_32_nv)
374	SET_SIZE(atomic_add_32)
375
376	/*
377	 * NOTE: If atomic_inc_64 and atomic_inc_64_nv are ever
378	 * separated, you need to also edit the libc sparcv9 platform
379	 * specific mapfile and remove the NODYNSORT attribute
380	 * from atomic_inc_64_nv.
381	 */
382	ENTRY(atomic_inc_64)
383	ALTENTRY(atomic_inc_64_nv)
384	ALTENTRY(atomic_inc_ulong)
385	ALTENTRY(atomic_inc_ulong_nv)
386	ba	add_64
387	  add	%g0, 1, %o1
388	SET_SIZE(atomic_inc_ulong_nv)
389	SET_SIZE(atomic_inc_ulong)
390	SET_SIZE(atomic_inc_64_nv)
391	SET_SIZE(atomic_inc_64)
392
393	/*
394	 * NOTE: If atomic_dec_64 and atomic_dec_64_nv are ever
395	 * separated, you need to also edit the libc sparcv9 platform
396	 * specific mapfile and remove the NODYNSORT attribute
397	 * from atomic_dec_64_nv.
398	 */
399	ENTRY(atomic_dec_64)
400	ALTENTRY(atomic_dec_64_nv)
401	ALTENTRY(atomic_dec_ulong)
402	ALTENTRY(atomic_dec_ulong_nv)
403	ba	add_64
404	  sub	%g0, 1, %o1
405	SET_SIZE(atomic_dec_ulong_nv)
406	SET_SIZE(atomic_dec_ulong)
407	SET_SIZE(atomic_dec_64_nv)
408	SET_SIZE(atomic_dec_64)
409
410	/*
411	 * NOTE: If atomic_add_64 and atomic_add_64_nv are ever
412	 * separated, you need to also edit the libc sparcv9 platform
413	 * specific mapfile and remove the NODYNSORT attribute
414	 * from atomic_add_64_nv.
415	 */
416	ENTRY(atomic_add_64)
417	ALTENTRY(atomic_add_64_nv)
418	ALTENTRY(atomic_add_ptr)
419	ALTENTRY(atomic_add_ptr_nv)
420	ALTENTRY(atomic_add_long)
421	ALTENTRY(atomic_add_long_nv)
422add_64:
423	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
4240:
425	ldx	[%o0], %o2
4261:
427	add	%o2, %o1, %o3
428	casx	[%o0], %o2, %o3
429	cmp	%o2, %o3
430	ATOMIC_BACKOFF_BRANCH(%xcc, 2f, 1b)
431	  mov	%o3, %o2
432	retl
433	add	%o2, %o1, %o0		! return new value
4342:
435	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, add64, 0b)
436	SET_SIZE(atomic_add_long_nv)
437	SET_SIZE(atomic_add_long)
438	SET_SIZE(atomic_add_ptr_nv)
439	SET_SIZE(atomic_add_ptr)
440	SET_SIZE(atomic_add_64_nv)
441	SET_SIZE(atomic_add_64)
442
443	/*
444	 * NOTE: If atomic_or_8 and atomic_or_8_nv are ever
445	 * separated, you need to also edit the libc sparcv9 platform
446	 * specific mapfile and remove the NODYNSORT attribute
447	 * from atomic_or_8_nv.
448	 */
449	ENTRY(atomic_or_8)
450	ALTENTRY(atomic_or_8_nv)
451	ALTENTRY(atomic_or_uchar)
452	ALTENTRY(atomic_or_uchar_nv)
453	and	%o0, 0x3, %o4		! %o4 = byte offset, left-to-right
454	xor	%o4, 0x3, %g1		! %g1 = byte offset, right-to-left
455	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
456	set	0xff, %o3		! %o3 = mask
457	sll	%o3, %g1, %o3		! %o3 = shifted to bit offset
458	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
459	and	%o1, %o3, %o1		! %o1 = single byte value
460	andn	%o0, 0x3, %o0		! %o0 = word address
461	ld	[%o0], %o2		! read old value
4621:
463	or	%o2, %o1, %o5		! or in the new value
464	cas	[%o0], %o2, %o5
465	cmp	%o2, %o5
466	bne,a,pn %icc, 1b
467	  mov	%o5, %o2		! %o2 = old value
468	or	%o2, %o1, %o5
469	and	%o5, %o3, %o5
470	retl
471	srl	%o5, %g1, %o0		! %o0 = new value
472	SET_SIZE(atomic_or_uchar_nv)
473	SET_SIZE(atomic_or_uchar)
474	SET_SIZE(atomic_or_8_nv)
475	SET_SIZE(atomic_or_8)
476
477	/*
478	 * NOTE: If atomic_or_16 and atomic_or_16_nv are ever
479	 * separated, you need to also edit the libc sparcv9 platform
480	 * specific mapfile and remove the NODYNSORT attribute
481	 * from atomic_or_16_nv.
482	 */
483	ENTRY(atomic_or_16)
484	ALTENTRY(atomic_or_16_nv)
485	ALTENTRY(atomic_or_ushort)
486	ALTENTRY(atomic_or_ushort_nv)
487	and	%o0, 0x2, %o4		! %o4 = byte offset, left-to-right
488	xor	%o4, 0x2, %g1		! %g1 = byte offset, right-to-left
489	sll	%o4, 3, %o4		! %o4 = bit offset, left-to-right
490	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
491	sethi	%hi(0xffff0000), %o3	! %o3 = mask
492	srl	%o3, %o4, %o3		! %o3 = shifted to bit offset
493	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
494	and	%o1, %o3, %o1		! %o1 = single short value
495	andn	%o0, 0x2, %o0		! %o0 = word address
496	! if low-order bit is 1, we will properly get an alignment fault here
497	ld	[%o0], %o2		! read old value
4981:
499	or	%o2, %o1, %o5		! or in the new value
500	cas	[%o0], %o2, %o5
501	cmp	%o2, %o5
502	bne,a,pn %icc, 1b
503	  mov	%o5, %o2		! %o2 = old value
504	or	%o2, %o1, %o5		! or in the new value
505	and	%o5, %o3, %o5
506	retl
507	srl	%o5, %g1, %o0		! %o0 = new value
508	SET_SIZE(atomic_or_ushort_nv)
509	SET_SIZE(atomic_or_ushort)
510	SET_SIZE(atomic_or_16_nv)
511	SET_SIZE(atomic_or_16)
512
513	/*
514	 * NOTE: If atomic_or_32 and atomic_or_32_nv are ever
515	 * separated, you need to also edit the libc sparcv9 platform
516	 * specific mapfile and remove the NODYNSORT attribute
517	 * from atomic_or_32_nv.
518	 */
519	ENTRY(atomic_or_32)
520	ALTENTRY(atomic_or_32_nv)
521	ALTENTRY(atomic_or_uint)
522	ALTENTRY(atomic_or_uint_nv)
523	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
5240:
525	ld	[%o0], %o2
5261:
527	or	%o2, %o1, %o3
528	cas	[%o0], %o2, %o3
529	cmp	%o2, %o3
530	ATOMIC_BACKOFF_BRANCH(%icc, 2f, 1b)
531	  mov	%o3, %o2
532	retl
533	or	%o2, %o1, %o0		! return new value
5342:
535	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, or32, 0b)
536	SET_SIZE(atomic_or_uint_nv)
537	SET_SIZE(atomic_or_uint)
538	SET_SIZE(atomic_or_32_nv)
539	SET_SIZE(atomic_or_32)
540
541	/*
542	 * NOTE: If atomic_or_64 and atomic_or_64_nv are ever
543	 * separated, you need to also edit the libc sparcv9 platform
544	 * specific mapfile and remove the NODYNSORT attribute
545	 * from atomic_or_64_nv.
546	 */
547	ENTRY(atomic_or_64)
548	ALTENTRY(atomic_or_64_nv)
549	ALTENTRY(atomic_or_ulong)
550	ALTENTRY(atomic_or_ulong_nv)
551	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
5520:
553	ldx	[%o0], %o2
5541:
555	or	%o2, %o1, %o3
556	casx	[%o0], %o2, %o3
557	cmp	%o2, %o3
558	ATOMIC_BACKOFF_BRANCH(%xcc, 2f, 1b)
559	  mov	%o3, %o2
560	retl
561	or	%o2, %o1, %o0		! return new value
5622:
563	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, or64, 0b)
564	SET_SIZE(atomic_or_ulong_nv)
565	SET_SIZE(atomic_or_ulong)
566	SET_SIZE(atomic_or_64_nv)
567	SET_SIZE(atomic_or_64)
568
569	/*
570	 * NOTE: If atomic_and_8 and atomic_and_8_nv are ever
571	 * separated, you need to also edit the libc sparcv9 platform
572	 * specific mapfile and remove the NODYNSORT attribute
573	 * from atomic_and_8_nv.
574	 */
575	ENTRY(atomic_and_8)
576	ALTENTRY(atomic_and_8_nv)
577	ALTENTRY(atomic_and_uchar)
578	ALTENTRY(atomic_and_uchar_nv)
579	and	%o0, 0x3, %o4		! %o4 = byte offset, left-to-right
580	xor	%o4, 0x3, %g1		! %g1 = byte offset, right-to-left
581	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
582	set	0xff, %o3		! %o3 = mask
583	sll	%o3, %g1, %o3		! %o3 = shifted to bit offset
584	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
585	orn	%o1, %o3, %o1		! all ones in other bytes
586	andn	%o0, 0x3, %o0		! %o0 = word address
587	ld	[%o0], %o2		! read old value
5881:
589	and	%o2, %o1, %o5		! and in the new value
590	cas	[%o0], %o2, %o5
591	cmp	%o2, %o5
592	bne,a,pn %icc, 1b
593	  mov	%o5, %o2		! %o2 = old value
594	and	%o2, %o1, %o5
595	and	%o5, %o3, %o5
596	retl
597	srl	%o5, %g1, %o0		! %o0 = new value
598	SET_SIZE(atomic_and_uchar_nv)
599	SET_SIZE(atomic_and_uchar)
600	SET_SIZE(atomic_and_8_nv)
601	SET_SIZE(atomic_and_8)
602
603	/*
604	 * NOTE: If atomic_and_16 and atomic_and_16_nv are ever
605	 * separated, you need to also edit the libc sparcv9 platform
606	 * specific mapfile and remove the NODYNSORT attribute
607	 * from atomic_and_16_nv.
608	 */
609	ENTRY(atomic_and_16)
610	ALTENTRY(atomic_and_16_nv)
611	ALTENTRY(atomic_and_ushort)
612	ALTENTRY(atomic_and_ushort_nv)
613	and	%o0, 0x2, %o4		! %o4 = byte offset, left-to-right
614	xor	%o4, 0x2, %g1		! %g1 = byte offset, right-to-left
615	sll	%o4, 3, %o4		! %o4 = bit offset, left-to-right
616	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
617	sethi	%hi(0xffff0000), %o3	! %o3 = mask
618	srl	%o3, %o4, %o3		! %o3 = shifted to bit offset
619	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
620	orn	%o1, %o3, %o1		! all ones in the other half
621	andn	%o0, 0x2, %o0		! %o0 = word address
622	! if low-order bit is 1, we will properly get an alignment fault here
623	ld	[%o0], %o2		! read old value
6241:
625	and	%o2, %o1, %o5		! and in the new value
626	cas	[%o0], %o2, %o5
627	cmp	%o2, %o5
628	bne,a,pn %icc, 1b
629	  mov	%o5, %o2		! %o2 = old value
630	and	%o2, %o1, %o5
631	and	%o5, %o3, %o5
632	retl
633	srl	%o5, %g1, %o0		! %o0 = new value
634	SET_SIZE(atomic_and_ushort_nv)
635	SET_SIZE(atomic_and_ushort)
636	SET_SIZE(atomic_and_16_nv)
637	SET_SIZE(atomic_and_16)
638
639	/*
640	 * NOTE: If atomic_and_32 and atomic_and_32_nv are ever
641	 * separated, you need to also edit the libc sparcv9 platform
642	 * specific mapfile and remove the NODYNSORT attribute
643	 * from atomic_and_32_nv.
644	 */
645	ENTRY(atomic_and_32)
646	ALTENTRY(atomic_and_32_nv)
647	ALTENTRY(atomic_and_uint)
648	ALTENTRY(atomic_and_uint_nv)
649	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
6500:
651	ld	[%o0], %o2
6521:
653	and	%o2, %o1, %o3
654	cas	[%o0], %o2, %o3
655	cmp	%o2, %o3
656	ATOMIC_BACKOFF_BRANCH(%icc, 2f, 1b)
657	  mov	%o3, %o2
658	retl
659	and	%o2, %o1, %o0		! return new value
6602:
661	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, and32, 0b)
662	SET_SIZE(atomic_and_uint_nv)
663	SET_SIZE(atomic_and_uint)
664	SET_SIZE(atomic_and_32_nv)
665	SET_SIZE(atomic_and_32)
666
667	/*
668	 * NOTE: If atomic_and_64 and atomic_and_64_nv are ever
669	 * separated, you need to also edit the libc sparcv9 platform
670	 * specific mapfile and remove the NODYNSORT attribute
671	 * from atomic_and_64_nv.
672	 */
673	ENTRY(atomic_and_64)
674	ALTENTRY(atomic_and_64_nv)
675	ALTENTRY(atomic_and_ulong)
676	ALTENTRY(atomic_and_ulong_nv)
677	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
6780:
679	ldx	[%o0], %o2
6801:
681	and	%o2, %o1, %o3
682	casx	[%o0], %o2, %o3
683	cmp	%o2, %o3
684	ATOMIC_BACKOFF_BRANCH(%xcc, 2f, 1b)
685	  mov	%o3, %o2
686	retl
687	and	%o2, %o1, %o0		! return new value
6882:
689	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, and64, 0b)
690	SET_SIZE(atomic_and_ulong_nv)
691	SET_SIZE(atomic_and_ulong)
692	SET_SIZE(atomic_and_64_nv)
693	SET_SIZE(atomic_and_64)
694
695	ENTRY(atomic_cas_8)
696	ALTENTRY(atomic_cas_uchar)
697	and	%o0, 0x3, %o4		! %o4 = byte offset, left-to-right
698	xor	%o4, 0x3, %g1		! %g1 = byte offset, right-to-left
699	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
700	set	0xff, %o3		! %o3 = mask
701	sll	%o3, %g1, %o3		! %o3 = shifted to bit offset
702	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
703	and	%o1, %o3, %o1		! %o1 = single byte value
704	sll	%o2, %g1, %o2		! %o2 = shifted to bit offset
705	and	%o2, %o3, %o2		! %o2 = single byte value
706	andn	%o0, 0x3, %o0		! %o0 = word address
707	ld	[%o0], %o4		! read old value
7081:
709	andn	%o4, %o3, %o4		! clear target bits
710	or	%o4, %o2, %o5		! insert the new value
711	or	%o4, %o1, %o4		! insert the comparison value
712	cas	[%o0], %o4, %o5
713	cmp	%o4, %o5		! did we succeed?
714	be,pt	%icc, 2f
715	  and	%o5, %o3, %o4		! isolate the old value
716	cmp	%o1, %o4		! should we have succeeded?
717	be,a,pt	%icc, 1b		! yes, try again
718	  mov	%o5, %o4		! %o4 = old value
7192:
720	retl
721	srl	%o4, %g1, %o0		! %o0 = old value
722	SET_SIZE(atomic_cas_uchar)
723	SET_SIZE(atomic_cas_8)
724
725	ENTRY(atomic_cas_16)
726	ALTENTRY(atomic_cas_ushort)
727	and	%o0, 0x2, %o4		! %o4 = byte offset, left-to-right
728	xor	%o4, 0x2, %g1		! %g1 = byte offset, right-to-left
729	sll	%o4, 3, %o4		! %o4 = bit offset, left-to-right
730	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
731	sethi	%hi(0xffff0000), %o3	! %o3 = mask
732	srl	%o3, %o4, %o3		! %o3 = shifted to bit offset
733	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
734	and	%o1, %o3, %o1		! %o1 = single short value
735	sll	%o2, %g1, %o2		! %o2 = shifted to bit offset
736	and	%o2, %o3, %o2		! %o2 = single short value
737	andn	%o0, 0x2, %o0		! %o0 = word address
738	! if low-order bit is 1, we will properly get an alignment fault here
739	ld	[%o0], %o4		! read old value
7401:
741	andn	%o4, %o3, %o4		! clear target bits
742	or	%o4, %o2, %o5		! insert the new value
743	or	%o4, %o1, %o4		! insert the comparison value
744	cas	[%o0], %o4, %o5
745	cmp	%o4, %o5		! did we succeed?
746	be,pt	%icc, 2f
747	  and	%o5, %o3, %o4		! isolate the old value
748	cmp	%o1, %o4		! should we have succeeded?
749	be,a,pt	%icc, 1b		! yes, try again
750	  mov	%o5, %o4		! %o4 = old value
7512:
752	retl
753	srl	%o4, %g1, %o0		! %o0 = old value
754	SET_SIZE(atomic_cas_ushort)
755	SET_SIZE(atomic_cas_16)
756
757	ENTRY(atomic_cas_32)
758	ALTENTRY(atomic_cas_uint)
759	cas	[%o0], %o1, %o2
760	retl
761	mov	%o2, %o0
762	SET_SIZE(atomic_cas_uint)
763	SET_SIZE(atomic_cas_32)
764
765	ENTRY(atomic_cas_64)
766	ALTENTRY(atomic_cas_ptr)
767	ALTENTRY(atomic_cas_ulong)
768	casx	[%o0], %o1, %o2
769	retl
770	mov	%o2, %o0
771	SET_SIZE(atomic_cas_ulong)
772	SET_SIZE(atomic_cas_ptr)
773	SET_SIZE(atomic_cas_64)
774
775	ENTRY(atomic_swap_8)
776	ALTENTRY(atomic_swap_uchar)
777	and	%o0, 0x3, %o4		! %o4 = byte offset, left-to-right
778	xor	%o4, 0x3, %g1		! %g1 = byte offset, right-to-left
779	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
780	set	0xff, %o3		! %o3 = mask
781	sll	%o3, %g1, %o3		! %o3 = shifted to bit offset
782	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
783	and	%o1, %o3, %o1		! %o1 = single byte value
784	andn	%o0, 0x3, %o0		! %o0 = word address
785	ld	[%o0], %o2		! read old value
7861:
787	andn	%o2, %o3, %o5		! clear target bits
788	or	%o5, %o1, %o5		! insert the new value
789	cas	[%o0], %o2, %o5
790	cmp	%o2, %o5
791	bne,a,pn %icc, 1b
792	  mov	%o5, %o2		! %o2 = old value
793	and	%o5, %o3, %o5
794	retl
795	srl	%o5, %g1, %o0		! %o0 = old value
796	SET_SIZE(atomic_swap_uchar)
797	SET_SIZE(atomic_swap_8)
798
799	ENTRY(atomic_swap_16)
800	ALTENTRY(atomic_swap_ushort)
801	and	%o0, 0x2, %o4		! %o4 = byte offset, left-to-right
802	xor	%o4, 0x2, %g1		! %g1 = byte offset, right-to-left
803	sll	%o4, 3, %o4		! %o4 = bit offset, left-to-right
804	sll	%g1, 3, %g1		! %g1 = bit offset, right-to-left
805	sethi	%hi(0xffff0000), %o3	! %o3 = mask
806	srl	%o3, %o4, %o3		! %o3 = shifted to bit offset
807	sll	%o1, %g1, %o1		! %o1 = shifted to bit offset
808	and	%o1, %o3, %o1		! %o1 = single short value
809	andn	%o0, 0x2, %o0		! %o0 = word address
810	! if low-order bit is 1, we will properly get an alignment fault here
811	ld	[%o0], %o2		! read old value
8121:
813	andn	%o2, %o3, %o5		! clear target bits
814	or	%o5, %o1, %o5		! insert the new value
815	cas	[%o0], %o2, %o5
816	cmp	%o2, %o5
817	bne,a,pn %icc, 1b
818	  mov	%o5, %o2		! %o2 = old value
819	and	%o5, %o3, %o5
820	retl
821	srl	%o5, %g1, %o0		! %o0 = old value
822	SET_SIZE(atomic_swap_ushort)
823	SET_SIZE(atomic_swap_16)
824
825	ENTRY(atomic_swap_32)
826	ALTENTRY(atomic_swap_uint)
827	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
8280:
829	ld	[%o0], %o2
8301:
831	mov	%o1, %o3
832	cas	[%o0], %o2, %o3
833	cmp	%o2, %o3
834	ATOMIC_BACKOFF_BRANCH(%icc, 2f, 1b)
835	  mov	%o3, %o2
836	retl
837	mov	%o3, %o0
8382:
839	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, swap32, 0b)
840	SET_SIZE(atomic_swap_uint)
841	SET_SIZE(atomic_swap_32)
842
843	ENTRY(atomic_swap_64)
844	ALTENTRY(atomic_swap_ptr)
845	ALTENTRY(atomic_swap_ulong)
846	ATOMIC_BACKOFF_INIT(%o4, %g4, %g5)
8470:
848	ldx	[%o0], %o2
8491:
850	mov	%o1, %o3
851	casx	[%o0], %o2, %o3
852	cmp	%o2, %o3
853	ATOMIC_BACKOFF_BRANCH(%xcc, 2f, 1b)
854	  mov	%o3, %o2
855	retl
856	mov	%o3, %o0
8572:
858	ATOMIC_BACKOFF_BACKOFF(%o4, %o5, %g4, %g5, swap64, 0b)
859	SET_SIZE(atomic_swap_ulong)
860	SET_SIZE(atomic_swap_ptr)
861	SET_SIZE(atomic_swap_64)
862
863	ENTRY(atomic_set_long_excl)
864	ATOMIC_BACKOFF_INIT(%o5, %g4, %g5)
865	mov	1, %o3
866	slln	%o3, %o1, %o3
8670:
868	ldn	[%o0], %o2
8691:
870	andcc	%o2, %o3, %g0		! test if the bit is set
871	bnz,a,pn %ncc, 2f		! if so, then fail out
872	  mov	-1, %o0
873	or	%o2, %o3, %o4		! set the bit, and try to commit it
874	casn	[%o0], %o2, %o4
875	cmp	%o2, %o4
876	ATOMIC_BACKOFF_BRANCH(%ncc, 5f, 1b)
877	  mov	%o4, %o2
878	mov	%g0, %o0
8792:
880	retl
881	nop
8825:
883	ATOMIC_BACKOFF_BACKOFF(%o5, %g1, %g4, %g5, setlongexcl, 0b)
884	SET_SIZE(atomic_set_long_excl)
885
886	ENTRY(atomic_clear_long_excl)
887	ATOMIC_BACKOFF_INIT(%o5, %g4, %g5)
888	mov	1, %o3
889	slln	%o3, %o1, %o3
8900:
891	ldn	[%o0], %o2
8921:
893	andncc	%o3, %o2, %g0		! test if the bit is clear
894	bnz,a,pn %ncc, 2f		! if so, then fail out
895	  mov	-1, %o0
896	andn	%o2, %o3, %o4		! clear the bit, and try to commit it
897	casn	[%o0], %o2, %o4
898	cmp	%o2, %o4
899	ATOMIC_BACKOFF_BRANCH(%ncc, 5f, 1b)
900	  mov	%o4, %o2
901	mov	%g0, %o0
9022:
903	retl
904	nop
9055:
906	ATOMIC_BACKOFF_BACKOFF(%o5, %g1, %g4, %g5, clrlongexcl, 0b)
907	SET_SIZE(atomic_clear_long_excl)
908
909#if !defined(_KERNEL)
910
911	/*
912	 * Spitfires and Blackbirds have a problem with membars in the
913	 * delay slot (SF_ERRATA_51).  For safety's sake, we assume
914	 * that the whole world needs the workaround.
915	 */
916	ENTRY(membar_enter)
917	membar	#StoreLoad|#StoreStore
918	retl
919	nop
920	SET_SIZE(membar_enter)
921
922	ENTRY(membar_exit)
923	membar	#LoadStore|#StoreStore
924	retl
925	nop
926	SET_SIZE(membar_exit)
927
928	ENTRY(membar_producer)
929	membar	#StoreStore
930	retl
931	nop
932	SET_SIZE(membar_producer)
933
934	ENTRY(membar_consumer)
935	membar	#LoadLoad
936	retl
937	nop
938	SET_SIZE(membar_consumer)
939
940#endif	/* !_KERNEL */
941