[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patches] [PATCH] Make libm smaller using wrappers



Hi,

The following patch substitutes several double-precision libm functions with single-precision counterparts. This transformation certainly degrades the precision of computations, but also reduces the size of the libm library by more than 50% on some architectures.

I should note that precision hit will be spread all across the math functions, not just those being replaced with the wrappers. If a double-precision function is not replaced by a wrapper in this patch, it usually due to the function being small enough not to be wroth the trouble. This, in turn, usually means that the function proxies the work to the other functions.

To make sure the libm functionality remains acceptable, this patch adds a new test that verifies the results within 0.01 error margin.

To control the new feature OPTION_EGLIBC_LIBM_BIG was added.

From the technical side, the patch adds new sysdeps directory ieee754/dbl-wrap which overrides certain "big" functions of ieee754/dbl-64. To enable the override the patch tweaks configure's processing of 'Implied' files and inserts ieee754/dbl-wrap into 'sysnames' just before ieee754/dbl-64. There was a complication with approach due to the fact that option groups configuration file gets processed only during 'make', so to get the setting of OPTION_EGLIBC_LIBM_BIG option configure has to peek into the option-groups.config file.

So far this work was tested on mips-linux-gnu with no changes when the feature is disabled. When wrappers are used, several math tests expectedly fail.

I will also run tests on powerpc-linux-gnuspe configuration and will follow up in a couple of days.

Please follow up with any question or comments.

Regards,

--
Maxim K.
CodeSourcery
 configure                           |   15 ++++++++
 configure.in                        |   15 ++++++++
 math/Makefile                       |    6 ++-
 math/test-dbl-wrap.c                |   67 +++++++++++++++++++++++++++++++++++
 option-groups.def                   |   13 +++++++
 option-groups.defaults              |    1 +
 sysdeps/ieee754/dbl-wrap/dbl-wrap.h |    7 ++++
 sysdeps/ieee754/dbl-wrap/dosincos.c |    1 +
 sysdeps/ieee754/dbl-wrap/e_asin.c   |   17 +++++++++
 sysdeps/ieee754/dbl-wrap/e_atan2.c  |    9 +++++
 sysdeps/ieee754/dbl-wrap/e_exp.c    |    9 +++++
 sysdeps/ieee754/dbl-wrap/e_exp2.c   |    9 +++++
 sysdeps/ieee754/dbl-wrap/e_log.c    |    8 ++++
 sysdeps/ieee754/dbl-wrap/e_pow.c    |    9 +++++
 sysdeps/ieee754/dbl-wrap/s_atan.c   |   13 +++++++
 sysdeps/ieee754/dbl-wrap/s_sin.c    |   27 ++++++++++++++
 sysdeps/ieee754/dbl-wrap/s_tan.c    |   13 +++++++
 sysdeps/ieee754/ldbl-opt/s_atan.c   |    6 +++-
 sysdeps/ieee754/ldbl-opt/s_sin.c    |    6 +++-
 sysdeps/ieee754/ldbl-opt/s_tan.c    |    6 +++-
 20 files changed, 252 insertions(+), 5 deletions(-)

diff --git a/configure b/configure
index 9daa1a9..13f8103 100755
--- a/configure
+++ b/configure
@@ -4441,6 +4441,10 @@ if test "$elf" = yes; then
   sysnames="`echo $elf_dirs | sed -e 's,//,/,g'` $sysnames"
 fi
 
+# Peek into option-groups.config to check if sysdeps/ieee754/dbl-wrap needs
+# to be included into $sysnames.
+grep "OPTION_EGLIBC_LIBM_BIG = n" option-groups.config
+use_dbl_wrap=$?
 
 # Expand the list of system names into a full list of directories
 # from each element's parent name and Implies file (if present).
@@ -4474,6 +4478,11 @@ while test $# -gt 0; do
     for x in $implied_candidate; do
       found=no
       if test -d $xsrcdir$name_base/$x; then
