This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC 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]

[patch] PR 31937 libffi soft-float support for linux-ppc-32


Hello all,

hopefully the last iteration.

This patch was tested by Janis Johnson on ppc64 to make sure I did not break anything there. Tested here locally on native 32-bit ppc with and w/o soft float support and last on a ppc-405 cross environment.
Libffi ok, and libjava as well.


Pat Haugen guided me through the branch prediction forest.

Thanks to Janis and Pat!

Is this patch ok for trunk?

Andreas


2007-11-30 Andreas Tobler <a.tobler@schweiz.org>


	PR libffi/31937
	* src/powerpc/ffitarget.h: Introduce new ABI FFI_LINUX_SOFT_FLOAT.
	Add local FFI_TYPE_UINT128 to handle soft-float long-double-128.
	* src/powerpc/ffi.c: Distinguish between __NO_FPRS__ and not and
	set the NUM_FPR_ARG_REGISTERS according to.
	Add support for potential soft-float support under hard-float
	architecture.
	(ffi_prep_args_SYSV): Set NUM_FPR_ARG_REGISTERS to 0 in case of
	FFI_LINUX_SOFT_FLOAT, handle float, doubles and long-doubles according
	to the FFI_LINUX_SOFT_FLOAT ABI.
	(ffi_prep_cif_machdep): Likewise.
	(ffi_closure_helper_SYSV): Likewise.
	* src/powerpc/ppc_closure.S: Make sure not to store float/double
	on archs where __NO_FPRS__ is true.
	Add FFI_TYPE_UINT128 support.
	* src/powerpc/sysv.S: Add support for soft-float long-double-128.
	Adjust copyright notice.
Index: src/powerpc/ffitarget.h
===================================================================
--- src/powerpc/ffitarget.h	(revision 130474)
+++ src/powerpc/ffitarget.h	(working copy)
@@ -1,5 +1,6 @@
 /* -----------------------------------------------------------------*-C-*-
    ffitarget.h - Copyright (c) 1996-2003  Red Hat, Inc.
+   Copyright (C) 2007 Free Software Foundation, Inc
    Target configuration macros for PowerPC.
 
    Permission is hereby granted, free of charge, to any person obtaining
@@ -42,15 +43,20 @@
 #ifdef POWERPC
   FFI_SYSV,
   FFI_GCC_SYSV,
+  FFI_LINUX_SOFT_FLOAT,
   FFI_LINUX64,
   FFI_LINUX,
 # ifdef POWERPC64
   FFI_DEFAULT_ABI = FFI_LINUX64,
 # else
-#  if __LDBL_MANT_DIG__ == 106
+#  if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106))
   FFI_DEFAULT_ABI = FFI_LINUX,
 #  else
+#   ifdef __NO_FPRS__
+  FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT,
+#   else
   FFI_DEFAULT_ABI = FFI_GCC_SYSV,
+#   endif
 #  endif
 # endif
 #endif
@@ -83,8 +89,14 @@
 #define FFI_CLOSURES 1
 #define FFI_NATIVE_RAW_API 0
 
+/* For additional types like the below, take care about the order in
+   ppc_closures.S. They must follow after the FFI_TYPE_LAST.  */
+
+/* Needed for soft-float long-double-128 support.  */
+#define FFI_TYPE_UINT128 (FFI_TYPE_LAST + 1)
+
 /* Needed for FFI_SYSV small structure returns.  */
-#define FFI_SYSV_TYPE_SMALL_STRUCT  (FFI_TYPE_LAST)
+#define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST + 2)
 
 #if defined(POWERPC64) || defined(POWERPC_AIX)
 #define FFI_TRAMPOLINE_SIZE 24
Index: src/powerpc/ffi.c
===================================================================
--- src/powerpc/ffi.c	(revision 130474)
+++ src/powerpc/ffi.c	(working copy)
@@ -50,10 +50,13 @@
 };
 
 /* About the SYSV ABI.  */
-enum {
-  NUM_GPR_ARG_REGISTERS = 8,
-  NUM_FPR_ARG_REGISTERS = 8
-};
+unsigned int NUM_GPR_ARG_REGISTERS = 8;
+#ifndef __NO_FPRS__
+unsigned int NUM_FPR_ARG_REGISTERS = 8;
+#else
+unsigned int NUM_FPR_ARG_REGISTERS = 0;
+#endif
+
 enum { ASM_NEEDS_REGISTERS = 4 };
 
 /* ffi_prep_args_SYSV is called by the assembly routine once stack space
@@ -116,7 +119,7 @@
   /* 'next_arg' grows up as we put parameters in it.  */
   valp next_arg;
 
-  int i;
+  int i, ii MAYBE_UNUSED;
   ffi_type **ptr;
   double double_tmp;
   union {
@@ -134,6 +137,9 @@
   size_t struct_copy_size;
   unsigned gprvalue;
 
+  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+    NUM_FPR_ARG_REGISTERS = 0;
+
   stacktop.c = (char *) stack + bytes;
   gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
   intarg_count = 0;
@@ -165,6 +171,9 @@
       switch ((*ptr)->type)
 	{
 	case FFI_TYPE_FLOAT:
+	  /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+	  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+	    goto soft_float_prep;
 	  double_tmp = **p_argv.f;
 	  if (fparg_count >= NUM_FPR_ARG_REGISTERS)
 	    {
@@ -178,6 +187,9 @@
 	  break;
 
 	case FFI_TYPE_DOUBLE:
+	  /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+	  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+	    goto soft_double_prep;
 	  double_tmp = **p_argv.d;
 
 	  if (fparg_count >= NUM_FPR_ARG_REGISTERS)
@@ -199,38 +211,75 @@
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	case FFI_TYPE_LONGDOUBLE:
-	  if (ecif->cif->abi != FFI_LINUX)
+	  if ((ecif->cif->abi != FFI_LINUX)
+		&& (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT))
 	    goto do_struct;
-	  double_tmp = (*p_argv.d)[0];
-
-	  if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
+	  /* The soft float ABI for long doubles works like this,
+	     a long double is passed in four consecutive gprs if available.
+	     A maximum of 2 long doubles can be passed in gprs.
+	     If we do not have 4 gprs left, the long double is passed on the
+	     stack, 4-byte aligned.  */
+	  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
 	    {
-	      if (intarg_count >= NUM_GPR_ARG_REGISTERS
-		  && intarg_count % 2 != 0)
+	      unsigned int int_tmp = (*p_argv.ui)[0];
+	      if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
 		{
-		  intarg_count++;
+		  if (intarg_count < NUM_GPR_ARG_REGISTERS)
+		    intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
+		  *next_arg.u = int_tmp;
 		  next_arg.u++;
+		  for (ii = 1; ii < 4; ii++)
+		    {
+		      int_tmp = (*p_argv.ui)[ii];
+		      *next_arg.u = int_tmp;
+		      next_arg.u++;
+		    }
 		}
-	      *next_arg.d = double_tmp;
-	      next_arg.u += 2;
-	      double_tmp = (*p_argv.d)[1];
-	      *next_arg.d = double_tmp;
-	      next_arg.u += 2;
+	      else
+		{
+		  *gpr_base.u++ = int_tmp;
+		  for (ii = 1; ii < 4; ii++)
+		    {
+		      int_tmp = (*p_argv.ui)[ii];
+		      *gpr_base.u++ = int_tmp;
+		    }
+		}
+	      intarg_count +=4;
 	    }
 	  else
 	    {
-	      *fpr_base.d++ = double_tmp;
-	      double_tmp = (*p_argv.d)[1];
-	      *fpr_base.d++ = double_tmp;
+	      double_tmp = (*p_argv.d)[0];
+
+	      if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
+		{
+		  if (intarg_count >= NUM_GPR_ARG_REGISTERS
+		      && intarg_count % 2 != 0)
+		    {
+		      intarg_count++;
+		      next_arg.u++;
+		    }
+		  *next_arg.d = double_tmp;
+		  next_arg.u += 2;
+		  double_tmp = (*p_argv.d)[1];
+		  *next_arg.d = double_tmp;
+		  next_arg.u += 2;
+		}
+	      else
+		{
+		  *fpr_base.d++ = double_tmp;
+		  double_tmp = (*p_argv.d)[1];
+		  *fpr_base.d++ = double_tmp;
+		}
+
+	      fparg_count += 2;
+	      FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	    }
-
-	  fparg_count += 2;
-	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	  break;
 #endif
 
 	case FFI_TYPE_UINT64:
 	case FFI_TYPE_SINT64:
+	soft_double_prep:
 	  if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
 	    intarg_count++;
 	  if (intarg_count >= NUM_GPR_ARG_REGISTERS)
@@ -293,6 +342,8 @@
 	case FFI_TYPE_UINT32:
 	case FFI_TYPE_SINT32:
 	case FFI_TYPE_POINTER:
+	soft_float_prep:
+
 	  gprvalue = **p_argv.ui;
 
 	putgpr:
@@ -546,6 +597,9 @@
   unsigned type = cif->rtype->type;
   unsigned size = cif->rtype->size;
 
+  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+    NUM_FPR_ARG_REGISTERS = 0;
+
   if (cif->abi != FFI_LINUX64)
     {
       /* All the machine-independent calculation of cif->bytes will be wrong.
@@ -582,14 +636,16 @@
      For LINUX64:
      - integer values in gpr3;
      - Structures/Unions by reference;
-     - Single/double FP values in fpr1, long double in fpr1,fpr2.  */
+     - Single/double FP values in fpr1, long double in fpr1,fpr2.
+     - soft-float float/doubles are treated as UINT32/UINT64 respectivley.
+     - soft-float long doubles are returned in gpr3-gpr6.  */
   switch (type)
     {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
     case FFI_TYPE_LONGDOUBLE:
-      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64)
+      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64
+	&& cif->abi != FFI_LINUX_SOFT_FLOAT)
 	goto byref;
-
       flags |= FLAG_RETURNS_128BITS;
       /* Fall through.  */
 #endif
@@ -597,7 +653,9 @@
       flags |= FLAG_RETURNS_64BITS;
       /* Fall through.  */
     case FFI_TYPE_FLOAT:
-      flags |= FLAG_RETURNS_FP;
+      /* With FFI_LINUX_SOFT_FLOAT no fp registers are used.  */
+      if (cif->abi != FFI_LINUX_SOFT_FLOAT)
+	flags |= FLAG_RETURNS_FP;
       break;
 
     case FFI_TYPE_UINT64:
@@ -660,18 +718,36 @@
 	switch ((*ptr)->type)
 	  {
 	  case FFI_TYPE_FLOAT:
+	    /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+	    if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	      goto soft_float_cif;
 	    fparg_count++;
 	    /* floating singles are not 8-aligned on stack */
 	    break;
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	  case FFI_TYPE_LONGDOUBLE:
-	    if (cif->abi != FFI_LINUX)
+	    if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
 	      goto do_struct;
-	    fparg_count++;
+	    if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	      {
+		if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3
+		  || intarg_count < NUM_GPR_ARG_REGISTERS)
+		  /* A long double in FFI_LINUX_SOFT_FLOAT can use only
+		     a set of four consecutive gprs. If we have not enough,
+		     we have to adjust the intarg_count value.  */
+		  intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
+		intarg_count += 4;
+		break;
+	      }
+	    else
+	      fparg_count++;
 	    /* Fall thru */
 #endif
 	  case FFI_TYPE_DOUBLE:
+	    /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+	    if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	      goto soft_double_cif;
 	    fparg_count++;
 	    /* If this FP arg is going on the stack, it must be
 	       8-byte-aligned.  */
@@ -683,6 +759,7 @@
 
 	  case FFI_TYPE_UINT64:
 	  case FFI_TYPE_SINT64:
+	  soft_double_cif:
 	    /* 'long long' arguments are passed as two words, but
 	       either both words must fit in registers or both go
 	       on the stack.  If they go on the stack, they must
@@ -710,6 +787,7 @@
 	    /* Fall through (allocate space for the pointer).  */
 
 	  default:
+	  soft_float_cif:
 	    /* Everything else is passed as a 4-byte word in a GPR, either
 	       the object itself or a pointer to it.  */
 	    intarg_count++;
@@ -723,8 +801,13 @@
 	  {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	  case FFI_TYPE_LONGDOUBLE:
-	    fparg_count += 2;
-	    intarg_count += 2;
+	    if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	      intarg_count += 4;
+	    else
+	      {
+		fparg_count += 2;
+		intarg_count += 2;
+	      }
 	    break;
 #endif
 	  case FFI_TYPE_FLOAT:
@@ -818,6 +901,7 @@
     case FFI_SYSV:
     case FFI_GCC_SYSV:
     case FFI_LINUX:
+    case FFI_LINUX_SOFT_FLOAT:
       ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
       break;
 #else
@@ -942,7 +1026,7 @@
        && !((cif->abi == FFI_SYSV) && (size <= 8)))
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
       || (cif->rtype->type == FFI_TYPE_LONGDOUBLE
-	  && cif->abi != FFI_LINUX)
+	  && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
 #endif
       )
     {
@@ -995,6 +1079,7 @@
 	case FFI_TYPE_SINT32:
 	case FFI_TYPE_UINT32:
 	case FFI_TYPE_POINTER:
+	soft_float_closure:
 	  /* there are 8 gpr registers used to pass values */
 	  if (ng < 8)
 	    {
@@ -1030,6 +1115,7 @@
 
 	case FFI_TYPE_SINT64:
 	case FFI_TYPE_UINT64:
+	soft_double_closure:
 	  /* passing long long ints are complex, they must
 	   * be passed in suitable register pairs such as
 	   * (r3,r4) or (r5,r6) or (r6,r7), or (r7,r8) or (r9,r10)
@@ -1061,6 +1147,9 @@
 	  break;
 
 	case FFI_TYPE_FLOAT:
+	  /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+	  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	    goto soft_float_closure;
 	  /* unfortunately float values are stored as doubles
 	   * in the ffi_closure_SYSV code (since we don't check
 	   * the type in that routine).
@@ -1089,6 +1178,9 @@
 	  break;
 
 	case FFI_TYPE_DOUBLE:
+	  /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+	  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	    goto soft_double_closure;
 	  /* On the outgoing stack all values are aligned to 8 */
 	  /* there are 8 64bit floating point registers */
 
@@ -1109,9 +1201,24 @@
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	case FFI_TYPE_LONGDOUBLE:
-	  if (cif->abi != FFI_LINUX)
+	  if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
 	    goto do_struct;
-
+	  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+	    { /* Test if for the whole long double, 4 gprs are available.
+		 otherwise the stuff ends up on the stack.  */
+	      if (ng < 5)
+		{
+		  avalue[i] = pgr;
+		  pgr += 4;
+		  ng += 4;
+		}
+	      else
+		{
+		  avalue[i] = pst;
+		  pst += 4;
+		}
+	      break;
+	    }
 	  if (nf < 7)
 	    {
 	      avalue[i] = pfr;
@@ -1147,10 +1254,34 @@
     return FFI_SYSV_TYPE_SMALL_STRUCT + size;
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
   else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE
-	   && cif->abi != FFI_LINUX)
+	   && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
     return FFI_TYPE_STRUCT;
 #endif
-  return cif->rtype->type;
+  /* With FFI_LINUX_SOFT_FLOAT floats and doubles are handled like UINT32
+     respectivley UINT64.  */
+  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+    {
+      switch (cif->rtype->type)
+	{
+	case FFI_TYPE_FLOAT:
+	  return FFI_TYPE_UINT32;
+	  break;
+	case FFI_TYPE_DOUBLE:
+	  return FFI_TYPE_UINT64;
+	  break;
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+	case FFI_TYPE_LONGDOUBLE:
+	  return FFI_TYPE_UINT128;
+	  break;
+#endif
+	default:
+	  return cif->rtype->type;
+	}
+    }
+  else
+    {
+      return cif->rtype->type;
+    }
 }
 
 int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
Index: src/powerpc/ppc_closure.S
===================================================================
--- src/powerpc/ppc_closure.S	(revision 130474)
+++ src/powerpc/ppc_closure.S	(working copy)
@@ -28,6 +28,7 @@
 	stw   %r9, 40(%r1)
 	stw   %r10,44(%r1)
 
+#ifndef __NO_FPRS__
 	# next save fpr 1 to fpr 8 (aligned to 8)
 	stfd  %f1, 48(%r1)
 	stfd  %f2, 56(%r1)
@@ -37,6 +38,7 @@
 	stfd  %f6, 88(%r1)
 	stfd  %f7, 96(%r1)
 	stfd  %f8, 104(%r1)
+#endif
 
 	# set up registers for the routine that actually does the work
 	# get the context pointer from the trampoline
@@ -171,6 +173,12 @@
 	addi %r1,%r1,144
 	blr
 
+# case FFI_TYPE_UINT128
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
+	lwz %r5,112+8(%r1)
+	bl .Luint128
+
 # The return types below are only used when the ABI type is FFI_SYSV.
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 1. One byte struct.
 	lbz %r3,112+0(%r1)
@@ -230,6 +238,12 @@
 	addi %r1,%r1,144
 	blr
 
+.Luint128:
+	lwz %r6,112+12(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
+
 END(ffi_closure_SYSV)
 
 	.section	".eh_frame",EH_FRAME_FLAGS,@progbits
Index: src/powerpc/sysv.S
===================================================================
--- src/powerpc/sysv.S	(revision 130474)
+++ src/powerpc/sysv.S	(working copy)
@@ -1,5 +1,6 @@
 /* -----------------------------------------------------------------------
-   sysv.h - Copyright (c) 1998 Geoffrey Keating
+   sysv.S - Copyright (c) 1998 Geoffrey Keating
+   Copyright (C) 2007 Free Software Foundation, Inc
 
    PowerPC Assembly glue.
 
@@ -98,13 +99,17 @@
 	bctrl
 
 	/* Now, deal with the return value.  */
-	mtcrf	0x01,%r31
+	mtcrf	0x01,%r31 /* cr7  */
 	bt-	31,L(small_struct_return_value)
 	bt-	30,L(done_return_value)
 	bt-	29,L(fp_return_value)
 	stw	%r3,0(%r30)
 	bf+	28,L(done_return_value)
 	stw	%r4,4(%r30)
+	mtcrf	0x02,%r31 /* cr6  */
+	bf	27,L(done_return_value)
+	stw     %r5,8(%r30)
+	stw	%r6,12(%r30)
 	/* Fall through...  */
 
 L(done_return_value):

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