llvmpipe: Factor out shared test code into a separate module.
authorJosé Fonseca <jfonseca@vmware.com>
Fri, 7 Aug 2009 00:20:01 +0000 (01:20 +0100)
committerJosé Fonseca <jfonseca@vmware.com>
Sat, 29 Aug 2009 08:21:24 +0000 (09:21 +0100)
src/gallium/drivers/llvmpipe/SConscript
src/gallium/drivers/llvmpipe/lp_bld_type.h
src/gallium/drivers/llvmpipe/lp_test.h [new file with mode: 0644]
src/gallium/drivers/llvmpipe/lp_test_blend.c
src/gallium/drivers/llvmpipe/lp_test_main.c [new file with mode: 0644]

index 58e6a888e8728852c4bbda8f93640e8a2b6fc0ba..7982e4219a3c2e9d9313178716b744ea7a72d4b5 100644 (file)
@@ -64,7 +64,7 @@ env.Program(
 
 env.Program(
     target = 'lp_test_blend',
-    source = ['lp_test_blend.c'],
+    source = ['lp_test_blend.c', 'lp_test_main.c'],
 )
 
 Export('llvmpipe')
index 566a86ed0656590e76a60c59c2643c5745f0fc38..37d6885049ccf0e2b6775b1e7287b1ca765ac3b6 100644 (file)
@@ -48,6 +48,8 @@
  */
 #define LP_MAX_VECTOR_LENGTH 16
 
+#define LP_MAX_TYPE_WIDTH 64
+
 
 /**
  * The LLVM type system can't conveniently express all the things we care about
diff --git a/src/gallium/drivers/llvmpipe/lp_test.h b/src/gallium/drivers/llvmpipe/lp_test.h
new file mode 100644 (file)
index 0000000..dd173fe
--- /dev/null
@@ -0,0 +1,124 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/**
+ * @file
+ * Shared testing code.
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+
+#ifndef LP_TEST_H
+#define LP_TEST_H
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+
+#include <llvm-c/Core.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/Transforms/Scalar.h>
+
+#include "pipe/p_state.h"
+#include "util/u_format.h"
+#include "util/u_math.h"
+#include "util/u_debug_dump.h"
+
+#include "lp_bld_type.h"
+
+
+void
+write_tsv_header(FILE *fp);
+
+
+boolean
+test_some(unsigned verbose, FILE *fp, unsigned long n);
+
+
+boolean
+test_all(unsigned verbose, FILE *fp);
+
+
+static INLINE uint64_t
+rdtsc(void)
+{
+#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64)
+   uint32_t hi, lo;
+   __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
+   return ((uint64_t)lo) | (((uint64_t)hi) << 32);
+#else
+   return 0;
+#endif
+}
+
+
+float
+random_float(void);
+
+
+void
+dump_type(FILE *fp, union lp_type type);
+
+
+double
+read_elem(union lp_type type, const void *src, unsigned index);
+
+
+void
+write_elem(union lp_type type, void *dst, unsigned index, double src);
+
+
+void
+random_elem(union lp_type type, void *dst, unsigned index);
+
+
+void
+read_vec(union lp_type type, const void *src, double *dst);
+
+
+void
+write_vec(union lp_type type, void *dst, const double *src);
+
+
+void
+random_vec(union lp_type type, void *dst);
+
+
+boolean
+compare_vec(union lp_type type, const void *res, const double *ref);
+
+
+void
+dump_vec(FILE *fp, union lp_type type, const void *src);
+
+
+#endif /* !LP_TEST_H */
index 56cd800b37ff99fe189f60fcadfcc0cfc343b6bd..f42a9a9e420aed51f9d56fbdb3484b8855d8bb4d 100644 (file)
  */
 
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <float.h>
-
-#include <llvm-c/Core.h>
-#include <llvm-c/Analysis.h>
-#include <llvm-c/ExecutionEngine.h>
-#include <llvm-c/Target.h>
-#include <llvm-c/BitWriter.h>
-#include <llvm-c/Transforms/Scalar.h>
-
-#include "pipe/p_state.h"
-#include "util/u_format.h"
-#include "util/u_math.h"
-#include "util/u_debug_dump.h"
-
 #include "lp_bld.h"
 #include "lp_bld_type.h"
 #include "lp_bld_arit.h"
-
-
-unsigned verbose = 0;
+#include "lp_test.h"
 
 
 typedef void (*blend_test_ptr_t)(const void *src, const void *dst, const void *con, void *res);
 
 
-static void
+void
 write_tsv_header(FILE *fp)
 {
    fprintf(fp,
@@ -145,19 +127,6 @@ dump_blend_type(FILE *fp,
 }
 
 
-static INLINE uint64_t
-rdtsc(void)
-{
-#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64)
-   uint32_t hi, lo;
-   __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
-   return ((uint64_t)lo) | (((uint64_t)hi) << 32);
-#else
-   return 0;
-#endif
-}
-
-
 static LLVMValueRef
 add_blend_test(LLVMModuleRef module,
                const struct pipe_blend_state *blend,
@@ -210,13 +179,6 @@ add_blend_test(LLVMModuleRef module,
 }
 
 
-static float
-random_float(void)
-{
-    return (float)((double)random()/(double)RAND_MAX);
-}
-
-
 /** Add and limit result to ceiling of 1.0 */
 #define ADD_SAT(R, A, B) \
 do { \
@@ -233,13 +195,13 @@ do { \
 static void
 compute_blend_ref_term(unsigned rgb_factor,
                        unsigned alpha_factor,
-                       const float *factor,
-                       const float *src, 
-                       const float *dst, 
-                       const float *con, 
-                       float *term)
+                       const double *factor,
+                       const double *src,
+                       const double *dst,
+                       const double *con,
+                       double *term)
 {
-   float temp;
+   double temp;
 
    switch (rgb_factor) {
    case PIPE_BLENDFACTOR_ONE:
@@ -379,13 +341,13 @@ compute_blend_ref_term(unsigned rgb_factor,
 
 static void
 compute_blend_ref(const struct pipe_blend_state *blend,
-                  const float *src, 
-                  const float *dst, 
-                  const float *con, 
-                  float *res)
+                  const double *src,
+                  const double *dst,
+                  const double *con,
+                  double *res)
 {
-   float src_term[4];
-   float dst_term[4];
+   double src_term[4];
+   double dst_term[4];
 
    compute_blend_ref_term(blend->rgb_src_factor, blend->alpha_src_factor, src, src, dst, con, src_term);
    compute_blend_ref_term(blend->rgb_dst_factor, blend->alpha_dst_factor, dst, src, dst, con, dst_term);
@@ -449,7 +411,8 @@ compute_blend_ref(const struct pipe_blend_state *blend,
 
 
 static boolean
-test_one(FILE *fp,
+test_one(unsigned verbose,
+         FILE *fp,
          const struct pipe_blend_state *blend,
          union lp_type type)
 {
@@ -464,7 +427,7 @@ test_one(FILE *fp,
    const unsigned n = 32;
    int64_t cycles[n];
    double cycles_avg = 0.0;
-   unsigned i, j, k;
+   unsigned i, j;
 
    if(verbose >= 1)
       dump_blend_type(stdout, blend, type);
@@ -510,108 +473,64 @@ test_one(FILE *fp,
 
    success = TRUE;
    for(i = 0; i < n && success; ++i) {
+      uint8_t src[LP_MAX_VECTOR_LENGTH*LP_MAX_TYPE_WIDTH/8];
+      uint8_t dst[LP_MAX_VECTOR_LENGTH*LP_MAX_TYPE_WIDTH/8];
+      uint8_t con[LP_MAX_VECTOR_LENGTH*LP_MAX_TYPE_WIDTH/8];
+      uint8_t res[LP_MAX_VECTOR_LENGTH*LP_MAX_TYPE_WIDTH/8];
+      double ref[LP_MAX_VECTOR_LENGTH];
       int64_t start_counter = 0;
       int64_t end_counter = 0;
 
-      if(type.floating && type.width == 32) {
-         float src[LP_MAX_VECTOR_LENGTH];
-         float dst[LP_MAX_VECTOR_LENGTH];
-         float con[LP_MAX_VECTOR_LENGTH];
-         float ref[LP_MAX_VECTOR_LENGTH];
-         float res[LP_MAX_VECTOR_LENGTH];
-
-         for(j = 0; j < type.length; ++j) {
-            src[j] = random_float();
-            dst[j] = random_float();
-            con[j] = random_float();
-         }
+      random_vec(type, src);
+      random_vec(type, dst);
+      random_vec(type, con);
+
+      {
+         double fsrc[LP_MAX_VECTOR_LENGTH];
+         double fdst[LP_MAX_VECTOR_LENGTH];
+         double fcon[LP_MAX_VECTOR_LENGTH];
+
+         read_vec(type, src, fsrc);
+         read_vec(type, dst, fdst);
+         read_vec(type, con, fcon);
 
          for(j = 0; j < type.length; j += 4)
-            compute_blend_ref(blend, src + j, dst + j, con + j, ref + j);
-
-         start_counter = rdtsc();
-         blend_test_ptr(src, dst, con, res);
-         end_counter = rdtsc();
-
-         for(j = 0; j < type.length; ++j)
-            if(fabs(res[j] - ref[j]) > FLT_EPSILON)
-               success = FALSE;
-
-         if (!success) {
-            dump_blend_type(stderr, blend, type);
-            fprintf(stderr, "\n");
-            fprintf(stderr, "MISMATCH\n");
-            fprintf(stderr, "  Result:   ");
-            for(j = 0; j < type.length; ++j)
-               fprintf(stderr, " %f", res[j]);
-            fprintf(stderr, "\n");
-            fprintf(stderr, "  Expected: ");
-            for(j = 0; j < type.length; ++j)
-               fprintf(stderr, " %f", ref[j]);
-            fprintf(stderr, "\n");
-         }
+            compute_blend_ref(blend, fsrc + j, fdst + j, fcon + j, ref + j);
       }
-      else if(!type.floating && !type.fixed && !type.sign && type.norm && type.width == 8) {
-         uint8_t src[LP_MAX_VECTOR_LENGTH];
-         uint8_t dst[LP_MAX_VECTOR_LENGTH];
-         uint8_t con[LP_MAX_VECTOR_LENGTH];
-         uint8_t ref[LP_MAX_VECTOR_LENGTH];
-         uint8_t res[LP_MAX_VECTOR_LENGTH];
-
-         for(j = 0; j < type.length; ++j) {
-            src[j] = random() & 0xff;
-            dst[j] = random() & 0xff;
-            con[j] = random() & 0xff;
-         }
 
-         for(j = 0; j < type.length; j += 4) {
-            float srcf[4];
-            float dstf[4];
-            float conf[4];
-            float reff[4];
+      start_counter = rdtsc();
+      blend_test_ptr(src, dst, con, res);
+      end_counter = rdtsc();
 
-            for(k = 0; k < 4; ++k) {
-               srcf[k] = (1.0f/255.0f)*src[j + k];
-               dstf[k] = (1.0f/255.0f)*dst[j + k];
-               conf[k] = (1.0f/255.0f)*con[j + k];
-            }
+      cycles[i] = end_counter - start_counter;
 
-            compute_blend_ref(blend, srcf, dstf, conf, reff);
+      success = compare_vec(type, res, ref);
 
-            for(k = 0; k < 4; ++k)
-               ref[j + k] = (uint8_t)(reff[k]*255.0f + 0.5f);
-         }
+      if (!success) {
+         dump_blend_type(stderr, blend, type);
+         fprintf(stderr, "\n");
+         fprintf(stderr, "MISMATCH\n");
 
-         start_counter = rdtsc();
-         blend_test_ptr(src, dst, con, res);
-         end_counter = rdtsc();
+         fprintf(stderr, "  Src: ");
+         dump_vec(stderr, type, src);
+         fprintf(stderr, "\n");
 
-         for(j = 0; j < type.length; ++j) {
-            int delta = (int)res[j] - (int)ref[j];
-            if (delta < 0)
-               delta = -delta;
-            if(delta > 1)
-               success = FALSE;
-         }
+         fprintf(stderr, "  Dst: ");
+         dump_vec(stderr, type, dst);
+         fprintf(stderr, "\n");
 
-         if (!success) {
-            dump_blend_type(stderr, blend, type);
-            fprintf(stderr, "\n");
-            fprintf(stderr, "MISMATCH\n");
-            fprintf(stderr, "  Result:   ");
-            for(j = 0; j < type.length; ++j)
-               fprintf(stderr, " %3u", res[j]);
-            fprintf(stderr, "\n");
-            fprintf(stderr, "  Expected: ");
-            for(j = 0; j < type.length; ++j)
-               fprintf(stderr, " %3u", ref[j]);
-            fprintf(stderr, "\n");
-         }
-      }
-      else
-         assert(0);
+         fprintf(stderr, "  Con: ");
+         dump_vec(stderr, type, con);
+         fprintf(stderr, "\n");
 
-      cycles[i] = end_counter - start_counter;
+         fprintf(stderr, "  Res: ");
+         dump_vec(stderr, type, res);
+         fprintf(stderr, "\n");
+
+         fprintf(stderr, "  Ref: ");
+         dump_vec(stderr, type, ref);
+         fprintf(stderr, "\n");
+      }
    }
 
    /*
@@ -725,8 +644,8 @@ const unsigned num_factors = sizeof(blend_factors)/sizeof(blend_factors[0]);
 const unsigned num_types = sizeof(blend_types)/sizeof(blend_types[0]);
 
 
-static boolean 
-test_all(FILE *fp)
+boolean
+test_all(unsigned verbose, FILE *fp)
 {
    const unsigned *rgb_func;
    const unsigned *rgb_src_factor;
@@ -759,7 +678,7 @@ test_all(FILE *fp)
                         blend.alpha_src_factor  = *alpha_src_factor;
                         blend.alpha_dst_factor  = *alpha_dst_factor;
 
-                        if(!test_one(fp, &blend, *type))
+                        if(!test_one(verbose, fp, &blend, *type))
                           success = FALSE;
 
                      }
@@ -774,8 +693,8 @@ test_all(FILE *fp)
 }
 
 
-static boolean 
-test_some(FILE *fp, unsigned long n)
+boolean
+test_some(unsigned verbose, FILE *fp, unsigned long n)
 {
    const unsigned *rgb_func;
    const unsigned *rgb_src_factor;
@@ -813,45 +732,10 @@ test_some(FILE *fp, unsigned long n)
          blend.alpha_src_factor  = *alpha_src_factor;
          blend.alpha_dst_factor  = *alpha_dst_factor;
 
-         if(!test_one(fp, &blend, *type))
+         if(!test_one(verbose, fp, &blend, *type))
            success = FALSE;
       }
    }
 
    return success;
 }
-
-
-int main(int argc, char **argv)
-{
-   unsigned long n = 1000;
-   FILE *fp = NULL;
-   unsigned i;
-   boolean success;
-
-   for(i = 1; i < argc; ++i) {
-      if(strcmp(argv[i], "-v") == 0)
-         ++verbose;
-      else if(strcmp(argv[i], "-o") == 0)
-         fp = fopen(argv[++i], "wt");
-      else
-         n = atoi(argv[i]);
-   }
-
-   if(fp) {
-      /* Warm up the caches */
-      test_some(NULL, 100);
-
-      write_tsv_header(fp);
-   }
-      
-   if(n)
-      success = test_some(fp, n);
-   else
-      success = test_all(fp);
-
-   if(fp)
-      fclose(fp);
-
-   return success ? 0 : 1;
-}
diff --git a/src/gallium/drivers/llvmpipe/lp_test_main.c b/src/gallium/drivers/llvmpipe/lp_test_main.c
new file mode 100644 (file)
index 0000000..2ce3fa1
--- /dev/null
@@ -0,0 +1,404 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+/**
+ * @file
+ * Shared testing code.
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+
+#include "lp_bld_const.h"
+#include "lp_test.h"
+
+
+void
+dump_type(FILE *fp,
+          union lp_type type)
+{
+   fprintf(fp, "%s%u%sx%u",
+           type.floating ? "f" : (type.fixed ? "h" : (type.sign ? "s" : "u")),
+           type.width,
+           type.norm ? "n" : "",
+           type.length);
+}
+
+
+double
+read_elem(union lp_type type, const void *src, unsigned index)
+{
+   double scale = lp_const_scale(type);
+   double value;
+   assert(index < type.length);
+   if (type.floating) {
+      switch(type.width) {
+      case 32:
+         value = *((const float *)src + index);
+         break;
+      case 64:
+         value =  *((const double *)src + index);
+         break;
+      default:
+         assert(0);
+         return 0.0;
+      }
+   }
+   else {
+      if(type.sign) {
+         switch(type.width) {
+         case 8:
+            value = *((const int8_t *)src + index);
+            break;
+         case 16:
+            value = *((const int16_t *)src + index);
+            break;
+         case 32:
+            value = *((const int32_t *)src + index);
+            break;
+         case 64:
+            value = *((const int64_t *)src + index);
+            break;
+         default:
+            assert(0);
+            return 0.0;
+         }
+      }
+      else {
+         switch(type.width) {
+         case 8:
+            value = *((const uint8_t *)src + index);
+            break;
+         case 16:
+            value = *((const uint16_t *)src + index);
+            break;
+         case 32:
+            value = *((const uint32_t *)src + index);
+            break;
+         case 64:
+            value = *((const uint64_t *)src + index);
+            break;
+         default:
+            assert(0);
+            return 0.0;
+         }
+      }
+   }
+   return value/scale;
+}
+
+
+void
+write_elem(union lp_type type, void *dst, unsigned index, double src)
+{
+   double scale = lp_const_scale(type);
+   double value = scale*src;
+   assert(index < type.length);
+   if (type.floating) {
+      switch(type.width) {
+      case 32:
+         *((float *)dst + index) = (float)(value);
+         break;
+      case 64:
+          *((double *)dst + index) = value;
+         break;
+      default:
+         assert(0);
+      }
+   }
+   else {
+      if(type.sign) {
+         switch(type.width) {
+         case 8:
+            *((int8_t *)dst + index) = (int8_t)round(value);
+            break;
+         case 16:
+            *((int16_t *)dst + index) = (int16_t)round(value);
+            break;
+         case 32:
+            *((int32_t *)dst + index) = (int32_t)round(value);
+            break;
+         case 64:
+            *((int64_t *)dst + index) = (int32_t)round(value);
+            break;
+         default:
+            assert(0);
+         }
+      }
+      else {
+         switch(type.width) {
+         case 8:
+            *((uint8_t *)dst + index) = (uint8_t)round(value);
+            break;
+         case 16:
+            *((uint16_t *)dst + index) = (uint16_t)round(value);
+            break;
+         case 32:
+            *((uint32_t *)dst + index) = (uint32_t)round(value);
+            break;
+         case 64:
+            *((uint64_t *)dst + index) = (uint64_t)round(value);
+            break;
+         default:
+            assert(0);
+         }
+      }
+   }
+}
+
+
+void
+random_elem(union lp_type type, void *dst, unsigned index)
+{
+   assert(index < type.length);
+   if (type.floating) {
+      double value = (double)random()/(double)RAND_MAX;
+      if(!type.norm)
+         value += (double)random();
+      if(type.sign)
+         if(random() & 1)
+            value = -value;
+      switch(type.width) {
+      case 32:
+         *((float *)dst + index) = (float)value;
+         break;
+      case 64:
+          *((double *)dst + index) = value;
+         break;
+      default:
+         assert(0);
+      }
+   }
+   else {
+      switch(type.width) {
+      case 8:
+         *((uint8_t *)dst + index) = (uint8_t)random();
+         break;
+      case 16:
+         *((uint16_t *)dst + index) = (uint16_t)random();
+         break;
+      case 32:
+         *((uint32_t *)dst + index) = (uint32_t)random();
+         break;
+      case 64:
+         *((uint64_t *)dst + index) = (uint64_t)random();
+         break;
+      default:
+         assert(0);
+      }
+   }
+}
+
+
+void
+read_vec(union lp_type type, const void *src, double *dst)
+{
+   unsigned i;
+   for (i = 0; i < type.length; ++i)
+      dst[i] = read_elem(type, src, i);
+}
+
+
+void
+write_vec(union lp_type type, void *dst, const double *src)
+{
+   unsigned i;
+   for (i = 0; i < type.length; ++i)
+      write_elem(type, dst, i, src[i]);
+}
+
+
+float
+random_float(void)
+{
+    return (float)((double)random()/(double)RAND_MAX);
+}
+
+
+void
+random_vec(union lp_type type, void *dst)
+{
+   unsigned i;
+   for (i = 0; i < type.length; ++i)
+      random_elem(type, dst, i);
+}
+
+
+boolean
+compare_vec(union lp_type type, const void *res, const double *ref)
+{
+   double eps;
+   unsigned i;
+
+   if (type.floating) {
+      switch(type.width) {
+      case 32:
+         eps = FLT_EPSILON;
+         break;
+      case 64:
+         eps = DBL_EPSILON;
+         break;
+      default:
+         assert(0);
+         eps = 0.0;
+         break;
+      }
+   }
+   else {
+      double scale = lp_const_scale(type);
+      eps = 1.0/scale;
+   }
+
+   for (i = 0; i < type.length; ++i) {
+      double res_elem = read_elem(type, res, i);
+      double ref_elem = ref[i];
+      double delta = fabs(res_elem - ref_elem);
+      if(delta >= 2.0*eps)
+         return FALSE;
+   }
+
+   return TRUE;
+}
+
+
+void
+dump_vec(FILE *fp, union lp_type type, const void *src)
+{
+   unsigned i;
+   for (i = 0; i < type.length; ++i) {
+      if(i)
+         fprintf(fp, " ");
+      if (type.floating) {
+         double value;
+         switch(type.width) {
+         case 32:
+            value = *((const float *)src + i);
+            break;
+         case 64:
+            value = *((const double *)src + i);
+            break;
+         default:
+            assert(0);
+            value = 0.0;
+         }
+         fprintf(fp, "%f", value);
+      }
+      else {
+         if(type.sign) {
+            long long value;
+            const char *format;
+            switch(type.width) {
+            case 8:
+               value = *((const int8_t *)src + i);
+               format = "%3lli";
+               break;
+            case 16:
+               value = *((const int16_t *)src + i);
+               format = "%5lli";
+               break;
+            case 32:
+               value = *((const int32_t *)src + i);
+               format = "%10lli";
+               break;
+            case 64:
+               value = *((const int64_t *)src + i);
+               format = "%20lli";
+               break;
+            default:
+               assert(0);
+               value = 0.0;
+               format = "?";
+            }
+            fprintf(fp, format, value);
+         }
+         else {
+            unsigned long long value;
+            const char *format;
+            switch(type.width) {
+            case 8:
+               value = *((const uint8_t *)src + i);
+               format = "%4llu";
+               break;
+            case 16:
+               value = *((const uint16_t *)src + i);
+               format = "%6llu";
+               break;
+            case 32:
+               value = *((const uint32_t *)src + i);
+               format = "%11llu";
+               break;
+            case 64:
+               value = *((const uint64_t *)src + i);
+               format = "%21llu";
+               break;
+            default:
+               assert(0);
+               value = 0.0;
+               format = "?";
+            }
+            fprintf(fp, format, value);
+         }
+      }
+   }
+}
+
+
+int main(int argc, char **argv)
+{
+   unsigned verbose = 0;
+   FILE *fp = NULL;
+   unsigned long n = 1000;
+   unsigned i;
+   boolean success;
+
+   for(i = 1; i < argc; ++i) {
+      if(strcmp(argv[i], "-v") == 0)
+         ++verbose;
+      else if(strcmp(argv[i], "-o") == 0)
+         fp = fopen(argv[++i], "wt");
+      else
+         n = atoi(argv[i]);
+   }
+
+   if(fp) {
+      /* Warm up the caches */
+      test_some(0, NULL, 100);
+
+      write_tsv_header(fp);
+   }
+      
+   if(n)
+      success = test_some(verbose, fp, n);
+   else
+      success = test_all(verbose, fp);
+
+   if(fp)
+      fclose(fp);
+
+   return success ? 0 : 1;
+}