This is the mail archive of the libffi-discuss@sourceware.org mailing list for the libffi 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 13/13] libffi: Support go closures on i386


---
 libffi/src/x86/ffi.c  |  88 ++++++++++++++++++++++++++++++++++++-----
 libffi/src/x86/sysv.S | 107 +++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 166 insertions(+), 29 deletions(-)

diff --git a/libffi/src/x86/ffi.c b/libffi/src/x86/ffi.c
index e3f82ef..77abbe3 100644
--- a/libffi/src/x86/ffi.c
+++ b/libffi/src/x86/ffi.c
@@ -162,8 +162,9 @@ struct ffi_call_frame
 extern void ffi_call_i386(struct ffi_call_frame *, char *)
 	FFI_HIDDEN __attribute__((fastcall));
 
-void
-ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+static void
+ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
+	      void **avalue, void *closure)
 {
   size_t rsize;
   struct ffi_call_frame *frame;
@@ -206,6 +207,21 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
   frame->flags = flags;
   frame->rvalue = rvalue;
 
+  /* Install the closure as the static chain value.  Note that the
+     static chain isn't part of an official ABI, it's what gcc itself
+     allocates for a given ABI.  Generally, this is a register that's
+     predictably unused on entry.  */
+  switch (cabi)
+    {
+    case FFI_THISCALL:
+    case FFI_FASTCALL:
+      frame->eax = (unsigned)closure;
+      break;
+    default:
+      frame->ecx = (unsigned)closure;
+      break;
+    }
+
   narg_reg = 0;
   switch (flags)
     {
@@ -265,6 +281,20 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
   ffi_call_i386(frame, stack);
 }
 
+void
+ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+  ffi_call_int (cif, fn, rvalue, avalue, NULL);
+}
+
+void
+ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
+	     void **avalue, void *closure)
+{
+  ffi_call_int (cif, fn, rvalue, avalue, closure);
+}
+
+
 /* ------- Closure API support ----------------------------------- */
 
 /* How to make a trampoline.  Derived from gcc/config/i386/i386.c. */
@@ -321,18 +351,19 @@ ffi_prep_closure_loc (ffi_closure* closure,
 
 struct ffi_closure_frame
 {
-  unsigned rettemp[4];	/* 0 */
-  unsigned eax;		/* 16 */
-  unsigned edx;		/* 20 */
-  unsigned ecx;		/* 24 */
-  ffi_closure *closure;	/* 28 */
+  unsigned rettemp[4];				/* 0 */
+  unsigned eax;					/* 16 */
+  unsigned edx;					/* 20 */
+  unsigned ecx;					/* 24 */
+  ffi_cif *cif;					/* 28 */
+  void (*fun)(ffi_cif*,void*,void**,void*);	/* 32 */
+  void *user_data;				/* 36 */
 };
 
 unsigned int FFI_HIDDEN __attribute__ ((fastcall))
 ffi_closure_inner (struct ffi_closure_frame *frame, char *argp)
 {
-  ffi_closure *closure = frame->closure;
-  ffi_cif *cif = closure->cif;
+  ffi_cif *cif = frame->cif;
   int cabi, i, n, flags, narg_reg;
   ffi_type **arg_types;
   void *rvalue;
@@ -389,7 +420,7 @@ ffi_closure_inner (struct ffi_closure_frame *frame, char *argp)
       avalue[i] = valp;
     }
 
-  closure->fun (cif, rvalue, avalue, closure->user_data);
+  frame->fun (cif, rvalue, avalue, frame->user_data);
 
   if (cabi == FFI_STDCALL)
     return flags + (cif->bytes << X86_RET_POP_SHIFT);
@@ -559,4 +590,41 @@ ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue)
   ffi_call_i386(frame, stack);
 }
 
