GCC Inline Assembly for Sparc architecture

2019-03-03 01:02发布

问题:

I've found in internet the implementation of __sync_val_compare_and_swap:

#define LOCK_PREFIX "lock ; "

struct __xchg_dummy { unsigned long a[100]; };
#define __xg(x) ((struct __xchg_dummy *)(x))

static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
                  unsigned long new, int size)
{
   unsigned long prev;
   switch (size) {
   case 1:
      __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
                 : "=a"(prev)
                 : "q"(new), "m"(*__xg(ptr)), "0"(old)
                 : "memory");
      return prev;
   case 2:
      __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
                 : "=a"(prev)
                 : "q"(new), "m"(*__xg(ptr)), "0"(old)
                 : "memory");
      return prev;
   case 4:
      __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
                 : "=a"(prev)
                 : "q"(new), "m"(*__xg(ptr)), "0"(old)
                 : "memory");
      return prev;
   }
   return old;
}

#define cmpxchg(ptr,o,n)\
   ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
               (unsigned long)(n),sizeof(*(ptr))))

When I compile and use this function (cmpxchg) for i386 architecture - all good! But, when i compile under Sparc architecture, i've the following error:

error: impossible constraint in `asm'

What's the problem?

回答1:

On Solaris, better don't write your own code for this (neither on SPARC nor on x86); rather, use the atomic_cas(3C) functions for the purpose:

static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
              unsigned long new, int size)
{
    switch (size) {
    case 1: return atomic_cas_8(ptr, (unsigned char)old, (unsigned char)new);
    case 2: return atomic_cas_16(ptr, (unsigned short)old, (unsigned short)new);
    case 4: return atomic_cas_32(ptr, (unsigned int)old, (unsigned int)new);
#ifdef _LP64
    case 8: return atomic_cas_64(ptr, old, new);
#endif
    default: break;    
    }
    return old;
}

That'll do for Solaris.

Edit: if you absolutely have to inline this kind of thing, the SPARC (v8+, aka UltraSPARC) instruction to use is "compare and swap", aka CAS. It's always atomic (sparc doesn't know lock prefixes). It only comes in 32bit and 64bit (CASX) variants, so that the 8/16bit library functions perform 32bit CAS masking out the non-targeted word/bytes. I won't help with reimplementing that - it's not a good idea, use the library interfaces.

Edit2: Some help with reimplementing you get by reading the sourcecode (if you cannot link with Solaris libc).



回答2:

cmpxchgb is a i386 instruction, it won't work under Sparc.



回答3:

You can't compile an x86 asm for the sparc. Here's what I get using clang:

[~] main% ~/ellcc/bin/sparc-linux-ecc asm.c
asm.c:13:20: error: invalid output constraint '=a' in asm
             : "=a"(prev)

'a' is not a sparc register, it is specific to the x86.

Even it you were to fix the constraint, you'd get an assembly time error when the sparc assembler sees the cmpxchgb opcode, which is x86 specific.