]> Cypherpunks repositories - gostls13.git/commitdiff
defer
authorKen Thompson <ken@golang.org>
Tue, 27 Jan 2009 20:03:53 +0000 (12:03 -0800)
committerKen Thompson <ken@golang.org>
Tue, 27 Jan 2009 20:03:53 +0000 (12:03 -0800)
R=r
OCL=23592
CL=23592

src/cmd/6g/gen.c
src/cmd/6g/gg.h
src/cmd/gc/go.h
src/cmd/gc/go.y
src/cmd/gc/lex.c
src/cmd/gc/subr.c
src/cmd/gc/walk.c
src/runtime/proc.c
src/runtime/rt0_amd64.s
src/runtime/runtime.h

index 769a72b8f2b24d40bde722b14d622abeabb6f84f..f01f1d8b5488484b886cbc84eaed70e86f5646bd 100644 (file)
@@ -26,6 +26,22 @@ if(newproc == N) {
        newproc->ullman = 1;
 }
 
+if(deferproc == N) {
+       deferproc = nod(ONAME, N, N);
+       deferproc->sym = pkglookup("deferproc", "sys");
+       deferproc->class = PEXTERN;
+       deferproc->addable = 1;
+       deferproc->ullman = 1;
+}
+
+if(deferreturn == N) {
+       deferreturn = nod(ONAME, N, N);
+       deferreturn->sym = pkglookup("deferreturn", "sys");
+       deferreturn->class = PEXTERN;
+       deferreturn->addable = 1;
+       deferreturn->ullman = 1;
+}
+
 if(throwindex == N) {
        throwindex = nod(ONAME, N, N);
        throwindex->sym = pkglookup("throwindex", "sys");
@@ -63,6 +79,7 @@ if(throwreturn == N) {
                }
        }
 
+       hasdefer = 0;
        walk(curfn);
        if(nerrors != 0)
                goto ret;
@@ -90,6 +107,8 @@ if(throwreturn == N) {
                gins(ACALL, N, throwreturn);
        }
 
+       if(hasdefer)
+               gins(ACALL, N, deferreturn);
        pc->as = ARET;  // overwrite AEND
        pc->lineno = lineno;
 
@@ -343,7 +362,11 @@ loop:
                break;
 
        case OPROC:
-               cgen_proc(n);
+               cgen_proc(n, 1);
+               break;
+
+       case ODEFER:
+               cgen_proc(n, 2);
                break;
 
        case ORETURN:
@@ -683,19 +706,26 @@ argsize(Type *t)
 /*
  * generate:
  *     call f
- * if proc, generate:
- *     push f
- *     push argsize
- *     call newproc
- *     pop
- *     pop
+ *     proc=0  normal call
+ *     proc=1  goroutine run in new proc
+ *     proc=2  defer call save away stack
  */
 void
 ginscall(Node *f, int proc)
 {
        Node reg, con;
 
-       if(proc) {
+       switch(proc) {
+       default:
+               fatal("ginscall: bad proc %d", proc);
+               break;
+
+       case 0: // normal call
+               gins(ACALL, N, f);
+               break;
+
+       case 1: // call in new proc (go)
+       case 2: // defered call (defer)
                nodreg(&reg, types[TINT64], D_AX);
                if(f->op != OREGISTER) {
                        gins(ALEAQ, f, &reg);
@@ -704,12 +734,14 @@ ginscall(Node *f, int proc)
                        gins(APUSHQ, f, N);
                nodconst(&con, types[TINT32], argsize(f->type));
                gins(APUSHQ, &con, N);
-               gins(ACALL, N, newproc);
+               if(proc == 1)
+                       gins(ACALL, N, newproc);
+               else
+                       gins(ACALL, N, deferproc);
                gins(APOPQ, N, &reg);
                gins(APOPQ, N, &reg);
-               return;
+               break;
        }
-       gins(ACALL, N, f);
 }
 
 /*
@@ -767,6 +799,9 @@ cgen_callinter(Node *n, Node *res, int proc)
 
 /*
  * generate call to non-interface method
+ *     proc=0  normal call
+ *     proc=1  goroutine run in new proc
+ *     proc=2  defer call save away stack
  */
 void
 cgen_callmeth(Node *n, int proc)
@@ -791,7 +826,9 @@ cgen_callmeth(Node *n, int proc)
 
 /*
  * generate function call;
- * if proc, run call in new proc.
+ *     proc=0  normal call
+ *     proc=1  goroutine run in new proc
+ *     proc=2  defer call save away stack
  */
 void
 cgen_call(Node *n, int proc)
@@ -851,22 +888,22 @@ ret:
  * generate code to start new proc running call n.
  */
 void
-cgen_proc(Node *n)
+cgen_proc(Node *n, int proc)
 {
        switch(n->left->op) {
        default:
                fatal("cgen_proc: unknown call %O", n->left->op);
 
        case OCALLMETH:
-               cgen_callmeth(n->left, 1);
+               cgen_callmeth(n->left, proc);
                break;
 
        case OCALLINTER:
-               cgen_callinter(n->left, N, 1);
+               cgen_callinter(n->left, N, proc);
                break;
 
        case OCALL:
-               cgen_call(n->left, 1);
+               cgen_call(n->left, proc);
                break;
        }
 
@@ -947,6 +984,8 @@ void
 cgen_ret(Node *n)
 {
        gen(n->left, L);        // copy out args
+       if(hasdefer)
+               gins(ACALL, N, deferreturn);
        gins(ARET, N, N);
 }
 
index a01e5b6e55868044bd0bebb9701f826e1b90e964..881a230737439174462dc13784155c161d766cc0 100644 (file)
@@ -116,6 +116,8 @@ EXTERN      Label*  labellist;
 EXTERN Label*  findlab(Sym*);
 EXTERN Node*   curfn;
 EXTERN Node*   newproc;
+EXTERN Node*   deferproc;
+EXTERN Node*   deferreturn;
 EXTERN Node*   throwindex;
 EXTERN Node*   throwreturn;
 
@@ -151,7 +153,7 @@ void        cgen_ret(Node*);
 void   cgen_call(Node*, int);
 void   cgen_callmeth(Node*, int);
 void   cgen_callinter(Node*, Node*, int);
-void   cgen_proc(Node*);
+void   cgen_proc(Node*, int);
 void   cgen_callret(Node*, Node*);
 void   cgen_div(int, Node*, Node*, Node*);
 void   cgen_bmul(int, Node*, Node*, Node*);
index abb08ebe2d0f87a58946f3b9b16f85d71006608b..a5d518b8d1a92bb1c42938e6766a39a2f486f638 100644 (file)
@@ -291,7 +291,7 @@ enum
        ODOT, ODOTPTR, ODOTMETH, ODOTINTER,
        ODCLFUNC, ODCLFIELD, ODCLARG,
        OLIST, OCMP, OPTR, OARRAY, ORANGE,
-       ORETURN, OFOR, OIF, OSWITCH,
+       ORETURN, OFOR, OIF, OSWITCH, ODEFER,
        OAS, OASOP, OCASE, OXCASE, OFALL, OXFALL,
        OGOTO, OPROC, OMAKE, ONEW, OEMPTY, OSELECT,
        OLEN, OCAP, OPANIC, OPANICN, OPRINT, OPRINTN, OTYPEOF,
@@ -498,6 +498,7 @@ EXTERN      int32   stksize;                // stack size for current frame
 EXTERN int32   initstksize;            // stack size for init function
 EXTERN ushort  blockgen;               // max block number
 EXTERN ushort  block;                  // current block number
+EXTERN int     hasdefer;               // flag that curfn has defer statetment
 
 EXTERN Node*   retnil;
 EXTERN Node*   fskel;
index ac764b94d858f3834eeafb0479805f94a0954e6e..4aafd0b0c15684684253d92e27b2bd518f08c67c 100644 (file)
@@ -15,7 +15,7 @@
 %token <val>           LLITERAL
 %token <lint>          LASOP
 %token <sym>           LNAME LBASETYPE LATYPE LPACK LACONST
-%token <sym>           LPACKAGE LIMPORT LEXPORT
+%token <sym>           LPACKAGE LIMPORT LDEFER
 %token <sym>           LMAP LCHAN LINTERFACE LFUNC LSTRUCT
 %token <sym>           LCOLAS LFALL LRETURN LDDD
 %token <sym>           LLEN LCAP LTYPEOF LPANIC LPANICN LPRINT LPRINTN
@@ -504,6 +504,11 @@ semi_stmt:
                $$ = nod(OCALL, $2, $4);
                $$ = nod(OPROC, $$, N);
        }
+|      LDEFER pexpr '(' oexpr_list ')'
+       {
+               $$ = nod(OCALL, $2, $4);
+               $$ = nod(ODEFER, $$, N);
+       }
 |      LGOTO new_name
        {
                $$ = nod(OGOTO, $2, N);
index 83fc1f8d398823f0fec01b6a398b1865ede92b5a..b8514549f8a66f91ddc94f0a4de72114cf6f83e9 100644 (file)
@@ -1056,7 +1056,7 @@ static    struct
        "continue",     LCONTINUE,      Txxx,
        "default",      LDEFAULT,       Txxx,
        "else",         LELSE,          Txxx,
-       "export",       LEXPORT,        Txxx,
+       "defer",        LDEFER,         Txxx,
        "fallthrough",  LFALL,          Txxx,
        "false",        LFALSE,         Txxx,
        "for",          LFOR,           Txxx,
@@ -1275,7 +1275,7 @@ struct
        LPRINT,         "PRINT",
        LPACKAGE,       "PACKAGE",
        LIMPORT,        "IMPORT",
-       LEXPORT,        "EXPORT",
+       LDEFER,         "DEFER",
        LPANIC,         "PANIC",
 };
 
index bfcdd08f5b832c0f49238e0c0ff80dc67afa28dd..98e99ab3b358ad4bd317a117fcc849b43c89edd4 100644 (file)
@@ -641,11 +641,12 @@ opnames[] =
        [ODCLARG]       = "DCLARG",
        [ODCLFIELD]     = "DCLFIELD",
        [ODCLFUNC]      = "DCLFUNC",
+       [ODEFER]        = "DEFER",
        [ODIV]          = "DIV",
-       [ODOT]          = "DOT",
-       [ODOTPTR]       = "DOTPTR",
-       [ODOTMETH]      = "DOTMETH",
        [ODOTINTER]     = "DOTINTER",
+       [ODOTMETH]      = "DOTMETH",
+       [ODOTPTR]       = "DOTPTR",
+       [ODOT]          = "DOT",
        [OEMPTY]        = "EMPTY",
        [OEND]          = "END",
        [OEQ]           = "EQ",
index 99fe055ea2b677b93f89501dc1e43cd0bbdf7de9..5004a86f02de2b05f162187e4e9b53d568e51d40 100644 (file)
@@ -145,6 +145,7 @@ loop:
        case OXFALL:
        case ORETURN:
        case OPROC:
+       case ODEFER:
                walktype(n, Etop);
                break;
        }
@@ -342,6 +343,8 @@ loop:
                walkstate(n->nelse);
                goto ret;
 
+       case ODEFER:
+               hasdefer = 1;
        case OPROC:
                if(top != Etop)
                        goto nottop;
index 7435830ff67ae4c5a4a0cae11c2ded52efb64034..3fe08df94d3b3ddf2f5563a1535b7a1b5c293990 100644 (file)
@@ -171,7 +171,7 @@ sys·newproc(int32 siz, byte* fn, byte* arg0)
 
        if((newg = gfget()) != nil){
                newg->status = Gwaiting;
-       }else{
+       } else {
                newg = malg(4096);
                newg->status = Gwaiting;
                newg->alllink = allg;
@@ -204,6 +204,41 @@ sys·newproc(int32 siz, byte* fn, byte* arg0)
 //printf(" goid=%d\n", newg->goid);
 }
 
+void
+sys·deferproc(int32 siz, byte* fn, byte* arg0)
+{
+       Defer *d;
+
+       d = mal(sizeof(*d) + siz - sizeof(d->args));
+       d->fn = fn;
+       d->sp = (byte*)&arg0;
+       d->siz = siz;
+       mcpy(d->args, d->sp, d->siz);
+
+       d->link = g->defer;
+       g->defer = d;
+}
+
+void
+sys·deferreturn(int32 arg0)
+{
+       // warning: jmpdefer knows the frame size
+       // of this routine. dont change anything
+       // that might change the frame size
+       Defer *d;
+       byte *sp;
+
+       d = g->defer;
+       if(d == nil)
+               return;
+       sp = (byte*)&arg0;
+       if(d->sp != sp)
+               return;
+       mcpy(d->sp, d->args, d->siz);
+       g->defer = d->link;
+       jmpdefer(d->fn);
+}
+
 void
 tracebackothers(G *me)
 {
index 8588d61a4e3a3e0e4314f76979f5db1a4bbe59c1..f8d4a381b3c4961638a3b3be6b9d3f078f700a93 100644 (file)
@@ -120,7 +120,7 @@ TEXT setspgoto(SB), 7, $0
 //     if(*val == old){
 //             *val = new;
 //             return 1;
-//     }else
+//     } else
 //             return 0;
 TEXT cas(SB), 7, $0
        MOVQ    8(SP), BX
@@ -133,3 +133,13 @@ TEXT cas(SB), 7, $0
        RET
        MOVL    $1, AX
        RET
+
+// void jmpdefer(byte*);
+// 1. pop the caller
+// 2. sub 5 bytes from the callers return
+// 3. jmp to the argument
+TEXT jmpdefer(SB), 7, $0
+       MOVQ    8(SP), AX       // function
+       ADDQ    $(8+56), SP     // pop saved PC and callers frame
+       SUBQ    $5, (SP)        // reposition his return address
+       JMP     AX              // and goto function
index 5552c9e94d29fb117931a00184e853b745a9bcda..78e2affc62733817af308dd6951839ec9c4b4cdb 100644 (file)
@@ -52,6 +52,7 @@ typedef       struct  SigTab          SigTab;
 typedef        struct  MCache          MCache;
 typedef        struct  Iface           Iface;
 typedef        struct  Itype           Itype;
+typedef        struct  Defer           Defer;
 
 /*
  * per cpu declaration
@@ -128,6 +129,7 @@ struct      G
 {
        byte*   stackguard;     // must not move
        byte*   stackbase;      // must not move
+       Defer*  defer;          // must not move
        byte*   stack0;         // first stack segment
        Gobuf   sched;
        G*      alllink;        // on allg
@@ -136,8 +138,8 @@ struct      G
        int32   goid;
        int32   selgen;         // valid sudog pointer
        G*      schedlink;
-       bool            readyonstop;
-       M*      m;      // for debuggers
+       bool    readyonstop;
+       M*      m;              // for debuggers
 };
 struct Mem
 {
@@ -151,8 +153,8 @@ struct      M
        G*      g0;             // g0 w interrupt stack - must not move
        uint64  morearg;        // arg to morestack - must not move
        uint64  cret;           // return value from C - must not move
-       uint64  procid; // for debuggers - must not move
-       G*      gsignal;                // signal-handling G - must not move
+       uint64  procid;         // for debuggers - must not move
+       G*      gsignal;        // signal-handling G - must not move
        G*      curg;           // current running goroutine
        G*      lastg;          // last running goroutine - to emulate fifo
        Gobuf   sched;
@@ -235,6 +237,18 @@ enum
        Amax
 };
 
+/*
+ * defered subroutine calls
+ */
+struct Defer
+{
+       int32   siz;
+       byte*   sp;
+       byte*   fn;
+       Defer*  link;
+       byte    args[8];        // padded to actual size
+};
+
 /*
  * external data
  */
@@ -286,6 +300,7 @@ int32       write(int32, void*, int32);
 void   close(int32);
 int32  fstat(int32, void*);
 bool   cas(uint32*, uint32, uint32);
+void   jmpdefer(byte*);
 void   exit1(int32);
 void   ready(G*);
 byte*  getenv(int8*);