
/* This is a test program that checks the parsing of instructions with
   xacquire and xrelease prefixes.  The tested insns are, afaics,
   exactly those listed in the Intel description for the two prefixes
   ("XACQUIRE/XRELEASE -- Hardware Lock Elision Prefix Hints"). */

#include <stdio.h>

typedef  unsigned long long int  ULong;

#define CAT2(_x,_y) _x##_y
#define CAT(_x,_y) CAT2(_x,_y)

#define GEN_BINARY(_insn) \
   void CAT(do_,_insn) ( void ) \
   { \
      volatile ULong n = 0x5555555555555555ULL; \
      ULong some = 0x271831415927D459ULL; \
      __asm__ __volatile__( \
         "\t" \
         "stc"                                            "\n\t" \
         "xacquire lock " #_insn "q $123456789, (%0)"     "\n\t" \
         "xrelease lock " #_insn "q $123456789, (%0)"     "\n\t" \
         "xacquire lock " #_insn "l $0x12345FE, (%0)"     "\n\t" \
         "xrelease lock " #_insn "l $0x12345FE, (%0)"     "\n\t" \
         "xacquire lock " #_insn "w $0x9876,    (%0)"     "\n\t" \
         "xrelease lock " #_insn "w $0x9876,    (%0)"     "\n\t" \
         "xacquire lock " #_insn "b $0x45,      (%0)"     "\n\t" \
         "xrelease lock " #_insn "b $0x45,      (%0)"     "\n\t" \
         "xacquire lock " #_insn "q %1,         (%0)"     "\n\t" \
         "xrelease lock " #_insn "q %1,         (%0)"     "\n\t" \
         "xacquire lock " #_insn "l %k1,        (%0)"     "\n\t" \
         "xrelease lock " #_insn "l %k1,        (%0)"     "\n\t" \
         "xacquire lock " #_insn "w %w1,        (%0)"     "\n\t" \
         "xrelease lock " #_insn "w %w1,        (%0)"     "\n\t" \
         "xacquire lock " #_insn "b %b1,        (%0)"     "\n\t" \
         "xrelease lock " #_insn "b %b1,        (%0)"     "\n\t" \
         : : "r"(&n), "r"(some) : "cc", "memory" \
      ); \
      printf("result for '%-3s' is %016llx\n", #_insn, n);  \
   }

GEN_BINARY(add)
GEN_BINARY(adc)
GEN_BINARY(and)
GEN_BINARY(or)
GEN_BINARY(sbb)
GEN_BINARY(sub)
GEN_BINARY(xor)

#define GEN_UNARY(_insn) \
   void CAT(do_,_insn) ( void ) \
   { \
      volatile ULong n = 0x5555555555555555ULL; \
      __asm__ __volatile__( \
         "\t" \
         "stc"                                "\n\t" \
         "xacquire lock " #_insn "q (%0)"     "\n\t" \
         "xrelease lock " #_insn "q (%0)"     "\n\t" \
         "xacquire lock " #_insn "l (%0)"     "\n\t" \
         "xrelease lock " #_insn "l (%0)"     "\n\t" \
         "xacquire lock " #_insn "w (%0)"     "\n\t" \
         "xrelease lock " #_insn "w (%0)"     "\n\t" \
         "xacquire lock " #_insn "b (%0)"     "\n\t" \
         "xrelease lock " #_insn "b (%0)"     "\n\t" \
         : : "r"(&n) : "cc", "memory" \
      ); \
      printf("result for '%-3s' is %016llx\n", #_insn, n);  \
   }

GEN_UNARY(dec)
GEN_UNARY(inc)
GEN_UNARY(neg)
GEN_UNARY(not)

void do_btc ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   __asm__ __volatile__(
      "xacquire lock btcq %1,  (%0)"     "\n\t"
      "xacquire lock btcq $57, (%0)"     "\n\t"
      "xrelease lock btcq %1,  (%0)"     "\n\t"
      "xrelease lock btcq $55, (%0)"     "\n\t"
      "xacquire lock btcl %k1, (%0)"     "\n\t"
      "xacquire lock btcl $27, (%0)"     "\n\t"
      "xrelease lock btcl %k1, (%0)"     "\n\t"
      "xrelease lock btcl $25, (%0)"     "\n\t"
      "xacquire lock btcw %w1, (%0)"     "\n\t"
      "xacquire lock btcw $12, (%0)"     "\n\t"
      "xrelease lock btcw %w1, (%0)"     "\n\t"
      "xrelease lock btcw $11, (%0)"     "\n\t"
      : : "r"(&n), "r"(6ULL) : "cc", "memory"
   );
   printf("result for '%-3s' is %016llx\n", "btc", n); \
}

void do_btr ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   __asm__ __volatile__(
      "xacquire lock btrq %1,  (%0)"     "\n\t"
      "xacquire lock btrq $57, (%0)"     "\n\t"
      "xrelease lock btrq %1,  (%0)"     "\n\t"
      "xrelease lock btrq $55, (%0)"     "\n\t"
      "xacquire lock btrl %k1, (%0)"     "\n\t"
      "xacquire lock btrl $27, (%0)"     "\n\t"
      "xrelease lock btrl %k1, (%0)"     "\n\t"
      "xrelease lock btrl $25, (%0)"     "\n\t"
      "xacquire lock btrw %w1, (%0)"     "\n\t"
      "xacquire lock btrw $12, (%0)"     "\n\t"
      "xrelease lock btrw %w1, (%0)"     "\n\t"
      "xrelease lock btrw $11, (%0)"     "\n\t"
      : : "r"(&n), "r"(6ULL) : "cc", "memory"
   );
   printf("result for '%-3s' is %016llx\n", "btr", n); \
}

