This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
[PATCH 13/13] libffi: Support go closures on i386
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: libffi-discuss at sourceware dot org, gofrontend-dev at googlegroups dot com
- Date: Fri, 10 Oct 2014 13:42:53 -0700
- Subject: [PATCH 13/13] libffi: Support go closures on i386
- Authentication-results: sourceware.org; auth=none
- References: <1412973773-3942-1-git-send-email-rth at redhat dot com>
---
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