+	# If !OPTION_EGLIBC_LIBM_BIG, add ieee754/dbl-wrap just before
+	# ieee754/dbl-64.
+	if test "$use_dbl_wrap" = "0" -a "$x" = "ieee754/dbl-64"; then
+	  implied="$implied $name_base/ieee754/dbl-wrap";
+	fi
 	implied="$implied $name_base/$x";
 	found=yes
       fi
@@ -4487,6 +4496,12 @@ while test $# -gt 0; do
 	 echo "DEBUG: $name implied $x try($d) {$try_srcdir}$try" >&2
 	if test $try != $xsrcdir$name_base/$x && test -d $try_srcdir$try;
 	then
+	  # If !OPTION_EGLIBC_LIBM_BIG, add ieee754/dbl-wrap just before
+	  # ieee754/dbl-64.
+	  if test "$use_dbl_wrap" = "0" -a "$try" = "sysdeps/ieee754/dbl-64";
+	  then
+	    implied="$implied sysdeps/ieee754/dbl-wrap";
+	  fi
 	  implied="$implied $try"
 	  found=yes
 	  case "$sysnames_add_ons" in
diff --git a/configure.in b/configure.in
index af49d81..eed31a9 100644
--- a/configure.in
+++ b/configure.in
@@ -740,6 +740,10 @@ if test "$elf" = yes; then
   sysnames="`echo $elf_dirs | sed -e 's,//,/,g'` $sysnames"
 fi
 
+# Peek into option-groups.config to check if sysdeps/ieee754/dbl-wrap needs
+# to be included into $sysnames.
+grep "OPTION_EGLIBC_LIBM_BIG = n" option-groups.config
+use_dbl_wrap=$?
 
 # Expand the list of system names into a full list of directories
 # from each element's parent name and Implies file (if present).
@@ -773,6 +777,11 @@ while test $# -gt 0; do
     for x in $implied_candidate; do
       found=no
       if test -d $xsrcdir$name_base/$x; then
+	# If !OPTION_EGLIBC_LIBM_BIG, add ieee754/dbl-wrap just before
+	# ieee754/dbl-64.
+	if test "$use_dbl_wrap" = "0" -a "$x" = "ieee754/dbl-64"; then
+	  implied="$implied $name_base/ieee754/dbl-wrap";
+	fi
 	implied="$implied $name_base/$x";
 	found=yes
       fi
@@ -786,6 +795,12 @@ while test $# -gt 0; do
 	 echo "[DEBUG]: $name implied $x try($d) {$try_srcdir}$try" >&2
 	if test $try != $xsrcdir$name_base/$x && test -d $try_srcdir$try;
 	then
+	  # If !OPTION_EGLIBC_LIBM_BIG, add ieee754/dbl-wrap just before
+	  # ieee754/dbl-64.
+	  if test "$use_dbl_wrap" = "0" -a "$try" = "sysdeps/ieee754/dbl-64";
+	  then
+	    implied="$implied sysdeps/ieee754/dbl-wrap";
+	  fi
 	  implied="$implied $try"
 	  found=yes
 	  case "$sysnames_add_ons" in
diff --git a/math/Makefile b/math/Makefile
index c263801..7d01be1 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -90,9 +90,11 @@ long-c-yes = $(calls:=l)
 distribute += $(filter-out $(generated),$(long-m-yes:=.c) $(long-c-yes:=.c))
 
 # Rules for the test suite.
-tests = test-matherr test-fenv atest-exp atest-sincos atest-exp2 basic-test \
+tests = test-matherr test-fenv basic-test \
 	test-misc test-fpucw tst-definitions test-tgmath test-tgmath-ret \
-	bug-nextafter bug-nexttoward bug-tgmath1 test-tgmath-int test-tgmath2
+	bug-nextafter bug-nexttoward bug-tgmath1 test-tgmath-int test-tgmath2 \
+	test-dbl-wrap
+tests-$(OPTION_EGLIBC_LIBM_BIG) += atest-exp atest-sincos atest-exp2
 # We do the `long double' tests only if this data type is available and
 # distinct from `double'.
 test-longdouble-yes = test-ldouble test-ildoubl