void do_bts ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   __asm__ __volatile__(
      "xacquire lock btsq %1,  (%0)"     "\n\t"
      "xacquire lock btsq $57, (%0)"     "\n\t"
      "xrelease lock btsq %1,  (%0)"     "\n\t"
      "xrelease lock btsq $55, (%0)"     "\n\t"
      "xacquire lock btsl %k1, (%0)"     "\n\t"
      "xacquire lock btsl $27, (%0)"     "\n\t"
      "xrelease lock btsl %k1, (%0)"     "\n\t"
      "xrelease lock btsl $25, (%0)"     "\n\t"
      "xacquire lock btsw %w1, (%0)"     "\n\t"
      "xacquire lock btsw $12, (%0)"     "\n\t"
      "xrelease lock btsw %w1, (%0)"     "\n\t"
      "xrelease lock btsw $11, (%0)"     "\n\t"
      : : "r"(&n), "r"(6ULL) : "cc", "memory"
   );
   printf("result for '%-3s' is %016llx\n", "bts", n); \
}

void do_cmpxchg ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   ULong some = 0x271831415927D459ULL;
   __asm__ __volatile__(
      "\t"
      "stc"                                         "\n\t"
      // zero out rax and get the flags in a known state
      "xorq    %%rax, %%rax"                        "\n\t"
      "xacquire lock cmpxchgq %1,         (%0)"     "\n\t"
      "xrelease lock cmpxchgq %1,         (%0)"     "\n\t"
      "xacquire lock cmpxchgl %k1,        (%0)"     "\n\t"
      "xrelease lock cmpxchgl %k1,        (%0)"     "\n\t"
      "xacquire lock cmpxchgw %w1,        (%0)"     "\n\t"
      "xrelease lock cmpxchgw %w1,        (%0)"     "\n\t"
      "xacquire lock cmpxchgb %b1,        (%0)"     "\n\t"
      "xrelease lock cmpxchgb %b1,        (%0)"     "\n\t"
      : : "r"(&n), "r"(some) : "cc", "memory", "rax"
   );
   printf("result for '%-3s' is %016llx\n", "cmpxchg", n);
}

void do_cmpxchg8b ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   __asm__ __volatile__(
      "xorq     %%rax, %%rax"     "\n\t"
      "xorq     %%rdx, %%rdx"     "\n\t"
      "movabsq $0x1122334455667788, %%rcx"   "\n\t"
      "movabsq $0xffeeddccbbaa9988, %%rbx"   "\n\t"
      "xacquire lock cmpxchg8b (%0)"     "\n\t"
      "xrelease lock cmpxchg8b (%0)"     "\n\t"
      : : "r"(&n) : "cc", "memory", "rax", "rbx", "rcx", "rdx"
   );
   printf("result for '%-3s' is %016llx\n", "cmpxchg8b", n);
}

void do_xadd ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   ULong some = 0x271831415927D459ULL;
   __asm__ __volatile__(
      "\t"
      "stc"                                         "\n\t"
      // zero out rax and get the flags in a known state
      "xorq    %%rax, %%rax"                        "\n\t"
      "xacquire lock xaddq %1,         (%0)"     "\n\t"
      "xrelease lock xaddq %1,         (%0)"     "\n\t"
      "xacquire lock xaddl %k1,        (%0)"     "\n\t"
      "xrelease lock xaddl %k1,        (%0)"     "\n\t"
      "xacquire lock xaddw %w1,        (%0)"     "\n\t"
      "xrelease lock xaddw %w1,        (%0)"     "\n\t"
      "xacquire lock xaddb %b1,        (%0)"     "\n\t"
      "xrelease lock xaddb %b1,        (%0)"     "\n\t"
      : : "r"(&n), "r"(some) : "cc", "memory", "rax"
      // not sure this constraint string is really correct, since %1
      // is written as well as read, in this case.  But I can't figure
      // out how to tell gcc that.
   );
   printf("result for '%-3s' is %016llx\n", "xadd", n);
}

