diff mbox

[libgfortran] Add FPU Support for powerpc

Message ID 4FBAEF9F.9000402@verizon.net
State New
Headers show

Commit Message

rbmj May 22, 2012, 1:45 a.m. UTC
Hi everyone,

This patch adds FPU support for powerpc on platforms that do not have 
glibc.  It is basically the same code as glibc has.  The motivation for 
this was that right now there is no fpu-target.h that works for 
powerpc-*-vxworks.

Again, 90% of this code comes directly from glibc.  But on vxworks 
targets there is no glibc.

I also patched the configure.host script in order to add this in.

Any opinions?

Robert Mason

Comments

Steven Bosscher June 28, 2012, 10:23 p.m. UTC | #1
On Tue, May 22, 2012 at 3:45 AM, rbmj <rbmj@verizon.net> wrote:
> Hi everyone,
>
> This patch adds FPU support for powerpc on platforms that do not have glibc.
>  It is basically the same code as glibc has.  The motivation for this was
> that right now there is no fpu-target.h that works for powerpc-*-vxworks.
>
> Again, 90% of this code comes directly from glibc.  But on vxworks targets
> there is no glibc.
>
> I also patched the configure.host script in order to add this in.
>
> Any opinions?

Since AFAICT nobody has responded...

I suppose this is something you need, or you would probably not be
working on it. I wouldn't have thought of VxWorks as an obvious target
platform for a Fortran compiler. :-)

The copying of the code from glibc (LGPL code) to libgfortran
(GPL+exception) is something that you probably need permission for
from the FSF. For the VxWorks specific bits, you could poke the only
listed VxWorks maintainer in MAINTAINERS (hi Nathan!).

For the configure.host bits,

+  powerpc)

Not powerpc64? Or at least "powerpc|ppc"?
IIUC this test is overridden for powerpc-linux by a glibc test
following your new code, right? What happens for e.g. powerpc-aix?
Shouldn't your test also be conditional on have_feenableexcept?

Ciao!
Steven
diff mbox

Patch

From f9449738730fa0d460a30affa826a157bf97cf62 Mon Sep 17 00:00:00 2001
From: rbmj <rbmj@verizon.net>
Date: Mon, 21 May 2012 15:56:06 -0400
Subject: [PATCH 3/6] Add FPU Support for PPC on VxWorks

libgfortran/config/fpu-ppc.h:
	Started work on a fpu-target.h that will work for VxWorks on PPC.
	90% of it is stolen from glibc.  I just took the necessary pieces
	and added the right VxWorks glue.  I made as few changes to the
	glibc code as possible in order to avoid introducing a bug.
	The VxWorks part seems to be correct, but I don't have a whole
	lot of experience in this area and documentation is incomplete.

libgfortran/configure.host:
	This just adds fpu-ppc.h to the list of targets.  For some
	reason, my powerpc-wrs-vxworks is getting mis-identified as
	AIX, even though it does not have the functions, so I'll also
	have to work on a patch for configure.ac.  I'll check on that.
	Also, I put it before glibc so that glibc targets will get
	to use glibc and not rely on my cruft.
---
 libgfortran/config/fpu-ppc.h |  407 ++++++++++++++++++++++++++++++++++++++++++
 libgfortran/configure.host   |   11 ++
 2 files changed, 418 insertions(+), 0 deletions(-)
 create mode 100644 libgfortran/config/fpu-ppc.h

