View previous topic :: View next topic |
Author |
Message |
goanuj Tux's lil' helper
Joined: 13 Jul 2002 Posts: 125 Location: California
|
Posted: Thu Oct 16, 2003 7:27 am Post subject: [sparc v9] need atomic operations in assemly help |
|
|
Does anyone have any sample assembly for Sparc V9 atomic operations such as atomic increment, atomic decrement? I understand that these can be different for each memory model (RMO,PSO,TSO), I understand how to implement certain assembly instructions, however, I need to see some examples where people use atomic add and have actually tested it on a SMP machine. Anyone?
I need to make sure that our team is implementing our atomic operations properly. I have looked at linux kernel code, but the kernel's usage is quite different from what I want to implement.
For atomic increments, I would probably do something like this:
Code: | !
! atomic add in assembly
!
MEMBAR #StoreLoad |
my exported "C" function would look like this
Code: | void atomicincrement(long *addr)
{
++(*addr);
# force other processors to see it
} |
Basically I just want to implement this in SparcV9 assembly. Has anyone done anything like this before? |
|
Back to top |
|
|
Ferris Retired Dev
Joined: 13 Jan 2003 Posts: 426 Location: N. Virginia (USA)
|
Posted: Thu Oct 16, 2003 3:29 pm Post subject: |
|
|
OK, here goes. Here is some assembly-in-C code which works on sparc-v9
SMP. (It is a direct steal from David Miller's kernel code found at
/usr/src/linux/arch/sparc64/lib/atomic.S, or wherever you keep your
kernel sources.)
This is a sample header file (and little demonstration) to include in anything
requiring the atomic operations.
Code: |
/* ==Id: atomic.S,v 1.4 2001/11/18 00:12:56 davem Exp ==
* atomic.S: These things are too big to do inline.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
*/
/* Recreated as
* $Id: Atomic.h,v 1.9 2003/10/16 15:15:56 fmccor Exp $
* for general use or as an example.
*
* Build with
* gcc -mcpu=v9 -fomit-frame-pointer -O2 [...]
*
* (-and-, #define __DEFINE_ATOMIC_HERE__
* in the module where you actually want them defined.)
* ((go down a few lines for that check for the actual code for these))
*
*
*/
#ifndef _Atomic_h_
#define _Atomic_h_
typedef struct {volatile int counter;} atomic_t;
#define atomic_set(v,i) (((v)->counter) = i)
#define atomic_read(v) ((v)->counter)
/*
Do everything in our power to keep from inlining!!!
*/
extern int atomic_add(int, atomic_t*) __attribute__ ((noinline)) ;
extern int atomic_sub(int, atomic_t*) __attribute__ ((noinline)) ;
extern int atomic_exchange_and_add(atomic_t*, int) __attribute__ ((noinline)) ;
/* Narrative:
* This is the linux kernel code for atomic add, atomic subtract on sparc-v9.
* They actually return the result of their operation.
*
* The exchange_and_add is like add, except that it returns the original
* value of the counter (instead of the new one), and for some reason, ** THE
* ORDER OF ITS ARGUMENTS IS REVERSED! **
*
* do {
* A. g5 <- value;
* B. g7 <- g5 [+/-] delta;
* C. if (value == g5) swap(g7, value);
* D. } while (g5 != g7); // g5 was original value, g7 is swapped from original value
* // if they are not the same, someone changed the memory
* // copy before the swap, so we start over with a new value
* E. Synchronize_memory_and_data_cache;
* F. return [value+delta | value - delta | value] depending on add/sub/exchange_add.
*
* Notice that step -C.- is an indivisible operation, so everything is good coming
* out. The entire operation can be retried of between -A.- and start of -C.- someone
* else changes the counter variable. The point is that you get a -coherent- result
* (which is to say, you don't operate on a stale counter value, and so replace it
* with something wrong for everyone). You might not be operating on the values
* at call. To do that, put global Mutex around the call itself.
*
* WARNINGS:
* Do NOT put these in a header file for inlining. You will get bus errors,
* or the results will be wrong if you don't (get the bus errors).
* That's why we force the __attribute__ ((noinline)) in this header!
*
* Do NOT use these on Sparc 2, 5, 10, 20 etc. The instructions are
* sparc-version 9.
*
* These are written for sparc(v9)-linux-gcc. I have no idea what they will do
* with Solaris or with other compilers. (Logic will work, because that's what
* the instructions do. I cannot speak to other compilers, though.)
*
*
*
* --
* Ferris McCormick <fmccor@inforead.com>
* 06.iii.03
*/
#ifdef __DEFINE_ATOMIC_HERE__
int
atomic_add(int i, atomic_t* v) {
__asm__ __volatile__ (
"1: lduw [%o1], %g5\n"
" add %g5, %o0, %g7\n"
" cas [%o1], %g5, %g7\n"
" cmp %g5, %g7\n"
" bne,pn %icc, 1b\n"
" membar #StoreLoad | #StoreStore\n"
" retl\n"
" add %g7, %o0, %o0\n"
);
return; /* Not Reached */
}
int
atomic_sub(int i, atomic_t *v) {
__asm__ __volatile__ (
"1: lduw [%o1], %g5\n"
" sub %g5, %o0, %g7\n"
" cas [%o1], %g5, %g7\n"
" cmp %g5, %g7\n"
" bne,pn %icc, 1b\n"
" membar #StoreLoad | #StoreStore\n"
" retl\n"
" sub %g7, %o0, %o0\n"
);
return; /* Not Reached */
}
int
atomic_exchange_and_add(atomic_t *v, int i) {
__asm__ __volatile__ (
"1: lduw [%o0], %g5\n"
" add %g5, %o1, %g7\n"
" cas [%o0], %g5, %g7\n"
" cmp %g5, %g7\n"
" bne,pn %icc, 1b\n"
" membar #StoreLoad | #StoreStore\n"
" retl\n"
" mov %g7, %o0\n"
);
return; /* Not Reached */
}
#endif /* __DEFINE_ATOMIC_HERE__ */
#endif /* _Atomic_h_ */
|
And, here is a little program to show how it is used.
Code: |
#define __DEFINE_ATOMIC_HERE__
#include "Atomic.h"
#include <stdio.h>
int
main(int argc, char * argv[]) {
atomic_t* a_t;
int a_t_before, a_t_after;
a_t = (atomic_t*) malloc(sizeof(atomic_t));
if(a_t == NULL) {
fprintf(stderr,"Can't allocate %d bytes for atomic_t\n", sizeof(atomic_t));
return (-1);
}
atomic_set(a_t, 43);
a_t_before = atomic_exchange_and_add(a_t, (-4));
a_t_after = atomic_read(a_t);
printf("Set a_t=43, exchange-add(-4) returns %d, and atomic_item now is %d\n",
a_t_before, a_t_after);
return 0;
}
|
Hope this gives you an idea. |
|
Back to top |
|
|
goanuj Tux's lil' helper
Joined: 13 Jul 2002 Posts: 125 Location: California
|
Posted: Thu Oct 16, 2003 11:28 pm Post subject: thanks! |
|
|
Thanks for you help,
I was confused because I was looking at the wrong file:
arch/sparc/lib/atomic.S
Can you clue me onto the difference between sparc and sparc64, is 'sparc64' just the 64 bit extension of sparc? or are they fundamentally different architectures?
Also, I am assuming most current machines are sparc64 right?
And do you have familiarity with the Solaris compiler?
CC - Sun WorkShop C++ Compiler 5.0
That is what I have to use to compile my assembly! I am not sure how to compile assembly on Solaris, I have to read up on fbe |
|
Back to top |
|
|
Ferris Retired Dev
Joined: 13 Jan 2003 Posts: 426 Location: N. Virginia (USA)
|
Posted: Fri Oct 17, 2003 12:13 pm Post subject: |
|
|
For our purposes, I think we can use sparc64, UltraSparc, and -v9
interchangeably. It is a proper superset of -v8:
- The very short answer is that sparc-v9 (sparc64) is an extension of sparc-v8. In
your example, -v9 has the compare-and-swap atomic instruction (cas) which
does exactly what you want. -v8 (SS20, SS10, etc) does not. Hence,
the two different kernel implementations.
Anything built for -v8 should run on -v9, but the converse is not the case. (Case in point: by accident I
forced a -v9 program to try to run on a SS20, and it took several reboots
to get the hardware to recover completely. Among other things, it killed
the lance ethernet card.)
The compiler's -mcpu=... flag controls which architecture you are
building for, and to get at the atomic instructions, you must specify
"-mcpu=v9" or (equivalently) "-mcpu=ultrasparc".
- The longer answer includes the observation that when the kernel is talking
about sparc64, it is refering to "kernel for the -v9" architecture because
it needs 64-bit addressing (Note to kernel people: Correct my description
here) even though linux for sparc does not currently support 64-bit
addresses in user space. (But see the thread on this topic in this forum.)
- Even longer answer. The SPARC Architecture Manual, Version 9 is
available from Prentice Hall, or on-line for free download (pdf) at
<http://www.sparc.com/standards/V9-R1.4.7.pdf>, and it explains
the differences between -v8 and -v9. While you are at it, Sun's UltraSPARC
User's Manual is available on-line (only); it was part number 802-7220-02,
but seems to have been superceded. See below for current.
Also, the Assembly manual is available on-line from Sun: 806-3774.
Try <http://www.sun.com/processors/manuals/805-0087.pdf> (which you
get to from <http://www.sun.com/processors/documentation.html>) for
current Sun UltraSparc-ii information,
and <http://docs.sun.com/db/doc/806-3774?a=load> to download an
assembly manual. (The assembly manuals change with Solaris versions,
and are useful mostly as a reference to go along with the architecture
manual from sparc.com.)
As to the Solaris compiler: I have never seen it. We do have a couple
Sparc systems running Solaris (U10, Solaris 8; SS20, Solaris 2.5.1),
but we use gcc on both of them. Last I knew, the native compiler(s)
for Solaris were too expensive for us to consider.
Hope this helps.
Regards, |
|
Back to top |
|
|
Ferris Retired Dev
Joined: 13 Jan 2003 Posts: 426 Location: N. Virginia (USA)
|
Posted: Fri Oct 17, 2003 2:29 pm Post subject: |
|
|
And, for completeness, let me add that on the <http://www.sparc.com>
Resources page, you will find Implementation Characteristics of Current SPARC-V9 -based
Products. This booklet collects the specific characteristics of both the
Sun and the not-Sun Sparc-v9 implementations in one convenient spot. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|