From 932ea4ad94af035195a4c9cd992425c565c3f87d Mon Sep 17 00:00:00 2001 From: Florian Krohm Date: Wed, 6 Jun 2012 02:27:51 +0000 Subject: [PATCH] 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 --- NEWS | 1 + docs/internals/s390-opcodes.csv | 6 +- memcheck/tests/s390x/Makefile.am | 2 +- memcheck/tests/s390x/cds.c | 82 +++++++++++++++++++ memcheck/tests/s390x/cds.stderr.exp | 10 +++ memcheck/tests/s390x/cds.stdout.exp | 0 memcheck/tests/s390x/cds.vgtest | 2 + memcheck/tests/s390x/cdsg.c | 65 +++++++++++++++ memcheck/tests/s390x/cdsg.stderr.exp | 10 +++ memcheck/tests/s390x/cdsg.stdout.exp | 0 memcheck/tests/s390x/cdsg.vgtest | 2 + none/tests/s390x/Makefile.am | 2 +- none/tests/s390x/cds.c | 114 +++++++++++++++++++++++++++ none/tests/s390x/cds.stderr.exp | 2 + none/tests/s390x/cds.stdout.exp | 14 ++++ none/tests/s390x/cds.vgtest | 1 + none/tests/s390x/cdsg.c | 105 ++++++++++++++++++++++++ none/tests/s390x/cdsg.stderr.exp | 2 + none/tests/s390x/cdsg.stdout.exp | 21 +++++ none/tests/s390x/cdsg.vgtest | 1 + 20 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 memcheck/tests/s390x/cds.c create mode 100644 memcheck/tests/s390x/cds.stderr.exp create mode 100644 memcheck/tests/s390x/cds.stdout.exp create mode 100644 memcheck/tests/s390x/cds.vgtest create mode 100644 memcheck/tests/s390x/cdsg.c create mode 100644 memcheck/tests/s390x/cdsg.stderr.exp create mode 100644 memcheck/tests/s390x/cdsg.stdout.exp create mode 100644 memcheck/tests/s390x/cdsg.vgtest create mode 100644 none/tests/s390x/cds.c create mode 100644 none/tests/s390x/cds.stderr.exp create mode 100644 none/tests/s390x/cds.stdout.exp create mode 100644 none/tests/s390x/cds.vgtest create mode 100644 none/tests/s390x/cdsg.c create mode 100644 none/tests/s390x/cdsg.stderr.exp create mode 100644 none/tests/s390x/cdsg.stdout.exp create mode 100644 none/tests/s390x/cdsg.vgtest diff --git a/NEWS b/NEWS index 18a32cab6f..737ce1d7ff 100644 --- a/NEWS +++ b/NEWS @@ -90,6 +90,7 @@ where XXXXXX is the bug number as listed below. 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 00c93ed663..58544ac21b 100644 --- a/docs/internals/s390-opcodes.csv +++ b/docs/internals/s390-opcodes.csv @@ -30,7 +30,7 @@ bxle,"branch on index low or equal",implemented, 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 @@ strag,"store read address",N/A,"privileged instruction" 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 @@ niy,"and immediate with long offset",implemented, 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 4369e5ca2b..0cd8a98add 100644 --- a/memcheck/tests/s390x/Makefile.am +++ b/memcheck/tests/s390x/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/Makefile.tool-tests.am 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 0000000000..ec5c533e07 --- /dev/null +++ b/memcheck/tests/s390x/cds.c @@ -0,0 +1,82 @@ +#include +#include + +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 0000000000..e72de94c82 --- /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 0000000000..e69de29bb2 diff --git a/memcheck/tests/s390x/cds.vgtest b/memcheck/tests/s390x/cds.vgtest new file mode 100644 index 0000000000..5195887e26 --- /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 0000000000..00ae2a5d16 --- /dev/null +++ b/memcheck/tests/s390x/cdsg.c @@ -0,0 +1,65 @@ +#include +#include + +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 0000000000..2ee711ba62 --- /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 0000000000..e69de29bb2 diff --git a/memcheck/tests/s390x/cdsg.vgtest b/memcheck/tests/s390x/cdsg.vgtest new file mode 100644 index 0000000000..598fd68d3a --- /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 b52ee13393..b74bb4b9f9 100644 --- a/none/tests/s390x/Makefile.am +++ b/none/tests/s390x/Makefile.am @@ -7,7 +7,7 @@ INSN_TESTS = clc clcle cvb cvd icm lpr tcxb lam_stam xc mvst add sub mul \ 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 0000000000..693f19b20b --- /dev/null +++ b/none/tests/s390x/cds.c @@ -0,0 +1,114 @@ +#include +#include + +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 0000000000..139597f9cb --- /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 0000000000..763694712a --- /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 0000000000..0daf980c9d --- /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 0000000000..b1392d3ec2 --- /dev/null +++ b/none/tests/s390x/cdsg.c @@ -0,0 +1,105 @@ +#include +#include + +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 0000000000..139597f9cb --- /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 0000000000..460bfbff8a --- /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 0000000000..e0cd61fb3a --- /dev/null +++ b/none/tests/s390x/cdsg.vgtest @@ -0,0 +1 @@ +prog: cdsg -- 2.47.2