cmd/gc, reflect, runtime: switch to indirect func value representation

Step 1 of http://golang.org/s/go11func.

R=golang-dev, r, daniel.morsing, remyoudompheng
CC=golang-dev
https://golang.org/cl/7393045
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index f09ddd0..4d8cb1a 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -75,7 +75,7 @@
 	CALL	runtime·schedinit(SB)
 
 	// create a new goroutine to start program
-	PUSHL	$runtime·main(SB)	// entry
+	PUSHL	$runtime·main·f(SB)	// entry
 	PUSHL	$0	// arg size
 	CALL	runtime·newproc(SB)
 	POPL	AX
@@ -87,6 +87,9 @@
 	INT $3
 	RET
 
+DATA	runtime·main·f+0(SB)/4,$runtime·main(SB)
+GLOBL	runtime·main·f(SB),8,$4
+
 TEXT runtime·breakpoint(SB),7,$0
 	INT $3
 	RET
@@ -147,6 +150,23 @@
 	JMP	AX
 	POPL	BX	// not reached
 
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+TEXT runtime·gogocallfn(SB), 7, $0
+	MOVL	8(SP), AX		// fn
+	MOVL	4(SP), BX		// gobuf
+	MOVL	gobuf_g(BX), DX
+	get_tls(CX)
+	MOVL	DX, g(CX)
+	MOVL	0(DX), CX		// make sure g != nil
+	MOVL	gobuf_sp(BX), SP	// restore SP
+	MOVL	gobuf_pc(BX), BX
+	PUSHL	BX
+	MOVL	0(AX), BX
+	JMP	BX
+	POPL	BX	// not reached
+
 // void mcall(void (*fn)(G*))
 // Switch to m->g0's stack, call fn(g).
 // Fn must never return.  It should gogo(&g->sched)
@@ -425,7 +445,8 @@
 	MOVL	8(SP), BX	// caller sp
 	LEAL	-4(BX), SP	// caller sp after CALL
 	SUBL	$5, (SP)	// return to CALL again
-	JMP	AX	// but first run the deferred function
+	MOVL	0(AX), BX
+	JMP	BX	// but first run the deferred function
 
 // Dummy function to use in saved gobuf.PC,
 // to match SP pointing at a return address.
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index 159b763..ea944e1 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -68,7 +68,7 @@
 	CALL	runtime·schedinit(SB)
 
 	// create a new goroutine to start program
-	PUSHQ	$runtime·main(SB)		// entry
+	PUSHQ	$runtime·main·f(SB)		// entry
 	PUSHQ	$0			// arg size
 	CALL	runtime·newproc(SB)
 	POPQ	AX
@@ -80,6 +80,9 @@
 	MOVL	$0xf1, 0xf1  // crash
 	RET
 
+DATA	runtime·main·f+0(SB)/8,$runtime·main(SB)
+GLOBL	runtime·main·f(SB),8,$8
+
 TEXT runtime·breakpoint(SB),7,$0
 	BYTE	$0xcc
 	RET
@@ -134,6 +137,23 @@
 	JMP	AX
 	POPQ	BX	// not reached
 
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+TEXT runtime·gogocallfn(SB), 7, $0
+	MOVQ	16(SP), AX		// fn
+	MOVQ	8(SP), BX		// gobuf
+	MOVQ	gobuf_g(BX), DX
+	get_tls(CX)
+	MOVQ	DX, g(CX)
+	MOVQ	0(DX), CX	// make sure g != nil
+	MOVQ	gobuf_sp(BX), SP	// restore SP
+	MOVQ	gobuf_pc(BX), BX
+	PUSHQ	BX
+	MOVQ	0(AX), BX
+	JMP	BX
+	POPQ	BX	// not reached
+
 // void mcall(void (*fn)(G*))
 // Switch to m->g0's stack, call fn(g).
 // Fn must never return.  It should gogo(&g->sched)
@@ -455,7 +475,8 @@
 	MOVQ	16(SP), BX	// caller sp
 	LEAQ	-8(BX), SP	// caller sp after CALL
 	SUBQ	$5, (SP)	// return to CALL again
-	JMP	AX	// but first run the deferred function
+	MOVQ	0(AX), BX
+	JMP	BX	// but first run the deferred function
 
 // Dummy function to use in saved gobuf.PC,
 // to match SP pointing at a return address.
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index b0678bc..0f6026c 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -50,7 +50,7 @@
 	BL	runtime·schedinit(SB)
 
 	// create a new goroutine to start program
