stdatomic.c revision 251539
1/*-
2 * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Copyright (c) 1998 Doug Rabson
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/mips/mips/stdatomic.c 251539 2013-06-08 16:24:49Z ed $");
32
33#include <sys/types.h>
34
35#ifdef _KERNEL
36#include "opt_global.h"
37#endif
38
39/*
40 * Memory barriers.
41 *
42 * It turns out __sync_synchronize() does not emit any code when used
43 * with GCC 4.2. Implement our own version that does work reliably.
44 *
45 * Although __sync_lock_test_and_set() should only perform an acquire
46 * barrier, make it do a full barrier like the other functions. This
47 * should make <stdatomic.h>'s atomic_exchange_explicit() work reliably.
48 */
49
50static inline void
51mips_sync(void)
52{
53
54	__asm volatile (
55#if !defined(_KERNEL) || defined(SMP)
56		".set noreorder\n"
57		"\tsync\n"
58		"\tnop\n"
59		"\tnop\n"
60		"\tnop\n"
61		"\tnop\n"
62		"\tnop\n"
63		"\tnop\n"
64		"\tnop\n"
65		"\tnop\n"
66		".set reorder\n"
67#else /* _KERNEL && !SMP */
68		""
69#endif /* !KERNEL || SMP */
70		: : : "memory");
71}
72
73typedef union {
74	uint8_t		v8[4];
75	uint32_t	v32;
76} reg_t;
77
78static inline uint32_t *
79round_to_word(void *ptr)
80{
81
82	return ((uint32_t *)((intptr_t)ptr & ~3));
83}
84
85/*
86 * 8-bit routines.
87 */
88
89static inline void
90put_1(reg_t *r, uint8_t *offset_ptr, uint8_t val)
91{
92	size_t offset;
93
94	offset = (intptr_t)offset_ptr & 3;
95	r->v8[offset] = val;
96}
97
98static inline uint8_t
99get_1(const reg_t *r, uint8_t *offset_ptr)
100{
101	size_t offset;
102
103	offset = (intptr_t)offset_ptr & 3;
104	return (r->v8[offset]);
105}
106
107uint8_t
108__sync_lock_test_and_set_1(uint8_t *mem8, uint8_t val8)
109{
110	uint32_t *mem32;
111	reg_t val32, negmask32, old;
112	uint32_t temp;
113
114	mem32 = round_to_word(mem8);
115	val32.v32 = 0x00000000;
116	put_1(&val32, mem8, val8);
117	negmask32.v32 = 0xffffffff;
118	put_1(&negmask32, mem8, val8);
119
120	mips_sync();
121	__asm volatile (
122		"1:"
123		"\tll	%0, %5\n"	/* Load old value. */
124		"\tand	%2, %4, %0\n"	/* Trim out affected part. */
125		"\tor	%2, %3\n"	/* Put in the new value. */
126		"\tsc	%2, %1\n"	/* Attempt to store. */
127		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
128		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp)
129		: "r" (val32.v32), "r" (negmask32.v32), "m" (*mem32));
130	return (get_1(&old, mem8));
131}
132
133uint8_t
134__sync_val_compare_and_swap_1(uint8_t *mem8, uint8_t expected, uint8_t desired)
135{
136	uint32_t *mem32;
137	reg_t expected32, desired32, posmask32, negmask32, old;
138	uint32_t temp;
139
140	mem32 = round_to_word(mem8);
141	expected32.v32 = 0x00000000;
142	put_1(&expected32, mem8, expected);
143	desired32.v32 = 0x00000000;
144	put_1(&desired32, mem8, desired);
145	posmask32.v32 = 0x00000000;
146	put_1(&posmask32, mem8, 0xff);
147	negmask32.v32 = ~posmask32.v32;
148
149	mips_sync();
150	__asm volatile (
151		"1:"
152		"\tll	%0, %7\n"	/* Load old value. */
153		"\tand	%2, %5, %0\n"	/* Isolate affected part. */
154		"\tbne	%2, %3, 2f\n"	/* Compare to expected value. */
155		"\tand	%2, %6, %0\n"	/* Trim out affected part. */
156		"\tor	%2, %4\n"	/* Put in the new value. */
157		"\tsc	%2, %1\n"	/* Attempt to store. */
158		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
159		"2:"
160		: "=&r" (old), "=m" (*mem32), "=&r" (temp)
161		: "r" (expected32.v32), "r" (desired32.v32),
162		  "r" (posmask32.v32), "r" (negmask32.v32), "m" (*mem32));
163	return (get_1(&old, mem8));
164}
165
166#define	EMIT_ARITHMETIC_FETCH_AND_OP_1(name, op)			\
167uint8_t									\
168__sync_##name##_1(uint8_t *mem8, uint8_t val8)				\
169{									\
170	uint32_t *mem32;						\
171	reg_t val32, posmask32, negmask32, old;				\
172	uint32_t temp1, temp2;						\
173									\
174	mem32 = round_to_word(mem8);					\
175	val32.v32 = 0x00000000;						\
176	put_1(&val32, mem8, val8);					\
177	posmask32.v32 = 0x00000000;					\
178	put_1(&posmask32, mem8, 0xff);					\
179	negmask32.v32 = ~posmask32.v32;					\
180									\
181	mips_sync();							\
182	__asm volatile (						\
183		"1:"							\
184		"\tll	%0, %7\n"	/* Load old value. */		\
185		"\t"op"	%2, %0, %4\n"	/* Calculate new value. */	\
186		"\tand	%2, %5\n"	/* Isolate affected part. */	\
187		"\tand	%3, %6, %0\n"	/* Trim out affected part. */	\
188		"\tor	%2, %3\n"	/* Put in the new value. */	\
189		"\tsc	%2, %1\n"	/* Attempt to store. */		\
190		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
191		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1),	\
192		  "=&r" (temp2)						\
193		: "r" (val32.v32), "r" (posmask32.v32),			\
194		  "r" (negmask32.v32), "m" (*mem32));			\
195	return (get_1(&old, mem8));					\
196}
197
198EMIT_ARITHMETIC_FETCH_AND_OP_1(fetch_and_add, "addu")
199EMIT_ARITHMETIC_FETCH_AND_OP_1(fetch_and_sub, "subu")
200
201#define	EMIT_BITWISE_FETCH_AND_OP_1(name, op, idempotence)		\
202uint8_t									\
203__sync_##name##_1(uint8_t *mem8, uint8_t val8)				\
204{									\
205	uint32_t *mem32;						\
206	reg_t val32, old;						\
207	uint32_t temp;							\
208									\
209	mem32 = round_to_word(mem8);					\
210	val32.v32 = idempotence ? 0xffffffff : 0x00000000;		\
211	put_1(&val32, mem8, val8);					\
212									\
213	mips_sync();							\
214	__asm volatile (						\
215		"1:"							\
216		"\tll	%0, %4\n"	/* Load old value. */		\
217		"\t"op"	%2, %3, %0\n"	/* Calculate new value. */	\
218		"\tsc	%2, %1\n"	/* Attempt to store. */		\
219		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
220		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp)		\
221		: "r" (val32.v32), "m" (*mem32));			\
222	return (get_1(&old, mem8));					\
223}
224
225EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_and, "and", 1)
226EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_or, "or", 0)
227EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_xor, "xor", 0)
228
229/*
230 * 16-bit routines.
231 */
232
233static inline void
234put_2(reg_t *r, uint16_t *offset_ptr, uint16_t val)
235{
236	size_t offset;
237	union {
238		uint16_t in;
239		uint8_t out[2];
240	} bytes;
241
242	offset = (intptr_t)offset_ptr & 3;
243	bytes.in = val;
244	r->v8[offset] = bytes.out[0];
245	r->v8[offset + 1] = bytes.out[1];
246}
247
248static inline uint16_t
249get_2(const reg_t *r, uint16_t *offset_ptr)
250{
251	size_t offset;
252	union {
253		uint8_t in[2];
254		uint16_t out;
255	} bytes;
256
257	offset = (intptr_t)offset_ptr & 3;
258	bytes.in[0] = r->v8[offset];
259	bytes.in[1] = r->v8[offset + 1];
260	return (bytes.out);
261}
262
263uint16_t
264__sync_lock_test_and_set_2(uint16_t *mem16, uint16_t val16)
265{
266	uint32_t *mem32;
267	reg_t val32, negmask32, old;
268	uint32_t temp;
269
270	mem32 = round_to_word(mem16);
271	val32.v32 = 0x00000000;
272	put_2(&val32, mem16, val16);
273	negmask32.v32 = 0xffffffff;
274	put_2(&negmask32, mem16, 0x0000);
275
276	mips_sync();
277	__asm volatile (
278		"1:"
279		"\tll	%0, %5\n"	/* Load old value. */
280		"\tand	%2, %4, %0\n"	/* Trim out affected part. */
281		"\tor	%2, %3\n"	/* Combine to new value. */
282		"\tsc	%2, %1\n"	/* Attempt to store. */
283		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
284		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp)
285		: "r" (val32.v32), "r" (negmask32.v32), "m" (*mem32));
286	return (get_2(&old, mem16));
287}
288
289uint16_t
290__sync_val_compare_and_swap_2(uint16_t *mem16, uint16_t expected,
291    uint16_t desired)
292{
293	uint32_t *mem32;
294	reg_t expected32, desired32, posmask32, negmask32, old;
295	uint32_t temp;
296
297	mem32 = round_to_word(mem16);
298	expected32.v32 = 0x00000000;
299	put_2(&expected32, mem16, expected);
300	desired32.v32 = 0x00000000;
301	put_2(&desired32, mem16, desired);
302	posmask32.v32 = 0x00000000;
303	put_2(&posmask32, mem16, 0xffff);
304	negmask32.v32 = ~posmask32.v32;
305
306	mips_sync();
307	__asm volatile (
308		"1:"
309		"\tll	%0, %7\n"	/* Load old value. */
310		"\tand	%2, %5, %0\n"	/* Isolate affected part. */
311		"\tbne	%2, %3, 2f\n"	/* Compare to expected value. */
312		"\tand	%2, %6, %0\n"	/* Trim out affected part. */
313		"\tor	%2, %4\n"	/* Put in the new value. */
314		"\tsc	%2, %1\n"	/* Attempt to store. */
315		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
316		"2:"
317		: "=&r" (old), "=m" (*mem32), "=&r" (temp)
318		: "r" (expected32.v32), "r" (desired32.v32),
319		  "r" (posmask32.v32), "r" (negmask32.v32), "m" (*mem32));
320	return (get_2(&old, mem16));
321}
322
323#define	EMIT_ARITHMETIC_FETCH_AND_OP_2(name, op)			\
324uint16_t								\
325__sync_##name##_2(uint16_t *mem16, uint16_t val16)			\
326{									\
327	uint32_t *mem32;						\
328	reg_t val32, posmask32, negmask32, old;				\
329	uint32_t temp1, temp2;						\
330									\
331	mem32 = round_to_word(mem16);					\
332	val32.v32 = 0x00000000;						\
333	put_2(&val32, mem16, val16);					\
334	posmask32.v32 = 0x00000000;					\
335	put_2(&posmask32, mem16, 0xffff);				\
336	negmask32.v32 = ~posmask32.v32;					\
337									\
338	mips_sync();							\
339	__asm volatile (						\
340		"1:"							\
341		"\tll	%0, %7\n"	/* Load old value. */		\
342		"\t"op"	%2, %0, %4\n"	/* Calculate new value. */	\
343		"\tand	%2, %5\n"	/* Isolate affected part. */	\
344		"\tand	%3, %6, %0\n"	/* Trim out affected part. */	\
345		"\tor	%2, %3\n"	/* Combine to new value. */	\
346		"\tsc	%2, %1\n"	/* Attempt to store. */		\
347		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
348		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1),	\
349		  "=&r" (temp2)						\
350		: "r" (val32.v32), "r" (posmask32.v32),			\
351		  "r" (negmask32.v32), "m" (*mem32));			\
352	return (get_2(&old, mem16));					\
353}
354
355EMIT_ARITHMETIC_FETCH_AND_OP_2(fetch_and_add, "addu")
356EMIT_ARITHMETIC_FETCH_AND_OP_2(fetch_and_sub, "subu")
357
358#define	EMIT_BITWISE_FETCH_AND_OP_2(name, op, idempotence)		\
359uint16_t								\
360__sync_##name##_2(uint16_t *mem16, uint16_t val16)			\
361{									\
362	uint32_t *mem32;						\
363	reg_t val32, old;						\
364	uint32_t temp;							\
365									\
366	mem32 = round_to_word(mem16);					\
367	val32.v32 = idempotence ? 0xffffffff : 0x00000000;		\
368	put_2(&val32, mem16, val16);					\
369									\
370	mips_sync();							\
371	__asm volatile (						\
372		"1:"							\
373		"\tll	%0, %4\n"	/* Load old value. */		\
374		"\t"op"	%2, %3, %0\n"	/* Calculate new value. */	\
375		"\tsc	%2, %1\n"	/* Attempt to store. */		\
376		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
377		: "=&r" (old.v32), "=m" (*mem32), "=&r" (temp)		\
378		: "r" (val32.v32), "m" (*mem32));			\
379	return (get_2(&old, mem16));					\
380}
381
382EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_and, "and", 1)
383EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_or, "or", 0)
384EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_xor, "xor", 0)
385
386/*
387 * 32-bit routines.
388 */
389
390uint32_t
391__sync_val_compare_and_swap_4(uint32_t *mem, uint32_t expected,
392    uint32_t desired)
393{
394	uint32_t old, temp;
395
396	mips_sync();
397	__asm volatile (
398		"1:"
399		"\tll	%0, %5\n"	/* Load old value. */
400		"\tbne	%0, %3, 2f\n"	/* Compare to expected value. */
401		"\tmove	%2, %4\n"	/* Value to store. */
402		"\tsc	%2, %1\n"	/* Attempt to store. */
403		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
404		"2:"
405		: "=&r" (old), "=m" (*mem), "=&r" (temp)
406		: "r" (expected), "r" (desired), "m" (*mem));
407	return (old);
408}
409
410#define	EMIT_FETCH_AND_OP_4(name, op)					\
411uint32_t								\
412__sync_##name##_4(uint32_t *mem, uint32_t val)				\
413{									\
414	uint32_t old, temp;						\
415									\
416	mips_sync();							\
417	__asm volatile (						\
418		"1:"							\
419		"\tll	%0, %4\n"	/* Load old value. */		\
420		"\t"op"\n"		/* Calculate new value. */	\
421		"\tsc	%2, %1\n"	/* Attempt to store. */		\
422		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
423		: "=&r" (old), "=m" (*mem), "=&r" (temp)		\
424		: "r" (val), "m" (*mem));				\
425	return (old);							\
426}
427
428EMIT_FETCH_AND_OP_4(lock_test_and_set, "move %2, %3")
429EMIT_FETCH_AND_OP_4(fetch_and_add, "addu %2, %0, %3")
430EMIT_FETCH_AND_OP_4(fetch_and_and, "and %2, %0, %3")
431EMIT_FETCH_AND_OP_4(fetch_and_or, "or %2, %0, %3")
432EMIT_FETCH_AND_OP_4(fetch_and_sub, "subu %2, %0, %3")
433EMIT_FETCH_AND_OP_4(fetch_and_xor, "xor %2, %0, %3")
434
435/*
436 * 64-bit routines.
437 *
438 * Note: All the 64-bit atomic operations are only atomic when running
439 * in 64-bit mode. It is assumed that code compiled for n32 and n64 fits
440 * into this definition and no further safeties are needed.
441 */
442
443#if defined(__mips_n32) || defined(__mips_n64)
444
445uint64_t
446__sync_val_compare_and_swap_8(uint64_t *mem, uint64_t expected,
447    uint64_t desired)
448{
449	uint64_t old, temp;
450
451	mips_sync();
452	__asm volatile (
453		"1:"
454		"\tlld	%0, %5\n"	/* Load old value. */
455		"\tbne	%0, %3, 2f\n"	/* Compare to expected value. */
456		"\tmove	%2, %4\n"	/* Value to store. */
457		"\tscd	%2, %1\n"	/* Attempt to store. */
458		"\tbeqz	%2, 1b\n"	/* Spin if failed. */
459		"2:"
460		: "=&r" (old), "=m" (*mem), "=&r" (temp)
461		: "r" (expected), "r" (desired), "m" (*mem));
462	return (old);
463}
464
465#define	EMIT_FETCH_AND_OP_8(name, op)					\
466uint64_t								\
467__sync_##name##_8(uint64_t *mem, uint64_t val)				\
468{									\
469	uint64_t old, temp;						\
470									\
471	mips_sync();							\
472	__asm volatile (						\
473		"1:"							\
474		"\tlld	%0, %4\n"	/* Load old value. */		\
475		"\t"op"\n"		/* Calculate new value. */	\
476		"\tscd	%2, %1\n"	/* Attempt to store. */		\
477		"\tbeqz	%2, 1b\n"	/* Spin if failed. */		\
478		: "=&r" (old), "=m" (*mem), "=&r" (temp)		\
479		: "r" (val), "m" (*mem));				\
480	return (old);							\
481}
482
483EMIT_FETCH_AND_OP_8(lock_test_and_set, "move %2, %3")
484EMIT_FETCH_AND_OP_8(fetch_and_add, "daddu %2, %0, %3")
485EMIT_FETCH_AND_OP_8(fetch_and_and, "and %2, %0, %3")
486EMIT_FETCH_AND_OP_8(fetch_and_or, "or %2, %0, %3")
487EMIT_FETCH_AND_OP_8(fetch_and_sub, "dsubu %2, %0, %3")
488EMIT_FETCH_AND_OP_8(fetch_and_xor, "xor %2, %0, %3")
489
490#endif /* __mips_n32 || __mips_n64 */
491