Commit 1e1cc4eb authored by Ken Thompson's avatar Ken Thompson

defer

R=r
OCL=23592
CL=23592
parent 4a903e0b
......@@ -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);
}
......
......@@ -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*);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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",
};
......
......@@ -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",
......
......@@ -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;
......
......@@ -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)
{
......
......@@ -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
......@@ -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*);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment