Fix sparc asm code for gcc prior to 3.2
[platform/upstream/gstreamer.git] / gst / gstatomic_impl.h
1 /* GStreamer
2  * Copyright (C) 1999, 2003 Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  *
20  * Much of the code in this file is taken from the Linux kernel. 
21  * The code is relicensed under the LGPL with the kind permission of
22  * Linus Torvalds,Ralf Baechle and Alan Cox
23  */
24
25 #ifndef __GST_ATOMIC_IMPL_H__
26 #define __GST_ATOMIC_IMPL_H__
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <glib.h>
33 #include "gstatomic.h"
34 #include "gstmacros.h"
35
36 G_BEGIN_DECLS
37
38 #if defined (GST_CAN_INLINE) || defined (__GST_ATOMIC_C__)
39   
40 /***** Intel x86 *****/
41 #if defined (HAVE_CPU_I386) && defined(__GNUC__)
42
43 #ifdef GST_CONFIG_NO_SMP
44 #define SMP_LOCK ""
45 #else
46 #define SMP_LOCK "lock ; "
47 #endif
48
49 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
50 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
51 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
52 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
53
54 GST_INLINE_FUNC void 
55 gst_atomic_int_add (GstAtomicInt *aint, gint val)
56 {
57   __asm__ __volatile__(
58     SMP_LOCK "addl %1,%0"
59       :"=m" (aint->counter)
60       :"ir" (val), "m" (aint->counter));
61 }
62
63 GST_INLINE_FUNC void
64 gst_atomic_int_inc (GstAtomicInt *aint)
65 {
66   __asm__ __volatile__(
67     SMP_LOCK "incl %0"
68       :"=m" (aint->counter)
69       :"m" (aint->counter));
70 }
71
72 GST_INLINE_FUNC gboolean
73 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
74 {
75   guchar res;
76
77   __asm__ __volatile__(
78     SMP_LOCK "decl %0; sete %1"
79       :"=m" (aint->counter), "=qm" (res)
80       :"m" (aint->counter) : "memory");
81
82   return res != 0;
83 }
84
85 /***** PowerPC *****/
86 #elif defined (HAVE_CPU_PPC) && defined(__GNUC__)
87
88 #ifdef GST_CONFIG_NO_SMP
89 #define SMP_SYNC        ""
90 #define SMP_ISYNC
91 #else
92 #define SMP_SYNC        "sync"
93 #define SMP_ISYNC       "\n\tisync"
94 #endif
95
96 /* Erratum #77 on the 405 means we need a sync or dcbt before every stwcx.
97  * The old ATOMIC_SYNC_FIX covered some but not all of this.
98  */
99 #ifdef GST_CONFIG_IBM405_ERR77
100 #define PPC405_ERR77(ra,rb)     "dcbt " #ra "," #rb ";"
101 #else
102 #define PPC405_ERR77(ra,rb)
103 #endif
104
105 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
106 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
107 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
108 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
109
110 GST_INLINE_FUNC void 
111 gst_atomic_int_add (GstAtomicInt *aint, gint val)
112 {
113   int t;
114
115   __asm__ __volatile__(
116     "1:     lwarx   %0,0,%3         # atomic_add\n\
117             add     %0,%2,%0\n"
118             PPC405_ERR77(0,%3)
119     "       stwcx.  %0,0,%3 \n\
120             bne-    1b"
121       : "=&r" (t), "=m" (aint->counter)
122       : "r" (val), "r" (&aint->counter), "m" (aint->counter)
123       : "cc");
124 }
125
126 GST_INLINE_FUNC void
127 gst_atomic_int_inc (GstAtomicInt *aint)
128 {
129   int t;
130
131   __asm__ __volatile__(
132     "1:     lwarx   %0,0,%2         # atomic_inc\n\
133             addic   %0,%0,1\n"
134             PPC405_ERR77(0,%2)
135     "       stwcx.  %0,0,%2 \n\
136             bne-    1b"
137       : "=&r" (t), "=m" (aint->counter)
138       : "r" (&aint->counter), "m" (aint->counter)
139       : "cc");
140 }
141
142 GST_INLINE_FUNC gboolean
143 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
144 {
145   int t;
146
147   __asm__ __volatile__(
148     "1:     lwarx   %0,0,%1         # atomic_dec_return\n\
149             addic   %0,%0,-1\n"
150             PPC405_ERR77(0,%1)
151     "       stwcx.  %0,0,%1\n\
152             bne-    1b"
153             SMP_ISYNC
154       : "=&r" (t)
155       : "r" (&aint->counter)
156       : "cc", "memory");
157
158   return t == 0;
159 }
160
161 /***** DEC[/Compaq/HP?/Intel?] Alpha *****/
162 #elif defined(HAVE_CPU_ALPHA) && defined(__GNUC__)
163
164 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
165 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
166 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
167 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
168
169 GST_INLINE_FUNC void 
170 gst_atomic_int_add (GstAtomicInt *aint, gint val)
171 {
172   unsigned long temp;
173
174   __asm__ __volatile__(
175     "1:     ldl_l %0,%1\n"
176     "       addl %0,%2,%0\n"
177     "       stl_c %0,%1\n"
178     "       beq %0,2f\n"
179     ".subsection 2\n"
180     "2:     br 1b\n"
181     ".previous"
182       :"=&r" (temp), "=m" (aint->counter)
183       :"Ir" (val), "m" (aint->counter));
184 }
185
186 GST_INLINE_FUNC void
187 gst_atomic_int_inc (GstAtomicInt *aint)
188 {
189   gst_atomic_int_add (aint, 1);
190 }
191
192 GST_INLINE_FUNC gboolean
193 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
194 {
195   long temp, result;
196   int val = 1;
197   __asm__ __volatile__(
198     "1:     ldl_l %0,%1\n"
199     "       subl %0,%3,%2\n"
200     "       subl %0,%3,%0\n"
201     "       stl_c %0,%1\n"
202     "       beq %0,2f\n"
203     "       mb\n"
204     ".subsection 2\n"
205     "2:     br 1b\n"
206     ".previous"
207       :"=&r" (temp), "=m" (aint->counter), "=&r" (result)
208       :"Ir" (val), "m" (aint->counter) : "memory");
209
210   return result == 0;
211 }
212
213 /***** Sun SPARC *****/
214 #elif defined(HAVE_CPU_SPARC) && defined(__GNUC__)
215
216 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
217
218 #ifdef GST_CONFIG_NO_SMP
219 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
220 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
221 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
222 #else
223 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
224 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
225
226 /*
227  * For SMP the trick is you embed the spin lock byte within
228  * the word, use the low byte so signedness is easily retained
229  * via a quick arithmetic shift.  It looks like this:
230  *
231  *      ----------------------------------------
232  *      | signed 24-bit counter value |  lock  |  atomic_t
233  *      ----------------------------------------
234  *       31                          8 7      0
235  */
236 GST_INLINE_FUNC gint
237 gst_atomic_int_read (GstAtomicInt *aint) 
238
239   int ret = aint->counter;
240
241   while (ret & 0xff)
242     ret = aint->counter;
243
244   return ret >> 8;
245 }
246 #endif /* GST_CONFIG_NO_SMP */
247
248 GST_INLINE_FUNC void 
249 gst_atomic_int_add (GstAtomicInt *aint, gint val)
250 {
251   volatile int increment, *ptr;
252   char lock;
253
254   ptr = &(aint->counter);
255
256 #if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
257  __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
258                       "\torcc %[lock], 0, %%g0\n"
259                       "\tbne 1b\n" /* go back until we have the lock */
260                       "\tld [%[ptr]], %[inc]\n"
261                       "\tsra %[inc], 8, %[inc]\n"
262                       "\tadd %[inc], %[val], %[inc]\n"
263                       "\tsll %[inc], 8, %[lock]\n"
264                       "\tst %[lock],[%[ptr]]\n" /* Release the lock */
265                       : [inc] "=&r" (increment), [lock] "=r" (lock)
266                       : "0" (increment), [ptr] "r" (ptr), [val] "r" (val)
267                       );
268 #else
269  __asm__ __volatile__("1: ldstub [%3 + 3], %1\n"
270                       "\torcc %1, 0, %%g0\n"
271                       "\tbne 1b\n" /* go back until we have the lock */
272                       "\tld [%3], %0\n"
273                       "\tsra %0, 8, %0\n"
274                       "\tadd %0, %4, %0\n"
275                       "\tsll %0, 8, %1\n"
276                       "\tst %1,[%3]\n" /* Release the lock */
277                       : "=&r" (increment), "=r" (lock)
278                       : "0" (increment), "r" (ptr), "r" (val)
279                       );
280 #endif
281 }
282
283 GST_INLINE_FUNC void
284 gst_atomic_int_inc (GstAtomicInt *aint)
285 {
286   gst_atomic_int_add (aint, 1);
287 }
288
289 GST_INLINE_FUNC gboolean
290 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
291 {
292   volatile int increment, *ptr;
293   char lock;
294
295   ptr = &aint->counter;
296
297   __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
298                        "\torcc %[lock], 0, %%g0\n"
299                        "\tbne 1b\n" /* go back until we have the lock */
300                        "\tld [%[ptr]], %[inc]\n"
301                        "\tsra %[inc], 8, %[inc]\n"
302                        "\tsub %[inc], 1, %[inc]\n"
303                        "\tsll %[inc], 8, %[lock]\n"
304                        "\tst %[lock],[%[ptr]]\n" /* Release the lock */
305                        : [inc] "=&r" (increment), [lock] "=r" (lock)
306                        : "0" (increment), [ptr] "r" (ptr)
307                        );
308
309   return increment == 0;
310 }
311
312 /***** MIPS *****/
313 /* This is disabled because the asm code is broken on most MIPS
314  * processors and doesn't generally compile. */
315 #elif defined(HAVE_CPU_MIPS) && defined(__GNUC__) && 0
316
317 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
318 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
319 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
320 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
321
322 /* this only works on MIPS II and better */
323 GST_INLINE_FUNC void 
324 gst_atomic_int_add (GstAtomicInt *aint, gint val)
325 {
326   unsigned long temp;
327
328   __asm__ __volatile__(
329     "1:   ll      %0, %1      # atomic_add\n"
330     "     addu    %0, %2                  \n"
331     "     sc      %0, %1                  \n"
332     "     beqz    %0, 1b                  \n"
333       : "=&r" (temp), "=m" (aint->counter)
334       : "Ir" (val), "m" (aint->counter));
335 }
336
337 GST_INLINE_FUNC void
338 gst_atomic_int_inc (GstAtomicInt *aint)
339 {
340   gst_atomic_int_add (aint, 1);
341 }
342
343 GST_INLINE_FUNC gboolean
344 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
345 {
346   unsigned long temp, result;
347   int val = 1;
348
349   __asm__ __volatile__(
350     ".set push                                   \n"
351     ".set noreorder           # atomic_sub_return\n"
352     "1:   ll    %1, %2                           \n"
353     "     subu  %0, %1, %3                       \n"
354     "     sc    %0, %2                           \n"
355     "     beqz  %0, 1b                           \n"
356     "     subu  %0, %1, %3                       \n"
357     ".set pop                                    \n"
358       : "=&r" (result), "=&r" (temp), "=m" (aint->counter)
359       : "Ir" (val), "m" (aint->counter)
360       : "memory");
361
362   return result == 0;
363 }
364
365 /***** S/390 *****/
366 #elif defined(HAVE_CPU_S390) && defined(__GNUC__)
367
368 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
369 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
370 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
371 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
372
373 #define __CS_LOOP(old_val, new_val, ptr, op_val, op_string)             \
374         __asm__ __volatile__("   l     %0,0(%3)\n"                      \
375                              "0: lr    %1,%0\n"                         \
376                              op_string "  %1,%4\n"                      \
377                              "   cs    %0,%1,0(%3)\n"                   \
378                              "   jl    0b"                              \
379                              : "=&d" (old_val), "=&d" (new_val),        \
380                                "+m" (((atomic_t *)(ptr))->counter)      \
381                              : "a" (ptr), "d" (op_val) : "cc" );
382
383 GST_INLINE_FUNC void 
384 gst_atomic_int_add (GstAtomicInt *aint, gint val)
385 {
386   int old_val, new_val;
387   __CS_LOOP(old_val, new_val, aint, val, "ar");
388 }
389
390 GST_INLINE_FUNC void
391 gst_atomic_int_inc (GstAtomicInt *aint)
392 {
393   int old_val, new_val;
394   __CS_LOOP(old_val, new_val, aint, 1, "ar");
395 }
396
397 GST_INLINE_FUNC gboolean
398 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
399 {
400   int old_val, new_val;
401   __CS_LOOP(old_val, new_val, aint, 1, "sr");
402   return new_val == 0;
403 }
404
405 #else 
406 #warning consider putting your architecture specific atomic implementations here
407
408 /*
409  * generic implementation
410  */
411 GST_INLINE_FUNC void
412 gst_atomic_int_init (GstAtomicInt *aint, gint val)
413 {
414   aint->counter = val;
415   aint->lock = g_mutex_new ();
416 }
417
418 GST_INLINE_FUNC void
419 gst_atomic_int_destroy (GstAtomicInt *aint)
420 {
421   g_mutex_free (aint->lock);
422 }
423
424 GST_INLINE_FUNC void
425 gst_atomic_int_set (GstAtomicInt *aint, gint val)
426 {
427   g_mutex_lock (aint->lock);
428   aint->counter = val;
429   g_mutex_unlock (aint->lock);
430 }
431
432 GST_INLINE_FUNC gint
433 gst_atomic_int_read (GstAtomicInt *aint)
434 {
435   gint res;
436
437   g_mutex_lock (aint->lock);
438   res = aint->counter;
439   g_mutex_unlock (aint->lock);
440
441   return res;
442 }
443
444 GST_INLINE_FUNC void 
445 gst_atomic_int_add (GstAtomicInt *aint, gint val)
446 {
447   g_mutex_lock (aint->lock);
448   aint->counter += val;
449   g_mutex_unlock (aint->lock);
450 }
451
452 GST_INLINE_FUNC void
453 gst_atomic_int_inc (GstAtomicInt *aint)
454 {
455   g_mutex_lock (aint->lock);
456   aint->counter++;
457   g_mutex_unlock (aint->lock);
458 }
459
460 GST_INLINE_FUNC gboolean
461 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
462 {
463   gboolean res;
464   
465   g_mutex_lock (aint->lock);
466   aint->counter--;
467   res = (aint->counter == 0);
468   g_mutex_unlock (aint->lock);
469
470   return res;
471 }
472
473 #endif 
474 /*
475  * common functions
476  */ 
477 GST_INLINE_FUNC GstAtomicInt*
478 gst_atomic_int_new (gint val)
479 {
480   GstAtomicInt *aint;
481
482   aint = g_new0 (GstAtomicInt, 1);
483   gst_atomic_int_init (aint, val);
484
485   return aint;
486 }
487
488 GST_INLINE_FUNC void
489 gst_atomic_int_free (GstAtomicInt *aint)
490 {
491   gst_atomic_int_destroy (aint);
492   g_free (aint);
493 }
494
495 #endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
496
497 G_END_DECLS
498
499 #endif /*  __GST_ATOMIC_IMPL_H__ */