This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Start converting libm tests from code to data


This patch starts the conversion of libm-test.inc to storing tests in
data rather than code.  Tests in data use the same TEST_*
function-like constructs to define them, but bracketed by START_DATA
and END_DATA calls; the function running the tests them uses
constructs such as RUN_TEST_LOOP_f_f.

This patch does an initial conversion of tests for acos (default
rounding mode, and rounding mode explicitly set to FE_TONEAREST).  The
idea is that if this is OK then I'll go on to convert other
functions.  Various of the complexity in gen-libm-test.pl to handle
tests both inside and outside functions can go away once all tests are
converted to the new scheme.

Thomas's sNaN tests patch had tests conditional on "if (SNAN_TESTS
(FLOAT))".  With tests in data rather than code, the intended way of
representing such "if" conditions on tests is that gen-libm-test.pl
should put a flag in the "exceptions" field (here, to indicate that
the test involves sNaN values), and that each RUN_TEST_* macro will
gain a conditional "if (enable_test (EXCEPTIONS))" or similar, where
enable_test would check all relevant such flags.  (As another example,
any future testing for -ffinite-math-only would check a flag to say
whether the test has non-finite arguments or results.  I also propose
to replace #if conditions on TEST_INLINE by a flag checked in this
way.)

Tested x86_64 and x86.

2013-05-10  Joseph Myers  <joseph@codesourcery.com>

	* math/gen-libm-test.pl (adjust_arg): New function.
	(special_functions): Handle generating output in both functions
	and arrays.
	(parse_args): Likewise.
	(generate_testfile): Handle START_DATA and END_DATA.  Pass extra
	$in_func argument to parse_args.
	* math/libm-test.inc (struct test_f_f_data): New type.
	(IF_ROUND_INIT_): New macro.
	(IF_ROUND_INIT_FE_DOWNWARD): Likewise.
	(IF_ROUND_INIT_FE_TONEAREST): Likewise.
	(IF_ROUND_INIT_FE_TOWARDZERO): Likewise.
	(IF_ROUND_INIT_FE_UPWARD): Likewise.
	(ROUND_RESTORE_): Likewise.
	(ROUND_RESTORE_FE_DOWNWARD): Likewise.
	(ROUND_RESTORE_FE_TONEAREST): Likewise.
	(ROUND_RESTORE_FE_TOWARDZERO): Likewise.
	(ROUND_RESTORE_FE_UPWARD): Likewise.
	(RUN_TEST_LOOP_f_f): New macro.
	(acos_test_data): New variable.
	(acos_test): Run tests with RUN_TEST_LOOP_f_f.
	(acos_tonearest_test_data): New variable.
	(acos_test_tonearest): Run tests with RUN_TEST_LOOP_f_f.

diff --git a/math/gen-libm-test.pl b/math/gen-libm-test.pl
index 3170b05..656c9ce 100755
--- a/math/gen-libm-test.pl
+++ b/math/gen-libm-test.pl
@@ -185,10 +185,21 @@ sub new_test {
   return $rest;
 }
 
