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");