diff --git a/math/test-dbl-wrap.c b/math/test-dbl-wrap.c
new file mode 100644
index 0000000..0462a3f
--- /dev/null
+++ b/math/test-dbl-wrap.c
@@ -0,0 +1,67 @@
+#include <math.h>
+
+#define N 4
+
+int
+main (void)
+{
+  int i;
+  int result = 0;
+
+  const double eps = 0.01, pi = 3.14;
+  const double sin_data[N][2]
+    = {{0.0, 0.0}, {pi / 6, 0.5}, {pi / 4, 0.707}, {pi / 3, 0.866}};
+  const double exp_data[N][2]
+    = {{0.0, 1.0}, {0.5, 1.649}, {1.0, 2.718}, {2.718, 15.150}};
+
+  for (i = 0; i < N; ++i)
+    {
+      double x, y;
+      double s1, c1, t1, e1;
+      double s2, c2, t2, as2, ac2, at2, e2, l2;
+
+      x = sin_data[i][0];
+      s1 = sin_data[i][1];
+      c1 = sqrt (1 - s1 * s1);
+      t1 = s1 / c1;
+
+      s2 = sin (x);
+      c2 = cos (x);
+      t2 = tan (x);
+      as2 = asin (s1);
+      ac2 = acos (c1);
+      at2 = atan (t1);
+
+      y = exp_data[i][0];
+      e1 = exp_data[i][1];
+
+      e2 = exp (y);
+      l2 = log (e1);
+
+      if (fabs (s1 - s2) > eps)
+	result |= 1;
+
+      if (fabs (c1 - c2) > eps)
+	result |= 2;
+
+      if (fabs (t1 - t2) > eps)
+	result |= 4;
+
+      if (fabs (x - as2) > eps)
+	result |= 8;
+
+      if (fabs (x - ac2) > eps)
+	result |= 16;
+
+      if (fabs (x - at2) > eps)
+	result |= 32;
+
+      if (fabs (e1 - e2) > eps)
+	result |= 64;
+
+      if (fabs (y - l2) > eps)
+	result |= 128;
+    }
+
+  return result;
+}
diff --git a/option-groups.def b/option-groups.def
index 26ef4bb..240cf52 100644
--- a/option-groups.def
+++ b/option-groups.def
@@ -434,6 +434,19 @@ config OPTION_EGLIBC_LIBM
        group, you will not be able to build 'libstdc++' against the
        resulting EGLIBC installation.
 
+config OPTION_EGLIBC_LIBM_BIG
+   bool "Math library size"
+   help
+       This option group enables default configuration of the math library.
+       Not selecting this option group removes most of extended and 
+       double precision math functions and replaces them with wrappers
+       to the single precision couterparts.
+       Doing so greatly degrades quality of calculations carried
+       out by the functions of the math library, but also significantly
+       reduces the size of the libm.
+       This option group is useful for systems that do not rely on precise
+       floating point math.
+
 config OPTION_EGLIBC_LOCALES
    bool "Locale definitions"
    help
diff --git a/option-groups.defaults b/option-groups.defaults
index 4653e64..8cc7e08 100644
--- a/option-groups.defaults
+++ b/option-groups.defaults
@@ -24,6 +24,7 @@ OPTION_EGLIBC_FTRAVERSE = y
 OPTION_EGLIBC_GETLOGIN = y
 OPTION_EGLIBC_INET = y
 OPTION_EGLIBC_LIBM = y
+OPTION_EGLIBC_LIBM_BIG = y
 OPTION_EGLIBC_LOCALES = y
 OPTION_EGLIBC_LOCALE_CODE = y
 OPTION_EGLIBC_NIS = y
