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]

PowerPC libffi long double support


This patch
1) Implements libffi support for the new PowerPC Linux ABI long double.
   This should probably have gone into 4.1, but nobody noticed because
   libffi isn't compiled with any 128-bit long double support by
   default..
2) Fixes a bug with PowerPC64 long double return values being treated
   as if they were only a double.  In r104600, Tom missed removing all
   of the hacky code I wrote which treated double and long double
   identically, loading both f1 and f2.
3) Fixes SysV ABI long double return values, which should be returned
   as for structs.
4) Tidies code and comments in a few places, most notably moving some
   of ffi_closure_SYSV epilogue code into the case table, replacing
   nops and reducing branches.

Bootstrapped and regression tested powerpc-linux and powerpc64-linux.

	* src/powerpc/ffitarget.h (enum ffi_abi): Add FFI_LINUX.  Default
	for 32-bit using IBM extended double format.  Fix FFI_LAST_ABI.
	* src/powerpc/ffi.c (ffi_prep_args_SYSV): Handle linux variant of
	FFI_TYPE_LONGDOUBLE.
	(ffi_prep_args64): Assert using IBM extended double.
	(ffi_prep_cif_machdep): Don't munge FFI_TYPE_LONGDOUBLE type.
	Handle FFI_LINUX FFI_TYPE_LONGDOUBLE return and args.
	(ffi_call): Handle FFI_LINUX.
	(ffi_closure_helper_SYSV): Non FFI_LINUX long double return needs
	gpr3 return pointer as for struct return.  Handle FFI_LINUX
	FFI_TYPE_LONGDOUBLE return and args.  Don't increment "nf"
	unnecessarily.
	* src/powerpc/ppc_closure.S (ffi_closure_SYSV): Load both f1 and f2
	for FFI_TYPE_LONGDOUBLE.  Move epilogue insns into case table.
	Don't use r6 as pointer to results, instead use sp offset.  Don't
	make a special call to load lr with case table address, instead
	use offset from previous call.
	* src/powerpc/sysv.S (ffi_call_SYSV): Save long double return.
	* src/powerpc/linux64.S (ffi_call_LINUX64): Simplify long double
	return.

Index: libffi/src/powerpc/ffitarget.h
===================================================================
--- libffi/src/powerpc/ffitarget.h	(revision 112282)
+++ libffi/src/powerpc/ffitarget.h	(working copy)
@@ -43,10 +43,15 @@ typedef enum ffi_abi {
   FFI_SYSV,
   FFI_GCC_SYSV,
   FFI_LINUX64,
+  FFI_LINUX,
 # ifdef POWERPC64
   FFI_DEFAULT_ABI = FFI_LINUX64,
 # else
+#  if __LDBL_MANT_DIG__ == 106
+  FFI_DEFAULT_ABI = FFI_LINUX,
+#  else
   FFI_DEFAULT_ABI = FFI_GCC_SYSV,
+#  endif
 # endif
 #endif
 
@@ -69,7 +74,7 @@ typedef enum ffi_abi {
   FFI_DEFAULT_ABI = FFI_SYSV,
 #endif
 
-  FFI_LAST_ABI = FFI_DEFAULT_ABI + 1
+  FFI_LAST_ABI
 } ffi_abi;
 #endif
 
Index: libffi/src/powerpc/ffi.c
===================================================================
--- libffi/src/powerpc/ffi.c	(revision 112282)
+++ libffi/src/powerpc/ffi.c	(working copy)
@@ -197,6 +197,38 @@ ffi_prep_args_SYSV (extended_cif *ecif, 
 	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	  break;
 
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+	case FFI_TYPE_LONGDOUBLE:
+	  if (ecif->cif->abi != FFI_LINUX)
+	    goto do_struct;
+	  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);
+	  break;
+#endif
+
 	case FFI_TYPE_UINT64:
 	case FFI_TYPE_SINT64:
 	  if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
@@ -232,7 +264,7 @@ ffi_prep_args_SYSV (extended_cif *ecif, 
 
 	case FFI_TYPE_STRUCT:
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-	case FFI_TYPE_LONGDOUBLE:
+	do_struct:
 #endif
 	  struct_copy_size = ((*ptr)->size + 15) & ~0xF;
 	  copy_space.c -= struct_copy_size;
@@ -433,6 +465,7 @@ ffi_prep_args64 (extended_cif *ecif, uns
 	  if (fparg_count < NUM_FPR_ARG_REGISTERS64)
 	    *fpr_base.d++ = double_tmp;
 	  fparg_count++;
+	  FFI_ASSERT (__LDBL_MANT_DIG__ == 106);
 	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	  break;
 #endif
@@ -536,11 +569,6 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 
       /* Space for the mandatory parm save area and general registers.  */
       bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof (long);
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-      if (type == FFI_TYPE_LONGDOUBLE)
-	type = FFI_TYPE_DOUBLE;
-#endif
     }
 
   /* Return value handling.  The rules for SYSV are as follows:
@@ -549,14 +577,24 @@ ffi_prep_cif_machdep (ffi_cif *cif)
      - 64-bit integer values and structures between 5 and 8 bytes are returned
      in gpr3 and gpr4;
      - Single/double FP values are returned in fpr1;
-     - Larger structures and long double (if not equivalent to double) values
-     are allocated space and a pointer is passed as the first argument.
+     - Larger structures are allocated space and a pointer is passed as
+     the first argument.
+     - long doubles (if not equivalent to double) are returned in
+     fpr1,fpr2 for Linux and as for large structs for SysV.
      For LINUX64:
      - integer values in gpr3;
      - Structures/Unions by reference;
      - Single/double FP values in fpr1, long double in fpr1,fpr2.  */
   switch (type)
     {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+    case FFI_TYPE_LONGDOUBLE:
+      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64)
+	goto byref;
+
+      flags |= FLAG_RETURNS_128BITS;
+      /* Fall through.  */
+#endif
     case FFI_TYPE_DOUBLE:
       flags |= FLAG_RETURNS_64BITS;
       /* Fall through.  */
@@ -598,15 +636,8 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 		}
 	    }
 	}
