Commit 9761b7bb70b4c47dc11dc74830964a0b3b3176d3

Richard Henderson 2014-10-17T21:26:52

alpha: Add support for Go closures

diff --git a/src/alpha/ffi.c b/src/alpha/ffi.c
index 1e5187e..efae4cc 100644
--- a/src/alpha/ffi.c
+++ b/src/alpha/ffi.c
@@ -41,9 +41,11 @@
 # define FFI_TYPE_LONGDOUBLE 4
 #endif
 
-extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)(void))
-  FFI_HIDDEN;
+extern void ffi_call_osf(void *stack, void *frame, unsigned flags,
+			 void *raddr, void (*fn)(void), void *closure)
+	FFI_HIDDEN;
 extern void ffi_closure_osf(void) FFI_HIDDEN;
+extern void ffi_go_closure_osf(void) FFI_HIDDEN;
 
 /* Promote a float value to its in-register double representation.
    Unlike actually casting to double, this does not trap on NaN.  */
@@ -222,12 +224,14 @@ extend_basic_type(void *valp, int type, int argn)
     }
 }
 
-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)
 {
   unsigned long *argp;
   long i, avn, argn, flags = cif->flags;
   ffi_type **arg_types;
+  void *frame;
 
   /* If the return value is a struct and we don't have a return
      value address then we need to make one.  */
@@ -236,7 +240,8 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 
   /* Allocate the space for the arguments, plus 4 words of temp
      space for ffi_call_osf.  */
-  argp = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
+  argp = frame = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
+  frame += cif->bytes;
 
   argn = 0;
   if (flags == ALPHA_RET_IN_MEM)
@@ -301,9 +306,21 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
     }
 
   flags = (flags >> ALPHA_ST_SHIFT) & 0xff;
-  ffi_call_osf(argp, cif->bytes, flags, rvalue, fn);
+  ffi_call_osf(argp, frame, flags, rvalue, fn, closure);
+}
+
+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);
+}
 
 ffi_status
 ffi_prep_closure_loc (ffi_closure* closure,
@@ -339,15 +356,31 @@ 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*))
+{
+  if (cif->abi != FFI_OSF)
+    return FFI_BAD_ABI;
+
+  closure->tramp = (void *)ffi_go_closure_osf;
+  closure->cif = cif;
+  closure->fun = fun;
+
+  return FFI_OK;
+}
+
 long FFI_HIDDEN
-ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
+ffi_closure_osf_inner (ffi_cif *cif,
+		       void (*fun)(ffi_cif*, void*, void**, void*),
+		       void *user_data,
+		       void *rvalue, unsigned long *argp)
 {
-  ffi_cif *cif;
   void **avalue;
   ffi_type **arg_types;
   long i, avn, argn, flags;
 
-  cif = closure->cif;
   avalue = alloca(cif->nargs * sizeof(void *));
   flags = cif->flags;
   argn = 0;
@@ -481,7 +514,7 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
     }
 
   /* Invoke the closure.  */
-  closure->fun (cif, rvalue, avalue, closure->user_data);
+  fun (cif, rvalue, avalue, user_data);
 
   /* Tell ffi_closure_osf how to perform return type promotions.  */
   return (flags >> ALPHA_LD_SHIFT) & 0xff;
