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