Support "compare double ansd swap" insns: CDS, CDSY, and CDSG 
valgrind bits for fixing bugzilla #291865. See also VEX r2372.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12615 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 18a32ca..737ce1d 100644
--- a/NEWS
+++ b/NEWS
@@ -90,6 +90,7 @@
 289939  wish: complete monitor cmd 'leak_check' with details about leaked or reachable blocks
 290655  Add support for AESKEYGENASSIST instruction 
 290974  vgdb must align pages to VKI_SHMLBA (16KB) on ARM 
+291865  s390x: Support the "Compare Double and Swap" family of instructions
 293088  Add some VEX sanity checks for ppc64 unhandled instructions
 294055  regtest none/tests/shell fails when locale is not set to C
 294190  --vgdb-error=xxx can be out of sync with errors shown to the user
diff --git a/docs/internals/s390-opcodes.csv b/docs/internals/s390-opcodes.csv
index 00c93ed..58544ac 100644
--- a/docs/internals/s390-opcodes.csv
+++ b/docs/internals/s390-opcodes.csv
@@ -30,7 +30,7 @@
 c,compare,implemented,
 cd,"compare (long)","not implemented",
 cdr,"compare (long)","not implemented",
-cds,"compare double and swap","not implemented","open bugzilla"
+cds,"compare double and swap",implemented,
 ce,"compare (short)","not implemented",
 cer,"compare (short)","not implemented",
 cfc,"compare and form codeword","not implemented",
@@ -441,7 +441,7 @@
 stctg,"store control 64",N/A,"privileged instruction"
 lctlg,"load control 64",N/A,"privileged instruction"
 csg,"compare and swap 64",implemented,
-cdsg,"compare double and swap 64","not implemented","open bugzilla"
+cdsg,"compare double and swap 64",implemented,
 clmh,"compare logical characters under mask high",implemented,
 stcmh,"store characters under mask high",implemented,
 icmh,"insert characters under mask high",implemented,
@@ -573,7 +573,7 @@
 ny,"and with long offset",implemented,
 cy,"compare with long offset",implemented,
 csy,"compare and swap with long offset",implemented,
-cdsy,"compare double and swap with long offset","not implemented","open bugzilla"
+cdsy,"compare double and swap with long offset",implemented,
 chy,"compare halfword with long offset",implemented,
 cly,"compare logical with long offset",implemented,
 cliy,"compare logical immediate with long offset",implemented,
diff --git a/memcheck/tests/s390x/Makefile.am b/memcheck/tests/s390x/Makefile.am
index 4369e5c..0cd8a98 100644
--- a/memcheck/tests/s390x/Makefile.am
+++ b/memcheck/tests/s390x/Makefile.am
@@ -2,7 +2,7 @@
 
 dist_noinst_SCRIPTS = filter_stderr
 
-INSN_TESTS = cs csg
+INSN_TESTS = cs csg cds cdsg
 
 check_PROGRAMS = $(INSN_TESTS) 
 
