Commit 1c0e9a7297ced15413c2d2d5d35f6c650c4b46c9

Anthony Green 2014-04-21T12:41:56

Merge pull request #101 from joshtriplett/fastcall-closures Support closures for fastcall

diff --git a/src/x86/ffi.c b/src/x86/ffi.c
index bcfc153..64b19ec 100644
--- a/src/x86/ffi.c
+++ b/src/x86/ffi.c
@@ -448,6 +448,8 @@ void FFI_HIDDEN ffi_closure_STDCALL (ffi_closure *)
      __attribute__ ((regparm(1)));
 void FFI_HIDDEN ffi_closure_THISCALL (ffi_closure *)
      __attribute__ ((regparm(1)));
+void FFI_HIDDEN ffi_closure_FASTCALL (ffi_closure *)
+     __attribute__ ((regparm(1)));
 #else
 void FFI_HIDDEN ffi_closure_win64 (ffi_closure *);
 #endif
@@ -672,6 +674,12 @@ ffi_prep_closure_loc (ffi_closure* closure,
                            &ffi_closure_SYSV,
                            (void*)codeloc);
     }
+  else if (cif->abi == FFI_FASTCALL)
+    {
+      FFI_INIT_TRAMPOLINE_STDCALL (&closure->tramp[0],
+				   &ffi_closure_FASTCALL,
+				   (void*)codeloc);
+    }
   else if (cif->abi == FFI_THISCALL)
     {
       FFI_INIT_TRAMPOLINE_STDCALL (&closure->tramp[0],
diff --git a/src/x86/win32.S b/src/x86/win32.S
index 0a655c4..699ea46 100644
--- a/src/x86/win32.S
+++ b/src/x86/win32.S
@@ -472,14 +472,19 @@ cd_epilogue:
         mov   esp, ebp
         pop   ebp
         pop   ecx
-        mov   ecx, DWORD PTR [ecx + (CLOSURE_CIF_OFFSET-10)]
-        cmp   DWORD PTR [ecx + CIF_ABI_OFFSET], 3
-        mov   ecx, DWORD PTR [ecx + CIF_BYTES_OFFSET]
-        jne   cd_not_thiscall
-        add   ecx, 4
-cd_not_thiscall:
         pop   edx
-        add   esp, ecx
+        mov   ecx, DWORD PTR [ecx + (CLOSURE_CIF_OFFSET-10)]
+        add   esp, DWORD PTR [ecx + CIF_BYTES_OFFSET]
+        mov   ecx, DWORD PTR [ecx + CIF_ABI_OFFSET]
+        cmp   ecx, 3
+        je    cd_thiscall
+        cmp   ecx, 4
+        jne   cd_not_fastcall
+
+        add   esp, 4
+cd_thiscall:
+        add   esp, 4
+cd_not_fastcall:
         jmp   edx
 ffi_closure_STDCALL ENDP
 
@@ -679,6 +684,20 @@ USCORE_SYMBOL(ffi_closure_THISCALL):
 	xchg	%ecx, (%esp)
 	push	%ecx
 	jmp	.ffi_closure_STDCALL_internal
+
+        .balign 16
+FFI_HIDDEN(ffi_closure_FASTCALL)
+	.globl	USCORE_SYMBOL(ffi_closure_FASTCALL)
+#if defined(X86_WIN32) && !defined(__OS2__)
+	.def	_ffi_closure_FASTCALL;	.scl	2;	.type	32;	.endef
+#endif
+USCORE_SYMBOL(ffi_closure_FASTCALL):
+	/* Insert the register arguments on the stack as the first two arguments */
+	xchg	%edx, 4(%esp)
+	xchg	%ecx, (%esp)
+	push	%edx
+	push	%ecx
+	jmp	.ffi_closure_STDCALL_internal
 .LFE1:
 
         # This assumes we are using gas.
@@ -1090,14 +1109,18 @@ USCORE_SYMBOL(ffi_closure_STDCALL):
 	movl	%ebp, %esp
 	popl	%ebp
 	popl	%ecx
+	popl	%edx
 	movl	(CLOSURE_CIF_OFFSET-10)(%ecx), %ecx
-	cmpl	$3, CIF_ABI_OFFSET(%ecx) /* FFI_THISCALL */
-	movl	CIF_BYTES_OFFSET(%ecx), %ecx
-	jne	1f
-	addl	$4, %ecx
-1:	popl	%edx
-	addl	%ecx, %esp
-	jmp	*%edx
+	addl	CIF_BYTES_OFFSET(%ecx), %esp
+	movl	CIF_ABI_OFFSET(%ecx), %ecx
+	cmpl	$3, %ecx /* FFI_THISCALL */
+	je	1f
+	cmpl	$4, %ecx /* FFI_FASTCALL */
+	jne	2f
+
+	addl	$4, %esp
+1:	addl	$4, %esp
+2:	jmp	*%edx
 .ffi_closure_STDCALL_end:
 .LFE5:
 
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index acd15c5..da10465 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -35,14 +35,14 @@ libffi.call/cls_align_longdouble_split2.c libffi.call/return_dbl2.c	\
 libffi.call/return_fl3.c libffi.call/stret_medium.c			\
 libffi.call/nested_struct6.c libffi.call/closure_fn3.c			\
 libffi.call/float3.c libffi.call/many2.c				\
-libffi.call/closure_stdcall.c libffi.call/cls_align_uint16.c		\
+libffi.call/closure_simple.c libffi.call/cls_align_uint16.c		\
 libffi.call/cls_9byte1.c libffi.call/closure_fn6.c			\
 libffi.call/cls_double_va.c libffi.call/cls_align_pointer.c		\
 libffi.call/cls_align_longdouble.c libffi.call/closure_fn2.c		\
 libffi.call/cls_sshort.c \
 libffi.call/nested_struct.c libffi.call/cls_20byte.c			\
 libffi.call/cls_longdouble.c libffi.call/cls_multi_uchar.c		\
-libffi.call/return_uc.c libffi.call/closure_thiscall.c			\
+libffi.call/return_uc.c \
 libffi.call/cls_18byte.c libffi.call/cls_8byte.c			\
 libffi.call/promotion.c \
 libffi.call/return_dbl.c libffi.call/cls_24byte.c			\
diff --git a/testsuite/libffi.call/closure_simple.c b/testsuite/libffi.call/closure_simple.c
new file mode 100644
index 0000000..5a4e728
--- /dev/null
+++ b/testsuite/libffi.call/closure_simple.c
@@ -0,0 +1,55 @@
+/* Area:	closure_call
+   Purpose:	Check simple closure handling with all ABIs
+   Limitations:	none.
+   PR:		none.
+   Originator:	<twalljava@dev.java.net> */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+static void
+closure_test(ffi_cif* cif __UNUSED__, void* resp, void** args, void* userdata)
+{
+  *(ffi_arg*)resp =
+    (int)*(int *)args[0] + (int)(*(int *)args[1])
+    + (int)(*(int *)args[2])  + (int)(*(int *)args[3])
+    + (int)(intptr_t)userdata;
+
+  printf("%d %d %d %d: %d\n",
+	 (int)*(int *)args[0], (int)(*(int *)args[1]),
+	 (int)(*(int *)args[2]), (int)(*(int *)args[3]),
+         (int)*(ffi_arg *)resp);
+
+}
+
+typedef int (ABI_ATTR *closure_test_type0)(int, int, int, int);
+
+int main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
+  ffi_type * cl_arg_types[17];
+  int res;
+
+  cl_arg_types[0] = &ffi_type_uint;
+  cl_arg_types[1] = &ffi_type_uint;
+  cl_arg_types[2] = &ffi_type_uint;
+  cl_arg_types[3] = &ffi_type_uint;
+  cl_arg_types[4] = NULL;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif(&cif, ABI_NUM, 4,
+		     &ffi_type_sint, cl_arg_types) == FFI_OK);
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, closure_test,
+                             (void *) 3 /* userdata */, code) == FFI_OK);
+
+  res = (*(closure_test_type0)code)(0, 1, 2, 3);
+  /* { dg-output "0 1 2 3: 9" } */
+
+  printf("res: %d\n",res);
+  /* { dg-output "\nres: 9" } */
+
+  exit(0);
+}
diff --git a/testsuite/libffi.call/closure_stdcall.c b/testsuite/libffi.call/closure_stdcall.c
deleted file mode 100644
index fd1e4b0..0000000
--- a/testsuite/libffi.call/closure_stdcall.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Area:	closure_call (stdcall convention)
-   Purpose:	Check handling when caller expects stdcall callee
-   Limitations:	none.
-   PR:		none.
-   Originator:	<twalljava@dev.java.net> */
-
-/* { dg-do run { target i?86-*-* } } */
-#include "ffitest.h"
-
-static void
-closure_test_stdcall(ffi_cif* cif __UNUSED__, void* resp, void** args,
-		 void* userdata)
-{
-  *(ffi_arg*)resp =
-    (int)*(int *)args[0] + (int)(*(int *)args[1])
-    + (int)(*(int *)args[2])  + (int)(*(int *)args[3])
-    + (int)(intptr_t)userdata;
-
-  printf("%d %d %d %d: %d\n",
-	 (int)*(int *)args[0], (int)(*(int *)args[1]),
-	 (int)(*(int *)args[2]), (int)(*(int *)args[3]),
-         (int)*(ffi_arg *)resp);
-
-}
-
-typedef int (__STDCALL__ *closure_test_type0)(int, int, int, int);
-
-int main (void)
-{
-  ffi_cif cif;
-  void *code;
-  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
-  ffi_type * cl_arg_types[17];
-  int res;
-
-  cl_arg_types[0] = &ffi_type_uint;
-  cl_arg_types[1] = &ffi_type_uint;
-  cl_arg_types[2] = &ffi_type_uint;
-  cl_arg_types[3] = &ffi_type_uint;
-  cl_arg_types[4] = NULL;
-
-  /* Initialize the cif */
-  CHECK(ffi_prep_cif(&cif, FFI_STDCALL, 4,
-		     &ffi_type_sint, cl_arg_types) == FFI_OK);
-
-  CHECK(ffi_prep_closure_loc(pcl, &cif, closure_test_stdcall,
-                             (void *) 3 /* userdata */, code) == FFI_OK);
-
-  res = (*(closure_test_type0)code)(0, 1, 2, 3);
-  /* { dg-output "0 1 2 3: 9" } */
-
-  printf("res: %d\n",res);
-  /* { dg-output "\nres: 9" } */
-
-  exit(0);
-}
diff --git a/testsuite/libffi.call/closure_thiscall.c b/testsuite/libffi.call/closure_thiscall.c
deleted file mode 100644
index 8f7d2fa..0000000
--- a/testsuite/libffi.call/closure_thiscall.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Area:	closure_call (thiscall convention)
-   Purpose:	Check handling when caller expects thiscall callee
-   Limitations:	none.
-   PR:		none.
-   Originator:	<ktietz@redhat.com> */
-
-/* { dg-do run { target i?86-*-* } } */
-#include "ffitest.h"
-
-static void
-closure_test_thiscall(ffi_cif* cif __UNUSED__, void* resp, void** args,
-		      void* userdata)
-{
-  *(ffi_arg*)resp =
-    (int)*(int *)args[0] + (int)(*(int *)args[1])
-    + (int)(*(int *)args[2])  + (int)(*(int *)args[3])
-    + (int)(intptr_t)userdata;
-
-  printf("%d %d %d %d: %d\n",
-	 (int)*(int *)args[0], (int)(*(int *)args[1]),
-	 (int)(*(int *)args[2]), (int)(*(int *)args[3]),
-         (int)*(ffi_arg *)resp);
-
-}
-
-typedef int (__THISCALL__ *closure_test_type0)(int, int, int, int);
-
-int main (void)
-{
-  ffi_cif cif;
-  void *code;
-  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
-  ffi_type * cl_arg_types[17];
-  int res;
-
-  cl_arg_types[0] = &ffi_type_uint;
-  cl_arg_types[1] = &ffi_type_uint;
-  cl_arg_types[2] = &ffi_type_uint;
-  cl_arg_types[3] = &ffi_type_uint;
-  cl_arg_types[4] = NULL;
-
-  /* Initialize the cif */
-  CHECK(ffi_prep_cif(&cif, FFI_THISCALL, 4,
-		     &ffi_type_sint, cl_arg_types) == FFI_OK);
-
-  CHECK(ffi_prep_closure_loc(pcl, &cif, closure_test_thiscall,
-                             (void *) 3 /* userdata */, code) == FFI_OK);
-
-  res = (*(closure_test_type0)code)(0, 1, 2, 3);
-  /* { dg-output "0 1 2 3: 9" } */
-
-  printf("res: %d\n",res);
-  /* { dg-output "\nres: 9" } */
-
-  exit(0);
-}