---
 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

Reply via email to