gc: implement panic and recover

R=ken2, r, ken3
CC=golang-dev
https://golang.org/cl/831042
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 3ef6ae8..6001c22 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -447,6 +447,32 @@
 	lock(&sched);
 	if(gosave(&m->sched) != 0){
 		gp = m->curg;
+		if(gp->status == Grecovery) {
+			// switched to scheduler to get stack unwound.
+			// don't go through the full scheduling logic.
+			Defer *d;
+
+			d = gp->defer;
+			gp->defer = d->link;
+			
+			// unwind to the stack frame with d->sp in it.
+			unwindstack(gp, d->sp);
+			if(d->sp < gp->stackguard || gp->stackbase < d->sp)
+				throw("bad stack in recovery");
+			
+			// make the deferproc for this d return again,
+			// this time returning 1.  function will jump to
+			// standard return epilogue.
+			// the -2*sizeof(uintptr) makes up for the
+			// two extra words that are on the stack at
+			// each call to deferproc.
+			// (the pc we're returning to does pop pop
+			// before it tests the return value.)
+			gp->sched.sp = d->sp - 2*sizeof(uintptr);
+			gp->sched.pc = d->pc;
+			free(d);
+			gogo(&gp->sched, 1);
+		}
 
 		// Jumped here via gosave/gogo, so didn't
 		// execute lock(&sched) above.
@@ -719,6 +745,10 @@
 	top->fp = m->morefp;
 	top->args = args;
 	top->free = free;
+	
+	// copy flag from panic
+	top->panic = g1->ispanic;
+	g1->ispanic = false;
 
 	g1->stackbase = (byte*)top;
 	g1->stackguard = stk + StackGuard;
@@ -819,7 +849,7 @@
 }
 
 #pragma textflag 7
-void
+uintptr
 ·deferproc(int32 siz, byte* fn, ...)
 {
 	Defer *d;
@@ -828,10 +858,19 @@
 	d->fn = fn;
 	d->sp = (byte*)(&fn+1);
 	d->siz = siz;
+	d->pc = ·getcallerpc(&siz);
 	mcpy(d->args, d->sp, d->siz);
 
 	d->link = g->defer;
 	g->defer = d;
+	
+	// deferproc returns 0 normally.
+	// a deferred func that stops a panic
+	// makes the deferproc return 1.
+	// the code the compiler generates always
+	// checks the return value and jumps to the
+	// end of the function if deferproc returns != 0.
+	return 0;
 }
 
 #pragma textflag 7
@@ -888,6 +927,131 @@
 	}
 }
 
+static void
+printpanics(Panic *p)
+{
+	if(p->link) {
+		printpanics(p->link);
+		printf("\t");
+	}
+	printf("panic: ");
+	printany(p->arg);
+	if(p->recovered)
+		printf(" [recovered]");
+	printf("\n");
+}
+	
+void
+·panic(Eface e)
+{
+	Defer *d;
+	Panic *p;
+
+	p = mal(sizeof *p);
+	p->arg = e;
+	p->link = g->panic;
+	p->stackbase = g->stackbase;
+	g->panic = p;
+
+	for(;;) {
+		d = g->defer;
+		if(d == nil)
+			break;
+		// take defer off list in case of recursive panic
+		g->defer = d->link;
+		g->ispanic = true;	// rock for newstack, where reflect.call ends up
+		reflect·call(d->fn, d->args, d->siz);
+		if(p->recovered) {
+			g->panic = p->link;
+			free(p);
+			// put recovering defer back on list
+			// for scheduler to find.
+			d->link = g->defer;
+			g->defer = d;
+			g->status = Grecovery;
+			gosched();
+			throw("recovery failed"); // gosched should not return
+		}
+		free(d);
+	}
+
+	// ran out of deferred calls - old-school panic now
+	fd = 2;
+	printpanics(g->panic);
+	panic(0);
+}
+
+#pragma textflag 7	/* no split, or else g->stackguard is not the stack for fp */
+void
+·recover(byte *fp, Eface ret)
+{
+	Stktop *top, *oldtop;
+	Panic *p;
+
+	// Must be a panic going on.
+	if((p = g->panic) == nil || p->recovered)
+		goto nomatch;
+
+	// Frame must be at the top of the stack segment,
+	// because each deferred call starts a new stack
+	// segment as a side effect of using reflect.call.
+	// (There has to be some way to remember the
+	// variable argument frame size, and the segment
+	// code already takes care of that for us, so we
+	// reuse it.)
+	//
+	// As usual closures complicate things: the fp that
+	// the closure implementation function claims to have
+	// is where the explicit arguments start, after the
+	// implicit pointer arguments and PC slot.
+	// If we're on the first new segment for a closure,
+	// then fp == top - top->args is correct, but if
+	// the closure has its own big argument frame and
+	// allocated a second segment (see below),
+	// the fp is slightly above top - top->args.
+	// That condition can't happen normally though
+	// (stack pointer go down, not up), so we can accept
+	// any fp between top and top - top->args as
+	// indicating the top of the segment.
+	top = (Stktop*)g->stackbase;
+	if(fp < (byte*)top - top->args || (byte*)top < fp)
+		goto nomatch;
+
+	// The deferred call makes a new segment big enough
+	// for the argument frame but not necessarily big
+	// enough for the function's local frame (size unknown
+	// at the time of the call), so the function might have
+	// made its own segment immediately.  If that's the
+	// case, back top up to the older one, the one that
+	// reflect.call would have made for the panic.
+	//
+	// The fp comparison here checks that the argument
+	// frame that was copied during the split (the top->args
+	// bytes above top->fp) abuts the old top of stack.
+	// This is a correct test for both closure and non-closure code.
+	oldtop = (Stktop*)top->stackbase;
+	if(oldtop != nil && top->fp == (byte*)oldtop - top->args)
+		top = oldtop;
+
+	// Now we have the segment that was created to
+	// run this call.  It must have been marked as a panic segment.
+	if(!top->panic)
+		goto nomatch;
+
+	// Okay, this is the top frame of a deferred call
+	// in response to a panic.  It can see the panic argument.
+	p->recovered = 1;
+	ret = p->arg;
+	FLUSH(&ret);
+	return;
+
+nomatch:
+	ret.type = nil;
+	ret.data = nil;
+	FLUSH(&ret);
+}
+
+
 // Put on gfree list.  Sched must be locked.
 static void
 gfput(G *g)