+/* ------- Go API support ---------------------------------------- */
+
+extern void ffi_go_closure_eax (void) FFI_HIDDEN;
+extern void ffi_go_closure_ecx (void) FFI_HIDDEN;
+extern void ffi_go_closure_stdcall (void) FFI_HIDDEN;
+
+ffi_status
+ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
+		     void (*fun)(ffi_cif*, void*, void**, void*))
+{
+  void (*dest)(void);
+
+  /* See the comment in ffi_call_int about the static chain.  */
+  switch (cif->abi)
+    {
+    case FFI_SYSV:
+    case FFI_MS_CDECL:
+      dest = ffi_go_closure_ecx;
+      break;
+    case FFI_THISCALL:
+    case FFI_FASTCALL:
+      dest = ffi_go_closure_eax;
+      break;
+    case FFI_STDCALL:
+      dest = ffi_go_closure_stdcall;
+      break;
+    default:
+      return FFI_BAD_ABI;
+    }
+
+  closure->tramp = dest;
+  closure->cif = cif;
+  closure->fun = fun;
+
+  return FFI_OK;
+}
+
 #endif /* !__x86_64__ */
diff --git a/libffi/src/x86/sysv.S b/libffi/src/x86/sysv.S
index d8256d0..2709b11 100644
--- a/libffi/src/x86/sysv.S
+++ b/libffi/src/x86/sysv.S
@@ -165,26 +165,37 @@ E X86_RET_UNUSED15
 	.cfi_endproc
 	.size	ffi_call_i386, . - ffi_call_i386
 
-/* The closure entry points are reached from the ffi_closure trampoline.
-   On entry, %eax contains the address of the ffi_closure.  */
-
-#define	ffi_closure_FS	(12 + 4*4 + 16)
+#define	ffi_closure_FS	(4 + 3*4 + 3*4 + 16)
 
-.macro FFI_CLOSURE_FIRST
-	subl	$ffi_closure_FS, %esp
-	.cfi_adjust_cfa_offset ffi_closure_FS
+/* Macros to help setting up the ffi_closure_data structure.  */
 
-	movl	%edx, 20(%esp)		/* save incoming register args */
+.macro FFI_CLOSURE_SAVE_REGS
+	movl	%eax, 16(%esp)		/* save incoming register args */
+	movl	%edx, 20(%esp)
 	movl	%ecx, 24(%esp)
-	movl	%eax, 28(%esp)		/* trampoline loaded closure */
+.endm
+
+.macro FFI_CLOSURE_COPY_TRAMP_DATA
+	movl	12(%eax), %edx		/* copy cif */
+	movl	16(%eax), %ecx		/* copy fun */
+	movl	20(%eax), %eax		/* copy user_data */
+	movl	%edx, 28(%esp)
+	movl	%ecx, 32(%esp)
+	movl	%eax, 36(%esp)
+.endm
 
-	movl	%esp, %ecx		/* pass save area to C */
-	leal	ffi_closure_FS+4(%esp), %edx
+.macro FFI_CLOSURE_COPY_USER_DATA base, ofs, t1
+	movl	\ofs(\base), \t1
+	movl	\t1, 36(%esp)
+.endm
 
+.macro FFI_CLOSURE_CALL_INNER
+	movl	%esp, %ecx			/* load ffi_closure_data */
+	leal	ffi_closure_FS+4(%esp), %edx	/* load incoming stack */
 #ifdef __PIC__
-	movl	%ebx, 32(%esp)		/* save ebx */
+	movl	%ebx, 40(%esp)			/* save ebx */
 	.cfi_rel_offset %esp, 32
-	call	__x86.get_pc_thunk.bx
+	call	__x86.get_pc_thunk.bx		/* load got register */
 	addl	$_GLOBAL_OFFSET_TABLE_, %ebx
 #endif
 #if defined HAVE_HIDDEN_VISIBILITY_ATTRIBUTE || !defined __PIC__
@@ -194,11 +205,11 @@ E X86_RET_UNUSED15
 #endif
 .endm
 
-.macro FFI_CLOSURE_SECOND
+.macro FFI_CLOSURE_MASK_AND_JUMP
 	andl	$X86_RET_TYPE_MASK, %eax
 #ifdef __PIC__
 	leal	0f@GOTOFF(%ebx, %eax, 8), %eax
-	movl	32(%esp), %ebx		/* restore ebx */
+	movl	40(%esp), %ebx		/* restore ebx */
 	.cfi_restore %ebx
 #else
 	leal	0f(, %eax, 8), %eax
@@ -206,6 +217,36 @@ E X86_RET_UNUSED15
 	jmp	*%eax
 .endm
 