+# Adjust an argument or expected value for use in a constant
+# initializer.
+sub adjust_arg {
+  my ($arg, $in_func) = @_;
+  if (!$in_func) {
+    $arg =~ s/(plus_zero|minus_zero|plus_infty|minus_infty|qnan_value
+	       |max_value|min_value|min_subnorm_value)/\U$1\E_INIT/xg;
+  }
+  return $arg;
+}
+
 # Treat some functions especially.
 # Currently only sincos needs extra treatment.
 sub special_functions {
-  my ($file, $args) = @_;
+  my ($file, $in_func, $args) = @_;
   my (@args, $str, $test, $cline);
 
   @args = split /,\s*/, $args;
@@ -196,33 +207,43 @@ sub special_functions {
   unless ($args[0] =~ /sincos/) {
     die ("Don't know how to handle $args[0] extra.");
   }
-  $cline = "  RUN_TEST_sincos ($args[1]";
+  if ($in_func) {
+    $cline = "  RUN_TEST_sincos (" . adjust_arg ($args[1], $in_func);
+  } else {
+    $cline = "    { " . adjust_arg ($args[1], $in_func);
+  }
 
   $str = 'sincos (' . &beautify ($args[1]) . ', &sin_res, &cos_res)';
   # handle sin
   $test = $str . ' puts ' . &beautify ($args[2]) . ' in sin_res';
 
-  $cline .= ", \"$test\", sin_res, $args[2]";
+  my ($sin_res_var) = ($in_func ? ", sin_res" : "");
+  $cline .= ", \"$test\"$sin_res_var, " . adjust_arg ($args[2], $in_func);
   $cline .= &new_test ($test, $args[4], 0);
 
   # handle cos
   $test = $str . ' puts ' . &beautify ($args[3]) . ' in cos_res';
-  $cline .= ", \"$test\", cos_res, $args[3]";
+  my ($cos_res_var) = ($in_func ? ", cos_res" : "");
+  $cline .= ", \"$test\"$cos_res_var, " . adjust_arg ($args[3], $in_func);
   $cline .= &new_test ($test, $args[4], 1);
-  $cline .= ");\n";
+  if ($in_func) {
+    $cline .= ");\n";
+  } else {
+    $cline .= " },\n";
+  }
   print $file $cline;
 }
 
 # Parse the arguments to TEST_x_y
 sub parse_args {
-  my ($file, $descr, $fct, $args) = @_;
+  my ($file, $descr, $fct, $in_func, $args) = @_;
   my (@args, $str, $descr_args, $descr_res, @descr);
   my ($current_arg, $cline, $i);
   my (@special);
   my ($extra_var, $call);
 
   if ($descr eq 'extra') {
-    &special_functions ($file, $args);
+    &special_functions ($file, $in_func, $args);
     return;
   }
   ($descr_args, $descr_res) = split /_/,$descr, 2;
@@ -297,19 +318,29 @@ sub parse_args {
   # Reset some variables to start again
   $current_arg = 1;
   $extra_var = 0;
-  $cline = "RUN_TEST_$descr";
+  if ($in_func) {
+    $cline = "RUN_TEST_$descr";
+  } else {
+    $cline = "{ ";
+  }
   # Special handling for some macros:
-  if ($args[0] =~ /fpclassify|isnormal|isfinite|isinf|isnan|issignaling|signbit
-      |isgreater|isgreaterequal|isless|islessequal
+  if ($in_func && $args[0] =~ /fpclassify|isnormal|isfinite|isinf|isnan
+      |issignaling|signbit|isgreater|isgreaterequal|isless|islessequal
       |islessgreater|isunordered/x) {
-      $cline = "${cline}_tg";
+    $cline = "${cline}_tg";
+  }
+  if ($in_func) {
+    $cline .= " (";
+  }
+  $cline .= "\"$str\"";
+  if ($in_func) {
+    $cline .= ", $args[0]";
   }
-  $cline .= " (\"$str\", $args[0]";
   @descr = split //,$descr_args;
   for ($i=0; $i <= $#descr; $i++) {
     # FLOAT, int, long int, long long int
     if ($descr[$i] =~ /f|i|l|L/) {
-      $cline .= ", $args[$current_arg]";
+      $cline .= ", " . adjust_arg ($args[$current_arg], $in_func);
       $current_arg++;
       next;
     }
@@ -319,7 +350,8 @@ sub parse_args {
     }
     # complex
     if ($descr[$i] eq 'c') {
-      $cline .= ", $args[$current_arg], $args[$current_arg+1]";
+      $cline .= ", " . adjust_arg ($args[$current_arg], $in_func);
+      $cline .= ", " . adjust_arg ($args[$current_arg+1], $in_func);
       $current_arg += 2;
       next;
     }
@@ -329,10 +361,11 @@ sub parse_args {
   @descr = split //,$descr_res;
   foreach (@descr) {
     if ($_ =~ /b|f|i|l|L/ ) {
-      $cline .= $args[$current_arg];
+      $cline .= adjust_arg ($args[$current_arg], $in_func);
       $current_arg++;
     } elsif ($_ eq 'c') {
-      $cline .= "$args[$current_arg], $args[$current_arg+1]";
+      $cline .= adjust_arg ($args[$current_arg], $in_func);
+      $cline .= ", " . adjust_arg ($args[$current_arg+1], $in_func);
       $current_arg += 2;
     } elsif ($_ eq '1') {
       push @special, $args[$current_arg];
@@ -348,11 +381,12 @@ sub parse_args {
       my ($extra_expected) = $special[0];
       my ($run_extra) = ($extra_expected ne "IGNORE" ? 1 : 0);
       my ($str) = "$call sets x to $extra_expected";
+      my ($out_var) = ($in_func ? ", x" : "");
       if (!$run_extra) {
 	$str = "";
 	$extra_expected = "0";
       }
-      $cline .= ", \"$str\", x, 123456789, $run_extra, $extra_expected";
+      $cline .= ", \"$str\"$out_var, 123456789, $run_extra, $extra_expected";
       if ($run_extra) {
 	$cline .= &new_test ($str, undef, 0);
       } else {
@@ -364,11 +398,12 @@ sub parse_args {
       my ($extra_expected) = $special[0];
       my ($run_extra) = ($extra_expected ne "IGNORE" ? 1 : 0);
       my ($str) = "$call sets signgam to $extra_expected";
+      my ($out_var) = ($in_func ? ", signgam" : "");
       if (!$run_extra) {
 	$str = "";
 	$extra_expected = "0";
       }
-      $cline .= ", \"$str\", signgam, 0, $run_extra, $extra_expected";
+      $cline .= ", \"$str\"$out_var, 0, $run_extra, $extra_expected";
       if ($run_extra) {
 	$cline .= &new_test ($str, undef, 0);
       } else {
@@ -380,11 +415,13 @@ sub parse_args {
       my ($extra_expected) = $special[0];
       my ($run_extra) = ($extra_expected ne "IGNORE" ? 1 : 0);
       my ($str) = "$call sets x to $extra_expected";
+      my ($out_var) = ($in_func ? ", x" : "");
       if (!$run_extra) {
 	$str = "";
 	$extra_expected = "0";
       }
-      $cline .= ", \"$str\", x, 123.456789, $run_extra, $extra_expected";
+      $extra_expected = adjust_arg ($extra_expected, $in_func);
+      $cline .= ", \"$str\"$out_var, 123.456789, $run_extra, $extra_expected";
       if ($run_extra) {
 	$cline .= &new_test ($str, undef, 0);
       } else {
@@ -396,11 +433,12 @@ sub parse_args {
       my ($extra_expected) = $special[0];
       my ($run_extra) = ($extra_expected ne "IGNORE" ? 1 : 0);
       my ($str) = "$call sets x to $extra_expected";
+      my ($out_var) = ($in_func ? ", x" : "");
       if (!$run_extra) {
 	$str = "";
 	$extra_expected = "0";
       }
-      $cline .= ", \"$str\", x, 123456789, $run_extra, $extra_expected";
+      $cline .= ", \"$str\"$out_var, 123456789, $run_extra, $extra_expected";
       if ($run_extra) {
 	$cline .= &new_test ($str, undef, 0);
       } else {
@@ -408,19 +446,24 @@ sub parse_args {
       }
     }
   }
-  print $file "  $cline);\n";
+  if ($in_func) {
+    print $file "  $cline);\n";
+  } else {
+    print $file "    $cline },\n";
+  }
 }
 
 # Generate libm-test.c
 sub generate_testfile {
   my ($input, $output) = @_;
   my ($lasttext);
-  my (@args, $i, $str, $thisfct);
+  my (@args, $i, $str, $thisfct, $in_func);
 
   open INPUT, $input or die ("Can't open $input: $!");
   open OUTPUT, ">$output" or die ("Can't open $output: $!");
 
   # Replace the special macros
+  $in_func = 0;
   while (<INPUT>) {
 
     # TEST_...
@@ -428,18 +471,30 @@ sub generate_testfile {
       my ($descr, $args);
       chop;
       ($descr, $args) = ($_ =~ /TEST_(\w+)\s*\((.*)\)/);
-      &parse_args (\*OUTPUT, $descr, $thisfct, $args);
+      &parse_args (\*OUTPUT, $descr, $thisfct, $in_func, $args);
+      next;
+    }
+    # START_DATA (function)
+    if (/START_DATA/) {
+      ($thisfct) = ($_ =~ /START_DATA\s*\((.*)\)/);
+      $in_func = 0;
       next;
     }
     # START (function)
     if (/START/) {
       ($thisfct) = ($_ =~ /START\s*\((.*)\)/);
+      $in_func = 1;
       print OUTPUT "  init_max_error ();\n";
       next;
     }
+    # END_DATA (function)
+    if (/END_DATA/) {
+      next;
+    }
     # END (function)
     if (/END/) {
       my ($fct, $line, $type);
+      $in_func = 0;
       if (/complex/) {
 	s/,\s*complex\s*//;
 	$type = 'complex';
diff --git a/math/libm-test.inc b/math/libm-test.inc
index 1ff59e0..6b4556d 100644
--- a/math/libm-test.inc
+++ b/math/libm-test.inc
@@ -863,12 +863,53 @@ check_longlong (const char *test_name, long long int computed,
   errno = 0;
 }
 
+/* Structures for each kind of test.  */
+struct test_f_f_data
+{
+  const char *test_name;
+  FLOAT arg;
+  FLOAT expected;
+  FLOAT max_ulp;
+  int exceptions;
+};
+
+/* Set the rounding mode, or restore the saved value.  */
+#define IF_ROUND_INIT_	/* Empty.  */
+#define IF_ROUND_INIT_FE_DOWNWARD		\
+  int save_round_mode = fegetround ();		\
+  if (fesetround (FE_DOWNWARD) == 0)
+#define IF_ROUND_INIT_FE_TONEAREST		\
+  int save_round_mode = fegetround ();		\
+  if (fesetround (FE_TONEAREST) == 0)
+#define IF_ROUND_INIT_FE_TOWARDZERO		\
+  int save_round_mode = fegetround ();		\
+  if (fesetround (FE_TOWARDZERO) == 0)
+#define IF_ROUND_INIT_FE_UPWARD			\
+  int save_round_mode = fegetround ();		\
+  if (fesetround (FE_UPWARD) == 0)
+#define ROUND_RESTORE_	/* Empty.  */
+#define ROUND_RESTORE_FE_DOWNWARD		\
+  fesetround (save_round_mode)
+#define ROUND_RESTORE_FE_TONEAREST		\
+  fesetround (save_round_mode)
+#define ROUND_RESTORE_FE_TOWARDZERO		\
+  fesetround (save_round_mode)
+#define ROUND_RESTORE_FE_UPWARD			\
+  fesetround (save_round_mode)
+
 /* Run an individual test, including any required setup and checking
-   of results.  */
+   of results, or loop over all tests in an array.  */
 #define RUN_TEST_f_f(TEST_NAME, FUNC_NAME, ARG, EXPECTED,	\
 		     MAX_ULP, EXCEPTIONS)			\
   check_float (TEST_NAME, FUNC (FUNC_NAME) (ARG), EXPECTED,	\
 	       MAX_ULP, EXCEPTIONS)
+#define RUN_TEST_LOOP_f_f(FUNC_NAME, ARRAY, ROUNDING_MODE)		\
+  IF_ROUND_INIT_ ## ROUNDING_MODE					\
+    for (size_t i = 0; i < sizeof (ARRAY) / sizeof (ARRAY)[0]; i++)	\
+      RUN_TEST_f_f ((ARRAY)[i].test_name, FUNC_NAME, (ARRAY)[i].arg,	\
+		    (ARRAY)[i].expected, (ARRAY)[i].max_ulp,		\
+		    (ARRAY)[i].exceptions);				\
+  ROUND_RESTORE_ ## ROUNDING_MODE
 #define RUN_TEST_2_f(TEST_NAME, FUNC_NAME, ARG1, ARG2, EXPECTED,	\
 		     MAX_ULP, EXCEPTIONS)				\
   check_float (TEST_NAME, FUNC (FUNC_NAME) (ARG1, ARG2), EXPECTED,	\
@@ -1012,6 +1053,43 @@ matherr (struct exception *x __attribute__ ((unused)))
   Please keep them alphabetically sorted!
 ****************************************************************************/
 
+static const struct test_f_f_data acos_test_data[] =
+  {
+    START_DATA (acos),
+    TEST_f_f (acos, plus_infty, qnan_value, INVALID_EXCEPTION),
+    TEST_f_f (acos, minus_infty, qnan_value, INVALID_EXCEPTION),
+    TEST_f_f (acos, qnan_value, qnan_value),
+
+    /* |x| > 1: */
+    TEST_f_f (acos, 1.125L, qnan_value, INVALID_EXCEPTION),
+    TEST_f_f (acos, -1.125L, qnan_value, INVALID_EXCEPTION),
+    TEST_f_f (acos, max_value, qnan_value, INVALID_EXCEPTION),
+    TEST_f_f (acos, -max_value, qnan_value, INVALID_EXCEPTION),
+
+    TEST_f_f (acos, 0, M_PI_2l),
+    TEST_f_f (acos, minus_zero, M_PI_2l),
+    TEST_f_f (acos, 1, 0),
+    TEST_f_f (acos, -1, M_PIl),
+    TEST_f_f (acos, 0.5, M_PI_6l*2.0),
+    TEST_f_f (acos, -0.5, M_PI_6l*4.0),
+    TEST_f_f (acos, 0.75L, 0.722734247813415611178377352641333362L),
+    TEST_f_f (acos, 2e-17L, 1.57079632679489659923132169163975144L),
+    TEST_f_f (acos, 0.0625L, 1.50825556499840522843072005474337068L),
+    TEST_f_f (acos, 0x0.ffffffp0L, 3.4526698471620358760324948263873649728491e-4L),
+    TEST_f_f (acos, -0x0.ffffffp0L, 3.1412473866050770348750401337968641476999L),
+#ifndef TEST_FLOAT
+    TEST_f_f (acos, 0x0.ffffffff8p0L, 1.5258789062648029736620564947844627548516e-5L),
+    TEST_f_f (acos, -0x0.ffffffff8p0L, 3.1415773948007305904329067627145550395696L),
+    TEST_f_f (acos, 0x0.ffffffffffffp0L, 8.4293697021788088529885473244391795127130e-8L),
+    TEST_f_f (acos, -0x0.ffffffffffffp0L, 3.1415925692960962166745548533940296398054L),
+#endif
+#if defined TEST_LDOUBLE && LDBL_MANT_DIG >= 64
+    TEST_f_f (acos, 0x0.ffffffffffffffffp0L, 3.2927225399135962333718255320079907245059e-10L),
+    TEST_f_f (acos, -0x0.ffffffffffffffffp0L, 3.1415926532605209844712837599423203309964L),
+#endif
+    END_DATA (acos)
+  };
+
 static void
 acos_test (void)
 {
@@ -1022,46 +1100,26 @@ acos_test (void)
     return;
 
   START (acos);
-
-  TEST_f_f (acos, plus_infty, qnan_value, INVALID_EXCEPTION);
-  TEST_f_f (acos, minus_infty, qnan_value, INVALID_EXCEPTION);
-  TEST_f_f (acos, qnan_value, qnan_value);
-
-  /* |x| > 1: */
-  TEST_f_f (acos, 1.125L, qnan_value, INVALID_EXCEPTION);
-  TEST_f_f (acos, -1.125L, qnan_value, INVALID_EXCEPTION);
-  TEST_f_f (acos, max_value, qnan_value, INVALID_EXCEPTION);
-  TEST_f_f (acos, -max_value, qnan_value, INVALID_EXCEPTION);
-
-  TEST_f_f (acos, 0, M_PI_2l);
-  TEST_f_f (acos, minus_zero, M_PI_2l);
-  TEST_f_f (acos, 1, 0);
-  TEST_f_f (acos, -1, M_PIl);
-  TEST_f_f (acos, 0.5, M_PI_6l*2.0);
-  TEST_f_f (acos, -0.5, M_PI_6l*4.0);
-  TEST_f_f (acos, 0.75L, 0.722734247813415611178377352641333362L);
-  TEST_f_f (acos, 2e-17L, 1.57079632679489659923132169163975144L);
-  TEST_f_f (acos, 0.0625L, 1.50825556499840522843072005474337068L);
-  TEST_f_f (acos, 0x0.ffffffp0L, 3.4526698471620358760324948263873649728491e-4L);
-  TEST_f_f (acos, -0x0.ffffffp0L, 3.1412473866050770348750401337968641476999L);
-#ifndef TEST_FLOAT
-  TEST_f_f (acos, 0x0.ffffffff8p0L, 1.5258789062648029736620564947844627548516e-5L);
-  TEST_f_f (acos, -0x0.ffffffff8p0L, 3.1415773948007305904329067627145550395696L);
-  TEST_f_f (acos, 0x0.ffffffffffffp0L, 8.4293697021788088529885473244391795127130e-8L);
-  TEST_f_f (acos, -0x0.ffffffffffffp0L, 3.1415925692960962166745548533940296398054L);
-#endif
-#if defined TEST_LDOUBLE && LDBL_MANT_DIG >= 64
-  TEST_f_f (acos, 0x0.ffffffffffffffffp0L, 3.2927225399135962333718255320079907245059e-10L);
-  TEST_f_f (acos, -0x0.ffffffffffffffffp0L, 3.1415926532605209844712837599423203309964L);
-#endif
+  RUN_TEST_LOOP_f_f (acos, acos_test_data, );
   END (acos);
 }
 
 
+static const struct test_f_f_data acos_tonearest_test_data[] =
+  {
+    START_DATA (acos_tonearest),
+    TEST_f_f (acos, 0, M_PI_2l),
+    TEST_f_f (acos, minus_zero, M_PI_2l),
+    TEST_f_f (acos, 1, 0),
+    TEST_f_f (acos, -1, M_PIl),
+    TEST_f_f (acos, 0.5, M_PI_6l*2.0),
+    TEST_f_f (acos, -0.5, M_PI_6l*4.0),
+    END_DATA (acos_tonearest)
+  };
+
 static void
 acos_test_tonearest (void)
 {
-  int save_round_mode;
   errno = 0;
   FUNC(acos) (0);
   if (errno == ENOSYS)
@@ -1069,21 +1127,7 @@ acos_test_tonearest (void)
     return;
 
   START (acos_tonearest);
-
-  save_round_mode = fegetround ();
-
-  if (!fesetround (FE_TONEAREST))
-    {
-      TEST_f_f (acos, 0, M_PI_2l);
-      TEST_f_f (acos, minus_zero, M_PI_2l);
-      TEST_f_f (acos, 1, 0);
-      TEST_f_f (acos, -1, M_PIl);
-      TEST_f_f (acos, 0.5, M_PI_6l*2.0);
-      TEST_f_f (acos, -0.5, M_PI_6l*4.0);
-    }
-
-  fesetround (save_round_mode);
-
+  RUN_TEST_LOOP_f_f (acos, acos_tonearest_test_data, FE_TONEAREST);
   END (acos_tonearest);
 }
 

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]