This commit is a fix for "Bug 330622 - Add test to regression suite for POWER
instruction: dcbzl" submitted by Anmol P. Paralkar <paralkar@freescale.com>

The patch adds the following files:

 none/tests/ppc64/data-cache-instructions.vgtest
 none/tests/ppc64/data-cache-instructions.c
 none/tests/ppc64/data-cache-instructions.stdout.exp
 none/tests/ppc64/data-cache-instructions.stderr.exp
 none/tests/ppc32/data-cache-instructions.stdout.exp
 none/tests/ppc32/data-cache-instructions.c
 none/tests/ppc32/data-cache-instructions.vgtest
 none/tests/ppc32/data-cache-instructions.stderr.exp
 tests/power_insn_available.c


The following files are modified:

 none/tests/ppc32/Makefile.am
 none/tests/ppc64/Makefile.am
 tests/Makefile.am





git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13791 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/none/tests/ppc32/Makefile.am b/none/tests/ppc32/Makefile.am
index 08486cd..5b3fef9 100644
--- a/none/tests/ppc32/Makefile.am
+++ b/none/tests/ppc32/Makefile.am
@@ -44,7 +44,8 @@
 	test_isa_2_07_part2.stderr.exp test_isa_2_07_part2.stdout.exp test_isa_2_07_part2.vgtest \
 	test_tm.stderr.exp test_tm.stdout.exp test_tm.vgtest \
 	test_touch_tm.stderr.exp test_touch_tm.stdout.exp test_touch_tm.vgtest \
-	ldst_multiple.stderr.exp ldst_multiple.stdout.exp ldst_multiple.vgtest
+	ldst_multiple.stderr.exp ldst_multiple.stdout.exp ldst_multiple.vgtest \
+	data-cache-instructions.stderr.exp data-cache-instructions.stdout.exp data-cache-instructions.vgtest
 
 
 check_PROGRAMS = \
@@ -61,7 +62,8 @@
 	test_isa_2_07_part2 \
 	test_tm \
 	test_touch_tm \
-	ldst_multiple
+	ldst_multiple \
+	data-cache-instructions
 
 AM_CFLAGS    += @FLAG_M32@
 AM_CXXFLAGS  += @FLAG_M32@
