1
0
mirror of https://github.com/golang/go synced 2024-10-06 12:21:22 -06:00
go/src/cmd/6g/cgen.c

896 lines
15 KiB
C
Raw Normal View History

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "gg.h"
void
cgen(Node *n, Node *res)
{
Node *nl, *nr, *r;
Node n1, n2;
2008-12-13 17:41:47 -07:00
int a, f;
Prog *p1, *p2, *p3;
Addr addr;
if(debug['g']) {
2008-06-08 17:11:14 -06:00
dump("\ncgen-res", res);
dump("cgen-r", n);
}
if(n == N || n->type == T)
return;
if(res == N || res->type == T)
fatal("cgen: res nil");
if(n->ullman >= UINF) {
if(n->op == OINDREG)
fatal("cgen: this is going to misscompile");
if(res->ullman >= UINF) {
tempname(&n1, n->type);
cgen(n, &n1);
cgen(&n1, res);
goto ret;
}
}
if(isfat(n->type)) {
sgen(n, res, n->type->width);
goto ret;
}
if(!res->addable) {
if(n->ullman > res->ullman) {
2008-06-06 18:01:33 -06:00
regalloc(&n1, n->type, res);
cgen(n, &n1);
cgen(&n1, res);
regfree(&n1);
goto ret;
}
2008-12-13 17:41:47 -07:00
if(res->ullman >= UINF)
goto gen;
f = 1; // gen thru register
switch(n->op) {
case OLITERAL:
if(smallintconst(n))
2008-12-13 17:41:47 -07:00
f = 0;
break;
case OREGISTER:
f = 0;
break;
}
if(sudoaddable(res, n->type, &addr)) {
2008-12-13 17:41:47 -07:00
a = optoas(OAS, res->type);
if(f) {
regalloc(&n2, res->type, N);
cgen(n, &n2);
p1 = gins(a, &n2, N);
regfree(&n2);
} else
p1 = gins(a, n, N);
p1->to = addr;
sudoclean();
2008-12-13 17:41:47 -07:00
goto ret;
}
gen:
igen(res, &n1, N);
cgen(n, &n1);
regfree(&n1);
goto ret;
}
if(n->addable) {
gmove(n, res);
goto ret;
}
nl = n->left;
nr = n->right;
if(nl != N && nl->ullman >= UINF)
if(nr != N && nr->ullman >= UINF) {
2008-06-27 17:59:14 -06:00
tempname(&n1, nr->type);
cgen(nr, &n1);
n2 = *n;
n2.right = &n1;
cgen(&n2, res);
goto ret;
}
if(sudoaddable(n, res->type, &addr)) {
a = optoas(OAS, n->type);
if(res->op == OREGISTER) {
p1 = gins(a, N, res);
p1->from = addr;
} else {
2008-12-13 17:41:47 -07:00
regalloc(&n2, n->type, &n1);
p1 = gins(a, N, &n2);
p1->from = addr;
2008-12-13 17:41:47 -07:00
gins(a, &n2, res);
regfree(&n2);
}
sudoclean();
2008-12-13 17:41:47 -07:00
goto ret;
}
switch(n->op) {
default:
dump("cgen", n);
fatal("cgen: unknown op %N", n);
break;
// these call bgen to get a bool value
case OOROR:
case OANDAND:
case OEQ:
case ONE:
case OLT:
case OLE:
case OGE:
case OGT:
case ONOT:
p1 = gbranch(AJMP, T);
p2 = pc;
gmove(booltrue, res);
p3 = gbranch(AJMP, T);
patch(p1, pc);
bgen(n, 1, p2);
gmove(boolfalse, res);
patch(p3, pc);
goto ret;
case OPLUS:
cgen(nl, res);
goto ret;
// unary
case OCOM:
a = optoas(OXOR, nl->type);
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
nodconst(&n2, nl->type, -1);
gins(a, &n2, &n1);
gmove(&n1, res);
regfree(&n1);
goto ret;
case OMINUS:
a = optoas(n->op, nl->type);
goto uop;
// symmetric binary
case OAND:
case OOR:
case OXOR:
case OADD:
case OMUL:
a = optoas(n->op, nl->type);
2008-11-07 15:20:32 -07:00
if(a != AIMULB)
goto sbop;
cgen_bmul(n->op, nl, nr, res);
break;
// asymmetric binary
case OSUB:
a = optoas(n->op, nl->type);
goto abop;
case OCONV:
if(eqtype(n->type, nl->type, 0)) {
cgen(nl, res);
break;
}
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
if(isptrarray(n->type) && isptrdarray(nl->type)) {
2008-08-27 18:28:30 -06:00
// convert dynamic array to static array
n2 = n1;
n2.op = OINDREG;
n2.xoffset = offsetof(Array,array);
n2.type = types[tptr];
gins(AMOVQ, &n2, &n1);
}
if(isptrdarray(n->type) && isptrarray(nl->type)) {
2008-08-27 18:28:30 -06:00
// conver static array to dynamic array
// it is assumed that the dope is just before the array
nodconst(&n2, types[tptr], offsetof(Array,b));
gins(ASUBQ, &n2, &n1);
}
gmove(&n1, res);
regfree(&n1);
break;
case ODOT:
case ODOTPTR:
2008-12-13 17:41:47 -07:00
case OINDEXPTR:
case OINDEX:
case OIND:
igen(n, &n1, res);
gmove(&n1, res);
regfree(&n1);
break;
case OLEN:
if(isptrto(nl->type, TSTRING)) {
regalloc(&n1, types[tptr], res);
2008-06-08 18:46:28 -06:00
cgen(nl, &n1);
nodconst(&n2, types[tptr], 0);
gins(optoas(OCMP, types[tptr]), &n1, &n2);
p1 = gbranch(optoas(OEQ, types[tptr]), T);
2008-11-22 18:58:53 -07:00
n2 = n1;
n2.op = OINDREG;
n2.type = types[TINT32];
gmove(&n2, &n1);
patch(p1, pc);
2008-11-22 18:58:53 -07:00
gmove(&n1, res);
regfree(&n1);
break;
}
if(isptrto(nl->type, TMAP)) {
regalloc(&n1, types[tptr], res);
cgen(nl, &n1);
n1.op = OINDREG;
n1.type = types[TINT32];
gmove(&n1, res);
regfree(&n1);
break;
}
if(isptrdarray(nl->type)) {
2008-08-27 18:28:30 -06:00
regalloc(&n1, types[tptr], res);
cgen(nl, &n1);
n1.op = OINDREG;
n1.type = types[TUINT32];
n1.xoffset = offsetof(Array,nel);
gmove(&n1, res);
regfree(&n1);
break;
}
fatal("cgen: OLEN: unknown type %lT", nl->type);
break;
2008-08-27 18:28:30 -06:00
case OCAP:
if(isptrdarray(nl->type)) {
2008-08-27 18:28:30 -06:00
regalloc(&n1, types[tptr], res);
cgen(nl, &n1);
n1.op = OINDREG;
n1.type = types[TUINT32];
n1.xoffset = offsetof(Array,cap);
gmove(&n1, res);
regfree(&n1);
break;
}
fatal("cgen: OCAP: unknown type %lT", nl->type);
break;
case OADDR:
agen(nl, res);
break;
case OCALLMETH:
cgen_callmeth(n, 0);
cgen_callret(n, res);
break;
case OCALLINTER:
cgen_callinter(n, res, 0);
cgen_callret(n, res);
break;
case OCALL:
cgen_call(n, 0);
cgen_callret(n, res);
break;
2008-06-06 21:43:29 -06:00
case OMOD:
case ODIV:
2008-06-09 14:16:50 -06:00
if(isfloat[n->type->etype]) {
a = optoas(n->op, nl->type);
goto abop;
}
2008-06-06 21:43:29 -06:00
cgen_div(n->op, nl, nr, res);
break;
2008-06-08 18:21:46 -06:00
case OLSH:
case ORSH:
cgen_shift(n->op, nl, nr, res);
break;
}
goto ret;
sbop: // symmetric binary
if(nl->ullman < nr->ullman) {
r = nl;
nl = nr;
nr = r;
}
abop: // asymmetric binary
if(nl->ullman >= nr->ullman) {
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
if(sudoaddable(nr, nl->type, &addr)) {
2008-12-13 17:41:47 -07:00
p1 = gins(a, N, &n1);
p1->from = addr;
gmove(&n1, res);
sudoclean();
2008-12-13 17:41:47 -07:00
regfree(&n1);
goto ret;
}
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
} else {
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
}
gins(a, &n2, &n1);
gmove(&n1, res);
regfree(&n1);
regfree(&n2);
goto ret;
uop: // unary
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
gins(a, N, &n1);
gmove(&n1, res);
regfree(&n1);
goto ret;
ret:
2008-06-28 18:27:39 -06:00
;
}
void
agen(Node *n, Node *res)
{
Node *nl, *nr;
Node n1, n2, n3, tmp;
2008-08-27 18:28:30 -06:00
Prog *p1;
uint32 w;
uint64 v;
Type *t;
if(debug['g']) {
dump("\nagen-res", res);
dump("agen-r", n);
}
if(n == N || n->type == T)
return;
if(!isptr[res->type->etype])
fatal("agen: not tptr: %T", res->type);
if(n->addable) {
regalloc(&n1, types[tptr], res);
gins(ALEAQ, n, &n1);
gmove(&n1, res);
regfree(&n1);
2008-06-08 18:21:46 -06:00
goto ret;
}
2008-06-08 18:21:46 -06:00
nl = n->left;
nr = n->right;
switch(n->op) {
default:
fatal("agen: unknown op %N", n);
break;
case OCALLMETH:
cgen_callmeth(n, 0);
cgen_aret(n, res);
break;
case OCALLINTER:
cgen_callinter(n, res, 0);
cgen_aret(n, res);
break;
case OCALL:
cgen_call(n, 0);
cgen_aret(n, res);
break;
case OINDEXPTR:
w = n->type->width;
if(nr->addable)
goto iprad;
if(nl->addable) {
if(whatis(nr) != Wlitint) {
regalloc(&n1, nr->type, N);
cgen(nr, &n1);
}
regalloc(&n3, types[tptr], res);
cgen(nl, &n3);
goto index;
}
cgen(nr, res);
tempname(&tmp, nr->type);
gmove(res, &tmp);
iprad:
regalloc(&n3, types[tptr], res);
cgen(nl, &n3);
if(whatis(nr) != Wlitint) {
regalloc(&n1, nr->type, N);
cgen(nr, &n1);
}
goto index;
case OINDEX:
w = n->type->width;
if(nr->addable)
goto irad;
if(nl->addable) {
if(whatis(nr) != Wlitint) {
regalloc(&n1, nr->type, N);
cgen(nr, &n1);
}
regalloc(&n3, types[tptr], res);
agen(nl, &n3);
goto index;
}
cgen(nr, res);
tempname(&tmp, nr->type);
gmove(res, &tmp);
irad:
regalloc(&n3, types[tptr], res);
agen(nl, &n3);
if(whatis(nr) != Wlitint) {
regalloc(&n1, nr->type, N);
cgen(nr, &n1);
}
goto index;
index:
// &a is in &n3 (allocated in res)
// i is in &n1 (if not constant)
// w is width
2008-08-27 18:28:30 -06:00
2008-09-26 18:41:23 -06:00
if(w == 0)
fatal("index is zero width");
// constant index
if(whatis(nr) == Wlitint) {
v = mpgetfix(nr->val.u.xval);
if(isptrdarray(nl->type)) {
if(!debug['B']) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = offsetof(Array, nel);
nodconst(&n2, types[TUINT64], v);
gins(optoas(OCMP, types[TUINT32]), &n1, &n2);
p1 = gbranch(optoas(OGT, types[TUINT32]), T);
gins(ACALL, N, throwindex);
patch(p1, pc);
}
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = offsetof(Array, array);
gmove(&n1, &n3);
} else
if(!debug['B']) {
if(v < 0)
yyerror("out of bounds on array");
else
if(isptrarray(nl->type)) {
if(v >= nl->type->type->bound)
yyerror("out of bounds on array");
} else
if(v >= nl->type->bound)
yyerror("out of bounds on array");
}
nodconst(&n2, types[tptr], v*w);
gins(optoas(OADD, types[tptr]), &n2, &n3);
gmove(&n3, res);
regfree(&n3);
break;
}
// type of the index
2008-08-27 18:28:30 -06:00
t = types[TUINT64];
if(issigned[n1.type->etype])
2008-08-27 18:28:30 -06:00
t = types[TINT64];
regalloc(&n2, t, &n1); // i
gmove(&n1, &n2);
regfree(&n1);
2008-08-27 18:28:30 -06:00
if(!debug['B']) {
// check bounds
if(isptrdarray(nl->type)) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = offsetof(Array, nel);
} else {
nodconst(&n1, types[TUINT64], nl->type->bound);
if(isptrarray(nl->type))
nodconst(&n1, types[TUINT64], nl->type->type->bound);
}
gins(optoas(OCMP, types[TUINT32]), &n2, &n1);
p1 = gbranch(optoas(OLT, types[TUINT32]), T);
gins(ACALL, N, throwindex);
patch(p1, pc);
}
if(isptrdarray(nl->type)) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = offsetof(Array, array);
gmove(&n1, &n3);
}
if(w == 1 || w == 2 || w == 4 || w == 8) {
p1 = gins(ALEAQ, &n2, &n3);
p1->from.scale = w;
p1->from.index = p1->from.type;
p1->from.type = p1->to.type + D_INDIR;
} else {
nodconst(&n1, t, w);
gins(optoas(OMUL, t), &n1, &n2);
gins(optoas(OADD, types[tptr]), &n2, &n3);
gmove(&n3, res);
}
gmove(&n3, res);
regfree(&n2);
regfree(&n3);
break;
case OIND:
cgen(nl, res);
break;
case ODOT:
t = nl->type;
agen(nl, res);
if(n->xoffset != 0) {
nodconst(&n1, types[TINT64], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
}
break;
case ODOTPTR:
t = nl->type;
if(!isptr[t->etype])
fatal("agen: not ptr %N", n);
cgen(nl, res);
if(n->xoffset != 0) {
nodconst(&n1, types[TINT64], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
}
break;
}
2008-06-08 18:21:46 -06:00
ret:
2008-06-28 18:27:39 -06:00
;
}
vlong
fieldoffset(Type *t, Node *n)
{
if(t->etype != TSTRUCT)
fatal("fieldoffset: not struct %lT", t);
if(n->op != ONAME)
fatal("fieldoffset: not field name %N", n);
return 0;
}
void
igen(Node *n, Node *a, Node *res)
{
regalloc(a, types[tptr], res);
agen(n, a);
a->op = OINDREG;
a->type = n->type;
}
void
bgen(Node *n, int true, Prog *to)
{
2008-06-08 17:11:14 -06:00
int et, a;
Node *nl, *nr, *r;
2008-06-08 18:21:46 -06:00
Node n1, n2, tmp;
Prog *p1, *p2;
if(debug['g']) {
dump("\nbgen", n);
}
2008-06-28 18:27:39 -06:00
if(n == N)
n = booltrue;
2008-06-08 18:21:46 -06:00
nl = n->left;
nr = n->right;
if(n->type == T) {
convlit(n, types[TBOOL]);
if(n->type == T)
goto ret;
}
et = n->type->etype;
if(et != TBOOL) {
yyerror("cgen: bad type %T for %O", n->type, n->op);
patch(gins(AEND, N, N), to);
goto ret;
}
nl = N;
nr = N;
switch(n->op) {
default:
regalloc(&n1, n->type, N);
cgen(n, &n1);
nodconst(&n2, n->type, 0);
gins(optoas(OCMP, n->type), &n1, &n2);
a = AJNE;
if(!true)
a = AJEQ;
patch(gbranch(a, n->type), to);
regfree(&n1);
goto ret;
case OLITERAL:
// need to ask if it is bool?
if(!true == !n->val.u.bval)
patch(gbranch(AJMP, T), to);
goto ret;
case ONAME:
nodconst(&n1, n->type, 0);
gins(optoas(OCMP, n->type), n, &n1);
a = AJNE;
if(!true)
a = AJEQ;
patch(gbranch(a, n->type), to);
goto ret;
case OANDAND:
if(!true)
goto caseor;
caseand:
p1 = gbranch(AJMP, T);
p2 = gbranch(AJMP, T);
patch(p1, pc);
bgen(n->left, !true, p2);
bgen(n->right, !true, p2);
p1 = gbranch(AJMP, T);
patch(p1, to);
patch(p2, pc);
goto ret;
case OOROR:
if(!true)
goto caseand;
caseor:
bgen(n->left, true, to);
bgen(n->right, true, to);
goto ret;
case OEQ:
case ONE:
case OLT:
case OGT:
case OLE:
case OGE:
nr = n->right;
if(nr == N || nr->type == T)
goto ret;
case ONOT: // unary
nl = n->left;
if(nl == N || nl->type == T)
goto ret;
}
switch(n->op) {
case ONOT:
bgen(nl, !true, to);
goto ret;
case OEQ:
case ONE:
case OLT:
case OGT:
case OLE:
case OGE:
a = n->op;
if(!true)
a = brcom(a);
// make simplest on right
if(nl->op == OLITERAL || nl->ullman < nr->ullman) {
a = brrev(a);
r = nl;
nl = nr;
nr = r;
}
2008-06-08 18:21:46 -06:00
a = optoas(a, nr->type);
2008-06-08 18:21:46 -06:00
if(nr->ullman >= UINF) {
regalloc(&n1, nr->type, N);
cgen(nr, &n1);
tempname(&tmp, nr->type);
gmove(&n1, &tmp);
regfree(&n1);
2008-06-08 18:21:46 -06:00
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
regalloc(&n2, nr->type, &n2);
cgen(&tmp, &n2);
gins(optoas(OCMP, nr->type), &n1, &n2);
patch(gbranch(a, nr->type), to);
regfree(&n1);
regfree(&n2);
break;
}
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
if(smallintconst(nr)) {
gins(optoas(OCMP, nr->type), &n1, nr);
patch(gbranch(a, nr->type), to);
regfree(&n1);
break;
}
2008-06-08 17:11:14 -06:00
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
gins(optoas(OCMP, nr->type), &n1, &n2);
patch(gbranch(a, nr->type), to);
2008-06-08 17:11:14 -06:00
regfree(&n1);
2008-06-08 17:11:14 -06:00
regfree(&n2);
break;
}
goto ret;
ret:
2008-06-28 18:27:39 -06:00
;
}
int32
stkof(Node *n)
{
2008-09-22 17:58:30 -06:00
Type *t;
Iter flist;
switch(n->op) {
case OINDREG:
return n->xoffset;
2008-09-22 17:58:30 -06:00
case OCALLMETH:
case OCALLINTER:
case OCALL:
t = n->left->type;
if(isptr[t->etype])
t = t->type;
t = structfirst(&flist, getoutarg(t));
if(t != T)
return t->width;
break;
}
2008-09-22 17:58:30 -06:00
// botch - probably failing to recognize address
// arithmetic on the above. eg INDEX and DOT
2008-09-22 17:58:30 -06:00
return -1000;
}
void
2008-09-22 17:58:30 -06:00
sgen(Node *n, Node *ns, int32 w)
{
Node nodl, nodr;
int32 c, q, odst, osrc;
if(debug['g']) {
dump("\nsgen-res", ns);
dump("sgen-r", n);
}
if(w == 0)
return;
if(n->ullman >= UINF && ns->ullman >= UINF) {
fatal("sgen UINF");
}
if(w < 0)
fatal("sgen copy %d", w);
// offset on the stack
osrc = stkof(n);
2008-09-22 17:58:30 -06:00
odst = stkof(ns);
//print("\nnsrc=%N\n", n);
//print("ndst=%N\n", ns);
//print("osrc=%d odst=%d w=%d\n", osrc, odst, w);
nodreg(&nodl, types[tptr], D_DI);
nodreg(&nodr, types[tptr], D_SI);
if(n->ullman >= ns->ullman) {
agen(n, &nodr);
agen(ns, &nodl);
} else {
agen(ns, &nodl);
agen(n, &nodr);
}
c = w % 8; // bytes
q = w / 8; // quads
// if we are copying forward on the stack and
// the src and dst overlap, then reverse direction
2008-09-22 17:58:30 -06:00
if(osrc < odst && odst < osrc+w) {
// reverse direction
gins(ASTD, N, N); // set direction flag
if(c > 0) {
gconreg(AADDQ, w-1, D_SI);
gconreg(AADDQ, w-1, D_DI);
gconreg(AMOVQ, c, D_CX);
gins(AREP, N, N); // repeat
gins(AMOVSB, N, N); // MOVB *(SI)-,*(DI)-
}
if(q > 0) {
if(c > 0) {
gconreg(AADDQ, -7, D_SI);
gconreg(AADDQ, -7, D_DI);
} else {
gconreg(AADDQ, w-8, D_SI);
gconreg(AADDQ, w-8, D_DI);
}
gconreg(AMOVQ, q, D_CX);
gins(AREP, N, N); // repeat
gins(AMOVSQ, N, N); // MOVQ *(SI)-,*(DI)-
}
// for future optimization
// we leave with the flag clear
gins(ACLD, N, N);
} else {
// normal direction
gins(ACLD, N, N); // clear direction flag
if(q > 0) {
gconreg(AMOVQ, q, D_CX);
gins(AREP, N, N); // repeat
gins(AMOVSQ, N, N); // MOVQ *(SI)+,*(DI)+
}
2008-06-26 18:54:44 -06:00
if(c > 0) {
gconreg(AMOVQ, c, D_CX);
gins(AREP, N, N); // repeat
gins(AMOVSB, N, N); // MOVB *(SI)+,*(DI)+
}
}
}