-      /* else fall through.  */
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-    case FFI_TYPE_LONGDOUBLE:
-      if (type == FFI_TYPE_LONGDOUBLE && cif->abi == FFI_LINUX64)
-	{
-	  flags |= FLAG_RETURNS_128BITS;
-	  flags |= FLAG_RETURNS_FP;
-	  break;
-	}
+    byref:
 #endif
       intarg_count++;
       flags |= FLAG_RETVAL_REFERENCE;
@@ -635,6 +666,13 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 	    /* 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)
+	      goto do_struct;
+	    fparg_count++;
+	    /* Fall thru */
+#endif
 	  case FFI_TYPE_DOUBLE:
 	    fparg_count++;
 	    /* If this FP arg is going on the stack, it must be
@@ -664,7 +702,7 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 
 	  case FFI_TYPE_STRUCT:
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-	  case FFI_TYPE_LONGDOUBLE:
+	  do_struct:
 #endif
 	    /* We must allocate space for a copy of these to enforce
 	       pass-by-value.  Pad the space up to a multiple of 16
@@ -793,6 +831,7 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
 #ifndef POWERPC64
     case FFI_SYSV:
     case FFI_GCC_SYSV:
+    case FFI_LINUX:
       /*@-usedef@*/
       ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
       /*@=usedef@*/