void do_xchg ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   ULong some = 0x271831415927D459ULL;
   __asm__ __volatile__(
      "\t"
      "stc"                                         "\n\t"
      // zero out rax and get the flags in a known state
      "xorq    %%rax, %%rax"                        "\n\t"
      "xacquire lock xchgq %1,         (%0)"     "\n\t"
      "xrelease lock xchgq %1,         (%0)"     "\n\t"
      "xacquire lock xchgl %k1,        (%0)"     "\n\t"
      "xrelease lock xchgl %k1,        (%0)"     "\n\t"
      "xacquire lock xchgw %w1,        (%0)"     "\n\t"
      "xrelease lock xchgw %w1,        (%0)"     "\n\t"
      "xacquire lock xchgb %b1,        (%0)"     "\n\t"
      "xrelease lock xchgb %b1,        (%0)"     "\n\t"
      : : "r"(&n), "r"(some) : "cc", "memory", "rax"
      // not sure this constraint string is really correct, since %1
      // is written as well as read, in this case.  But I can't figure
      // out how to tell gcc that.
   );
   printf("result for '%-3s' is %016llx\n", "xchg", n);
}

void do_xchg_no_lock ( void )
{
   volatile ULong n = 0x5555555555555555ULL;
   ULong some = 0x271831415927D459ULL;
   __asm__ __volatile__(
      "\t"
      "stc"                                         "\n\t"
      // zero out rax and get the flags in a known state
      "xorq    %%rax, %%rax"                        "\n\t"
      "xacquire xchgq %1,         (%0)"     "\n\t"
      "xrelease xchgq %1,         (%0)"     "\n\t"
      "xacquire xchgl %k1,        (%0)"     "\n\t"
      "xrelease xchgl %k1,        (%0)"     "\n\t"
      "xacquire xchgw %w1,        (%0)"     "\n\t"
      "xrelease xchgw %w1,        (%0)"     "\n\t"
      "xacquire xchgb %b1,        (%0)"     "\n\t"
      "xrelease xchgb %b1,        (%0)"     "\n\t"
      : : "r"(&n), "r"(some) : "cc", "memory", "rax"
      // not sure this constraint string is really correct, since %1
      // is written as well as read, in this case.  But I can't figure
      // out how to tell gcc that.
   );
   printf("result for '%-3s' is %016llx\n", "xchg-no-lock", n);
}

void do_mov ( void )
{
   // According to the Intel docs, we only need to allow xrelease here.
   volatile ULong n = 0x5555555555555555ULL;
   ULong some = 0x271831415927D459ULL;
   __asm__ __volatile__(
      "\t"
      "xrelease movq %1,   0(%0)"     "\n\t"
      "xrelease movl %k1,  1(%0)"     "\n\t"
      "xrelease movw %w1,  3(%0)"     "\n\t"
      "xrelease movb %b1,  7(%0)"     "\n\t"
      : : "r"(&n), "r"(some) : "cc", "memory"
   );
   printf("result for '%-3s' is %016llx\n", "mov-reg", n);
   n = 0xAAAAAAAAAAAAAAAAULL;
   __asm__ __volatile__(
      "\t"
      "xrelease movq $-0x79876543, 0(%0)"     "\n\t"
      "xrelease movl $0xEFCDAB89, 1(%0)"     "\n\t"
      "xrelease movw $0xF00D,     3(%0)"     "\n\t"
      "xrelease movb $0x42,       7(%0)"     "\n\t"
      : : "r"(&n) : "cc", "memory"
   );
   printf("result for '%-3s' is %016llx\n", "mov-imm", n);
}

int main ( void )
{
  do_add();
  do_adc();
  do_and();
  do_or();
  do_sbb();
  do_sub();
  do_xor();
  do_dec();
  do_inc();
  do_neg();
  do_not();
  do_btc();
  do_btr();
  do_bts();
  do_cmpxchg();
  do_cmpxchg8b();
  do_xadd();
  do_xchg();
  do_xchg_no_lock();
  do_mov();
  return 0;
}
