runtime: change mcall to take a Go function value

For future work in bringing in the Go 1.8 GC, change the mcall
function to take a Go function value, which means that mcall can take
a closure rather than just a straight C function pointer.

As part of this change move kickoff from C to Go, which we want to do
anyhow so that we run the write barriers that it generates.

Change-Id: Ice5d2184e5c65d2949e60f7050514ff6d036c37c
Reviewed-on: https://go-review.googlesource.com/40935
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go
index 2f86eb3..9f467ee 100644
--- a/libgo/go/runtime/proc.go
+++ b/libgo/go/runtime/proc.go
@@ -34,6 +34,7 @@
 //go:linkname helpgc runtime.helpgc
 //go:linkname stopTheWorldWithSema runtime.stopTheWorldWithSema
 //go:linkname startTheWorldWithSema runtime.startTheWorldWithSema
+//go:linkname kickoff runtime.kickoff
 //go:linkname mstart runtime.mstart
 //go:linkname mstart1 runtime.mstart1
 //go:linkname globrunqput runtime.globrunqput
@@ -55,6 +56,7 @@
 func makeGContext(*g, unsafe.Pointer, uintptr)
 func mstartInitContext(*g, unsafe.Pointer)
 func getTraceback(me, gp *g)
+func gtraceback(*g)
 func _cgo_notify_runtime_init_done()
 func alreadyInCallers() bool
 
@@ -969,6 +971,23 @@
 	_g_.m.locks--
 }
 
+// First function run by a new goroutine.
+// This is passed to makecontext.
+func kickoff() {
+	gp := getg()
+
+	if gp.traceback != nil {
+		gtraceback(gp)
+	}
+
+	fv := gp.entry
+	param := gp.param
+	gp.entry = nil
+	gp.param = nil
+	fv(param)
+	goexit1()
+}
+
 // Called to start an M.
 // For gccgo this is called directly by pthread_create.
 //go:nosplit
@@ -978,7 +997,7 @@
 	_g_.m = mp
 	setg(_g_)
 
-	_g_.entry = 0
+	_g_.entry = nil
 	_g_.param = nil
 
 	mstartInitContext(_g_, unsafe.Pointer(&mp))
@@ -2137,7 +2156,7 @@
 	gp.m = nil
 	gp.lockedm = nil
 	_g_.m.lockedg = nil
-	gp.entry = 0
+	gp.entry = nil
 	gp.paniconfault = false
 	gp._defer = nil // should be true already but just in case.
 	gp._panic = nil // non-nil for Goexit during panic. points at stack-allocated data.
@@ -2560,7 +2579,14 @@
 		throw("newproc1: new g is not Gdead")
 	}
 
-	newg.entry = fn
+	// Store the C function pointer into entryfn, take the address
+	// of entryfn, convert it to a Go function value, and store
+	// that in entry.
+	newg.entryfn = fn
+	var entry func(unsafe.Pointer)
+	*(*unsafe.Pointer)(unsafe.Pointer(&entry)) = unsafe.Pointer(&newg.entryfn)
+	newg.entry = entry
+
 	newg.param = arg
 	newg.gopc = getcallerpc(unsafe.Pointer(&fn))
 	if isSystemGoroutine(newg) {
diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go
index f5599ee..69769f9 100644
--- a/libgo/go/runtime/runtime2.go
+++ b/libgo/go/runtime/runtime2.go
@@ -409,8 +409,9 @@
 	gcinitialsp   unsafe.Pointer
 	gcregs        g_ucontext_t
 
-	entry    uintptr // goroutine entry point
-	fromgogo bool    // whether entered from gogo function
+	entry    func(unsafe.Pointer) // goroutine function to run
+	entryfn  uintptr              // function address passed to __go_go
+	fromgogo bool                 // whether entered from gogo function
 
 	issystem     bool // do not output in stack dump
 	isbackground bool // ignore in deadlock detector
diff --git a/libgo/runtime/heapdump.c b/libgo/runtime/heapdump.c
index 4c673f4..ab65d39 100644
--- a/libgo/runtime/heapdump.c
+++ b/libgo/runtime/heapdump.c
@@ -606,6 +606,8 @@
 	runtime_gogo(gp);
 }
 
+static FuncVal mdumpGo = { (void(*)(void))mdump };
+
 void runtime_debug_WriteHeapDump(uintptr)
   __asm__(GOSYM_PREFIX "runtime_debug.WriteHeapDump");
 
@@ -633,7 +635,7 @@
 	g = runtime_g();
 	g->atomicstatus = _Gwaiting;
 	g->waitreason = runtime_gostringnocopy((const byte*)"dumping heap");
-	runtime_mcall(mdump);
+	runtime_mcall(&mdumpGo);
 
 	// Reset dump file.
 	dumpfd = 0;
diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c
index 00b93a3..13e04f0 100644
--- a/libgo/runtime/mgc0.c
+++ b/libgo/runtime/mgc0.c
@@ -2023,6 +2023,7 @@
 
 static void gc(struct gc_args *args);
 static void mgc(G *gp);
+static FuncVal mgcGo = { (void(*)(void))mgc };
 
 static int32
 readgogc(void)
@@ -2113,7 +2114,7 @@
 		g->param = &a;
 		g->atomicstatus = _Gwaiting;
 		g->waitreason = runtime_gostringnocopy((const byte*)"garbage collection");