diff --git a/sysdeps/ieee754/dbl-wrap/dbl-wrap.h b/sysdeps/ieee754/dbl-wrap/dbl-wrap.h
new file mode 100644
index 0000000..81c0faa
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/dbl-wrap.h
@@ -0,0 +1,7 @@
+#ifdef NO_LONG_DOUBLE
+typedef float wrap_type_t;
+# define WRAP_FUNC(func) func ## f
+#else
+typedef long double wrap_type_t;
+# define WRAP_FUNC(func) func ## l
+#endif
diff --git a/sysdeps/ieee754/dbl-wrap/dosincos.c b/sysdeps/ieee754/dbl-wrap/dosincos.c
new file mode 100644
index 0000000..40a8c17
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/dosincos.c
@@ -0,0 +1 @@
+/* empty */
diff --git a/sysdeps/ieee754/dbl-wrap/e_asin.c b/sysdeps/ieee754/dbl-wrap/e_asin.c
new file mode 100644
index 0000000..86416b3
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_asin.c
@@ -0,0 +1,17 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_asin) (wrap_type_t);
+
+double
+__ieee754_asin (double x)
+{
+  return (double) WRAP_FUNC (__ieee754_asin) ((wrap_type_t) x);
+}
+
+wrap_type_t WRAP_FUNC (__ieee754_acos) (wrap_type_t);
+
+double
+__ieee754_acos (double x)
+{
+  return (double) WRAP_FUNC (__ieee754_acos) ((wrap_type_t) x);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/e_atan2.c b/sysdeps/ieee754/dbl-wrap/e_atan2.c
new file mode 100644
index 0000000..4bcb058
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_atan2.c
@@ -0,0 +1,9 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_atan2) (wrap_type_t, wrap_type_t);
+
+double
+__ieee754_atan2 (double y, double x)
+{
+  return (double) WRAP_FUNC (__ieee754_atan2) ((wrap_type_t) y, (wrap_type_t) x);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/e_exp.c b/sysdeps/ieee754/dbl-wrap/e_exp.c
new file mode 100644
index 0000000..df3447e
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_exp.c
@@ -0,0 +1,9 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_exp) (wrap_type_t);
+
+double
+__ieee754_exp (double x)
+{
+  return (double) WRAP_FUNC (__ieee754_exp) ((wrap_type_t) x);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/e_exp2.c b/sysdeps/ieee754/dbl-wrap/e_exp2.c
new file mode 100644
index 0000000..121c297
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_exp2.c
@@ -0,0 +1,9 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_exp2) (wrap_type_t);
+
+double
+__ieee754_exp2 (double x)
+{
+  return (double) WRAP_FUNC (__ieee754_exp2) ((wrap_type_t) x);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/e_log.c b/sysdeps/ieee754/dbl-wrap/e_log.c
new file mode 100644
index 0000000..557775f
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_log.c
@@ -0,0 +1,8 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_log) (wrap_type_t);
+
+double __ieee754_log (double x)
+{
+  return (double) WRAP_FUNC (__ieee754_log) ((wrap_type_t) x);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/e_pow.c b/sysdeps/ieee754/dbl-wrap/e_pow.c
new file mode 100644
index 0000000..0f74d51
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/e_pow.c
@@ -0,0 +1,9 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__ieee754_pow) (wrap_type_t, wrap_type_t);
+
+double
+__ieee754_pow (double x, double y)
+{
+  return (double) WRAP_FUNC (__ieee754_pow) ((wrap_type_t) x, (wrap_type_t) y);
+}
diff --git a/sysdeps/ieee754/dbl-wrap/s_atan.c b/sysdeps/ieee754/dbl-wrap/s_atan.c
new file mode 100644
index 0000000..5099dd6
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/s_atan.c
@@ -0,0 +1,13 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (atan) (wrap_type_t);
+
+double
+atan (double x)
+{
+  return (double) WRAP_FUNC (atan) ((wrap_type_t) x);
+}
+
+#ifdef NO_LONG_DOUBLE
+weak_alias (atan, atanl)
+#endif
diff --git a/sysdeps/ieee754/dbl-wrap/s_sin.c b/sysdeps/ieee754/dbl-wrap/s_sin.c
new file mode 100644
index 0000000..fbe2a97
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/s_sin.c
@@ -0,0 +1,27 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (__sin) (wrap_type_t);
+
+double
+__sin (double x)
+{
+  return (double) WRAP_FUNC (__sin) ((wrap_type_t) x);
+}
+
+wrap_type_t WRAP_FUNC (__cos) (wrap_type_t);
+
+double
+__cos (double x)
+{
+  return (double) WRAP_FUNC (__cos) ((wrap_type_t) x);
+}
+
+weak_alias (__cos, cos)
+weak_alias (__sin, sin)
+
+#ifdef NO_LONG_DOUBLE
+strong_alias (__sin, __sinl)
+weak_alias (__sin, sinl)
+strong_alias (__cos, __cosl)
+weak_alias (__cos, cosl)
+#endif
diff --git a/sysdeps/ieee754/dbl-wrap/s_tan.c b/sysdeps/ieee754/dbl-wrap/s_tan.c
new file mode 100644
index 0000000..ddc99af
--- /dev/null
+++ b/sysdeps/ieee754/dbl-wrap/s_tan.c
@@ -0,0 +1,13 @@
+#include "dbl-wrap.h"
+
+wrap_type_t WRAP_FUNC (tan) (wrap_type_t);
+
+double
+tan (double x)
+{
+  return (double) WRAP_FUNC (tan) ((wrap_type_t) x);
+}
+
+#ifdef NO_LONG_DOUBLE
+weak_alias (tan, tanl)
+#endif
diff --git a/sysdeps/ieee754/ldbl-opt/s_atan.c b/sysdeps/ieee754/ldbl-opt/s_atan.c
index 5fbd5e6..aa88c52 100644
--- a/sysdeps/ieee754/ldbl-opt/s_atan.c
+++ b/sysdeps/ieee754/ldbl-opt/s_atan.c
@@ -1,5 +1,9 @@
 #include <math_ldbl_opt.h>