+/* The go closure entry points are called directly from Go code.
+   The suffix is the register in which the static chain is located.  */
+
+
+.macro FFI_GO_CLOSURE	suffix, chain, t1, t2
+	.align	16
+	.globl	ffi_go_closure_\suffix
+	.type	ffi_go_closure_\suffix, @function
+	FFI_HIDDEN (ffi_go_closure_\suffix)
+ffi_go_closure_\suffix:
+	.cfi_startproc
+	subl	$ffi_closure_FS, %esp
+	.cfi_adjust_cfa_offset ffi_closure_FS
+	FFI_CLOSURE_SAVE_REGS
+	movl	4(\chain), \t1		/* copy cif */
+	movl	8(\chain), \t2		/* copy fun */
+	movl	\t1, 28(%esp)
+	movl	\t2, 32(%esp)
+	movl	\chain, 36(%esp)	/* closure is user_data  */
+	jmp	88f
+	.cfi_endproc
+	.size	ffi_go_closure_\suffix, . - ffi_go_closure_\suffix
+.endm
+
+FFI_GO_CLOSURE	eax, %eax, %edx, %ecx
+FFI_GO_CLOSURE	ecx, %ecx, %edx, %eax
+
+/* The closure entry points are reached from the ffi_closure trampoline.
+   On entry, %eax contains the address of the ffi_closure.  */
+
 	.align	16
 	.globl	ffi_closure_i386
 	.type	ffi_closure_i386, @function
@@ -213,8 +254,16 @@ E X86_RET_UNUSED15
 
 ffi_closure_i386:
 	.cfi_startproc
-	FFI_CLOSURE_FIRST
-	FFI_CLOSURE_SECOND
+	subl	$ffi_closure_FS, %esp
+	.cfi_adjust_cfa_offset ffi_closure_FS
+
+	FFI_CLOSURE_SAVE_REGS
+	FFI_CLOSURE_COPY_TRAMP_DATA
+
+88:	/* Entry point from preceeding Go closures.  */
+
+	FFI_CLOSURE_CALL_INNER
+	FFI_CLOSURE_MASK_AND_JUMP
 
 	.align	8
 0:
@@ -284,6 +333,8 @@ E X86_RET_UNUSED15
 	.cfi_endproc
 	.size	ffi_closure_i386, . - ffi_closure_i386
 
+FFI_GO_CLOSURE	stdcall, %ecx, %edx, %eax
+
 	.align	16
 	.globl	ffi_closure_i386_stdcall
 	.type	ffi_closure_i386_stdcall, @function
@@ -291,16 +342,34 @@ E X86_RET_UNUSED15
 
 ffi_closure_i386_stdcall:
 	.cfi_startproc
-	FFI_CLOSURE_FIRST
+	subl	$ffi_closure_FS, %esp
+	.cfi_adjust_cfa_offset ffi_closure_FS
+
+	FFI_CLOSURE_SAVE_REGS
+	FFI_CLOSURE_COPY_TRAMP_DATA
+
+88:	/* Entry point from preceeding Go closure.  */
+
+	FFI_CLOSURE_CALL_INNER
 
 	movl	%eax, %ecx
 	shrl	$4, %ecx			    /* isolate pop count */
 	leal	ffi_closure_FS(%esp, %ecx), %ecx    /* compute popped esp */
 	movl	ffi_closure_FS(%esp), %edx	    /* move return address */
 	movl	%edx, (%ecx)
+
+	/* New pseudo-stack frame based off ecx.  This is unwind trickery
+	   in that the CFA *has* changed, to the proper popped stack address.
+	   Note that the location to which we moved the return address
+	   is (the new) CFA-4, so that's unchanged.  */
 	.cfi_def_cfa %ecx, 4
+	/* Normally esp is unwound to CFA + the caller's ARGS_SIZE.
+	   We've just set the CFA to that final value.  Tell the unwinder
+	   to restore esp from CFA without the ARGS_SIZE:
+	   DW_CFA_val_expression %esp, DW_OP_call_frame_cfa.  */
+	.cfi_escape 0x16, 4, 1, 0x9c
 
-	FFI_CLOSURE_SECOND
+	FFI_CLOSURE_MASK_AND_JUMP
 
 	.align	8
 0:
-- 
1.9.3


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