@@ -920,14 +959,17 @@ ffi_closure_helper_SYSV (ffi_closure *cl
      For FFI_SYSV the result is passed in r3/r4 if the struct size is less
      or equal 8 bytes.  */
 
-  if (cif->rtype->type == FFI_TYPE_STRUCT)
+  if ((cif->rtype->type == FFI_TYPE_STRUCT
+       && !((cif->abi == FFI_SYSV) && (size <= 8)))
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+      || (cif->rtype->type == FFI_TYPE_LONGDOUBLE
+	  && cif->abi != FFI_LINUX)
+#endif
+      )
     {
-      if (!((cif->abi == FFI_SYSV) && (size <= 8)))
-	{
-	  rvalue = (void *) *pgr;
-	  ng++;
-	  pgr++;
-	}
+      rvalue = (void *) *pgr;
+      ng++;
+      pgr++;
     }
 
   i = 0;
@@ -989,6 +1031,9 @@ ffi_closure_helper_SYSV (ffi_closure *cl
 	  break;
 
 	case FFI_TYPE_STRUCT:
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+	do_struct:
+#endif
 	  /* Structs are passed by reference. The address will appear in a
 	     gpr if it is one of the first 8 arguments.  */
 	  if (ng < 8)
@@ -1060,7 +1105,6 @@ ffi_closure_helper_SYSV (ffi_closure *cl
 	       * naughty thing to do but...
 	       */
 	      avalue[i] = pst;
-	      nf++;
 	      pst += 1;
 	    }
 	  break;
@@ -1080,11 +1124,32 @@ ffi_closure_helper_SYSV (ffi_closure *cl
 	      if (((long) pst) & 4)
 		pst++;
 	      avalue[i] = pst;
-	      nf++;
 	      pst += 2;
 	    }
 	  break;
 
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+	case FFI_TYPE_LONGDOUBLE:
+	  if (cif->abi != FFI_LINUX)
+	    goto do_struct;
+
+	  if (nf < 7)
+	    {
+	      avalue[i] = pfr;
+	      pfr += 2;
+	      nf += 2;
+	    }
+	  else
+	    {
+	      if (((long) pst) & 4)
+		pst++;
+	      avalue[i] = pst;
+	      pst += 4;
+	      nf = 8;
+	    }
+	  break;
+#endif
+
 	default:
 	  FFI_ASSERT (0);
 	}
@@ -1101,8 +1166,12 @@ ffi_closure_helper_SYSV (ffi_closure *cl
   if (cif->abi == FFI_SYSV && cif->rtype->type == FFI_TYPE_STRUCT
       && size <= 8)
     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)
+    return FFI_TYPE_STRUCT;
+#endif
   return cif->rtype->type;
-
 }
 
 int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
Index: libffi/src/powerpc/ppc_closure.S
===================================================================
--- libffi/src/powerpc/ppc_closure.S	(revision 112282)
+++ libffi/src/powerpc/ppc_closure.S	(working copy)
@@ -58,218 +58,178 @@ ENTRY(ffi_closure_SYSV)
 
 	# make the call
 	bl ffi_closure_helper_SYSV@local
-
+.Lret:
 	# now r3 contains the return type
 	# so use it to look up in a table
 	# so we know how to deal with each type
 
 	# look up the proper starting point in table
 	# by using return type as offset
-	addi %r6,%r1,112   # get pointer to results area
-	bl .Lget_ret_type0_addr # get pointer to .Lret_type0 into LR
-	mflr %r4           # move to r4
-	slwi %r3,%r3,4     # now multiply return type by 16
-	add %r3,%r3,%r4    # add contents of table to table address
+
+	mflr %r4		# move address of .Lret to r4
+	slwi %r3,%r3,4		# now multiply return type by 16
+	addi %r4, %r4, .Lret_type0 - .Lret
+	lwz %r0,148(%r1)
+	add %r3,%r3,%r4		# add contents of table to table address
 	mtctr %r3
-	bctr               # jump to it
+	bctr			# jump to it
 .LFE1:
 
 # Each of the ret_typeX code fragments has to be exactly 16 bytes long
 # (4 instructions). For cache effectiveness we align to a 16 byte boundary
 # first.
 	.align 4
-
-	nop
-	nop
-	nop
-.Lget_ret_type0_addr:
-	blrl
-
 # case FFI_TYPE_VOID
 .Lret_type0:
-	b .Lfinish
-	nop
-	nop
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 	nop
 
 # case FFI_TYPE_INT
-.Lret_type1:
-	lwz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lwz %r3,112+0(%r1)
+	mtlr %r0
+.Lfinish:
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_FLOAT
-.Lret_type2:
-	lfs %f1,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lfs %f1,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_DOUBLE
-.Lret_type3:
-	lfd %f1,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lfd %f1,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_LONGDOUBLE
-.Lret_type4:
-	lfd %f1,0(%r6)
+	lfd %f1,112+0(%r1)
+	lfd %f2,112+8(%r1)
+	mtlr %r0
 	b .Lfinish
-	nop
-	nop
 
 # case FFI_TYPE_UINT8