diff --git a/memcheck/tests/s390x/cds.c b/memcheck/tests/s390x/cds.c
new file mode 100644
index 0000000..ec5c533
--- /dev/null
+++ b/memcheck/tests/s390x/cds.c
@@ -0,0 +1,82 @@
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct {
+   uint64_t high;
+   uint64_t low;
+} quad_word;
+
+void 
+test(quad_word op1_init, uint64_t op2_init, quad_word op3_init)
+{
+   int cc; // unused
+   quad_word op1 = op1_init;
+   uint64_t  op2 = op2_init;
+   quad_word op3 = op3_init;
+
+   __asm__ volatile (
+                     "lmg     %%r0,%%r1,%1\n\t"
+                     "lmg     %%r2,%%r3,%3\n\t"
+                     "cds     %%r0,%%r2,%2\n\t"  //  cds 1st,3rd,2nd
+                     "stmg    %%r0,%%r1,%1\n"    // store r0,r1 to op1
+                     "stmg    %%r2,%%r3,%3\n"    // store r2,r3 to op3
+                     : "=d" (cc), "+QS" (op1), "+QS" (op2), "+QS" (op3)
+                     :
+                     : "r0", "r1", "r2", "r3", "cc");
+
+}
+
+// Return a quad-word that only bits low[32:63] are undefined
+quad_word
+make_undefined(void)
+{
+   quad_word val;
+
+   val.high = 0;
+   val.low |= 0xFFFFFFFF00000000ull;
+
+   return val;
+}
+
+void op1_undefined(void)
+{
+   quad_word op1, op3;
+   uint64_t op2;
+
+   // op1 undefined
+   op1 = make_undefined();
+   op2 = 42;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3);  // complaint
+}
+
+void op2_undefined(void)
+{
+   quad_word op1, op3;
+   uint64_t op2;
+
+   op1.high = op1.low = 42;
+   // op2 undefined
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3);  // complaint
+}
+
+void op3_undefined(void)
+{
+   quad_word op1, op3;
+   uint64_t op2;
+
+   op1.high = op1.low = 42;
+   op2 = 100;
+   op3 = make_undefined();
+   test(op1, op2, op3);  // no complaint; op3 is just copied around
+}
+
+int main ()
+{
+   op1_undefined();
+   op2_undefined();
+   op3_undefined();
+
+   return 0;
+}
diff --git a/memcheck/tests/s390x/cds.stderr.exp b/memcheck/tests/s390x/cds.stderr.exp
new file mode 100644
index 0000000..e72de94
--- /dev/null
+++ b/memcheck/tests/s390x/cds.stderr.exp
@@ -0,0 +1,10 @@
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: test (cds.c:17)
+   by 0x........: op1_undefined (cds.c:50)
+   by 0x........: main (cds.c:77)
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: test (cds.c:17)
+   by 0x........: op2_undefined (cds.c:61)
+   by 0x........: main (cds.c:78)
+
diff --git a/memcheck/tests/s390x/cds.stdout.exp b/memcheck/tests/s390x/cds.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/s390x/cds.stdout.exp
diff --git a/memcheck/tests/s390x/cds.vgtest b/memcheck/tests/s390x/cds.vgtest
new file mode 100644
index 0000000..5195887
--- /dev/null
+++ b/memcheck/tests/s390x/cds.vgtest
@@ -0,0 +1,2 @@
+prog: cds
+vgopts: -q
diff --git a/memcheck/tests/s390x/cdsg.c b/memcheck/tests/s390x/cdsg.c
new file mode 100644
index 0000000..00ae2a5
--- /dev/null
+++ b/memcheck/tests/s390x/cdsg.c
@@ -0,0 +1,65 @@
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct {
+   uint64_t high;
+   uint64_t low;
+} quad_word;
+
+void 
+test(quad_word op1_init, quad_word op2_init, quad_word op3_init)
+{
+   int cc; // unused
+   quad_word op1 = op1_init;
+   quad_word op2 = op2_init;
+   quad_word op3 = op3_init;
+
+   __asm__ volatile (
+                     "lmg     %%r0,%%r1,%1\n\t"
+                     "lmg     %%r2,%%r3,%3\n\t"
+                     "cdsg    %%r0,%%r2,%2\n\t"  //  cdsg 1st,3rd,2nd
+                     "stmg    %%r0,%%r1,%1\n"    // store r0,r1 to op1
+                     "stmg    %%r2,%%r3,%3\n"    // store r2,r3 to op3
+                     : "=d"(cc), "+QS" (op1), "+QS" (op2), "+QS" (op3)
+                     :
+                     : "r0", "r1", "r2", "r3", "cc");
+}
+
+void op1_undefined(void)
+{
+   quad_word op1, op2, op3;
+
+   // op1 undefined
+   op2.high = op2.low = 42;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3);  // complaint
+}
+
+void op2_undefined(void)
+{
+   quad_word op1, op2, op3;
+
+   op1.high = op1.low = 42;
+   // op2 undefined
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3);  // complaint
+}
+
+void op3_undefined(void)
+{
+   quad_word op1, op2, op3;
+
+   op1.high = op1.low = 42;
+   op2 = op1;
+   // op3 undefined
+   test(op1, op2, op3);  // no complaint; op3 is just copied around
+}
+
+int main ()
+{
+   op1_undefined();
+   op2_undefined();
+   op3_undefined();
+
+   return 0;
+}
diff --git a/memcheck/tests/s390x/cdsg.stderr.exp b/memcheck/tests/s390x/cdsg.stderr.exp
new file mode 100644
index 0000000..2ee711b
--- /dev/null
+++ b/memcheck/tests/s390x/cdsg.stderr.exp
@@ -0,0 +1,10 @@
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: test (cdsg.c:17)
+   by 0x........: op1_undefined (cdsg.c:35)
+   by 0x........: main (cdsg.c:60)
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: test (cdsg.c:17)
+   by 0x........: op2_undefined (cdsg.c:45)
+   by 0x........: main (cdsg.c:61)
+
diff --git a/memcheck/tests/s390x/cdsg.stdout.exp b/memcheck/tests/s390x/cdsg.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/s390x/cdsg.stdout.exp
diff --git a/memcheck/tests/s390x/cdsg.vgtest b/memcheck/tests/s390x/cdsg.vgtest
new file mode 100644
index 0000000..598fd68
--- /dev/null
+++ b/memcheck/tests/s390x/cdsg.vgtest
@@ -0,0 +1,2 @@
+prog: cdsg
+vgopts: -q
diff --git a/none/tests/s390x/Makefile.am b/none/tests/s390x/Makefile.am
index b52ee13..b74bb4b 100644
--- a/none/tests/s390x/Makefile.am
+++ b/none/tests/s390x/Makefile.am
@@ -7,7 +7,7 @@
              and_EI or_EI xor_EI insert_EI mul_GE add_GE condloadstore \
              op_exception fgx stck stckf stcke stfle cksm mvcl clcl troo \
              trto trot trtt tr tre cij cgij clij clgij crj cgrj clrj clgrj \
