diff options
Diffstat (limited to 'src/cmd/6g/ggen.c')
-rw-r--r-- | src/cmd/6g/ggen.c | 154 |
1 files changed, 142 insertions, 12 deletions
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 5e426753c..9fad9f7f1 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -9,15 +9,59 @@ #include "gg.h" #include "opt.h" +static Prog* appendp(Prog*, int, int, vlong, int, vlong); + void -defframe(Prog *ptxt) +defframe(Prog *ptxt, Bvec *bv) { + int i, j; + uint32 frame; + Prog *p; + // fill in argument size ptxt->to.offset = rnd(curfn->type->argwid, widthptr); // fill in final stack size ptxt->to.offset <<= 32; - ptxt->to.offset |= rnd(stksize+maxarg, widthptr); + frame = rnd(stksize+maxarg, widthptr); + ptxt->to.offset |= frame; + + // insert code to clear pointered part of the frame, + // so that garbage collector only sees initialized values + // when it looks for pointers. + p = ptxt; + if(stkzerosize >= 8*widthptr) { + p = appendp(p, AMOVQ, D_CONST, 0, D_AX, 0); + p = appendp(p, AMOVQ, D_CONST, stkzerosize/widthptr, D_CX, 0); + p = appendp(p, ALEAQ, D_SP+D_INDIR, frame-stkzerosize, D_DI, 0); + p = appendp(p, AREP, D_NONE, 0, D_NONE, 0); + appendp(p, ASTOSQ, D_NONE, 0, D_NONE, 0); + } else { + j = (stkptrsize - stkzerosize)/widthptr * 2; + for(i=0; i<stkzerosize; i+=widthptr) { + if(bvget(bv, j) || bvget(bv, j+1)) + p = appendp(p, AMOVQ, D_CONST, 0, D_SP+D_INDIR, frame-stkzerosize+i); + j += 2; + } + } +} + +static Prog* +appendp(Prog *p, int as, int ftype, vlong foffset, int ttype, vlong toffset) +{ + Prog *q; + + q = mal(sizeof(*q)); + clearp(q); + q->as = as; + q->lineno = p->lineno; + q->from.type = ftype; + q->from.offset = foffset; + q->to.type = ttype; + q->to.offset = toffset; + q->link = p->link; + p->link = q; + return q; } // Sweep the prog list to mark any used nodes. @@ -36,7 +80,7 @@ markautoused(Prog* p) } } -// Fixup instructions after compactframe has moved all autos around. +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. void fixautoused(Prog *p) { @@ -70,10 +114,29 @@ fixautoused(Prog *p) void ginscall(Node *f, int proc) { + int32 arg; Prog *p; Node reg, con; Node r1; + if(f->type != T) + setmaxarg(f->type); + + arg = -1; + // Most functions have a fixed-size argument block, so traceback uses that during unwind. + // Not all, though: there are some variadic functions in package runtime, + // and for those we emit call-specific metadata recorded by caller. + // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub), + // so we do this for all indirect calls as well. + if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) { + arg = f->type->argwid; + if(proc == 1 || proc == 2) + arg += 2*widthptr; + } + + if(arg != -1) + gargsize(arg); + switch(proc) { default: fatal("ginscall: bad proc %d", proc); @@ -82,6 +145,19 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == deferreturn) { + // Deferred calls will appear to be returning to + // the CALL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction byte before the return PC. + // To avoid that being an unrelated instruction, + // insert an x86 NOP that we will have the right line number. + // x86 NOP 0x90 is really XCHG AX, AX; use that description + // because the NOP pseudo-instruction would be removed by + // the linker. + nodreg(®, types[TINT], D_AX); + gins(AXCHGL, ®, ®); + } p = gins(ACALL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) @@ -130,6 +206,9 @@ ginscall(Node *f, int proc) } break; } + + if(arg != -1) + gargsize(-1); } /* @@ -178,6 +257,7 @@ cgen_callinter(Node *n, Node *res, int proc) regalloc(&nodr, types[tptr], &nodo); if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); + cgen_checknil(&nodo); // in case offset is huge nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; if(proc == 0) { @@ -189,14 +269,11 @@ cgen_callinter(Node *n, Node *res, int proc) gins(ALEAQ, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f] } - // BOTCH nodr.type = fntype; nodr.type = n->left->type; ginscall(&nodr, proc); regfree(&nodr); regfree(&nodo); - - setmaxarg(n->left->type); } /* @@ -224,8 +301,6 @@ cgen_call(Node *n, int proc) genlist(n->list); // assign the args t = n->left->type; - setmaxarg(t); - // call tempname pointer if(n->left->ullman >= UINF) { regalloc(&nod, types[tptr], N); @@ -325,11 +400,18 @@ cgen_aret(Node *n, Node *res) void cgen_ret(Node *n) { + Prog *p; + genlist(n->list); // copy out args - if(hasdefer || curfn->exit) + if(hasdefer || curfn->exit) { gjmp(retpc); - else - gins(ARET, N, N); + return; + } + p = gins(ARET, N, N); + if(n->op == ORETJMP) { + p->to.type = D_EXTERN; + p->to.sym = n->left->sym; + } } /* @@ -514,7 +596,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res) check = 0; if(issigned[t->etype]) { check = 1; - if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1)) + if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -(1ULL<<(t->width*8-1))) check = 0; else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1) check = 0; @@ -987,3 +1069,51 @@ clearfat(Node *nl) restx(&n1, &oldn1); restx(&ax, &oldax); } + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +void +expandchecks(Prog *firstp) +{ + Prog *p, *p1, *p2; + + for(p = firstp; p != P; p = p->link) { + if(p->as != ACHECKNIL) + continue; + if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers + warnl(p->lineno, "generated nil check"); + // check is + // CMP arg, $0 + // JNE 2(PC) (likely) + // MOV AX, 0 + p1 = mal(sizeof *p1); + p2 = mal(sizeof *p2); + clearp(p1); + clearp(p2); + p1->link = p2; + p2->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + p2->lineno = p->lineno; + p1->loc = 9999; + p2->loc = 9999; + p->as = ACMPQ; + p->to.type = D_CONST; + p->to.offset = 0; + p1->as = AJNE; + p1->from.type = D_CONST; + p1->from.offset = 1; // likely + p1->to.type = D_BRANCH; + p1->to.u.branch = p2->link; + // crash by write to memory address 0. + // if possible, since we know arg is 0, use 0(arg), + // which will be shorter to encode than plain 0. + p2->as = AMOVL; + p2->from.type = D_AX; + if(regtyp(&p->from)) + p2->to.type = p->from.type + D_INDIR; + else + p2->to.type = D_INDIR+D_NONE; + p2->to.offset = 0; + } +} |