diff --git a/src/alpha/ffitarget.h b/src/alpha/ffitarget.h
index 60f92fd..a02dbd0 100644
--- a/src/alpha/ffitarget.h
+++ b/src/alpha/ffitarget.h
@@ -50,6 +50,7 @@ typedef enum ffi_abi {
 /* ---- Definitions for closures ----------------------------------------- */
 
 #define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
 #define FFI_TRAMPOLINE_SIZE 24
 #define FFI_NATIVE_RAW_API 0
 
diff --git a/src/alpha/osf.S b/src/alpha/osf.S
index 4059f82..b031828 100644
--- a/src/alpha/osf.S
+++ b/src/alpha/osf.S
@@ -39,10 +39,10 @@
 	.org	99b + \index * 16
 .endm
 
-/* ffi_call_osf (void *args, unsigned long bytes, unsigned flags,
-		 void *raddr, void (*fnaddr)(void));
+/* ffi_call_osf (void *stack, void *frame, unsigned flags,
+		 void *raddr, void (*fnaddr)(void), void *closure)
 
-   Bit o trickiness here -- ARGS+BYTES is the base of the stack frame
+   Bit o trickiness here -- FRAME is the base of the stack frame
    for this function.  This has been allocated by ffi_call.  We also
    deallocate some of the stack that has been alloca'd.  */
 
@@ -52,22 +52,21 @@
 	FFI_HIDDEN(ffi_call_osf)
 
 ffi_call_osf:
-	.frame	$15, 32, $26, 0
-	.mask   0x4008000, -32
 	cfi_startproc
-	addq	$16,$17,$1
+	cfi_def_cfa($17, 32)
 	mov	$16, $30
-	stq	$26, 0($1)
-	stq	$15, 8($1)
-	stq	$18, 16($1)
-	mov	$1, $15
+	stq	$26, 0($17)
+	stq	$15, 8($17)
+	mov	$17, $15
 	.prologue 0
-	cfi_def_cfa($15, 32)
+	cfi_def_cfa_register($15)
 	cfi_rel_offset($26, 0)
 	cfi_rel_offset($15, 8)
 
-	stq	$19, 24($1)
-	mov	$20, $27
+	stq	$18, 16($17)		# save flags into frame
+	stq	$19, 24($17)		# save rvalue into frame
+	mov	$20, $27		# fn into place for call
+	mov	$21, $1			# closure into static chain
 
 	# Load up all of the (potential) argument registers.
 	ldq	$16, 0($30)
@@ -89,16 +88,16 @@ ffi_call_osf:
 	jsr	$26, ($27), 0
 0:
 	ldah	$29, 0($26)		!gpdisp!1
-	ldq	$2, 24($15)
+	ldq	$2, 24($15)		# reload rvalue
 	lda	$29, 0($29)		!gpdisp!1
-	ldq	$3, 16($15)
+	ldq	$3, 16($15)		# reload flags
 	lda	$1, 99f-0b($26)
 	ldq	$26, 0($15)
 	ldq	$15, 8($15)
 	cfi_restore($26)
 	cfi_restore($15)
 	cfi_def_cfa($sp, 0)
-	cmoveq	$2, ALPHA_ST_VOID, $3	# mash null return to void
+	cmoveq	$2, ALPHA_ST_VOID, $3	# mash null rvalue to void
 	addq	$3, $3, $3
 	s8addq	$3, $1, $1		# 99f + stcode * 16
 	jmp	$31, ($1), $st_int
@@ -136,13 +135,37 @@ E ALPHA_ST_CPLXD
 #define CLOSURE_FS	(16*8)
 
 	.align	4
+	.globl	ffi_go_closure_osf
+	.ent	ffi_go_closure_osf
+	FFI_HIDDEN(ffi_go_closure_osf)
+
+ffi_go_closure_osf:
+	cfi_startproc
+	ldgp	$29, 0($27)
+	subq	$30, CLOSURE_FS, $30
+	cfi_adjust_cfa_offset(CLOSURE_FS)
+	stq	$26, 0($30)
+	.prologue 1
+	cfi_rel_offset($26, 0)
+
+	stq	$16, 10*8($30)
+	stq	$17, 11*8($30)
+	stq	$18, 12*8($30)
+
+	ldq	$16, 8($1)			# load cif
+	ldq	$17, 16($1)			# load fun
+	mov	$1, $18				# closure is user_data
+	br	$do_closure
+
+	cfi_endproc
+	.end	ffi_go_closure_osf
+
+	.align	4
 	.globl	ffi_closure_osf
 	.ent	ffi_closure_osf
 	FFI_HIDDEN(ffi_closure_osf)
 
 ffi_closure_osf:
-	.frame	$30, CLOSURE_FS, $26, 0
-	.mask	0x4000000, -CLOSURE_FS
 	cfi_startproc
 	ldgp	$29, 0($27)
 	subq	$30, CLOSURE_FS, $30
@@ -152,23 +175,28 @@ ffi_closure_osf:
 	cfi_rel_offset($26, 0)
 
 	# Store all of the potential argument registers in va_list format.
-	stt	$f16, 4*8($30)
-	stt	$f17, 5*8($30)
-	stt	$f18, 6*8($30)
-	stt	$f19, 7*8($30)
-	stt	$f20, 8*8($30)
-	stt	$f21, 9*8($30)
 	stq	$16, 10*8($30)
 	stq	$17, 11*8($30)
 	stq	$18, 12*8($30)
+
+	ldq	$16, 24($1)			# load cif
+	ldq	$17, 32($1)			# load fun
+	ldq	$18, 40($1)			# load user_data
+
+$do_closure:
 	stq	$19, 13*8($30)
 	stq	$20, 14*8($30)
 	stq	$21, 15*8($30)
+	stt	$f16, 4*8($30)
+	stt	$f17, 5*8($30)
+	stt	$f18, 6*8($30)
+	stt	$f19, 7*8($30)
+	stt	$f20, 8*8($30)
+	stt	$f21, 9*8($30)
 
 	# Call ffi_closure_osf_inner to do the bulk of the work.
-	mov	$1, $16
-	lda	$17, 2*8($30)
-	lda	$18, 10*8($30)
+	lda	$19, 2*8($30)
+	lda	$20, 10*8($30)
 	jsr	$26, ffi_closure_osf_inner
 0:
 	ldah	$29, 0($26)			!gpdisp!2