diff --git a/none/tests/ppc32/data-cache-instructions.c b/none/tests/ppc32/data-cache-instructions.c
new file mode 100644
index 0000000..566f5c9
--- /dev/null
+++ b/none/tests/ppc32/data-cache-instructions.c
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Derived from the test case for the "dcbzl" instruction support by
+ * Dave Goodell * <goodell@mcs.anl.gov>
+ * (see: Bug 135264 - dcbzl instruction missing)
+ * and: coregrind/m_machine.c/find_ppc_dcbz_sz()
+ ******************************************************************************/
+
+/* ensure we have posix_memalign */
+#define _POSIX_C_SOURCE 200112L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+static int query_block_size(void)
+{
+#define MAX_DCBZL_SZB (128) /* largest known effect of dcbzl */
+  char *test_block = NULL;
+  register char *rb asm ("r14");
+  int block_size, test_block_size = 4 * MAX_DCBZL_SZB, err;
+  char *p;
+
+  err = posix_memalign ((void **)&test_block, MAX_DCBZL_SZB, test_block_size);
+  if (err) {
+    fprintf(stderr, "posix_memalign() failed (err = %d [%s])\n", err, strerror(err));
+    return err;
+  }
+
+  rb = test_block;
+
+  memset(rb, 0xff, test_block_size);
+  asm volatile ("dcbzl 0, %[RB]" : : [RB] "r" (rb));
+  for (block_size = 0, p = rb; (p - rb) < test_block_size; p++)
+    if (!*p)
+      block_size++;
+  assert(block_size == 16 || block_size == 32 || block_size == 64 || block_size == 128);
+
+  free(test_block);
+  return block_size;
+}
+
+/* Test dcbzl at addr in buffer given dcbzl_block_size */
+static void test_dcbzl_at(char *addr, char *buffer, int block_size)
+{
+  int i;
+
+  /* Note: Assumption is that the length of buffer is three times the block_size. */
+  memset(buffer, 0xff, 3 * block_size);
+  asm volatile ("dcbzl %[RA], %[RB]" : : [RA] "r" (0), [RB] "r" (addr));
+  for (i = 0; i < block_size; i++) {
+    assert(buffer[i] == 0xff);
+    assert(buffer[block_size + i] == 0x00);
+    assert(buffer[2 * block_size + i] == 0xff);
+  }
+}
+
+/* Test for insn: dcbzl */  
+static int test_dcbzl(void)
+{
+  int err;
+  char *buffer = NULL;
+  int buffer_size;
+  int block_size;
+  
+  block_size = query_block_size();
+  assert(block_size == 16 || block_size == 32 || block_size == 64 || block_size == 128);
+  buffer_size = 3 * block_size;
+  err = posix_memalign((void **) &buffer, block_size, buffer_size);
+  if (err) {
+    fprintf(stderr, "posix_memalign() failed (err = %d [%s])\n", err, strerror(err));
+    return err;
+  }
+  
+  /* check at aligned address within the test block */
+  test_dcbzl_at(&buffer[block_size], buffer, block_size);
+  fprintf(stdout, "Passed dcbzl test at aligned address within the test block.\n");
+
+  /* check at un-aligned (1 modulo block_size) address within the test block */
+  test_dcbzl_at(&buffer[block_size+1], buffer, block_size);
+  fprintf(stdout, "Passed dcbzl test at un-aligned (1 modulo block_size) address within the test block.\n");
+
+  /* check at un-aligned ((block_size - 1) modulo block_size) address within the test block */
+  test_dcbzl_at(&buffer[2 * block_size - 1], buffer, block_size);
+  fprintf(stdout, "Passed dcbzl test at un-aligned ((block_size - 1) modulo block_size) address within the test block.\n");
+  
+  free(buffer);
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  int status;
+  status = test_dcbzl ();
+  return status;
+}
diff --git a/none/tests/ppc32/data-cache-instructions.stderr.exp b/none/tests/ppc32/data-cache-instructions.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/ppc32/data-cache-instructions.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/ppc32/data-cache-instructions.stdout.exp b/none/tests/ppc32/data-cache-instructions.stdout.exp
new file mode 100644
index 0000000..bf98e13
--- /dev/null
+++ b/none/tests/ppc32/data-cache-instructions.stdout.exp
@@ -0,0 +1,3 @@
+Passed dcbzl test at aligned address within the test block.
+Passed dcbzl test at un-aligned (1 modulo block_size) address within the test block.
+Passed dcbzl test at un-aligned ((block_size - 1) modulo block_size) address within the test block.
diff --git a/none/tests/ppc32/data-cache-instructions.vgtest b/none/tests/ppc32/data-cache-instructions.vgtest
new file mode 100644
index 0000000..42a3b31
--- /dev/null
+++ b/none/tests/ppc32/data-cache-instructions.vgtest
@@ -0,0 +1,2 @@
+prereq: ../../../tests/power_insn_available dcbzl
+prog: data-cache-instructions
diff --git a/none/tests/ppc64/Makefile.am b/none/tests/ppc64/Makefile.am
index fc43f08..20a2ebb 100644
--- a/none/tests/ppc64/Makefile.am
+++ b/none/tests/ppc64/Makefile.am
@@ -32,7 +32,8 @@
 	test_isa_2_07_part2.stderr.exp test_isa_2_07_part2.stdout.exp test_isa_2_07_part2.vgtest \
 	test_tm.stderr.exp test_tm.stdout.exp test_tm.vgtest \
 	test_touch_tm.stderr.exp test_touch_tm.stdout.exp test_touch_tm.vgtest \
-	ldst_multiple.stderr.exp ldst_multiple.stdout.exp ldst_multiple.vgtest
+	ldst_multiple.stderr.exp ldst_multiple.stdout.exp ldst_multiple.vgtest \
+	data-cache-instructions.stderr.exp data-cache-instructions.stdout.exp data-cache-instructions.vgtest
 
 check_PROGRAMS = \
 	allexec \
@@ -40,7 +41,7 @@
 	power6_mf_gpr test_isa_2_06_part1 test_isa_2_06_part2 \
 	test_isa_2_06_part3 test_dfp1 test_dfp2 test_dfp3 test_dfp4 \
 	test_dfp5 test_isa_2_07_part1 test_isa_2_07_part2 \
-	test_tm test_touch_tm ldst_multiple
+	test_tm test_touch_tm ldst_multiple data-cache-instructions
 
 AM_CFLAGS    += @FLAG_M64@
 AM_CXXFLAGS  += @FLAG_M64@
