Commit ab83cbb90b280195e636976098d3674f5ffc1d0a

Richard Henderson 2014-10-29T14:38:42

arm: Add support for Go closures

diff --git a/src/arm/ffi.c b/src/arm/ffi.c
index eabab47..9c8732d 100644
--- a/src/arm/ffi.c
+++ b/src/arm/ffi.c
@@ -308,6 +308,7 @@ struct call_frame
   void *lr;
   void *rvalue;
   int flags;
+  void *closure;
 };
 
 extern void ffi_call_SYSV (void *stack, struct call_frame *,
@@ -315,8 +316,9 @@ extern void ffi_call_SYSV (void *stack, struct call_frame *,
 extern void ffi_call_VFP (void *vfp_space, struct call_frame *,
 			   void (*fn) (void), unsigned vfp_used) FFI_HIDDEN;
 
-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)
 {
   int flags = cif->flags;
   ffi_type *rtype = cif->rtype;
@@ -364,6 +366,7 @@ ffi_call (ffi_cif * cif, void (*fn) (void), void *rvalue, void **avalue)
 
   frame->rvalue = new_rvalue;
   frame->flags = flags;
+  frame->closure = closure;
 
   if (vfp_space)
     {
@@ -380,6 +383,19 @@ ffi_call (ffi_cif * cif, void (*fn) (void), void *rvalue, void **avalue)
     memcpy (rvalue, new_rvalue, rtype->size);
 }
 
+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);
+}
+
 static void *
 ffi_prep_incoming_args_SYSV (ffi_cif *cif, void *rvalue,
 			     char *argp, void **avalue)
@@ -476,31 +492,43 @@ ffi_prep_incoming_args_VFP (ffi_cif *cif, void *rvalue, char *stack,
   return rvalue;
 }
 
+struct closure_frame
+{
+  char vfp_space[8*8] __attribute__((aligned(8)));
+  char result[8*4];
+  char argp[];
+};
+
 int FFI_HIDDEN
-ffi_closure_inner_SYSV (ffi_closure *closure, void *rvalue, char *argp)
+ffi_closure_inner_SYSV (ffi_cif *cif,
+		        void (*fun) (ffi_cif *, void *, void **, void *),
+		        void *user_data,
+		        struct closure_frame *frame)
 {
-  ffi_cif *cif = closure->cif;
   void **avalue = (void **) alloca (cif->nargs * sizeof (void *));
-
-  rvalue = ffi_prep_incoming_args_SYSV (cif, rvalue, argp, avalue);
-  closure->fun (cif, rvalue, avalue, closure->user_data);
+  void *rvalue = ffi_prep_incoming_args_SYSV (cif, frame->result,
+					      frame->argp, avalue);
+  fun (cif, rvalue, avalue, user_data);
   return cif->flags;
 }
 
 int FFI_HIDDEN
-ffi_closure_inner_VFP (ffi_closure *closure, void *rvalue,
-		       char *argp, char *vfp_space)
+ffi_closure_inner_VFP (ffi_cif *cif,
+		       void (*fun) (ffi_cif *, void *, void **, void *),
+		       void *user_data,
+		       struct closure_frame *frame)
 {
-  ffi_cif *cif = closure->cif;
   void **avalue = (void **) alloca (cif->nargs * sizeof (void *));
-
-  rvalue = ffi_prep_incoming_args_VFP (cif, rvalue, argp, vfp_space, avalue);
-  closure->fun (cif, rvalue, avalue, closure->user_data);
+  void *rvalue = ffi_prep_incoming_args_VFP (cif, frame->result, frame->argp,
+					     frame->vfp_space, avalue);
+  fun (cif, rvalue, avalue, user_data);
   return cif->flags;
 }
 
 void ffi_closure_SYSV (void) FFI_HIDDEN;
 void ffi_closure_VFP (void) FFI_HIDDEN;
+void ffi_go_closure_SYSV (void) FFI_HIDDEN;
+void ffi_go_closure_VFP (void) FFI_HIDDEN;
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
 
@@ -785,6 +813,28 @@ ffi_prep_closure_loc (ffi_closure * closure,
   return FFI_OK;
 }
 
+ffi_status
+ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif,
+		     void (*fun) (ffi_cif *, void *, void **, void *))
+{
+  void (*closure_func) (void) = ffi_go_closure_SYSV;
+
+  if (cif->abi == FFI_VFP)
+    {
+      /* We only need take the vfp path if there are vfp arguments.  */
+      if (cif->vfp_used)
+	closure_func = ffi_go_closure_VFP;
+    }
+  else if (cif->abi != FFI_SYSV)
+    return FFI_BAD_ABI;
+
+  closure->tramp = closure_func;
+  closure->cif = cif;
+  closure->fun = fun;
+
+  return FFI_OK;
+}
+
 /* Below are routines for VFP hard-float support. */
 
 /* A subroutine of vfp_type_p.  Given a structure type, return the type code
diff --git a/src/arm/ffitarget.h b/src/arm/ffitarget.h
index 744a1e1..4f473f9 100644
--- a/src/arm/ffitarget.h
+++ b/src/arm/ffitarget.h
@@ -62,6 +62,7 @@ typedef enum ffi_abi {
 /* ---- Definitions for closures ----------------------------------------- */
 
 #define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
 #define FFI_TRAMPOLINE_SIZE 12
 #define FFI_NATIVE_RAW_API 0
 
