]> Cypherpunks repositories - gostls13.git/commitdiff
gc: implement panic and recover
authorRuss Cox <rsc@golang.org>
Wed, 31 Mar 2010 18:46:01 +0000 (11:46 -0700)
committerRuss Cox <rsc@golang.org>
Wed, 31 Mar 2010 18:46:01 +0000 (11:46 -0700)
R=ken2, r, ken3
CC=golang-dev
https://golang.org/cl/831042

16 files changed:
src/cmd/5g/ggen.c
src/cmd/6g/ggen.c
src/cmd/8g/ggen.c
src/cmd/gc/builtin.c.boot
src/cmd/gc/dcl.c
src/cmd/gc/go.h
src/cmd/gc/init.c
src/cmd/gc/lex.c
src/cmd/gc/runtime.go
src/cmd/gc/subr.c
src/cmd/gc/walk.c
src/libmach/8db.c
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.c
src/pkg/runtime/runtime.h
test/golden.out

index 50c90912e06ae259500a228385ca35a4f71d5172..5831d597e4dbbabf8eb208c9f403866700a7de7a 100644 (file)
@@ -190,7 +190,7 @@ ginscall(Node *f, int proc)
 
                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;
@@ -222,7 +222,7 @@ ginscall(Node *f, int proc)
 
                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;
@@ -233,8 +233,13 @@ ginscall(Node *f, int proc)
                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;
        }
 }
index 140020fdafbac2747f2e1cff9671111c397df6da..10cd5829373e03cd4c52f059bed3b7cb10061167 100644 (file)
@@ -142,8 +142,8 @@ ginscall(Node *f, int proc)
                break;
 
        case 1: // call in new proc (go)
-       case 2: // defered call (defer)
-               nodreg(&reg, types[TINT64], D_AX);
+       case 2: // deferred call (defer)
+               nodreg(&reg, types[TINT64], D_CX);
                gins(APUSHQ, f, N);
                nodconst(&con, types[TINT32], argsize(f->type));
                gins(APUSHQ, &con, N);
@@ -156,6 +156,11 @@ ginscall(Node *f, int proc)
                }
                gins(APOPQ, N, &reg);
                gins(APOPQ, N, &reg);
+               if(proc == 2) {
+                       nodreg(&reg, types[TINT64], D_AX);
+                       gins(ATESTQ, &reg, &reg);
+                       patch(gbranch(AJNE, T), pret);
+               }
                break;
        }
 }
index 468f67ae982928428fa220647fc067714469c549..193058e206d22698178d96909c2d69deaf190b33 100644 (file)
@@ -182,8 +182,8 @@ ginscall(Node *f, int proc)
                break;
 
        case 1: // call in new proc (go)
-       case 2: // defered call (defer)
-               nodreg(&reg, types[TINT32], D_AX);
+       case 2: // deferred call (defer)
+               nodreg(&reg, types[TINT32], D_CX);
                gins(APUSHL, f, N);
                nodconst(&con, types[TINT32], argsize(f->type));
                gins(APUSHL, &con, N);
@@ -193,6 +193,11 @@ ginscall(Node *f, int proc)
                        ginscall(deferproc, 0);
                gins(APOPL, N, &reg);
                gins(APOPL, N, &reg);
+               if(proc == 2) {
+                       nodreg(&reg, types[TINT64], D_AX);
+                       gins(ATESTL, &reg, &reg);
+                       patch(gbranch(AJNE, T), pret);
+               }
                break;
        }
 }
index b74e7f5e50d17ab66c848663c26bfe511926c6e0..59a917a9aea6c65ce474aa7f405aa19910e63608 100644 (file)
@@ -5,7 +5,7 @@ char *runtimeimport =
        "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"
index ecd72a56bcc45caa43c2f4aceca047773adea510..bb81d2a2225b278552b9fef72ee19549c1acf585 100644 (file)
@@ -1276,7 +1276,7 @@ addmethod(Sym *sf, Type *t, int local)
 }
 
 void
-funccompile(Node *n)
+funccompile(Node *n, int isclosure)
 {
        stksize = BADWIDTH;
        maxarg = 0;
@@ -1289,6 +1289,18 @@ funccompile(Node *n)
 
        // 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);
index 7ae5d99287de9a9929f8f5d5dba9a649be49fad6..dabf5d3f5998edc81a1cfada2797640f2779ec93 100644 (file)
@@ -1004,7 +1004,7 @@ NodeList* constiter(NodeList*, Node*, NodeList*);
 
 Node*  unsafenmagic(Node*, NodeList*);
 void   dclchecks(void);
-void   funccompile(Node*);
+void   funccompile(Node*, int);
 
 Node*  typedcl0(Sym*);
 Node*  typedcl1(Node*, Node*, int);
@@ -1169,6 +1169,8 @@ EXTERN    Prog*   breakpc;
 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);
