From 830dc991f3bc055717fe0c46bd24746f8177f455 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 22 Feb 2016 10:53:57 +0000 Subject: [PATCH] Support powerpc This fixes #113, based on files from FreeBSD. Tested on machine hosted by [OSUOSL](http://osuosl.org/services/powerdev/). --- Make.inc | 5 + Makefile | 4 +- include/openlibm_fenv.h | 2 + include/openlibm_fenv_powerpc.h | 274 ++++++++++++++++++++++++++++++++ powerpc/Make.files | 1 + powerpc/fenv.c | 48 ++++++ src/Make.files | 15 +- src/fpmath.h | 2 + src/powerpc_fpmath.h | 49 ++++++ src/s_nearbyint.c | 1 - src/s_rintl.c | 22 +++ 11 files changed, 415 insertions(+), 8 deletions(-) create mode 100644 include/openlibm_fenv_powerpc.h create mode 100644 powerpc/Make.files create mode 100644 powerpc/fenv.c create mode 100644 src/powerpc_fpmath.h diff --git a/Make.inc b/Make.inc index 6858ea3..815d0c9 100644 --- a/Make.inc +++ b/Make.inc @@ -48,7 +48,12 @@ endif CFLAGS_add += -std=c99 -Wall -I$(OPENLIBM_HOME) -I$(OPENLIBM_HOME)/include -I$(OPENLIBM_HOME)/ld80 -I$(OPENLIBM_HOME)/$(ARCH) -I$(OPENLIBM_HOME)/src -DASSEMBLER -D__BSD_VISIBLE -Wno-implicit-function-declaration # The optimization flag may be overriden with the environment variable CFLAGS. +ifeq ($(ARCH),powerpc) +# tests hang at higher optimization levels +CFLAGS ?= -O0 +else CFLAGS ?= -O2 +endif default: all diff --git a/Makefile b/Makefile index d05f97e..ca03ae8 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,10 @@ include ./Make.inc SUBDIRS = src $(ARCH) bsdsrc ifneq ($(ARCH), arm) +ifneq ($(ARCH), powerpc) SUBDIRS += ld80 endif +endif define INC_template TEST=test @@ -26,7 +28,7 @@ OBJS = $(patsubst %.f,%.f.o,\ .PHONY: all check test clean distclean install -all: libopenlibm.a libopenlibm.$(SHLIB_EXT) +all: libopenlibm.a libopenlibm.$(SHLIB_EXT) check test: test/test-double test/test-float test/test-double diff --git a/include/openlibm_fenv.h b/include/openlibm_fenv.h index 4474861..7b9cdbb 100644 --- a/include/openlibm_fenv.h +++ b/include/openlibm_fenv.h @@ -8,6 +8,8 @@ #include #elif defined(__i386__) #include +#elif defined(__powerpc__) +#include #else #error "Unsupported platform" #endif diff --git a/include/openlibm_fenv_powerpc.h b/include/openlibm_fenv_powerpc.h new file mode 100644 index 0000000..15ffdf4 --- /dev/null +++ b/include/openlibm_fenv_powerpc.h @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef __uint32_t fenv_t; +typedef __uint32_t fexcept_t; + +/* Exception flags */ +#define FE_INEXACT 0x02000000 +#define FE_DIVBYZERO 0x04000000 +#define FE_UNDERFLOW 0x08000000 +#define FE_OVERFLOW 0x10000000 +#define FE_INVALID 0x20000000 /* all types of invalid FP ops */ + +/* + * The PowerPC architecture has extra invalid flags that indicate the + * specific type of invalid operation occurred. These flags may be + * tested, set, and cleared---but not masked---separately. All of + * these bits are cleared when FE_INVALID is cleared, but only + * FE_VXSOFT is set when FE_INVALID is explicitly set in software. + */ +#define FE_VXCVI 0x00000100 /* invalid integer convert */ +#define FE_VXSQRT 0x00000200 /* square root of a negative */ +#define FE_VXSOFT 0x00000400 /* software-requested exception */ +#define FE_VXVC 0x00080000 /* ordered comparison involving NaN */ +#define FE_VXIMZ 0x00100000 /* inf * 0 */ +#define FE_VXZDZ 0x00200000 /* 0 / 0 */ +#define FE_VXIDI 0x00400000 /* inf / inf */ +#define FE_VXISI 0x00800000 /* inf - inf */ +#define FE_VXSNAN 0x01000000 /* operation on a signalling NaN */ +#define FE_ALL_INVALID (FE_VXCVI | FE_VXSQRT | FE_VXSOFT | FE_VXVC | \ + FE_VXIMZ | FE_VXZDZ | FE_VXIDI | FE_VXISI | \ + FE_VXSNAN | FE_INVALID) +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_ALL_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0001 +#define FE_UPWARD 0x0002 +#define FE_DOWNWARD 0x0003 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _FPUSW_SHIFT 22 +#define _ENABLE_MASK ((FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) >> _FPUSW_SHIFT) + +#ifndef _SOFT_FLOAT +#define __mffs(__env) __asm __volatile("mffs %0" : "=f" (*(__env))) +#define __mtfsf(__env) __asm __volatile("mtfsf 255,%0" : : "f" (__env)) +#else +#define __mffs(__env) +#define __mtfsf(__env) +#endif + +union __fpscr { + double __d; + struct { + __uint32_t __junk; + fenv_t __reg; + } __bits; +}; + +__fenv_static inline int +feclearexcept(int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_INVALID; + __mffs(&__r.__d); + __r.__bits.__reg &= ~__excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__flagp = __r.__bits.__reg & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_EXCEPT; + __mffs(&__r.__d); + __r.__bits.__reg &= ~__excepts; + __r.__bits.__reg |= *__flagp & __excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_VXSOFT; + __mffs(&__r.__d); + __r.__bits.__reg |= __excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return (__r.__bits.__reg & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return (__r.__bits.__reg & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + union __fpscr __r; + + if (__round & ~_ROUND_MASK) + return (-1); + __mffs(&__r.__d); + __r.__bits.__reg &= ~_ROUND_MASK; + __r.__bits.__reg |= __round; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__envp = __r.__bits.__reg; + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__envp = __r.__d; + __r.__bits.__reg &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + union __fpscr __r; + + __r.__bits.__reg = *__envp; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + __r.__bits.__reg &= FE_ALL_EXCEPT; + __r.__bits.__reg |= *__envp; + __mtfsf(__r.__d); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +static inline int +feenableexcept(int __mask) +{ + union __fpscr __r; + fenv_t __oldmask; + + __mffs(&__r.__d); + __oldmask = __r.__bits.__reg; + __r.__bits.__reg |= (__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT; + __mtfsf(__r.__d); + return ((__oldmask & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +static inline int +fedisableexcept(int __mask) +{ + union __fpscr __r; + fenv_t __oldmask; + + __mffs(&__r.__d); + __oldmask = __r.__bits.__reg; + __r.__bits.__reg &= ~((__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT); + __mtfsf(__r.__d); + return ((__oldmask & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +static inline int +fegetexcept(void) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return ((__r.__bits.__reg & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/powerpc/Make.files b/powerpc/Make.files new file mode 100644 index 0000000..483a7cc --- /dev/null +++ b/powerpc/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/powerpc/fenv.c b/powerpc/fenv.c new file mode 100644 index 0000000..78e6015 --- /dev/null +++ b/powerpc/fenv.c @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +const fenv_t __fe_dfl_env = 0x00000000; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); diff --git a/src/Make.files b/src/Make.files index 4b5723f..c9e0be9 100644 --- a/src/Make.files +++ b/src/Make.files @@ -4,7 +4,7 @@ $(CUR_SRCS) = common.c \ e_expf.c e_fmod.c e_fmodf.c \ e_hypot.c e_hypotf.c e_j0.c e_j0f.c e_j1.c e_j1f.c \ e_jn.c e_jnf.c e_lgamma.c e_lgamma_r.c e_lgammaf.c e_lgammaf_r.c \ - e_lgammal.c e_log.c e_log10.c e_log10f.c e_log2.c e_log2f.c e_logf.c \ + e_log.c e_log10.c e_log10f.c e_log2.c e_log2f.c e_logf.c \ e_pow.c e_powf.c e_remainder.c e_remainderf.c \ e_rem_pio2.c e_rem_pio2f.c \ e_sinh.c e_sinhf.c e_sqrt.c e_sqrtf.c \ @@ -20,12 +20,12 @@ $(CUR_SRCS) = common.c \ s_fminf.c s_fpclassify.c \ s_frexp.c s_frexpf.c s_ilogb.c s_ilogbf.c \ s_isinf.c s_isfinite.c s_isnormal.c s_isnan.c \ - s_llrint.c s_llrintf.c s_llround.c s_llroundf.c s_llroundl.c \ + s_llrint.c s_llrintf.c s_llround.c s_llroundf.c \ s_log1p.c s_log1pf.c s_logb.c s_logbf.c s_lrint.c s_lrintf.c \ - s_lround.c s_lroundf.c s_lroundl.c s_modf.c s_modff.c \ + s_lround.c s_lroundf.c s_modf.c s_modff.c \ s_nearbyint.c s_nextafter.c s_nextafterf.c \ s_nexttowardf.c s_remquo.c s_remquof.c \ - s_rint.c s_rintf.c s_round.c s_roundf.c s_roundl.c \ + s_rint.c s_rintf.c s_round.c s_roundf.c \ s_scalbln.c s_scalbn.c s_scalbnf.c s_signbit.c \ s_signgam.c s_sin.c s_sincos.c \ s_sinf.c s_sincosf.c s_tan.c s_tanf.c s_tanh.c s_tanhf.c s_tgammaf.c \ @@ -37,17 +37,19 @@ $(CUR_SRCS) += s_nan.c endif ifneq ($(ARCH), arm) +ifneq ($(ARCH), powerpc) # C99 long double functions $(CUR_SRCS) += s_copysignl.c s_fabsl.c s_llrintl.c s_lrintl.c s_modfl.c # If long double != double use these; otherwise, we alias the double versions. $(CUR_SRCS) += e_acosl.c e_asinl.c e_atan2l.c e_fmodl.c \ s_fmaxl.c s_fminl.c s_ilogbl.c \ - e_hypotl.c e_remainderl.c e_sqrtl.c \ + e_hypotl.c e_lgammal.c e_remainderl.c e_sqrtl.c \ s_atanl.c s_ceill.c s_cosl.c s_cprojl.c \ s_csqrtl.c s_floorl.c s_fmal.c \ s_frexpl.c s_logbl.c s_nexttoward.c \ - s_remquol.c s_cpowl.c s_cargl.c \ + s_remquol.c s_roundl.c s_lroundl.c s_llroundl.c \ + s_cpowl.c s_cargl.c \ s_sinl.c s_sincosl.c s_tanl.c s_truncl.c w_cabsl.c \ s_nextafterl.c s_rintl.c s_scalbnl.c polevll.c \ s_casinl.c s_ctanl.c \ @@ -55,6 +57,7 @@ $(CUR_SRCS) += e_acosl.c e_asinl.c e_atan2l.c e_fmodl.c \ s_catanl.c s_csinl.c s_cacosl.c s_cexpl.c s_csinhl.c s_ccoshl.c \ s_clogl.c s_ctanhl.c s_ccosl.c s_cbrtl.c endif +endif # C99 complex functions $(CUR_SRCS) += s_ccosh.c s_ccoshf.c s_cexp.c s_cexpf.c \ diff --git a/src/fpmath.h b/src/fpmath.h index 492bf90..2b9afaa 100644 --- a/src/fpmath.h +++ b/src/fpmath.h @@ -37,6 +37,8 @@ #else #include "i386_fpmath.h" #endif +#elif defined(__powerpc__) +#include "powerpc_fpmath.h" #endif #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) diff --git a/src/powerpc_fpmath.h b/src/powerpc_fpmath.h new file mode 100644 index 0000000..6d80eb4 --- /dev/null +++ b/src/powerpc_fpmath.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2003 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +union IEEEl2bits { + long double e; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; + } bits; +}; + +#define mask_nbit_l(u) ((void)0) +#define LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 + +#define LDBL_MANH_SIZE 20 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while(0) diff --git a/src/s_nearbyint.c b/src/s_nearbyint.c index 7d6af82..ee9cf6f 100644 --- a/src/s_nearbyint.c +++ b/src/s_nearbyint.c @@ -54,4 +54,3 @@ fn(type x) \ DECL(double, nearbyint, rint) DECL(float, nearbyintf, rintf) -DECL(long double, nearbyintl, rintl) diff --git a/src/s_rintl.c b/src/s_rintl.c index 6a35be6..1983ec7 100644 --- a/src/s_rintl.c +++ b/src/s_rintl.c @@ -28,6 +28,7 @@ //__FBSDID("$FreeBSD: src/lib/msun/src/s_rintl.c,v 1.5 2008/02/22 11:59:05 bde Exp $"); #include +#include #include #include "fpmath.h" @@ -92,3 +93,24 @@ rintl(long double x) return (x); } + +/* + * We save and restore the floating-point environment to avoid raising + * an inexact exception. We can get away with using fesetenv() + * instead of feclearexcept()/feupdateenv() to restore the environment + * because the only exception defined for rint() is overflow, and + * rounding can't overflow as long as emax >= p. + */ +#define DECL(type, fn, rint) \ +DLLEXPORT type \ +fn(type x) \ +{ \ + type ret; \ + fenv_t env; \ + \ + fegetenv(&env); \ + ret = rint(x); \ + fesetenv(&env); \ + return (ret); \ +} +DECL(long double, nearbyintl, rintl)