-             cs csg
+             cs csg cds cdsg
 
 check_PROGRAMS = $(INSN_TESTS) \
 		 allexec \
diff --git a/none/tests/s390x/cds.c b/none/tests/s390x/cds.c
new file mode 100644
index 0000000..693f19b
--- /dev/null
+++ b/none/tests/s390x/cds.c
@@ -0,0 +1,114 @@
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct {
+   uint64_t high;
+   uint64_t low;
+} quad_word;
+
+void 
+test(quad_word op1_init, uint64_t op2_init, quad_word op3_init,
+     int expected_cc)
+{
+   int cc = 1 - expected_cc;
+
+   quad_word op1 = op1_init;
+   uint64_t  op2 = op2_init;
+   quad_word op3 = op3_init;
+
+   quad_word op1_before = op1;
+   uint64_t  op2_before = op2;
+   quad_word op3_before = op3;
+
+   printf("before op1 = (%#lx, %#lx)\n", op1.high, op1.low);
+   printf("before op2 =  %#lx\n", op2);
+   printf("before op3 = (%#lx, %#lx)\n", op3.high, op3.low);
+
+   __asm__ volatile (
+                     "lmg     %%r0,%%r1,%1\n\t"
+                     "lmg     %%r2,%%r3,%3\n\t"
+                     "cds     %%r0,%%r2,%2\n\t"  //  cds 1st,3rd,2nd
+                     "stmg    %%r0,%%r1,%1\n"    // store r0,r1 to op1
+                     "stmg    %%r2,%%r3,%3\n"    // store r2,r3 to op3
+                     "ipm     %0\n\t"
+                     "srl     %0,28\n\t"
+                     : "=d" (cc), "+QS" (op1), "+QS" (op2), "+QS" (op3)
+                     :
+                     : "r0", "r1", "r2", "r3", "cc");
+
+   printf("after  op1 = (%#lx, %#lx)\n", op1.high, op1.low);
+   printf("after  op2 = %#lx\n", op2);
+   printf("after  op3 = (%#lx, %#lx)\n", op3.high, op3.low);
+   printf("cc = %d\n", cc);
+
+   // Check the condition code
+   if (cc != expected_cc) {
+      printf("condition code is incorrect\n");
+   }
+
+   // op3 never changes
+   if (op3.low != op3_before.low || op3.high != op3_before.high) {
+      printf("operand #3 modified\n");
+   }
+
+   if (expected_cc == 0) {
+      // 3rd operand stored at 2nd operand location
+
+      // op1 did not change
+      if (op1.low != op1_before.low || op1.high != op1_before.high) {
+         printf("operand #1 modified\n");
+      }
+
+      // lower 32 bits of op2 are the lower 32 bits of op3.low
+      if ((op2 & 0xffffffff) != (op3.low & 0xffffffff)) {
+         printf("operand #2 [32:63] incorrect\n");
+      }
+      // higher 32 bits of op2 are the lower 32 bits of op3.high
+      if ((op2 >> 32) != (op3.high & 0xffffffff)) {
+         printf("operand #2 [0:31] incorrect\n");
+      }
+   } else {
+      // 2nd operand stored at 1st operand location
+
+      // op2 did not change
+      if (op2 != op2_before) {
+         printf("operand #2 modified\n");
+      }
+
+      // bits [0:31] of op1 (both parts) are unchanged
+      if ((op1.high >> 32) != (op1_before.high >> 32) ||
+          (op1.low  >> 32) != (op1_before.low >> 32)) {
+         printf("operand #1 [0:31] modified\n");
+      }
+
+      if ((op1.low & 0xffffffff) != (op2 & 0xffffffff)) {
+         printf("operand #1 low[32:63] incorrect\n");
+      }
+      if ((op1.high & 0xffffffff) != (op2 >> 32)) {
+         printf("operand #1 high[32:63] not updated\n");
+      }
+   }
+}
+
+int main ()
+{
+   quad_word op1, op3;
+   uint64_t  op2;
+
+   // (op1.high[32:63], op1.low[32:63]) == op2
+   op1.high = 0x0000000044556677ull;
+   op1.low  = 0x111111118899aabbull;
+   op2      = 0x445566778899aabbull;
+
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3, 0);
+
+   // (op1.high[32:63], op1.low[32:63]) != op2
+   op1.high = 0x1000000000000000ull;
+   op1.low  = 0x0000000000000000ull;
+   op2      = 0x8000000000000001ull;;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3, 1);
+
+   return 0;
+}
diff --git a/none/tests/s390x/cds.stderr.exp b/none/tests/s390x/cds.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/s390x/cds.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/s390x/cds.stdout.exp b/none/tests/s390x/cds.stdout.exp
new file mode 100644
index 0000000..7636947
--- /dev/null
+++ b/none/tests/s390x/cds.stdout.exp
@@ -0,0 +1,14 @@
+before op1 = (0x44556677, 0x111111118899aabb)
+before op2 =  0x445566778899aabb
+before op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op1 = (0x44556677, 0x111111118899aabb)
+after  op2 = 0xdeadbabedeadbabe
+after  op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+cc = 0
+before op1 = (0x1000000000000000, 0)
+before op2 =  0x8000000000000001
+before op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op1 = (0x1000000080000000, 0x1)
+after  op2 = 0x8000000000000001
+after  op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+cc = 1
diff --git a/none/tests/s390x/cds.vgtest b/none/tests/s390x/cds.vgtest
new file mode 100644
index 0000000..0daf980
--- /dev/null
+++ b/none/tests/s390x/cds.vgtest
@@ -0,0 +1 @@
+prog: cds
diff --git a/none/tests/s390x/cdsg.c b/none/tests/s390x/cdsg.c
new file mode 100644
index 0000000..b1392d3
--- /dev/null
+++ b/none/tests/s390x/cdsg.c
@@ -0,0 +1,105 @@
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct {
+   uint64_t high;
+   uint64_t low;
+} quad_word;
+
+void 
+test(quad_word op1_init, quad_word op2_init, quad_word op3_init,
+     int expected_cc)
+{
+   int cc = 1 - expected_cc;
+
+   quad_word op1 = op1_init;
+   quad_word op2 = op2_init;
+   quad_word op3 = op3_init;
+
+   quad_word op1_before = op1;
+   quad_word op2_before = op2;
+   quad_word op3_before = op3;
+
+   printf("before op1 = (%#lx, %#lx)\n", op1.high, op1.low);
+   printf("before op2 = (%#lx, %#lx)\n", op2.high, op2.low);
+   printf("before op3 = (%#lx, %#lx)\n", op3.high, op3.low);
+
+   __asm__ volatile (
+                     "lmg     %%r0,%%r1,%1\n\t"
+                     "lmg     %%r2,%%r3,%3\n\t"
+                     "cdsg    %%r0,%%r2,%2\n\t"  //  cdsg 1st,3rd,2nd
+                     "stmg    %%r0,%%r1,%1\n"    // store r0,r1 to op1
+                     "stmg    %%r2,%%r3,%3\n"    // store r2,r3 to op3
+                     "ipm     %0\n\t"
+                     "srl     %0,28\n\t"
+                     : "=d" (cc), "+QS" (op1), "+QS" (op2), "+QS" (op3)
+                     :
+                     : "r0", "r1", "r2", "r3", "cc");
+
+   printf("after  op1 = (%#lx, %#lx)\n", op1.high, op1.low);
+   printf("after  op2 = (%#lx, %#lx)\n", op2.high, op2.low);
+   printf("after  op3 = (%#lx, %#lx)\n", op3.high, op3.low);
+   printf("cc = %d\n", cc);
+
+   if (cc != expected_cc) {
+      printf("condition code is incorrect\n");
+   }
+
+   // op3 never changes
+   if (op3.low != op3_before.low || op3.high != op3_before.high) {
+      printf("operand #3 modified\n");
+   }
+
+   if (expected_cc == 0) {
+      // 3rd operand stored at 2nd operand location
+
+      // op1 did not change
+      if (op1.low != op1_before.low || op1.high != op1_before.high) {
+         printf("operand #1 modified\n");
+      }
+      if (op2.high != op3.high || op2.low != op3.low) {
+         printf("operand #2 incorrect\n");
+      }
+   } else {
+      // 2nd operand stored at 1st operand location
+
+      // op2 did not change
+      if (op2.low != op2_before.low || op2.high != op2_before.high) {
+         printf("operand #2 modified\n");
+      }
+
+      if (op1.high != op2.high || op1.low != op2.low) {
+         printf("operand #1 incorrect\n");
+      }
+   }
+}
+
+int main ()
+{
+   quad_word op1, op2, op3;
+
+   // op1 == op2
+   op1.high = 0x0011223344556677ull;
+   op1.low  = 0x8899aabbccddeeffull;
+   op2 = op1;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3, 0);
+
+   // op1 != op2 (only MSB differs)
+   op1.high = 0x8000000000000000ull;
+   op1.low  = 0x0000000000000000ull;
+   op2.high = 0;
+   op2.low  = 1;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3, 1);
+
+   // op1 != op2 (only LSB differs)
+   op1.high = 0x0000000000000000ull;
+   op1.low  = 0x0000000000000001ull;
+   op2.high = 1;
+   op2.low  = 0;
+   op3.high = op3.low = 0xdeadbeefdeadbabeull;
+   test(op1, op2, op3, 1);
+
+   return 0;
+}
diff --git a/none/tests/s390x/cdsg.stderr.exp b/none/tests/s390x/cdsg.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/s390x/cdsg.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/s390x/cdsg.stdout.exp b/none/tests/s390x/cdsg.stdout.exp
new file mode 100644
index 0000000..460bfbf
--- /dev/null
+++ b/none/tests/s390x/cdsg.stdout.exp
@@ -0,0 +1,21 @@
+before op1 = (0x11223344556677, 0x8899aabbccddeeff)
+before op2 = (0x11223344556677, 0x8899aabbccddeeff)
+before op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op1 = (0x11223344556677, 0x8899aabbccddeeff)
+after  op2 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+cc = 0
+before op1 = (0x8000000000000000, 0)
+before op2 = (0, 0x1)
+before op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op1 = (0, 0x1)
+after  op2 = (0, 0x1)
+after  op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+cc = 1
+before op1 = (0, 0x1)
+before op2 = (0x1, 0)
+before op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+after  op1 = (0x1, 0)
+after  op2 = (0x1, 0)
+after  op3 = (0xdeadbeefdeadbabe, 0xdeadbeefdeadbabe)
+cc = 1
diff --git a/none/tests/s390x/cdsg.vgtest b/none/tests/s390x/cdsg.vgtest
new file mode 100644
index 0000000..e0cd61f
--- /dev/null
+++ b/none/tests/s390x/cdsg.vgtest
@@ -0,0 +1 @@
+prog: cdsg