-	MOVW	$runtime·main(SB), R0
+	MOVW	$runtime·main·f(SB), R0
 	MOVW.W	R0, -4(R13)
 	MOVW	$8, R0
 	MOVW.W	R0, -4(R13)
@@ -66,6 +66,9 @@
 	MOVW	$1000, R1
 	MOVW	R0, (R1)	// fail hard
 
+DATA	runtime·main·f+0(SB)/4,$runtime·main(SB)
+GLOBL	runtime·main·f(SB),8,$4
+
 TEXT runtime·breakpoint(SB),7,$0
 	// gdb won't skip this breakpoint instruction automatically,
 	// so you must manually "set $pc+=4" to skip it and continue.
@@ -126,6 +129,24 @@
 	MOVW	gobuf_pc(R3), LR
 	MOVW	R1, PC
 
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+// using frame size $-4 means do not save LR on stack.
+TEXT runtime·gogocallfn(SB), 7, $-4
+	MOVW	0(FP), R3		// gobuf
+	MOVW	4(FP), R1		// fn
+	MOVW	8(FP), R2		// fp offset
+	MOVW	gobuf_g(R3), g
+	MOVW	0(g), R0		// make sure g != nil
+	MOVW	cgo_save_gm(SB), R0
+	CMP 	$0, R0 // if in Cgo, we have to save g and m
+	BL.NE	(R0) // this call will clobber R0
+	MOVW	gobuf_sp(R3), SP	// restore SP
+	MOVW	gobuf_pc(R3), LR
+	MOVW	R1, R0
+	MOVW	0(R1), PC
+
 // void mcall(void (*fn)(G*))
 // Switch to m->g0's stack, call fn(g).
 // Fn must never return.  It should gogo(&g->sched)
@@ -242,7 +263,8 @@
 	MOVW	fn+0(FP), R0
 	MOVW	argp+4(FP), SP
 	MOVW	$-4(SP), SP	// SP is 4 below argp, due to saved LR
-	B		(R0)
+	MOVW	0(R0), R1
+	B	(R1)
 
 // Dummy function to use in saved gobuf.PC,
 // to match SP pointing at a return address.
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 4f68b46..f89ac46 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -95,6 +95,8 @@
 
 // Call from Go to C.
 
+static FuncVal unlockOSThread = { runtime·unlockOSThread };
+
 void
 runtime·cgocall(void (*fn)(void*), void *arg)
 {
@@ -121,7 +123,7 @@
 	 * cgo callback. Add entry to defer stack in case of panic.
 	 */
 	runtime·lockOSThread();
-	d.fn = (byte*)runtime·unlockOSThread;
+	d.fn = &unlockOSThread;
 	d.siz = 0;
 	d.link = g->defer;
 	d.argp = (void*)-1;  // unused because unlockm never recovers
@@ -154,7 +156,7 @@
 		m->cgomal = nil;
 	}
 
-	if(g->defer != &d || d.fn != (byte*)runtime·unlockOSThread)
+	if(g->defer != &d || d.fn != &unlockOSThread)
 		runtime·throw("runtime: bad defer entry in cgocallback");
 	g->defer = d.link;
 	runtime·unlockOSThread();
@@ -201,13 +203,17 @@
 
 // Call from C back to Go.
 
+static FuncVal unwindmf = {unwindm};
+
 void
 runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
 {
 	Defer d;
+	FuncVal fv;
 
+	fv.fn = fn;
 	if(m->racecall) {
-		reflect·call((byte*)fn, arg, argsize);
+		reflect·call(&fv, arg, argsize);
 		return;
 	}
 
@@ -222,7 +228,7 @@
 	}
 
 	// Add entry to defer stack in case of panic.
-	d.fn = (byte*)unwindm;
+	d.fn = &unwindmf;
 	d.siz = 0;
 	d.link = g->defer;
 	d.argp = (void*)-1;  // unused because unwindm never recovers
@@ -234,7 +240,7 @@
 		runtime·raceacquire(&cgosync);
 
 	// Invoke callback.
-	reflect·call((byte*)fn, arg, argsize);
+	reflect·call(&fv, arg, argsize);
 
 	if(raceenabled)
 		runtime·racereleasemerge(&cgosync);
@@ -242,7 +248,7 @@
 	// Pop defer.
 	// Do not unwind m->g0->sched.sp.
 	// Our caller, cgocallback, will do that.
