Commit 1f51d279 authored by Russ Cox's avatar Russ Cox

cmd/gc: move genembedtramp into portable code

Requires adding new linker instruction
        RET	f(SB)
meaning return but then immediately call f.
This is what you'd use to implement a tail call after
fiddling with the arguments, but the compiler only
uses it in genwrapper.

This CL eliminates the copy-and-paste genembedtramp
functions from 5g/8g/6g and makes the code run on ARM
for the first time. It removes a small special case for function
generation, which should help Carl a bit, but at the same time
it does not bother to implement general tail call optimization,
which we do not want anyway.

Fixes #5627.

R=ken2
CC=golang-dev
https://golang.org/cl/10057044
parent 9b2561ef
...@@ -365,11 +365,19 @@ cgen_aret(Node *n, Node *res) ...@@ -365,11 +365,19 @@ cgen_aret(Node *n, Node *res)
void void
cgen_ret(Node *n) cgen_ret(Node *n)
{ {
Prog *p;
genlist(n->list); // copy out args genlist(n->list); // copy out args
if(hasdefer || curfn->exit) if(hasdefer || curfn->exit) {
gjmp(retpc); gjmp(retpc);
else return;
gins(ARET, N, N); }
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;
}
} }
/* /*
......
...@@ -518,88 +518,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) ...@@ -518,88 +518,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
return off; return off;
} }
void
genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
{
// TODO(kaib): re-implement genembedtramp
genwrapper(rcvr, method, newnam, iface);
/*
Sym *e;
int c, d, o;
Prog *p;
Type *f;
e = method->sym;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(e, rcvr, d, nil, 0);
if(c == 1)
goto out;
}
fatal("genembedtramp %T.%S", rcvr, method->sym);
out:
newplist()->name = newname(newnam);
//TEXT main·S_test2(SB),7,$0
p = pc;
gins(ATEXT, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = newnam;
p->to.type = D_CONST2;
p->to.offset = 0; // stack size
p->to.offset2 = rnd(method->type->argwid, widthptr); // argument size
p->reg = 7; // textflag
p->to.reg = NREG;
//print("1. %P\n", p);
o = 0;
for(c=d-1; c>=0; c--) {
f = dotlist[c].field;
o += f->width;
if(!isptr[f->type->etype])
continue;
//MOVW o(R0), R0
p = pc;
gins(AMOVW, N, N);
p->from.type = D_OREG;
p->from.reg = REGARG;
p->from.offset = o;
p->to.type = D_REG;
p->to.reg = REGARG;
//print("2. %P\n", p);
o = 0;
}
if(o != 0) {
//MOVW $XX(R0), R0
p = pc;
gins(AMOVW, N, N);
p->from.type = D_CONST;
p->from.reg = REGARG;
p->from.offset = o;
p->to.type = D_REG;
p->to.reg = REGARG;
//print("3. %P\n", p);
}
f = dotlist[0].field;
//B main·*Sub_test2(SB)
if(isptr[f->type->etype])
f = f->type;
p = pc;
gins(AB, N, N);
p->to.type = D_OREG;
p->to.reg = NREG;
p->to.name = D_EXTERN;
p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
//print("4. %P\n", p);
pc->as = ARET; // overwrite AEND
*/
}
void void
nopout(Prog *p) nopout(Prog *p)
{ {
......
...@@ -62,7 +62,7 @@ linkcase(Prog *casep) ...@@ -62,7 +62,7 @@ linkcase(Prog *casep)
void void
noops(void) noops(void)
{ {
Prog *p, *q, *q1; Prog *p, *q, *q1, *q2;
int o; int o;
Prog *pmorestack; Prog *pmorestack;
Sym *symmorestack; Sym *symmorestack;
...@@ -343,9 +343,14 @@ noops(void) ...@@ -343,9 +343,14 @@ noops(void)
if(!autosize) { if(!autosize) {
p->as = AB; p->as = AB;
p->from = zprg.from; p->from = zprg.from;
if(p->to.sym) { // retjmp
p->to.type = D_BRANCH;
p->cond = p->to.sym->text;
} else {
p->to.type = D_OREG; p->to.type = D_OREG;
p->to.offset = 0; p->to.offset = 0;
p->to.reg = REGLINK; p->to.reg = REGLINK;
}
break; break;
} }
} }
...@@ -359,6 +364,17 @@ noops(void) ...@@ -359,6 +364,17 @@ noops(void)
// If there are instructions following // If there are instructions following
// this ARET, they come from a branch // this ARET, they come from a branch
// with the same stackframe, so no spadj. // with the same stackframe, so no spadj.
if(p->to.sym) { // retjmp
p->to.reg = REGLINK;
q2 = appendp(p);
q2->as = AB;
q2->to.type = D_BRANCH;
q2->to.sym = p->to.sym;
q2->cond = p->to.sym->text;
p->to.sym = nil;
p = q2;
}
break; break;
case AADD: case AADD:
......
...@@ -325,11 +325,18 @@ cgen_aret(Node *n, Node *res) ...@@ -325,11 +325,18 @@ cgen_aret(Node *n, Node *res)
void void
cgen_ret(Node *n) cgen_ret(Node *n)
{ {
Prog *p;
genlist(n->list); // copy out args genlist(n->list); // copy out args
if(hasdefer || curfn->exit) if(hasdefer || curfn->exit) {
gjmp(retpc); gjmp(retpc);
else return;
gins(ARET, N, N); }
p = gins(ARET, N, N);
if(n->op == ORETJMP) {
p->to.type = D_EXTERN;
p->to.sym = n->left->sym;
}
} }
/* /*
......
...@@ -497,115 +497,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) ...@@ -497,115 +497,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
return off; return off;
} }
void
genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
{
Sym *e;
int c, d, mov, add, loaded;
int64 o;
Prog *p;
Type *f;
USED(iface);
if(0 && debug['r'])
print("genembedtramp %T %T %S\n", rcvr, method, newnam);
e = method->sym;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(e, rcvr, d, nil, 0);
if(c == 1)
goto out;
}
fatal("genembedtramp %T.%S", rcvr, method->sym);
out:
newplist()->name = newname(newnam);
//TEXT main·S_test2(SB),7,$0
p = pc;
gins(ATEXT, N, N);
p->from.type = D_EXTERN;
p->from.sym = newnam;
p->to.type = D_CONST;
p->to.offset = 0; // stack size
p->to.offset |= rnd(method->type->argwid, widthptr) << 32; // argument size
p->from.scale = 7; // textflag
//print("1. %P\n", p);
mov = AMOVQ;
add = AADDQ;
loaded = 0;
o = 0;
for(c=d-1; c>=0; c--) {
f = dotlist[c].field;
o += f->width;
if(!isptr[f->type->etype])
continue;
if(!loaded) {
loaded = 1;
//MOVQ 8(SP), AX
p = pc;
gins(mov, N, N);
p->from.type = D_INDIR+D_SP;
p->from.offset = widthptr;
p->to.type = D_AX;
//print("2. %P\n", p);
}
//MOVQ o(AX), AX
p = pc;
gins(mov, N, N);
p->from.type = D_INDIR+D_AX;
p->from.offset = o;
p->to.type = D_AX;
//print("3. %P\n", p);
o = 0;
}
if(o != 0) {
//ADDQ $XX, AX
p = pc;
gins(add, N, N);
p->from.type = D_CONST;
p->from.offset = o;
if(loaded)
p->to.type = D_AX;
else {
p->to.type = D_INDIR+D_SP;
p->to.offset = widthptr;
}
//print("4. %P\n", p);
}
//MOVQ AX, 8(SP)
if(loaded) {
p = pc;
gins(mov, N, N);
p->from.type = D_AX;
p->to.type = D_INDIR+D_SP;
p->to.offset = widthptr;
//print("5. %P\n", p);
} else {
// TODO(rsc): obviously this is unnecessary,
// but 6l has a bug, and it can't handle
// JMP instructions too close to the top of
// a new function.
gins(ANOP, N, N);
}
f = dotlist[0].field;
//JMP main·*Sub_test2(SB)
if(isptr[f->type->etype])
f = f->type;
p = pc;
gins(AJMP, N, N);
p->to.type = D_EXTERN;
p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
//print("6. %P\n", p);
pc->as = ARET; // overwrite AEND
}
void void
nopout(Prog *p) nopout(Prog *p)
{ {
......
...@@ -318,7 +318,7 @@ patch(void) ...@@ -318,7 +318,7 @@ patch(void)
if(p->to.type == D_INDIR+D_GS) if(p->to.type == D_INDIR+D_GS)
p->to.type = D_INDIR+D_FS; p->to.type = D_INDIR+D_FS;
} }
if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) {
s = p->to.sym; s = p->to.sym;
if(s) { if(s) {
if(debug['c']) if(debug['c'])
...@@ -747,6 +747,8 @@ dostkoff(void) ...@@ -747,6 +747,8 @@ dostkoff(void)
// the cleanup. // the cleanup.
p->spadj = +autoffset; p->spadj = +autoffset;
} }
if(p->to.sym) // retjmp
p->as = AJMP;
} }
} }
} }
......
...@@ -360,11 +360,18 @@ cgen_aret(Node *n, Node *res) ...@@ -360,11 +360,18 @@ cgen_aret(Node *n, Node *res)
void void
cgen_ret(Node *n) cgen_ret(Node *n)
{ {
Prog *p;
genlist(n->list); // copy out args genlist(n->list); // copy out args
if(retpc) if(retpc) {
gjmp(retpc); gjmp(retpc);
else return;
gins(ARET, N, N); }
p = gins(ARET, N, N);
if(n->op == ORETJMP) {
p->to.type = D_EXTERN;
p->to.sym = n->left->sym;
}
} }
/* /*
......
...@@ -507,112 +507,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) ...@@ -507,112 +507,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
return off; return off;
} }
void
genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
{
Sym *e;
int c, d, o, mov, add, loaded;
Prog *p;
Type *f;
USED(iface);
e = method->sym;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(e, rcvr, d, nil, 0);
if(c == 1)
goto out;
}
fatal("genembedtramp %T.%S", rcvr, method->sym);
out:
newplist()->name = newname(newnam);
//TEXT main·S_test2(SB),7,$0
p = pc;
gins(ATEXT, N, N);
p->from.type = D_EXTERN;
p->from.sym = newnam;
p->to.type = D_CONST;
p->to.offset = 0; // stack skize
p->to.offset2 = rnd(method->type->argwid, widthptr); // argument size
p->from.scale = 7; // textflag
//print("1. %P\n", p);
mov = AMOVL;
add = AADDL;
loaded = 0;
o = 0;
for(c=d-1; c>=0; c--) {
f = dotlist[c].field;
o += f->width;
if(!isptr[f->type->etype])
continue;
if(!loaded) {
loaded = 1;
//MOVL 4(SP), AX
p = pc;
gins(mov, N, N);
p->from.type = D_INDIR+D_SP;
p->from.offset = widthptr;
p->to.type = D_AX;
//print("2. %P\n", p);
}
//MOVL o(AX), AX
p = pc;
gins(mov, N, N);
p->from.type = D_INDIR+D_AX;
p->from.offset = o;
p->to.type = D_AX;
//print("3. %P\n", p);
o = 0;
}
if(o != 0) {
//ADDL $XX, AX
p = pc;
gins(add, N, N);
p->from.type = D_CONST;
p->from.offset = o;
if(loaded)
p->to.type = D_AX;
else {
p->to.type = D_INDIR+D_SP;
p->to.offset = widthptr;
}
//print("4. %P\n", p);
}
//MOVL AX, 4(SP)
if(loaded) {
p = pc;
gins(mov, N, N);
p->from.type = D_AX;
p->to.type = D_INDIR+D_SP;
p->to.offset = widthptr;
//print("5. %P\n", p);
} else {
// TODO(rsc): obviously this is unnecessary,
// but 6l has a bug, and it can't handle
// JMP instructions too close to the top of
// a new function.
gins(ANOP, N, N);
}
f = dotlist[0].field;
//JMP main·*Sub_test2(SB)
if(isptr[f->type->etype])
f = f->type;
p = pc;
gins(AJMP, N, N);
p->to.type = D_EXTERN;
p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
//print("6. %P\n", p);
pc->as = ARET; // overwrite AEND
}
void void
nopout(Prog *p) nopout(Prog *p)
{ {
......
...@@ -329,7 +329,7 @@ patch(void) ...@@ -329,7 +329,7 @@ patch(void)
p->from.offset = 0; p->from.offset = 0;
} }
} }
if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH)) { if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) {
s = p->to.sym; s = p->to.sym;
if(p->to.type == D_INDIR+D_ADDR) { if(p->to.type == D_INDIR+D_ADDR) {
/* skip check if this is an indirect call (CALL *symbol(SB)) */ /* skip check if this is an indirect call (CALL *symbol(SB)) */
...@@ -692,6 +692,8 @@ dostkoff(void) ...@@ -692,6 +692,8 @@ dostkoff(void)
// the cleanup. // the cleanup.
p->spadj = +autoffset; p->spadj = +autoffset;
} }
if(p->to.sym) // retjmp
p->as = AJMP;
} }
} }
} }
......
...@@ -870,6 +870,10 @@ stmtfmt(Fmt *f, Node *n) ...@@ -870,6 +870,10 @@ stmtfmt(Fmt *f, Node *n)
fmtprint(f, "return %,H", n->list); fmtprint(f, "return %,H", n->list);
break; break;
case ORETJMP:
fmtprint(f, "retjmp %S", n->sym);
break;
case OPROC: case OPROC:
fmtprint(f, "go %N", n->left); fmtprint(f, "go %N", n->left);
break; break;
......
...@@ -489,6 +489,7 @@ gen(Node *n) ...@@ -489,6 +489,7 @@ gen(Node *n)
break; break;
case ORETURN: case ORETURN:
case ORETJMP:
cgen_ret(n); cgen_ret(n);
break; break;
......
...@@ -581,6 +581,7 @@ enum ...@@ -581,6 +581,7 @@ enum
OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
OLROT, // left rotate: AROL. OLROT, // left rotate: AROL.
ORROTC, // right rotate-carry: ARCR. ORROTC, // right rotate-carry: ARCR.
ORETJMP, // return to other function
OEND, OEND,
}; };
...@@ -1461,7 +1462,6 @@ void fixautoused(Prog*); ...@@ -1461,7 +1462,6 @@ void fixautoused(Prog*);
void gdata(Node*, Node*, int); void gdata(Node*, Node*, int);
void gdatacomplex(Node*, Mpcplx*); void gdatacomplex(Node*, Mpcplx*);
void gdatastring(Node*, Strlit*); void gdatastring(Node*, Strlit*);
void genembedtramp(Type*, Type*, Sym*, int iface);
void ggloblnod(Node *nam); void ggloblnod(Node *nam);
void ggloblsym(Sym *s, int32 width, int dupok, int rodata); void ggloblsym(Sym *s, int32 width, int dupok, int rodata);
Prog* gjmp(Prog*); Prog* gjmp(Prog*);
......
...@@ -197,6 +197,7 @@ ishairy(Node *n, int *budget) ...@@ -197,6 +197,7 @@ ishairy(Node *n, int *budget)
case ODEFER: case ODEFER:
case ODCLTYPE: // can't print yet case ODCLTYPE: // can't print yet
case ODCLCONST: // can't print yet case ODCLCONST: // can't print yet
case ORETJMP:
return 1; return 1;
break; break;
......
...@@ -218,6 +218,7 @@ orderstmt(Node *n, NodeList **out) ...@@ -218,6 +218,7 @@ orderstmt(Node *n, NodeList **out)
case_OFALL: case_OFALL:
case OGOTO: case OGOTO:
case OLABEL: case OLABEL:
case ORETJMP:
// Special: n->left is not an expression; save as is. // Special: n->left is not an expression; save as is.
*out = list(*out, n); *out = list(*out, n);
break; break;
......
...@@ -364,6 +364,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) ...@@ -364,6 +364,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OIF: case OIF:
case OCALLMETH: case OCALLMETH:
case ORETURN: case ORETURN:
case ORETJMP:
case OSWITCH: case OSWITCH:
case OSELECT: case OSELECT:
case OEMPTY: case OEMPTY:
......
...@@ -208,15 +208,7 @@ methods(Type *t) ...@@ -208,15 +208,7 @@ methods(Type *t)
if(!(a->isym->flags & SymSiggen)) { if(!(a->isym->flags & SymSiggen)) {
a->isym->flags |= SymSiggen; a->isym->flags |= SymSiggen;
if(!eqtype(this, it) || this->width < types[tptr]->width) { if(!eqtype(this, it) || this->width < types[tptr]->width) {
// Is okay to call genwrapper here always,
// but we can generate more efficient code
// using genembedtramp if all that is necessary
// is a pointer adjustment and a JMP.
compiling_wrappers = 1; compiling_wrappers = 1;
if(isptr[it->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(it, f, a->isym, 1);
else
genwrapper(it, f, a->isym, 1); genwrapper(it, f, a->isym, 1);
compiling_wrappers = 0; compiling_wrappers = 0;
} }
...@@ -226,10 +218,6 @@ methods(Type *t) ...@@ -226,10 +218,6 @@ methods(Type *t)
a->tsym->flags |= SymSiggen; a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) { if(!eqtype(this, t)) {
compiling_wrappers = 1; compiling_wrappers = 1;
if(isptr[t->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(t, f, a->tsym, 0);
else
genwrapper(t, f, a->tsym, 0); genwrapper(t, f, a->tsym, 0);
compiling_wrappers = 0; compiling_wrappers = 0;
} }
......
...@@ -2495,13 +2495,13 @@ structargs(Type **tl, int mustname) ...@@ -2495,13 +2495,13 @@ structargs(Type **tl, int mustname)
void void
genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
{ {
Node *this, *fn, *call, *n, *t, *pad; Node *this, *fn, *call, *n, *t, *pad, *dot, *as;
NodeList *l, *args, *in, *out; NodeList *l, *args, *in, *out;
Type *tpad; Type *tpad, *methodrcvr;
int isddd; int isddd;
Val v; Val v;
if(debug['r']) if(0 && debug['r'])
print("genwrapper rcvrtype=%T method=%T newnam=%S\n", print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
rcvr, method, newnam); rcvr, method, newnam);
...@@ -2547,8 +2547,10 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) ...@@ -2547,8 +2547,10 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
isddd = l->n->left->isddd; isddd = l->n->left->isddd;
} }
methodrcvr = getthisx(method->type)->type->type;
// generate nil pointer check for better error // generate nil pointer check for better error
if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) { if(isptr[rcvr->etype] && rcvr->type == methodrcvr) {
// generating wrapper from *T to T. // generating wrapper from *T to T.
n = nod(OIF, N, N); n = nod(OIF, N, N);
n->ntest = nod(OEQ, this->left, nodnil()); n->ntest = nod(OEQ, this->left, nodnil());
...@@ -2568,8 +2570,22 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) ...@@ -2568,8 +2570,22 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
fn->nbody = list(fn->nbody, n); fn->nbody = list(fn->nbody, n);
} }
dot = adddot(nod(OXDOT, this->left, newname(method->sym)));
// generate call // generate call
call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); if(isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) {
// skip final .M
dot = dot->left;
if(!isptr[dotlist[0].field->type->etype])
dot = nod(OADDR, dot, N);
as = nod(OAS, this->left, nod(OCONVNOP, dot, N));
as->right->type = rcvr;
fn->nbody = list(fn->nbody, as);
n = nod(ORETJMP, N, N);
n->left = newname(methodsym(method->sym, methodrcvr, 0));
fn->nbody = list(fn->nbody, n);
} else {
call = nod(OCALL, dot, N);
call->list = args; call->list = args;
call->isddd = isddd; call->isddd = isddd;
if(method->type->outtuple > 0) { if(method->type->outtuple > 0) {
...@@ -2578,6 +2594,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) ...@@ -2578,6 +2594,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
call = n; call = n;
} }
fn->nbody = list(fn->nbody, call); fn->nbody = list(fn->nbody, call);
}
if(0 && debug['r']) if(0 && debug['r'])
dumplist("genwrapper body", fn->nbody); dumplist("genwrapper body", fn->nbody);
......
...@@ -1652,6 +1652,10 @@ reswitch: ...@@ -1652,6 +1652,10 @@ reswitch:
typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument"); typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
goto ret; goto ret;
case ORETJMP:
ok |= Etop;
goto ret;
case OSELECT: case OSELECT:
ok |= Etop; ok |= Etop;
typecheckselect(n); typecheckselect(n);
...@@ -3282,6 +3286,7 @@ isterminating(NodeList *l, int top) ...@@ -3282,6 +3286,7 @@ isterminating(NodeList *l, int top)
case OGOTO: case OGOTO:
case ORETURN: case ORETURN:
case ORETJMP:
case OPANIC: case OPANIC:
case OXFALL: case OXFALL:
return 1; return 1;
......
...@@ -282,6 +282,9 @@ walkstmt(Node **np) ...@@ -282,6 +282,9 @@ walkstmt(Node **np)
n->list = ll; n->list = ll;
break; break;
case ORETJMP:
break;
case OSELECT: case OSELECT:
walkselect(n); walkselect(n);
break; break;
......
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