x86: Add support for Go closures
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
diff --git a/src/x86/ffi.c b/src/x86/ffi.c
index a0d0cf3..359a864 100644
--- a/src/x86/ffi.c
+++ b/src/x86/ffi.c
@@ -218,25 +218,28 @@ struct call_frame
struct abi_params
{
int dir; /* parameter growth direction */
+ int static_chain; /* the static chain register used by gcc */
int nregs; /* number of register parameters */
int regs[3];
};
static const struct abi_params abi_params[FFI_LAST_ABI] = {
- [FFI_SYSV] = { 1, 0 },
- [FFI_THISCALL] = { 1, 1, { R_ECX } },
- [FFI_FASTCALL] = { 1, 2, { R_ECX, R_EDX } },
- [FFI_STDCALL] = { 1, 0 },
- [FFI_PASCAL] = { -1, 0 },
- [FFI_REGISTER] = { -1, 3, { R_EAX, R_EDX, R_ECX } },
- [FFI_MS_CDECL] = { 1, 0 }
+ [FFI_SYSV] = { 1, R_ECX, 0 },
+ [FFI_THISCALL] = { 1, R_EAX, 1, { R_ECX } },
+ [FFI_FASTCALL] = { 1, R_EAX, 2, { R_ECX, R_EDX } },
+ [FFI_STDCALL] = { 1, R_ECX, 0 },
+ [FFI_PASCAL] = { -1, R_ECX, 0 },
+ /* ??? No defined static chain; gcc does not support REGISTER. */
+ [FFI_REGISTER] = { -1, R_ECX, 3, { R_EAX, R_EDX, R_ECX } },
+ [FFI_MS_CDECL] = { 1, R_ECX, 0 }
};
extern void ffi_call_i386(struct call_frame *, char *)
FFI_HIDDEN __declspec(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, bytes;
struct call_frame *frame;
@@ -281,6 +284,7 @@ ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
frame->fn = fn;
frame->flags = flags;
frame->rvalue = rvalue;
+ frame->regs[pabi->static_chain] = (unsigned)closure;
narg_reg = 0;
switch (flags)
@@ -345,6 +349,18 @@ 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);
+}
/** private members **/
@@ -493,6 +509,42 @@ ffi_prep_closure_loc (ffi_closure* closure,
return FFI_OK;
}
+void FFI_HIDDEN ffi_go_closure_EAX(void);
+void FFI_HIDDEN ffi_go_closure_ECX(void);
+void FFI_HIDDEN ffi_go_closure_STDCALL(void);
+
+ffi_status
+ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
+ void (*fun)(ffi_cif*,void*,void**,void*))
+{
+ void (*dest)(void);
+
+ 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_ECX;
+ break;
+ case FFI_STDCALL:
+ case FFI_PASCAL:
+ dest = ffi_go_closure_STDCALL;
+ break;
+ case FFI_REGISTER:
+ default:
+ return FFI_BAD_ABI;
+ }
+
+ closure->tramp = dest;
+ closure->cif = cif;
+ closure->fun = fun;
+
+ return FFI_OK;
+}
+
/* ------- Native raw API support -------------------------------- */
#if !FFI_NO_RAW_API
diff --git a/src/x86/ffitarget.h b/src/x86/ffitarget.h
index 8fff29f..580522f 100644
--- a/src/x86/ffitarget.h
+++ b/src/x86/ffitarget.h
@@ -117,6 +117,7 @@ typedef enum ffi_abi {
/* ---- Definitions for closures ----------------------------------------- */
#define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
#define FFI_TYPE_SMALL_STRUCT_1B (FFI_TYPE_LAST + 1)
#define FFI_TYPE_SMALL_STRUCT_2B (FFI_TYPE_LAST + 2)
@@ -127,7 +128,6 @@ typedef enum ffi_abi {
|| (defined (__x86_64__) && defined (X86_DARWIN))
# define FFI_TRAMPOLINE_SIZE 24
# define FFI_NATIVE_RAW_API 0
-# define FFI_GO_CLOSURES 1
#else
# define FFI_TRAMPOLINE_SIZE 12
# define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */
diff --git a/src/x86/sysv.S b/src/x86/sysv.S
index 7b898ae..f412b7a 100644
--- a/src/x86/sysv.S
+++ b/src/x86/sysv.S
@@ -228,6 +228,28 @@ ENDF(C(ffi_call_i386))
jmp *%eax
.endm
+.macro FFI_GO_CLOSURE suffix, chain, t1, t2
+ .align 16
+ .globl C(ffi_go_closure_\suffix)
+ FFI_HIDDEN(C(ffi_go_closure_\suffix))
+C(ffi_go_closure_\suffix):
+ cfi_startproc
+ subl $closure_FS, %esp
+ cfi_adjust_cfa_offset(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
+ENDF(C(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. */
@@ -242,6 +264,9 @@ C(ffi_closure_i386):
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
@@ -303,6 +328,8 @@ E(X86_RET_UNUSED15)
cfi_endproc
ENDF(C(ffi_closure_i386))
+FFI_GO_CLOSURE STDCALL, %ecx, %edx, %eax
+
/* For REGISTER, we have no available parameter registers, and so we
enter here having pushed the closure onto the stack. */
@@ -339,8 +366,13 @@ C(ffi_closure_STDCALL):
cfi_adjust_cfa_offset(closure_FS)
FFI_CLOSURE_SAVE_REGS
-0:
+
+0: /* Entry point from ffi_closure_REGISTER. */
+
FFI_CLOSURE_COPY_TRAMP_DATA
+
+88: /* Entry point from preceeding Go closure. */
+
FFI_CLOSURE_CALL_INNER
movl %eax, %ecx