diff --git a/libgfortran/config/fpu-ppc.h b/libgfortran/config/fpu-ppc.h
new file mode 100644
index 0000000..f27df7f
--- /dev/null
+++ b/libgfortran/config/fpu-ppc.h
@@ -0,0 +1,407 @@ 
+/* FPU-related code for systems without glibc on powerpc.
+   Copyright (C) 1997, 1998, 1999, 2008 Free Software Foundation, Inc.
+
+This file is part of the GNU Fortran runtime library (libgfortran).
+This file incorporates code from the GNU C Library (glibc) under the GNU LGPL.
+
+Libgfortran is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+Libgfortran is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+   
+/* FPU Types/Constants: */
+
+/* Type representing exception flags.  */
+typedef unsigned int fexcept_t;
+
+/* Type representing floating-point environment.  We leave it as 'double'
+   for efficiency reasons (rather than writing it to a 32-bit integer). */
+typedef double fenv_t;
+
+/* We want to specify the bit pattern of the __fe_*_env constants, so 
+   pretend they're really `long long' instead of `double'.  */
+
+/* If the default argument is used we use this value.  */
+const unsigned long long __fe_dfl_env __attribute__ ((aligned (8))) = 
+0xfff8000000000000ULL;
+#define FE_DFL_ENV	((double*)(&__fe_dfl_env))
+
+/* Floating-point environment where all exceptions are enabled.  Note that
+   this is not sufficient to give you SIGFPE.  */
+const unsigned long long __fe_enabled_env __attribute__ ((aligned (8))) = 
+0xfff80000000000f8ULL;
+# define FE_ENABLED_ENV	((double*)(&__fe_enabled_env))
+
+/* Floating-point environment with (processor-dependent) non-IEEE floating
+   point.  */
+const unsigned long long __fe_nonieee_env __attribute__ ((aligned (8))) = 
+0xfff8000000000004ULL;
+# define FE_NONIEEE_ENV	((double*)(&__fe_nonieee_env))
+
+/* Provide __fe_mask_env and __fe_nomask_env */
+#ifdef __VXWORKS__
+
+/* These are based loosly off of glibc
+  
+   see also glibc/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/fe{,no}mask.c
+ */
+
+#include <arch/ppc/vxPpcLib.h>
+#include <arch/ppc/archPpc.h>
+
+#if !defined (_PPC_MSR_FE0) || !defined (_PPC_MSR_FE1)
+#error FPU Support does not appear to exist on target platform
+#else
+
+const fenv_t *
+__fe_nomask_env(void)
+{
+  UINT32 msr = vxMsrGet();
+  msr |= _PPC_MSR_FE0 | _PPC_MSR_FE1;
+  vxMsrSet(msr);
+  return FE_ENABLED_ENV;
+}
+
+const fenv_t *
+__fe_nomask_env(void)
+{
+  UINT32 msr = vxMsrGet();
+  msr &= ~(_PPC_MSR_FE0 | _PPC_MSR_FE1);
+  vxMsrSet(msr);
+  return FE_DFL_ENV;
+}
+
+#endif
+    
+#else
+
+/* VxWorks is the only OS that this header supports so far.  It would be trivial
+   to also support Linux on PowerPC, but this should be unnecessary as Linux
+   uses glibc so fpu-glibc.h should be used instead anyway. */
+   
+/* These stubs won't work... error unless there's reason not to do so: */
+#error Unsupported target, use fpu-glibc.h or similar instead.
+
+#include <errno.h>
+
+/* This is a generic stub. An OS specific override is required to set
+   the FE0/FE1 bits in the MSR.  MSR update is privileged, so this will
+   normally involve a syscall.  */
+
+const fenv_t *
+__fe_nomask_env(void)
+{
+  __set_errno (ENOSYS);
+  return FE_ENABLED_ENV;
+}
+
+/* This is a generic stub. An OS specific override is required to clear
+   the FE0/FE1 bits in the MSR.  MSR update is privileged, so this will
+   normally involve a syscall.  */
+
+const fenv_t *
+__fe_mask_env(void)
+{
+  __set_errno (ENOSYS);
+  return FE_DFL_ENV;
+}
+#endif
+
+/* Define bits representing the exception.  We use the bit positions of
+   the appropriate bits in the FPSCR...  */
+enum
+  {
+    FE_INEXACT = 1 << (31 - 6),
+#define FE_INEXACT	FE_INEXACT
+    FE_DIVBYZERO = 1 << (31 - 5),
+#define FE_DIVBYZERO	FE_DIVBYZERO
+    FE_UNDERFLOW = 1 << (31 - 4),
+#define FE_UNDERFLOW	FE_UNDERFLOW
+    FE_OVERFLOW = 1 << (31 - 3),
+#define FE_OVERFLOW	FE_OVERFLOW
+
+    /* ... except for FE_INVALID, for which we use bit 31. FE_INVALID
+       actually corresponds to bits 7 through 12 and 21 through 23
+       in the FPSCR, but we can't use that because the current draft
+       says that it must be a power of 2.  Instead we use bit 2 which
+       is the summary bit for all the FE_INVALID exceptions, which
+       kind of makes sense.  */
+    FE_INVALID = 1 << (31 - 2),
+#define FE_INVALID	FE_INVALID
+
+    /* Breakdown of the FE_INVALID bits. Setting FE_INVALID on an
+       input to a routine is equivalent to setting all of these bits;
+       FE_INVALID will be set on output from a routine iff one of
+       these bits is set.  Note, though, that you can't disable or
+       enable these exceptions individually.  */
+
+    /* Operation with SNaN. */
+    FE_INVALID_SNAN = 1 << (31 - 7),
+# define FE_INVALID_SNAN	FE_INVALID_SNAN
+
+    /* Inf - Inf */
+    FE_INVALID_ISI = 1 << (31 - 8),
+# define FE_INVALID_ISI		FE_INVALID_ISI
+
+    /* Inf / Inf */
+    FE_INVALID_IDI = 1 << (31 - 9),
+# define FE_INVALID_IDI		FE_INVALID_IDI
+
+    /* 0 / 0 */
+    FE_INVALID_ZDZ = 1 << (31 - 10),
+# define FE_INVALID_ZDZ		FE_INVALID_ZDZ
+
+    /* Inf * 0 */
+    FE_INVALID_IMZ = 1 << (31 - 11),
+# define FE_INVALID_IMZ		FE_INVALID_IMZ
+
+    /* Comparison with NaN or SNaN.  */
+    FE_INVALID_COMPARE = 1 << (31 - 12),
+# define FE_INVALID_COMPARE	FE_INVALID_COMPARE
+
+    /* Invalid operation flag for software (not set by hardware).  */
+    /* Note that some chips don't have this implemented, presumably
+       because no-one expected anyone to write software for them %-).  */
+    FE_INVALID_SOFTWARE = 1 << (31 - 21),
+# define FE_INVALID_SOFTWARE	FE_INVALID_SOFTWARE
+
+    /* Square root of negative number (including -Inf).  */
+    /* Note that some chips don't have this implemented.  */
+    FE_INVALID_SQRT = 1 << (31 - 22),
+# define FE_INVALID_SQRT	FE_INVALID_SQRT
+
+    /* Conversion-to-integer of a NaN or a number too large or too small.  */
+    FE_INVALID_INTEGER_CONVERSION = 1 << (31 - 23)
+# define FE_INVALID_INTEGER_CONVERSION	FE_INVALID_INTEGER_CONVERSION
+
+# define FE_ALL_INVALID \
+        (FE_INVALID_SNAN | FE_INVALID_ISI | FE_INVALID_IDI | FE_INVALID_ZDZ \
+	 | FE_INVALID_IMZ | FE_INVALID_COMPARE | FE_INVALID_SOFTWARE \
+	 | FE_INVALID_SQRT | FE_INVALID_INTEGER_CONVERSION)
+  };
+
+#define FE_ALL_EXCEPT \
+	(FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID)
+
+/* PowerPC chips support all of the four defined rounding modes.  We
+   use the bit pattern in the FPSCR as the values for the
+   appropriate macros.  */
+enum
+  {
+    FE_TONEAREST = 0,
+#define FE_TONEAREST	FE_TONEAREST
+    FE_TOWARDZERO = 1,
+#define FE_TOWARDZERO	FE_TOWARDZERO
+    FE_UPWARD = 2,
+#define FE_UPWARD	FE_UPWARD
+    FE_DOWNWARD = 3
+#define FE_DOWNWARD	FE_DOWNWARD
+  };
+
+/* Floating-point environment with all exceptions enabled.  Note that
+   just evaluating this value does not change the processor exception mode.
+   Passing this mask to fesetenv will result in a prctl syscall to change
+   the MSR FE0/FE1 bits to "Precise Mode".  On some processors this will
+   result in slower floating point execution.  This will last until an
+   fenv or exception mask is installed that disables all FP exceptions.  */
+# define FE_NOMASK_ENV	FE_ENABLED_ENV
+
+/* Floating-point environment with all exceptions disabled.  Note that
+   just evaluating this value does not change the processor exception mode.
+   Passing this mask to fesetenv will result in a prctl syscall to change
+   the MSR FE0/FE1 bits to "Ignore Exceptions Mode".  On most processors
+   this allows the fastest possible floating point execution.*/
+# define FE_MASK_ENV	FE_DFL_ENV
+
+/* Type to represent floating point environment */
+
+typedef union
+{
+  fenv_t fenv;
+  unsigned int l[2];
+} fenv_union_t;
+
+/* Set/Get FPSCR Register */
+
+#define fegetenv_register() \
+        ({ fenv_t env; asm volatile ("mffs %0" : "=f" (env)); env; })
+        
+#define fesetenv_register(env) \
+	do { \
+	  double d = (env); \
+	  if(GLRO(dl_hwcap) & PPC_FEATURE_HAS_DFP) \
+	    asm volatile (".machine push; " \
+			  ".machine \"power6\"; " \
+			  "mtfsf 0xff,%0,1,0; " \
+			  ".machine pop" : : "f" (d)); \
+	  else \
+	    asm volatile ("mtfsf 0xff,%0" : : "f" (d)); \
+	} while(0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+__fegetexcept (void)
+{
+  fenv_union_t fe;
+  int result = 0;
+
+  fe.fenv = fegetenv_register ();
+
+  if (fe.l[1] & (1 << (31 - FPSCR_XE)))
+      result |= FE_INEXACT;
+  if (fe.l[1] & (1 << (31 - FPSCR_ZE)))
+      result |= FE_DIVBYZERO;
+  if (fe.l[1] & (1 << (31 - FPSCR_UE)))
+      result |= FE_UNDERFLOW;
+  if (fe.l[1] & (1 << (31 - FPSCR_OE)))
+      result |= FE_OVERFLOW;
+  if (fe.l[1] & (1 << (31 - FPSCR_VE)))
+      result |= FE_INVALID;
+
+  return result;
+}
+
+int
+feenableexcept (int excepts)
+{
+  fenv_union_t fe;
+  int result, new;
+
+  result = __fegetexcept ();
+
+  if ((excepts & FE_ALL_INVALID) == FE_ALL_INVALID)
+    excepts = (excepts | FE_INVALID) & ~ FE_ALL_INVALID;
+
+  fe.fenv = fegetenv_register ();
+  if (excepts & FE_INEXACT)
+    fe.l[1] |= (1 << (31 - FPSCR_XE));
+  if (excepts & FE_DIVBYZERO)
+    fe.l[1] |= (1 << (31 - FPSCR_ZE));
+  if (excepts & FE_UNDERFLOW)
+    fe.l[1] |= (1 << (31 - FPSCR_UE));
+  if (excepts & FE_OVERFLOW)
+    fe.l[1] |= (1 << (31 - FPSCR_OE));
+  if (excepts & FE_INVALID)
+    fe.l[1] |= (1 << (31 - FPSCR_VE));
+  fesetenv_register (fe.fenv);
+
+  new = __fegetexcept ();
+  if (new != 0 && result == 0)
+    (void)__fe_nomask_env ();
+
+  if ((new & excepts) != excepts)
+    result = -1;
+
+  return result;
+}
+
+int
+fedisableexcept (int excepts)
+{
+  fenv_union_t fe;
+  int result, new;
+
+  result = __fegetexcept ();
+
+  if ((excepts & FE_ALL_INVALID) == FE_ALL_INVALID)
+    excepts = (excepts | FE_INVALID) & ~ FE_ALL_INVALID;
+
+  fe.fenv = fegetenv_register ();
+  if (excepts & FE_INEXACT)
+    fe.l[1] &= ~(1 << (31 - FPSCR_XE));
+  if (excepts & FE_DIVBYZERO)
+    fe.l[1] &= ~(1 << (31 - FPSCR_ZE));
+  if (excepts & FE_UNDERFLOW)
+    fe.l[1] &= ~(1 << (31 - FPSCR_UE));
+  if (excepts & FE_OVERFLOW)
+    fe.l[1] &= ~(1 << (31 - FPSCR_OE));
+  if (excepts & FE_INVALID)
+    fe.l[1] &= ~(1 << (31 - FPSCR_VE));
+  fesetenv_register (fe.fenv);
+
+  new = __fegetexcept ();
+  if (new == 0 && result != 0)
+    (void)__fe_mask_env ();
+
+  if ((new & excepts) != 0)
+    result = -1;
+  return result;
+}
+
+/* This is from fpu-glibc.h: This is what *all of the above* is for */
+
+void set_fpu (void)
+{
+  if (FE_ALL_EXCEPT != 0)
+    fedisableexcept (FE_ALL_EXCEPT);
+
+  if (options.fpe & GFC_FPE_INVALID)
+#ifdef FE_INVALID
+    feenableexcept (FE_INVALID);
+#else
+    estr_write ("Fortran runtime warning: IEEE 'invalid operation' "
+	        "exception not supported.\n");
+#endif
+
+/* glibc does never have a FE_DENORMAL.  */
+  if (options.fpe & GFC_FPE_DENORMAL)
+#ifdef FE_DENORMAL
+    feenableexcept (FE_DENORMAL);
+#else
+    estr_write ("Fortran runtime warning: Floating point 'denormal operand' "
+	        "exception not supported.\n");
+#endif
+
+  if (options.fpe & GFC_FPE_ZERO)
+#ifdef FE_DIVBYZERO
+    feenableexcept (FE_DIVBYZERO);
+#else
+    estr_write ("Fortran runtime warning: IEEE 'division by zero' "
+	        "exception not supported.\n");
+#endif
+
+  if (options.fpe & GFC_FPE_OVERFLOW)
+#ifdef FE_OVERFLOW
+    feenableexcept (FE_OVERFLOW);
+#else
+    estr_write ("Fortran runtime warning: IEEE 'overflow' "
+	        "exception not supported.\n");
+#endif
+
+  if (options.fpe & GFC_FPE_UNDERFLOW)
+#ifdef FE_UNDERFLOW
+    feenableexcept (FE_UNDERFLOW);
+#else
+    estr_write ("Fortran runtime warning: IEEE 'underflow' "
+	        "exception not supported.\n");
+#endif
+
+  if (options.fpe & GFC_FPE_INEXACT)
+#ifdef FE_INEXACT
+    feenableexcept (FE_INEXACT);
+#else
+    estr_write ("Fortran runtime warning: IEEE 'inexact' "
+	        "exception not supported.\n");
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libgfortran/configure.host b/libgfortran/configure.host
index 92b6433..5e4af03 100644
--- a/libgfortran/configure.host
+++ b/libgfortran/configure.host
@@ -20,6 +20,17 @@ 
 # DEFAULTS
 fpu_host='fpu-generic'
 
+# powerpc asm fpu_host based off of glibc for systems without glibc
+# this is before the glibc test so that fpu-glibc will override it, as
+# it requires a *small* amount of OS support (which is only implemented
+# right now with VxWorks), and since it is based off of glibc in theory
+# glibc will always be better/more current/more optimized.
+
+case "${host_cpu}" in
+  powerpc)
+    fpu_host='fpu-ppc' ;;
+esac
+
 if test "x${have_feenableexcept}" = "xyes"; then
   fpu_host='fpu-glibc'
 fi
-- 
1.7.5.4