diff --git a/src/arm/sysv.S b/src/arm/sysv.S
index ce5450d..fd16589 100644
--- a/src/arm/sysv.S
+++ b/src/arm/sysv.S
@@ -136,6 +136,7 @@ ARM_FUNC_START(ffi_call_SYSV, 1)
 
 	mov	sp, r0		@ install the stack pointer
 	mov	lr, r2		@ move the fn pointer out of the way
+	ldr	ip, [fp, #16]	@ install the static chain
 	ldmia	sp!, {r0-r3}	@ move first 4 parameters in registers.
 	blx	lr		@ call fn
 
@@ -180,22 +181,33 @@ ARM_FUNC_END(ffi_call_SYSV)
 
 
 /*
-	unsigned int FFI_HIDDEN
-	ffi_closure_inner (closure, respp, args)
-	     ffi_closure *closure;
-	     void **respp;
-  	     void *args;
+	int ffi_closure_inner_* (cif, fun, user_data, frame)
 */
 
+ARM_FUNC_START(ffi_go_closure_SYSV, 1)
+	cfi_startproc
+	stmdb	sp!, {r0-r3}			@ save argument regs
+	cfi_adjust_cfa_offset(16)
+	ldr	r0, [ip, #4]			@ load cif
+	ldr	r1, [ip, #8]			@ load fun
+	mov	r2, ip				@ load user_data
+	b	0f
+	cfi_endproc
+ARM_FUNC_END(ffi_go_closure_SYSV)
+
 ARM_FUNC_START(ffi_closure_SYSV, 1)
 	UNWIND	.fnstart
 	cfi_startproc
 	stmdb	sp!, {r0-r3}			@ save argument regs
 	cfi_adjust_cfa_offset(16)
-	mov	r0, ip				@ load closure
+	ldr	r0, [ip, #FFI_TRAMPOLINE_SIZE]	  @ load cif
+	ldr	r1, [ip, #FFI_TRAMPOLINE_SIZE+4]  @ load fun
+	ldr	r2, [ip, #FFI_TRAMPOLINE_SIZE+8]  @ load user_data
+0:
 	add	ip, sp, #16			@ compute entry sp
-	sub	sp, sp, #32			@ allocate rvalue space
-	stmdb	sp!, {sp,lr}
+	sub	sp, sp, #64+32			@ allocate frame
+	cfi_adjust_cfa_offset(64+32)
+	stmdb	sp!, {ip,lr}
 
 	/* Remember that EABI unwind info only applies at call sites.
 	   We need do nothing except note the save of the stack pointer
@@ -204,46 +216,53 @@ ARM_FUNC_START(ffi_closure_SYSV, 1)
 	cfi_adjust_cfa_offset(8)
 	cfi_rel_offset(lr, 4)
 
-	add	r1, sp, #8			@ load respp
-	add	r2, sp, #8+32			@ load args
-	mov	r3, #0				@ load vfp_args
-
+	add	r3, sp, #8			@ load frame
 	bl	CNAME(ffi_closure_inner_SYSV)
 
 	@ Load values returned in registers.
-	add	r2, sp, #8			@ load respp
+	add	r2, sp, #8+64			@ load result
 	adr	r3, CNAME(ffi_closure_ret)
 	add	pc, r3, r0, lsl #3
 	cfi_endproc
 	UNWIND	.fnend
 ARM_FUNC_END(ffi_closure_SYSV)
 
+ARM_FUNC_START(ffi_go_closure_VFP, 1)
+	cfi_startproc
+	stmdb	sp!, {r0-r3}			@ save argument regs
+	cfi_adjust_cfa_offset(16)
+	ldr	r0, [ip, #4]			@ load cif
+	ldr	r1, [ip, #8]			@ load fun
+	mov	r2, ip				@ load user_data
+	b	0f
+	cfi_endproc
+ARM_FUNC_END(ffi_go_closure_VFP)
+
 ARM_FUNC_START(ffi_closure_VFP, 1)
 	UNWIND	.fnstart
 	cfi_startproc
 	stmdb	sp!, {r0-r3}			@ save argument regs
 	cfi_adjust_cfa_offset(16)
-	sub	sp, sp, #64+32			@ allocate vfp+rvalue space
+	ldr	r0, [ip, #FFI_TRAMPOLINE_SIZE]	  @ load cif
+	ldr	r1, [ip, #FFI_TRAMPOLINE_SIZE+4]  @ load fun
+	ldr	r2, [ip, #FFI_TRAMPOLINE_SIZE+8]  @ load user_data
+0:
+	add	ip, sp, #16
+	sub	sp, sp, #64+32			@ allocate frame
 	cfi_adjust_cfa_offset(64+32)
 	stc	p11, cr0, [sp], {16}		@ vstm sp, {d0-d7}
-	mov	r0, ip				@ load closure
-	add	ip, sp, #16+64+32		@ compute entry sp
 	stmdb	sp!, {ip,lr}
 
 	/* See above.  */
 	UNWIND	.save {sp,lr}
 	cfi_adjust_cfa_offset(8)
-	cfi_rel_offset(sp, 0)
 	cfi_rel_offset(lr, 4)
 
-	add	r1, sp, #8+64			@ load respp
-	add	r2, sp, #8+64+32		@ load args
-	add	r3, sp, #8			@ load vfp_args
-
+	add	r3, sp, #8			@ load frame
 	bl	CNAME(ffi_closure_inner_VFP)
 
 	@ Load values returned in registers.
-	add	r2, sp, #8+64			@ load respp
+	add	r2, sp, #8+64			@ load result
 	adr	r3, CNAME(ffi_closure_ret)
 	add	pc, r3, r0, lsl #3
 	cfi_endproc