diff --git a/none/tests/ppc64/data-cache-instructions.c b/none/tests/ppc64/data-cache-instructions.c
new file mode 120000
index 0000000..d2b3df0
--- /dev/null
+++ b/none/tests/ppc64/data-cache-instructions.c
@@ -0,0 +1 @@
+../ppc32/data-cache-instructions.c
\ No newline at end of file
diff --git a/none/tests/ppc64/data-cache-instructions.stderr.exp b/none/tests/ppc64/data-cache-instructions.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/ppc64/data-cache-instructions.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/ppc64/data-cache-instructions.stdout.exp b/none/tests/ppc64/data-cache-instructions.stdout.exp
new file mode 100644
index 0000000..bf98e13
--- /dev/null
+++ b/none/tests/ppc64/data-cache-instructions.stdout.exp
@@ -0,0 +1,3 @@
+Passed dcbzl test at aligned address within the test block.
+Passed dcbzl test at un-aligned (1 modulo block_size) address within the test block.
+Passed dcbzl test at un-aligned ((block_size - 1) modulo block_size) address within the test block.
diff --git a/none/tests/ppc64/data-cache-instructions.vgtest b/none/tests/ppc64/data-cache-instructions.vgtest
new file mode 100644
index 0000000..42a3b31
--- /dev/null
+++ b/none/tests/ppc64/data-cache-instructions.vgtest
@@ -0,0 +1,2 @@
+prereq: ../../../tests/power_insn_available dcbzl
+prog: data-cache-instructions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 974f0a5..adfbec4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -30,7 +30,8 @@
 	true \
 	x86_amd64_features \
 	s390x_features \
-	mips_features
+	mips_features \
+	power_insn_available
 
 AM_CFLAGS   += $(AM_FLAG_M3264_PRI)
 AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
diff --git a/tests/power_insn_available.c b/tests/power_insn_available.c
new file mode 100644
index 0000000..1bfea2a
--- /dev/null
+++ b/tests/power_insn_available.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+
+typedef enum exit_codes_ {
+
+#if defined(VGA_ppc32) || defined(VGA_ppc64)
+  /* If the insn that got queried for: exists */
+  POWER_INSN_AVAILABLE    = 0,
+  /* If the insn that got queried for: does not exist on this platform */
+  POWER_INSN_UNAVAILABLE  = 1,
+  /* If the insn that got queried for: does not exist in the vocabulary of this program */
+  POWER_INSN_UNRECOGNIZED = 2,
+
+  /* Note: Please keep USAGE_ERROR last. */
+  USAGE_ERROR
+#else
+  /* When not on a POWER system: */
+  NOT_POWER_ARCH          = 255,
+#endif
+
+} exit_code;
+
+#if defined(VGA_ppc32) || defined(VGA_ppc64)
+/* Signal Handling support for unsupported instructions. */
+static jmp_buf unsup_insn_env;
+static void unsup_insn_handler(int signal_number)
+{
+  if (signal_number == SIGILL)
+    longjmp(unsup_insn_env, 1);
+  return;
+}
+static struct sigaction unsup_insn_action = (struct sigaction) {
+  .sa_handler = &unsup_insn_handler,
+};
+
+/* Instruction existence tests. */
+static bool dcbzl_available(void)
+{
+#define MAX_DCBZL_SZB (128) /* largest known effect of dcbzl */
+  char *test_block = NULL;
+  register char *rb asm ("r14");
+  int err;
+  bool dcbzl_exists = false;
+
+  err = posix_memalign ((void **)&test_block, MAX_DCBZL_SZB, 4 * MAX_DCBZL_SZB);
+  if (err) {
+    fprintf(stderr, "posix_memalign() failed (err = %d [%s])\n", err, strerror(err));
+    return err;
+  }
+
+  rb = test_block;
+
+  if (setjmp(unsup_insn_env) != 0)
+    dcbzl_exists = false;
+  else {
+    sigaction(SIGILL, &unsup_insn_action, NULL);
+    asm volatile ("dcbzl 0, %[RB]" : : [RB] "r" (rb));
+    dcbzl_exists = true;
+  }
+
+  free(test_block);
+  return dcbzl_exists;
+}
+#endif
+
+/* main() */
+int main(int argc, char **argv)
+{
+  exit_code status;
+
+#if defined(VGA_ppc32) || defined(VGA_ppc64)
+  char *insn;
+  if (argc != 2) {
+    fprintf(stderr, "usage: power_insn_available <insn>\n" );
+    exit(USAGE_ERROR);
+  }
+
+  insn = argv[1];
+  if (strcmp (insn, "dcbzl") == 0)
+    status = ((dcbzl_available ()) ? POWER_INSN_AVAILABLE : POWER_INSN_UNAVAILABLE);
+  else
+    /* power_insn_available has not been taught anything about this insn yet. */
+    status = POWER_INSN_UNRECOGNIZED;
+#else
+    status = NOT_POWER_ARCH;
+#endif
+  return status;
+}