-#include <sysdeps/ieee754/dbl-64/s_atan.c>
+#if __OPTION_EGLIBC_LIBM_BIG
+# include <sysdeps/ieee754/dbl-64/s_atan.c>
+#else
+# include <sysdeps/ieee754/dbl-wrap/s_atan.c>
+#endif
 #if LONG_DOUBLE_COMPAT(libm, GLIBC_2_0)
 compat_symbol (libm, atan, atanl, GLIBC_2_0);
 #endif
diff --git a/sysdeps/ieee754/ldbl-opt/s_sin.c b/sysdeps/ieee754/ldbl-opt/s_sin.c
index a11d5a3..c1dd48a 100644
--- a/sysdeps/ieee754/ldbl-opt/s_sin.c
+++ b/sysdeps/ieee754/ldbl-opt/s_sin.c
@@ -3,7 +3,11 @@
 #include <math_ldbl_opt.h>
 #undef NAN
 #undef sincos
-#include <sysdeps/ieee754/dbl-64/s_sin.c>
+#if __OPTION_EGLIBC_LIBM_BIG
+# include <sysdeps/ieee754/dbl-64/s_sin.c>
+#else
+# include <sysdeps/ieee754/dbl-wrap/s_sin.c>
+#endif
 #if LONG_DOUBLE_COMPAT(libm, GLIBC_2_0)
 compat_symbol (libm, __sin, sinl, GLIBC_2_0);
 compat_symbol (libm, __cos, cosl, GLIBC_2_0);
diff --git a/sysdeps/ieee754/ldbl-opt/s_tan.c b/sysdeps/ieee754/ldbl-opt/s_tan.c
index 6b0fec0..ddf71e8 100644
--- a/sysdeps/ieee754/ldbl-opt/s_tan.c
+++ b/sysdeps/ieee754/ldbl-opt/s_tan.c
@@ -1,5 +1,9 @@
 #include <math_ldbl_opt.h>
-#include <sysdeps/ieee754/dbl-64/s_tan.c>
+#if __OPTION_EGLIBC_LIBM_BIG
+# include <sysdeps/ieee754/dbl-64/s_tan.c>
+#else
+# include <sysdeps/ieee754/dbl-wrap/s_tan.c>
+#endif
 #if LONG_DOUBLE_COMPAT(libm, GLIBC_2_0)
 compat_symbol (libm, tan, tanl, GLIBC_2_0);
 #endif