-		runtime_mcall(mgc);
+		runtime_mcall(&mgcGo);
 		m = runtime_m();
 	}
 
diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c
index d6bb1de..10dd0a8 100644
--- a/libgo/runtime/proc.c
+++ b/libgo/runtime/proc.c
@@ -55,7 +55,8 @@
 
 uintptr runtime_stacks_sys;
 
-static void gtraceback(G*);
+void gtraceback(G*)
+  __asm__(GOSYM_PREFIX "runtime.gtraceback");
 
 #ifdef __rtems__
 #define __thread
@@ -272,24 +273,6 @@
 	}
 }
 
-// First function run by a new goroutine.  This replaces gogocall.
-static void
-kickoff(void)
-{
-	void (*fn)(void*);
-	void *param;
-
-	if(g->traceback != nil)
-		gtraceback(g);
-
-	fn = (void (*)(void*))(g->entry);
-	param = g->param;
-	g->entry = 0;
-	g->param = nil;
-	fn(param);
-	runtime_goexit1();
-}
-
 // Switch context to a different goroutine.  This is like longjmp.
 void runtime_gogo(G*) __attribute__ ((noinline));
 void
@@ -309,9 +292,9 @@
 // setjmp.  Because getcontext always returns 0, unlike setjmp, we use
 // g->fromgogo as a code.  It will be true if we got here via
 // setcontext.  g == nil the first time this is called in a new m.
-void runtime_mcall(void (*)(G*)) __attribute__ ((noinline));
+void runtime_mcall(FuncVal *) __attribute__ ((noinline));
 void
-runtime_mcall(void (*pfn)(G*))
+runtime_mcall(FuncVal *fv)
 {
 	M *mp;
 	G *gp;
@@ -357,7 +340,7 @@
 #ifdef USING_SPLIT_STACK
 		__splitstack_setcontext(&mp->g0->stackcontext[0]);
 #endif
-		mp->g0->entry = (uintptr)pfn;
+		mp->g0->entry = fv;
 		mp->g0->param = gp;
 
 		// It's OK to set g directly here because this case
@@ -371,16 +354,6 @@
 	}
 }
 
-// mcall called from Go code.
-void gomcall(FuncVal *)
-  __asm__ (GOSYM_PREFIX "runtime.mcall");
-
-void
-gomcall(FuncVal *fv)
-{
-	runtime_mcall((void*)fv->fn);
-}
-
 // Goroutine scheduler
 // The scheduler's job is to distribute ready-to-run goroutines over worker threads.
 //
@@ -403,6 +376,8 @@
 
 bool	runtime_isarchive;
 
+extern void kickoff(void)
+  __asm__(GOSYM_PREFIX "runtime.kickoff");
 extern void mstart1(void)
   __asm__(GOSYM_PREFIX "runtime.mstart1");
 extern void stopm(void)
@@ -468,7 +443,7 @@
 // Do a stack trace of gp, and then restore the context to
 // gp->dotraceback.
 
-static void
+void
 gtraceback(G* gp)
 {
 	Traceback* traceback;
@@ -520,11 +495,12 @@
 
 	if(gp->entry != 0) {
 		// Got here from mcall.
-		void (*pfn)(G*) = (void (*)(G*))gp->entry;
+		FuncVal *fv = gp->entry;
+		void (*pfn)(G*) = (void (*)(G*))fv->fn;
 		G* gp1 = (G*)gp->param;
-		gp->entry = 0;
+		gp->entry = nil;
 		gp->param = nil;
-		pfn(gp1);
+		__builtin_call_with_static_chain(pfn(gp1), fv);
 		*(int*)0x21 = 0x21;
 	}
 
@@ -560,7 +536,7 @@
 
 	initcontext();
 	gp = g;
-	gp->entry = 0;
+	gp->entry = nil;
 	gp->param = nil;
 #ifdef USING_SPLIT_STACK
 	__splitstack_getcontext(&gp->stackcontext[0]);
@@ -576,11 +552,12 @@
 
 	if(gp->entry != 0) {
 		// Got here from mcall.
-		void (*pfn)(G*) = (void (*)(G*))gp->entry;
+		FuncVal *fv = gp->entry;
+		void (*pfn)(G*) = (void (*)(G*))fv->fn;
 		G* gp1 = (G*)gp->param;
-		gp->entry = 0;
+		gp->entry = nil;
 		gp->param = nil;
-		pfn(gp1);
+		__builtin_call_with_static_chain(pfn(gp1), fv);
 		*(int*)0x22 = 0x22;
 	}
 }
diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h
index 39c92b5..ac86fa4 100644
--- a/libgo/runtime/runtime.h
+++ b/libgo/runtime/runtime.h
@@ -309,7 +309,8 @@
   __asm__ (GOSYM_PREFIX "runtime.mallocinit");
 void	runtime_mprofinit(void);
 #define runtime_getcallersp(p) __builtin_frame_address(0)
-void	runtime_mcall(void(*)(G*));
+void	runtime_mcall(FuncVal*)
+  __asm__ (GOSYM_PREFIX "runtime.mcall");
 uint32	runtime_fastrand(void) __asm__ (GOSYM_PREFIX "runtime.fastrand");
 int32	runtime_timediv(int64, int32, int32*)
   __asm__ (GOSYM_PREFIX "runtime.timediv");