index 3c2ecf27ff71fab7168768dde7d934bc39c0ca2b..a89c0181fa8d05532874d678c357cdcbff2c6ee4 100644 (file)
@@ -197,5 +197,5 @@ fninit(NodeList *n)
        fn->nbody = r;
        funcbody(fn);
        typecheck(&fn, Etop);
-       funccompile(fn);
+       funccompile(fn, 0);
 }
index bde1367da039b33ca1685b5308313c7702805e01..4202ba7cd2c42431a4a75ca4ae8d61cc1fc7f08a 100644 (file)
@@ -138,14 +138,14 @@ main(int argc, char *argv[])
        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();
 
@@ -1443,6 +1443,13 @@ lexfini(void)
                *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
index 0103c53d9f8dc37e9c88637ae563fa93f04ed4b8..ba79ab92d345161c9725145debcf7f906fd8d80e 100644 (file)
@@ -16,7 +16,7 @@ func throwreturn()
 func throwinit()
 
 func panic(interface{})
-func recover() interface{}
+func recover(*int32) interface{}
 
 func printbool(bool)
 func printfloat(float64)
index b38ea9dfb9fdc352057caa313d23d123c84261cd..54968dc15fabbf6e9eda00d693d1a74c462f3799 100644 (file)
@@ -2923,7 +2923,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam)
 
        funcbody(fn);
        typecheck(&fn, Etop);
-       funccompile(fn);
+       funccompile(fn, 0);
 }
 
 /*
index 1d5db4c045d9cea456083fe00fec83cf9ed88f4d..37b5efa6f83561314dcb42155980c92fbe049f0c 100644 (file)
@@ -391,6 +391,7 @@ walkstmt(Node **np)
        case OPRINTN:
        case OPANIC:
        case OEMPTY:
+       case ORECOVER:
                if(n->typecheck == 0)
                        fatal("missing typecheck");
                init = n->ninit;
@@ -631,7 +632,7 @@ walkexpr(Node **np, NodeList **init)
                goto ret;
 
        case ORECOVER:
-               n = mkcall("recover", n->type, init);
+               n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
                goto ret;
 
        case OLITERAL:
index 3c670cdb1a9d9b12c58a6d69f8b69a111e88d761..dfa87da29df470fe9ae845d675e9e9fa87e982f8 100644 (file)
@@ -147,8 +147,6 @@ i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
        // 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;
index 3ef6ae8efe762179f36d16d5dabab2e92518f384..6001c22892c16db6390407bd773e19bfd34b3d30 100644 (file)
@@ -447,6 +447,32 @@ scheduler(void)
        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 @@ newstack(void)
        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 @@ newproc1(byte *fn, byte *argp, int32 narg, int32 nret)
 }
 
 #pragma textflag 7
-void
+uintptr
 ·deferproc(int32 siz, byte* fn, ...)
 {
        Defer *d;
@@ -828,10 +858,19 @@ void
        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 @@ unwindstack(G *gp, byte *sp)
        }
 }
 
+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)
index c6655d9ec04054fb6c2fb5d26c89b21457b2f09e..02509deb69726017fed2c774a270037078198a2f 100644 (file)
@@ -41,15 +41,6 @@ panic(int32 unused)
        exit(2);
 }
 
-void
-·panic(Eface e)
-{
-       fd = 2;
-       printf("panic: ");
-       printany(e);
-       panic(0);
-}
-
 void
 ·throwindex(void)
 {
index a0c0dd7a18ec7b2d6f428384d3e88a493e16ab2d..adb83116bb439c38198823e006bd0cf233060da9 100644 (file)
@@ -62,6 +62,7 @@ typedef       struct  Itab            Itab;
 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;
@@ -98,6 +99,7 @@ enum
        Gwaiting,
        Gmoribund,
        Gdead,
+       Grecovery,
 };
 enum
 {
@@ -176,7 +178,8 @@ struct      G
        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
@@ -186,6 +189,7 @@ struct      G
        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
@@ -240,6 +244,7 @@ struct      Stktop
        // 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
 {
@@ -311,11 +316,23 @@ struct Defer
 {
        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
  */
@@ -400,6 +417,7 @@ void*       malloc(uintptr size);
 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);
index e8f7037cf57212fe9c721d1d891f1a9f99d7e5f0..cae5509f831527a6a48298eebf42e3b8756bd177 100644 (file)
@@ -190,5 +190,6 @@ bar
 bal
 bal
 panic: barCount != 1
+
 panic PC=xxx
 BUG