break;
- case 2: // defered call (defer)
+ case 2: // deferred call (defer)
regalloc(&r, types[tptr], N);
p = gins(AMOVW, N, &r);
p->from.type = D_OREG;
ginscall(deferproc, 0);
- regalloc(&r, types[tptr], N);
+ nodreg(&r, types[tptr], D_R1);
p = gins(AMOVW, N, &r);
p->from.type = D_OREG;
p->from.reg = REGSP;
p->to.reg = REGSP;
p->to.offset = 8;
p->scond |= C_WBIT;
- regfree(&r);
-
+
+ if(proc == 2) {
+ nodconst(&con, types[TINT32], 0);
+ nodreg(&r, types[tptr], D_R0);
+ gins(ACMP, &con, &r);
+ patch(gbranch(ABNE, T), pret);
+ }
break;
}
}
break;
case 1: // call in new proc (go)
- case 2: // defered call (defer)
- nodreg(®, types[TINT64], D_AX);
+ case 2: // deferred call (defer)
+ nodreg(®, types[TINT64], D_CX);
gins(APUSHQ, f, N);
nodconst(&con, types[TINT32], argsize(f->type));
gins(APUSHQ, &con, N);
}
gins(APOPQ, N, ®);
gins(APOPQ, N, ®);
+ if(proc == 2) {
+ nodreg(®, types[TINT64], D_AX);
+ gins(ATESTQ, ®, ®);
+ patch(gbranch(AJNE, T), pret);
+ }
break;
}
}
break;
case 1: // call in new proc (go)
- case 2: // defered call (defer)
- nodreg(®, types[TINT32], D_AX);
+ case 2: // deferred call (defer)
+ nodreg(®, types[TINT32], D_CX);
gins(APUSHL, f, N);
nodconst(&con, types[TINT32], argsize(f->type));
gins(APUSHL, &con, N);
ginscall(deferproc, 0);
gins(APOPL, N, ®);
gins(APOPL, N, ®);
+ if(proc == 2) {
+ nodreg(®, types[TINT64], D_AX);
+ gins(ATESTL, ®, ®);
+ patch(gbranch(AJNE, T), pret);
+ }
break;
}
}
"func \"\".throwreturn ()\n"
"func \"\".throwinit ()\n"
"func \"\".panic (? interface { })\n"
- "func \"\".recover () interface { }\n"
+ "func \"\".recover (? *int32) interface { }\n"
"func \"\".printbool (? bool)\n"
"func \"\".printfloat (? float64)\n"
"func \"\".printint (? int64)\n"
}
void
-funccompile(Node *n)
+funccompile(Node *n, int isclosure)
{
stksize = BADWIDTH;
maxarg = 0;
// assign parameter offsets
checkwidth(n->type);
+
+ // record offset to actual frame pointer.
+ // for closure, have to skip over leading pointers and PC slot.
+ nodfp->xoffset = 0;
+ if(isclosure) {
+ NodeList *l;
+ for(l=n->nname->ntype->list; l; l=l->next) {
+ nodfp->xoffset += widthptr;
+ if(l->n->left == N) // found slot for PC
+ break;
+ }
+ }
if(curfn)
fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym);
Node* unsafenmagic(Node*, NodeList*);
void dclchecks(void);
-void funccompile(Node*);
+void funccompile(Node*, int);
Node* typedcl0(Sym*);
Node* typedcl1(Node*, Node*, int);
EXTERN Prog* pc;
EXTERN Prog* firstpc;
+EXTERN Node* nodfp;
+
void allocparams(void);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
fn->nbody = r;
funcbody(fn);
typecheck(&fn, Etop);
- funccompile(fn);
+ funccompile(fn, 0);
}
resumecheckwidth();
for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC)
- funccompile(l->n);
+ funccompile(l->n, 0);
if(nerrors == 0)
fninit(xtop);
while(closures) {
l = closures;
closures = nil;
for(; l; l=l->next)
- funccompile(l->n);
+ funccompile(l->n, 1);
}
dclchecks();
*s->def = *nodbool(0);
s->def->sym = s;
}
+
+ nodfp = nod(ONAME, N, N);
+ nodfp->noescape = 1;
+ nodfp->type = types[TINT32];
+ nodfp->xoffset = 0;
+ nodfp->class = PPARAM;
+ nodfp->sym = lookup(".fp");
}
struct
func throwinit()
func panic(interface{})
-func recover() interface{}
+func recover(*int32) interface{}
func printbool(bool)
func printfloat(float64)
funcbody(fn);
typecheck(&fn, Etop);
- funccompile(fn);
+ funccompile(fn, 0);
}
/*
case OPRINTN:
case OPANIC:
case OEMPTY:
+ case ORECOVER:
if(n->typecheck == 0)
fatal("missing typecheck");
init = n->ninit;
goto ret;
case ORECOVER:
- n = mkcall("recover", n->type, init);
+ n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
goto ret;
case OLITERAL:
// G is
// byte* stackguard
// byte* stackbase (= Stktop*)
- // Defer* defer
- // Gobuf sched
// TODO(rsc): Need some way to get at the g for other threads.
// Probably need to pass it into the trace function.
g = 0;
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.
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;
}
#pragma textflag 7
-void
+uintptr
·deferproc(int32 siz, byte* fn, ...)
{
Defer *d;
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
}
}
+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)
exit(2);
}
-void
-·panic(Eface e)
-{
- fd = 2;
- printf("panic: ");
- printany(e);
- panic(0);
-}
-
void
·throwindex(void)
{
typedef struct Eface Eface;
typedef struct Type Type;
typedef struct Defer Defer;
+typedef struct Panic Panic;
typedef struct hash Hmap;
typedef struct Hchan Hchan;
typedef struct Complex64 Complex64;
Gwaiting,
Gmoribund,
Gdead,
+ Grecovery,
};
enum
{
byte* stackguard; // cannot move - also known to linker, libmach, libcgo
byte* stackbase; // cannot move - also known to libmach, libcgo
Defer* defer;
- Gobuf sched; // cannot move - also known to libmach
+ Panic* panic;
+ Gobuf sched;
byte* stack0;
byte* entry; // initial function
G* alllink; // on allg
uint32 selgen; // valid sudog pointer
G* schedlink;
bool readyonstop;
+ bool ispanic;
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
void (*cgofn)(void*); // for cgo/ffi
// function call, which uses an off-stack argument frame.
uint8* fp;
bool free; // call stackfree for this frame?
+ bool panic; // is this frame the top of a panic?
};
struct Alg
{
{
int32 siz;
byte* sp;
+ byte* pc;
byte* fn;
Defer* link;
byte args[8]; // padded to actual size
};
+/*
+ * panics
+ */
+struct Panic
+{
+ Eface arg; // argument to panic
+ byte* stackbase; // g->stackbase in panic
+ Panic* link; // link to earlier panic
+ bool recovered; // whether this panic is over
+};
+
/*
* external data
*/
void free(void *v);
void addfinalizer(void*, void(*fn)(void*), int32);
void walkfintab(void (*fn)(void*));
+void runpanic(Panic*);
void exit(int32);
void breakpoint(void);
bal
bal
panic: barCount != 1
+
panic PC=xxx
BUG