#ifndef ATOMIC_OPS_H #define ATOMIC_OPS_H /* Atomic ops inspired by Linux kernel smp.c, semaphore.c FILES THAT #include THIS FILE MUST BE COMPILED WITH gcc; SUN cc WON'T WORK. For documentation on the gcc __asm__, see the info files distributed with emacs, node "(gcc) Extended Asm". */ /* Someone who ought to know tells me that almost all Sun systems ever built implement the total store order (TSO) memory model, and that those that don't have to be run in TSO mode in order for important software (like Solaris) to run correctly. This means in practice that the only time a memory barrier is needed is between a store and a subsequent load which must be ordered wrt one another. Note that all atomic instructions perform an implicit barrier. */ static __inline__ void membar() { __asm__ __volatile__("membar #StoreLoad"); } static __inline__ unsigned long swap(volatile unsigned long *ptr, unsigned long val) { __asm__ __volatile__(" \ swap [%2], %0 \ " : "=&r"(val) : "0"(val), "r"(ptr) : "memory"); return val; } static __inline__ unsigned long cas(volatile unsigned long *ptr, unsigned long old, unsigned long new) { __asm__ __volatile__(" \ cas [%2], %3, %0 \ " : "=&r"(new) : "0"(new), "r"(ptr), "r"(old) : "memory"); return new; } /* Sadly, gcc doesn't seem to understand Sparc extended (64-bit) registers. I have to gather bits into registers and split them out again. */ static __inline__ unsigned long /* bool */ casx(volatile unsigned long long *ptr, unsigned long expected_high, unsigned long expected_low, unsigned long new_high, unsigned long new_low) { unsigned long /* bool*/ success; __asm__ __volatile__(" \ sllx %1, 32, %%o4; \ or %%o4, %2, %%o4; \ sllx %3, 32, %%o5; \ or %%o5, %4, %%o5; \ casx [%5], %%o4, %%o5; \ cmp %%o4, %%o5; \ be,pt %%xcc,1f; \ mov 1, %0; \ mov %%g0, %0; \ 1: \ " : "=r"(success) : "r"(expected_high), "r"(expected_low), "r"(new_high), "r"(new_low), "r"(ptr) : "o4", "o5", "memory"); return success; } /* Swap is a deprecated instruction in the Sparc V9 instruction set, and as such has no 64-bit version. The only way to get the effect of a 64-bit swap is with CASX in a loop. */ static __inline__ void swapx(volatile unsigned long long *ptr, unsigned long new_high, unsigned long new_low, unsigned long long *old) { __asm__ __volatile__(" \ ldx [%3], %%o4; \ sllx %0, 32, %%o3; \ 1: or %%o3, %1, %%o5; \ casx [%3], %%o4, %%o5; \ cmp %%o4, %%o5; \ bne,pn %%xcc,1b; \ mov %%o5, %%o4; \ stx %%o4, [%2] \ " :: "r"(new_high), "r"(new_low), "r"(old), "r"(ptr) : "o3", "o4", "o5", "memory"); } static __inline__ void mvx(volatile unsigned long long *from, volatile unsigned long long *to) { __asm__ __volatile__(" \ ldx [%0], %%o4; \ stx %%o4, [%1] \ " :: "r"(from), "r"(to) : "o4", "memory"); } static __inline__ unsigned long tas(volatile unsigned long *ptr) { unsigned long result; __asm__ __volatile__(" \ ldstub [%1], %0 \ " : "=r"(result) : "r"(ptr) : "memory"); return result; } static __inline__ unsigned long fai(volatile unsigned long *ptr) { unsigned long found = *ptr; unsigned long expected; do { expected = found; } while ((found = cas(ptr, expected, expected+1)) != expected); return found; } #endif ATOMIC_OPS_H