-	if(g->defer != &d || d.fn != (byte*)unwindm)
+	if(g->defer != &d || d.fn != &unwindmf)
 		runtime·throw("runtime: bad defer entry in cgocallback");
 	g->defer = d.link;
 
diff --git a/src/pkg/runtime/closure_386.c b/src/pkg/runtime/closure_386.c
index b4d8677..c4ef3ae 100644
--- a/src/pkg/runtime/closure_386.c
+++ b/src/pkg/runtime/closure_386.c
@@ -18,6 +18,7 @@
 	if(siz < 0 || siz%4 != 0)
 		runtime·throw("bad closure size");
 
+	fn = *(byte**)fn;
 	ret = (byte**)((byte*)&arg0 + siz);
 
 	if(siz > 100) {
@@ -40,8 +41,10 @@
 	if(n%4)
 		n += 4 - n%4;
 
-	p = runtime·mal(n);
+	p = runtime·mal(4+n);
 	*ret = p;
+	*(byte**)p = p+4;
+	p += 4;
 	q = p + n - siz;
 
 	if(siz > 0) {
diff --git a/src/pkg/runtime/closure_amd64.c b/src/pkg/runtime/closure_amd64.c
index 481b4a8..f7deb7b 100644
--- a/src/pkg/runtime/closure_amd64.c
+++ b/src/pkg/runtime/closure_amd64.c
@@ -18,6 +18,7 @@
 	if(siz < 0 || siz%8 != 0)
 		runtime·throw("bad closure size");
 
+	fn = *(byte**)fn;
 	ret = (byte**)((byte*)&arg0 + siz);
 
 	if(siz > 100) {
@@ -40,10 +41,13 @@
 	if(n%8)
 		n += 8 - n%8;
 
-	p = runtime·mal(n);
+	p = runtime·mal(8+n);
 	*ret = p;
+	*(byte**)p = (p+8);
+	p += 8;
 	q = p + n - siz;
 
+
 	if(siz > 0) {
 		runtime·memmove(q, (byte*)&arg0, siz);
 
diff --git a/src/pkg/runtime/closure_arm.c b/src/pkg/runtime/closure_arm.c
index 119e91b..08792ac 100644
--- a/src/pkg/runtime/closure_arm.c
+++ b/src/pkg/runtime/closure_arm.c
@@ -56,6 +56,7 @@
 	if(siz < 0 || siz%4 != 0)
 		runtime·throw("bad closure size");
 
+	fn = *(byte**)fn;
 	ret = (byte**)((byte*)&arg0 + siz);
 
 	if(siz > 100) {
@@ -73,8 +74,10 @@
 	// store args aligned after code, so gc can find them.
 	n += siz;
 
-	p = runtime·mal(n);
+	p = runtime·mal(4+n);
 	*ret = p;
+	*(byte**)p = p+4;
+	p += 4;
 	q = p + n - siz;
 
 	pc = (uint32*)p;
diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h
index 5874741..c795a6f 100644
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -474,7 +474,7 @@
 void	runtime·helpgc(int32 nproc);
 void	runtime·gchelper(void);
 
-bool	runtime·getfinalizer(void *p, bool del, void (**fn)(void*), uintptr *nret);
+bool	runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret);
 void	runtime·walkfintab(void (*fn)(void*));
 
 enum
diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c
index ab45071..2f5e427 100644
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -11,7 +11,7 @@
 typedef struct Fin Fin;
 struct Fin
 {
-	void (*fn)(void*);
+	FuncVal *fn;
 	uintptr nret;
 };
 
@@ -42,7 +42,7 @@
 } fintab[TABSZ];
 
 static void
-addfintab(Fintab *t, void *k, void (*fn)(void*), uintptr nret)
+addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
 {
 	int32 i, j;
 
@@ -137,7 +137,7 @@
 }
 
 bool
-runtime·addfinalizer(void *p, void (*f)(void*), uintptr nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
 {
 	Fintab *tab;
 	byte *base;
@@ -175,7 +175,7 @@
 // get finalizer; if del, delete finalizer.
 // caller is responsible for updating RefHasFinalizer (special) bit.
 bool
-runtime·getfinalizer(void *p, bool del, void (**fn)(void*), uintptr *nret)
+runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
 {
 	Fintab *tab;
 	bool res;
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index 0266a10..f6c7614 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -104,7 +104,7 @@
 typedef struct Finalizer Finalizer;
 struct Finalizer
 {
-	void (*fn)(void*);
+	FuncVal *fn;
 	void *arg;
 	uintptr nret;
 };
@@ -1328,7 +1328,7 @@
 static bool
 handlespecial(byte *p, uintptr size)
 {
-	void (*fn)(void*);
+	FuncVal *fn;
 	uintptr nret;
 	FinBlock *block;
 	Finalizer *f;
@@ -1656,6 +1656,7 @@
 {
 	byte *p;
 	struct gc_args a, *ap;
+	FuncVal gcv;
 
 	// The atomic operations are not atomic if the uint64s
 	// are not aligned on uint64 boundaries. This has been
@@ -1689,7 +1690,8 @@
 	a.force = force;
 	ap = &a;
 	m->moreframesize_minalloc = StackBig;
-	reflect·call((byte*)gc, (byte*)&ap, sizeof(ap));
+	gcv.fn = (void*)gc;
+	reflect·call(&gcv, (byte*)&ap, sizeof(ap));
 
 	if(gctrace > 1 && !force) {
 		a.force = 1;
@@ -1697,6 +1699,8 @@
 	}
 }
 
+static FuncVal runfinqv = {runfinq};
+
 static void
 gc(struct gc_args *args)
 {
@@ -1786,7 +1790,7 @@
 		m->locks++;	// disable gc during the mallocs in newproc
 		// kick off or wake up goroutine to run queued finalizers
 		if(fing == nil)
-			fing = runtime·newproc1((byte*)runfinq, nil, 0, 0, runtime·gc);
+			fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc);
 		else if(fingwait) {
 			fingwait = 0;
 			runtime·ready(fing);
@@ -1924,7 +1928,7 @@
 					framecap = framesz;
 				}
 				*(void**)frame = f->arg;
-				reflect·call((byte*)f->fn, frame, sizeof(uintptr) + f->nret);
+				reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
 				f->fn = nil;
 				f->arg = nil;
 			}
diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c
index 44c9e99..76cd201 100644
--- a/src/pkg/runtime/mheap.c
+++ b/src/pkg/runtime/mheap.c
@@ -391,6 +391,8 @@
 	return sumreleased;
 }
 
+static FuncVal forcegchelperv = {(void(*)(void))forcegchelper};
+
 // Release (part of) unused memory to OS.
 // Goroutine created at startup.
 // Loop forever.
@@ -437,7 +439,7 @@
 			// GC blocks other goroutines via the runtime·worldsema.
 			runtime·noteclear(&note);
 			notep = &note;
-			runtime·newproc1((byte*)forcegchelper, (byte*)&notep, sizeof(notep), 0, runtime·MHeap_Scavenger);
+			runtime·newproc1(&forcegchelperv, (byte*)&notep, sizeof(notep), 0, runtime·MHeap_Scavenger);
 			runtime·entersyscallblock();
 			runtime·notesleep(&note);
 			runtime·exitsyscall();
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index 603ff62..2f553f4 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -119,7 +119,7 @@
 // functions that split the stack.
 #pragma textflag 7
 uintptr
-runtime·deferproc(int32 siz, byte* fn, ...)
+runtime·deferproc(int32 siz, FuncVal *fn, ...)
 {
 	Defer *d;
 
@@ -156,7 +156,8 @@
 runtime·deferreturn(uintptr arg0)
 {
 	Defer *d;
-	byte *argp, *fn;
+	byte *argp;
+	FuncVal *fn;
 
 	d = g->defer;
 	if(d == nil)
diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
index d146727..aa5537d 100644
--- a/src/pkg/runtime/parfor.c
+++ b/src/pkg/runtime/parfor.c
@@ -76,7 +76,7 @@
 void
 runtime·parforsetup2(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void *body)
 {
-	runtime·parforsetup(desc, nthr, n, ctx, wait, (void(*)(ParFor*, uint32))body);
+	runtime·parforsetup(desc, nthr, n, ctx, wait, *(void(**)(ParFor*, uint32))body);
 }
 
 void
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 9909182..e2ba4b6 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -226,6 +226,8 @@
 extern void main·init(void);
 extern void main·main(void);
 
+static FuncVal scavenger = {runtime·MHeap_Scavenger};
+
 // The main goroutine.
 void
 runtime·main(void)
@@ -240,7 +242,7 @@
 	// From now on, newgoroutines may use non-main threads.
 	setmcpumax(runtime·gomaxprocs);
 	runtime·sched.init = true;
-	scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main);
+	scvg = runtime·newproc1(&scavenger, nil, 0, 0, runtime·main);
 	scvg->issystem = true;
 	// The deadlock detection has false negatives.
 	// Let scvg start up, to eliminate the false negative
@@ -1170,7 +1172,7 @@
 		runtime·resetcpuprofiler(hz);
 
 	if(gp->sched.pc == (byte*)runtime·goexit) {	// kickoff
-		runtime·gogocall(&gp->sched, (void(*)(void))gp->entry);
+		runtime·gogocallfn(&gp->sched, gp->fnstart);
 	}
 	runtime·gogo(&gp->sched, 0);
 }
@@ -1419,7 +1421,7 @@
 // functions that split the stack.
 #pragma textflag 7
 void
-runtime·newproc(int32 siz, byte* fn, ...)
+runtime·newproc(int32 siz, FuncVal* fn, ...)
 {
 	byte *argp;
 
@@ -1435,7 +1437,7 @@
 // address of the go statement that created this.  The new g is put
 // on the queue of g's waiting to run.
 G*
-runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
+runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
 {
 	byte *sp;
 	G *newg;
@@ -1484,7 +1486,7 @@
 	newg->sched.sp = (uintptr)sp;
 	newg->sched.pc = (byte*)runtime·goexit;
 	newg->sched.g = newg;
-	newg->entry = fn;
+	newg->fnstart = fn;
 	newg->gopc = (uintptr)callerpc;
 	if(raceenabled)
 		newg->racectx = racectx;
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 75a3d04..e98f13b 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -57,6 +57,7 @@
 typedef	struct	Slice		Slice;
 typedef	struct	Stktop		Stktop;
 typedef	struct	String		String;
+typedef	struct	FuncVal		FuncVal;
 typedef	struct	SigTab		SigTab;
 typedef	struct	MCache		MCache;
 typedef	struct	FixAlloc	FixAlloc;
@@ -78,11 +79,11 @@
 typedef	struct	SEH		SEH;
 typedef	struct	Timers		Timers;
 typedef	struct	Timer		Timer;
-typedef struct	GCStats		GCStats;
-typedef struct	LFNode		LFNode;
-typedef struct	ParFor		ParFor;
-typedef struct	ParForThread	ParForThread;
-typedef struct	CgoMal		CgoMal;
+typedef	struct	GCStats		GCStats;
+typedef	struct	LFNode		LFNode;
+typedef	struct	ParFor		ParFor;
+typedef	struct	ParForThread	ParForThread;
+typedef	struct	CgoMal		CgoMal;
 
 /*
  * Per-CPU declaration.
@@ -154,6 +155,11 @@
 	byte*	str;
 	intgo	len;
 };
+struct FuncVal
+{
+	void	(*fn)(void);
+	// variable-size, fn-specific data here
+};
 struct Iface
 {
 	Itab*	tab;
@@ -209,7 +215,7 @@
 	uintptr	gcsp;		// if status==Gsyscall, gcsp = sched.sp to use during gc
 	uintptr	gcguard;		// if status==Gsyscall, gcguard = stackguard to use during gc
 	uintptr	stack0;
-	byte*	entry;		// initial function
+	FuncVal*	fnstart;		// initial function
 	G*	alllink;	// on allg
 	void*	param;		// passed parameter on wakeup
 	int16	status;
@@ -416,7 +422,7 @@
 	// a well-behaved function and not block.
 	int64	when;
 	int64	period;
-	void	(*f)(int64, Eface);
+	FuncVal	*fv;
 	Eface	arg;
 };
 
@@ -552,7 +558,7 @@
 	bool	free; // if special, free when done
 	byte*	argp;  // where args were copied from
 	byte*	pc;
-	byte*	fn;
+	FuncVal*	fn;
 	Defer*	link;
 	void*	args[1];	// padded to actual size
 };
@@ -610,6 +616,7 @@
 
 void	runtime·gogo(Gobuf*, uintptr);
 void	runtime·gogocall(Gobuf*, void(*)(void));
+void	runtime·gogocallfn(Gobuf*, FuncVal*);
 void	runtime·gosave(Gobuf*);
 void	runtime·lessstack(void);
 void	runtime·goargs(void);
@@ -652,7 +659,7 @@
 uint64	runtime·atomicload64(uint64 volatile*);
 void*	runtime·atomicloadp(void* volatile*);
 void	runtime·atomicstorep(void* volatile*, void*);
-void	runtime·jmpdefer(byte*, void*);
+void	runtime·jmpdefer(FuncVal*, void*);
 void	runtime·exit1(int32);
 void	runtime·ready(G*);
 byte*	runtime·getenv(int8*);
@@ -678,7 +685,7 @@
 uintptr	runtime·efacehash(Eface, uintptr);
 void*	runtime·malloc(uintptr size);
 void	runtime·free(void *v);
-bool	runtime·addfinalizer(void*, void(*fn)(void*), uintptr);
+bool	runtime·addfinalizer(void*, FuncVal *fn, uintptr);
 void	runtime·runpanic(Panic*);
 void*	runtime·getcallersp(void*);
 int32	runtime·mcount(void);
@@ -699,7 +706,7 @@
 void	runtime·entersyscall(void);
 void	runtime·entersyscallblock(void);
 void	runtime·exitsyscall(void);
-G*	runtime·newproc1(byte*, byte*, int32, int32, void*);
+G*	runtime·newproc1(FuncVal*, byte*, int32, int32, void*);
 bool	runtime·sigsend(int32 sig);
 int32	runtime·callers(int32, uintptr*, int32);
 int32	runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
@@ -835,7 +842,7 @@
 void	runtime·printhex(uint64);
 void	runtime·printslice(Slice);
 void	runtime·printcomplex(Complex128);
-void	reflect·call(byte*, byte*, uint32);
+void	reflect·call(FuncVal*, byte*, uint32);
 void	runtime·panic(Eface);
 void	runtime·panicindex(void);
 void	runtime·panicslice(void);
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index ac00e53..d1d5c8f 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -273,7 +273,10 @@
 	label.sp = (uintptr)sp;
 	label.pc = (byte*)runtime·lessstack;
 	label.g = m->curg;
-	runtime·gogocall(&label, m->morepc);
+	if(reflectcall)
+		runtime·gogocallfn(&label, (FuncVal*)m->morepc);
+	else
+		runtime·gogocall(&label, m->morepc);
 
 	*(int32*)345 = 123;	// never return
 }
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index d962b74..2babb17 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -57,6 +57,8 @@
 	runtime·ready(e.data);
 }
 
+static FuncVal readyv = {(void(*)(void))ready};
+
 // Put the current goroutine to sleep for ns nanoseconds.
 void
 runtime·tsleep(int64 ns, int8 *reason)
@@ -68,13 +70,15 @@
 
 	t.when = runtime·nanotime() + ns;
 	t.period = 0;
-	t.f = ready;
+	t.fv = &readyv;
 	t.arg.data = g;
 	runtime·lock(&timers);
 	addtimer(&t);
 	runtime·park(runtime·unlock, &timers, reason);
 }
 
+static FuncVal timerprocv = {timerproc};
+
 // Add a timer to the heap and start or kick the timer proc
 // if the new timer is earlier than any of the others.
 static void
@@ -109,7 +113,7 @@
 		}
 	}
 	if(timers.timerproc == nil) {
-		timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer);
+		timers.timerproc = runtime·newproc1(&timerprocv, nil, 0, 0, addtimer);
 		timers.timerproc->issystem = true;
 	}
 }
@@ -182,7 +186,7 @@
 				siftdown(0);
 				t->i = -1;  // mark as removed
 			}
-			f = t->f;
+			f = (void*)t->fv->fn;
 			arg = t->arg;
 			runtime·unlock(&timers);
 			if(raceenabled)
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index 5c83168..cafab3f 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -32,8 +32,8 @@
 	waspanic = false;
 
 	// If the PC is goexit, the goroutine hasn't started yet.
-	if(pc == (uintptr)runtime·goexit && gp->entry != 0) {
-		pc = (uintptr)gp->entry;
+	if(pc == (uintptr)runtime·goexit && gp->fnstart != nil) {
+		pc = (uintptr)gp->fnstart->fn;
 		lr = (uintptr)runtime·goexit;
 	}
 
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index f5d8f2a..4ee5f0d 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -40,10 +40,10 @@
 	waspanic = false;
 	
 	// If the PC is goexit, the goroutine hasn't started yet.
-	if(pc0 == gp->sched.pc && sp == (byte*)gp->sched.sp && pc0 == (byte*)runtime·goexit && gp->entry != 0) {
+	if(pc0 == gp->sched.pc && sp == (byte*)gp->sched.sp && pc0 == (byte*)runtime·goexit && gp->fnstart != nil) {
 		fp = sp;
 		lr = pc;
-		pc = (uintptr)gp->entry;
+		pc = (uintptr)gp->fnstart->fn;
 	}
 	
 	// If the PC is zero, it's likely a nil function call.