diff options
Diffstat (limited to 'src/cmd/5g/ggen.c')
-rw-r--r-- | src/cmd/5g/ggen.c | 171 |
1 files changed, 159 insertions, 12 deletions
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index de1671bb6..040c3d2a9 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -9,9 +9,15 @@ #include "gg.h" #include "opt.h" +static Prog* appendp(Prog*, int, int, int, int32, int, int, int32); + void -defframe(Prog *ptxt) +defframe(Prog *ptxt, Bvec *bv) { + int i, j, first; + uint32 frame; + Prog *p, *p1; + // fill in argument size ptxt->to.type = D_CONST2; ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); @@ -19,8 +25,62 @@ defframe(Prog *ptxt) // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); + frame = rnd(maxstksize+maxarg, widthptr); + ptxt->to.offset = frame; maxstksize = 0; + + // insert code to clear pointered part of the frame, + // so that garbage collector only sees initialized values + // when it looks for pointers. + p = ptxt; + while(p->link->as == AFUNCDATA || p->link->as == APCDATA || p->link->as == ATYPE) + p = p->link; + if(stkzerosize >= 8*widthptr) { + p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0); + p = appendp(p, AADD, D_CONST, NREG, 4+frame-stkzerosize, D_REG, 1, 0); + p->reg = REGSP; + p = appendp(p, AADD, D_CONST, NREG, stkzerosize, D_REG, 2, 0); + p->reg = 1; + p1 = p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, 1, 4); + p->scond |= C_PBIT; + p = appendp(p, ACMP, D_REG, 1, 0, D_NONE, 0, 0); + p->reg = 2; + p = appendp(p, ABNE, D_NONE, NREG, 0, D_BRANCH, NREG, 0); + patch(p, p1); + } else { + first = 1; + j = (stkptrsize - stkzerosize)/widthptr * 2; + for(i=0; i<stkzerosize; i+=widthptr) { + if(bvget(bv, j) || bvget(bv, j+1)) { + if(first) { + p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0); + first = 0; + } + p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, REGSP, 4+frame-stkzerosize+i); + } + j += 2; + } + } +} + +static Prog* +appendp(Prog *p, int as, int ftype, int freg, int32 foffset, int ttype, int treg, int32 toffset) +{ + Prog *q; + + q = mal(sizeof(*q)); + clearp(q); + q->as = as; + q->lineno = p->lineno; + q->from.type = ftype; + q->from.reg = freg; + q->from.offset = foffset; + q->to.type = ttype; + q->to.reg = treg; + q->to.offset = toffset; + q->link = p->link; + p->link = q; + return q; } // Sweep the prog list to mark any used nodes. @@ -39,7 +99,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) { @@ -73,9 +133,28 @@ fixautoused(Prog* p) void ginscall(Node *f, int proc) { + int32 arg; Prog *p; Node n1, r, r1, con; + 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 += 3*widthptr; + } + + if(arg != -1) + gargsize(arg); + switch(proc) { default: fatal("ginscall: bad proc %d", proc); @@ -84,6 +163,20 @@ 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 BL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction before that return PC. + // To avoid that instruction being an unrelated instruction, + // insert a NOP so that we will have the right line number. + // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0. + // Use the latter form because the NOP pseudo-instruction + // would be removed by the linker. + nodreg(&r, types[TINT], 0); + p = gins(AAND, &r, &r); + p->scond = C_SCOND_EQ; + } p = gins(ABL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) @@ -156,6 +249,9 @@ ginscall(Node *f, int proc) } break; } + + if(arg != -1) + gargsize(-1); } /* @@ -211,6 +307,7 @@ cgen_callinter(Node *n, Node *res, int proc) nodo.xoffset -= widthptr; cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab + cgen_checknil(&nodr); // in case offset is huge nodo.xoffset = n->left->xoffset + 3*widthptr + 8; @@ -225,14 +322,11 @@ cgen_callinter(Node *n, Node *res, int proc) p->from.type = D_CONST; // REG = &(20+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); } /* @@ -260,8 +354,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); @@ -365,11 +457,19 @@ 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.name = D_EXTERN; + p->to.type = D_CONST; + p->to.sym = n->left->sym; + } } /* @@ -601,6 +701,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) gshift(AMOVW, &n2, SHIFT_LL, v, &n1); gshift(AORR, &n2, SHIFT_LR, w-v, &n1); regfree(&n2); + // Ensure sign/zero-extended result. + gins(optoas(OAS, nl->type), &n1, &n1); } gmove(&n1, res); regfree(&n1); @@ -626,6 +728,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) else // OLSH gshift(AMOVW, &n1, SHIFT_LL, sc, &n1); } + if(w < 32 && op == OLSH) + gins(optoas(OAS, nl->type), &n1, &n1); gmove(&n1, res); regfree(&n1); return; @@ -699,6 +803,9 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) regfree(&n3); patch(p3, pc); + // Left-shift of smaller word must be sign/zero-extended. + if(w < 32 && op == OLSH) + gins(optoas(OAS, nl->type), &n2, &n2); gmove(&n2, res); regfree(&n1); @@ -759,7 +866,7 @@ clearfat(Node *nl) } while(c > 0) { - p = gins(AMOVBU, &nz, &dst); + p = gins(AMOVB, &nz, &dst); p->to.type = D_OREG; p->to.offset = 1; p->scond |= C_PBIT; @@ -769,3 +876,43 @@ clearfat(Node *nl) regfree(&dst); regfree(&nz); } + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +void +expandchecks(Prog *firstp) +{ + int reg; + Prog *p, *p1; + + 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"); + if(p->from.type != D_REG) + fatal("invalid nil check %P", p); + reg = p->from.reg; + // check is + // CMP arg, $0 + // MOV.EQ arg, 0(arg) + p1 = mal(sizeof *p1); + clearp(p1); + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + p1->loc = 9999; + p1->as = AMOVW; + p1->from.type = D_REG; + p1->from.reg = reg; + p1->to.type = D_OREG; + p1->to.reg = reg; + p1->to.offset = 0; + p1->scond = C_SCOND_EQ; + p->as = ACMP; + p->from.type = D_CONST; + p->from.reg = NREG; + p->from.offset = 0; + p->reg = reg; + } +} |