-.Lret_type5:
-	lbz %r3,3(%r6)
-	b .Lfinish
-	nop
-	nop
+	lbz %r3,112+3(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_SINT8
-.Lret_type6:
-	lbz %r3,3(%r6)
+	lbz %r3,112+3(%r1)
 	extsb %r3,%r3
+	mtlr %r0
 	b .Lfinish
-	nop
 
 # case FFI_TYPE_UINT16
-.Lret_type7:
-	lhz %r3,2(%r6)
-	b .Lfinish
-	nop
-	nop
+	lhz %r3,112+2(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_SINT16
-.Lret_type8:
-	lha %r3,2(%r6)
-	b .Lfinish
-	nop
-	nop
+	lha %r3,112+2(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_UINT32
-.Lret_type9:
-	lwz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lwz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_SINT32
-.Lret_type10:
-	lwz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lwz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_TYPE_UINT64
-.Lret_type11:
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
+	mtlr %r0
 	b .Lfinish
-	nop
 
 # case FFI_TYPE_SINT64
-.Lret_type12:
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
+	mtlr %r0
 	b .Lfinish
-	nop
 
 # case FFI_TYPE_STRUCT
-.Lret_type13:
-	b .Lfinish
-	nop
-	nop
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 	nop
 
 # case FFI_TYPE_POINTER
-.Lret_type14:
-	lwz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lwz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # The return types below are only used when the ABI type is FFI_SYSV.
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 1. One byte struct.
-.Lret_type15:
-# fall through.
-	lbz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lbz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 2. Two byte struct.
-.Lret_type16:
-# fall through.
-	lhz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lhz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 3. Three byte struct.
-.Lret_type17:
-# fall through.
-	lwz %r3,0(%r6)
+	lwz %r3,112+0(%r1)
 	srwi %r3,%r3,8
+	mtlr %r0
 	b .Lfinish
-	nop
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 4. Four byte struct.
-.Lret_type18:
-# this one handles the structs from above too.
-	lwz %r3,0(%r6)
-	b .Lfinish
-	nop
-	nop
+	lwz %r3,112+0(%r1)
+	mtlr %r0
+	addi %r1,%r1,144
+	blr
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 5. Five byte struct.
-.Lret_type19:
-# fall through.
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
 	li %r5,24
 	b .Lstruct567
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 6. Six byte struct.
-.Lret_type20:
-# fall through.
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
 	li %r5,16
 	b .Lstruct567
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 7. Seven byte struct.
-.Lret_type21:
-# fall through.
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
 	li %r5,8
 	b .Lstruct567
 
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 8. Eight byte struct.
-.Lret_type22:
-# this one handles the above unhandled structs.
-	lwz %r3,0(%r6)
-	lwz %r4,4(%r6)
+	lwz %r3,112+0(%r1)
+	lwz %r4,112+4(%r1)
+	mtlr %r0
 	b .Lfinish
-	nop
 
-# case done
-.Lfinish:
-
-	lwz %r0,148(%r1)
+.Lstruct567:
+	subfic %r6,%r5,32
+	srw %r4,%r4,%r5
+	slw %r6,%r3,%r6
+	srw %r3,%r3,%r5
+	or %r4,%r6,%r4
 	mtlr %r0
 	addi %r1,%r1,144
 	blr
 
-.Lstruct567:
-	subfic %r0,%r5,32
-	srw %r4,%r4,%r5
-	slw %r0,%r3,%r0
-	srw %r3,%r3,%r5
-	or %r4,%r0,%r4
-	b .Lfinish
 END(ffi_closure_SYSV)
 
 	.section	".eh_frame",EH_FRAME_FLAGS,@progbits
Index: libffi/src/powerpc/sysv.S
===================================================================
--- libffi/src/powerpc/sysv.S	(revision 112282)
+++ libffi/src/powerpc/sysv.S	(working copy)
@@ -121,6 +121,8 @@ L(done_return_value):
 L(fp_return_value):
 	bf	28,L(float_return_value)
 	stfd	%f1,0(%r30)
+	bf	27,L(done_return_value)
+	stfd	%f2,8(%r30)
 	b	L(done_return_value)
 L(float_return_value):
 	stfs	%f1,0(%r30)
Index: libffi/src/powerpc/linux64.S
===================================================================
--- libffi/src/powerpc/linux64.S	(revision 112282)
+++ libffi/src/powerpc/linux64.S	(working copy)
@@ -120,12 +120,9 @@ ffi_call_LINUX64:
 	blr
 
 .Lfp_return_value:
-	bt	27, .Lfd_return_value
 	bf	28, .Lfloat_return_value
 	stfd	%f1, 0(%r30)
-	b	.Ldone_return_value
-.Lfd_return_value:
-	stfd	%f1, 0(%r30)
+	bf	27, .Ldone_return_value
 	stfd	%f2, 8(%r30)
 	b	.Ldone_return_value
 .Lfloat_return_value:

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


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