Commit 4928b207 authored by Shenghou Ma's avatar Shenghou Ma

[dev.power64] cmd/9g: first check point

LGTM=rsc
R=rsc, iant
CC=golang-codereviews
https://golang.org/cl/123170043
parent 552d8b79
// 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 <u.h>
#include <libc.h>
#include "gg.h"
/*
* generate:
* res = n;
* simplifies and calls gmove.
*/
void
cgen(Node *n, Node *res)
{
Node *nl, *nr, *r;
Node n1, n2;
int a, f;
Prog *p1, *p2, *p3;
Addr addr;
//print("cgen %N(%d) -> %N(%d)\n", n, n->addable, res, res->addable);
if(debug['g']) {
dump("\ncgen-n", n);
dump("cgen-res", res);
}
if(n == N || n->type == T)
goto ret;
if(res == N || res->type == T)
fatal("cgen: res nil");
while(n->op == OCONVNOP)
n = n->left;
switch(n->op) {
case OSLICE:
case OSLICEARR:
case OSLICESTR:
case OSLICE3:
case OSLICE3ARR:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_slice(n, &n1);
cgen(&n1, res);
} else
cgen_slice(n, res);
goto ret;
case OEFACE:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_eface(n, &n1);
cgen(&n1, res);
} else
cgen_eface(n, res);
goto ret;
}
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)) {
if(n->type->width < 0)
fatal("forgot to compute width for %T", n->type);
sgen(n, res, n->type->width);
goto ret;
}
if(!res->addable) {
if(n->ullman > res->ullman) {
regalloc(&n1, n->type, res);
cgen(n, &n1);
if(n1.ullman > res->ullman) {
dump("n1", &n1);
dump("res", res);
fatal("loop in cgen");
}
cgen(&n1, res);
regfree(&n1);
goto ret;
}
if(res->ullman >= UINF)
goto gen;
if(complexop(n, res)) {
complexgen(n, res);
goto ret;
}
f = 1; // gen thru register
switch(n->op) {
case OLITERAL:
if(smallintconst(n))
f = 0;
break;
case OREGISTER:
f = 0;
break;
}
if(!iscomplex[n->type->etype]) {
a = optoas(OAS, res->type);
if(sudoaddable(a, res, &addr)) {
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;
if(debug['g'])
print("%P [ignore previous line]\n", p1);
sudoclean();
goto ret;
}
}
gen:
igen(res, &n1, N);
cgen(n, &n1);
regfree(&n1);
goto ret;
}
// update addressability for string, slice
// can't do in walk because n->left->addable
// changes if n->left is an escaping local variable.
switch(n->op) {
case OSPTR:
case OLEN:
if(isslice(n->left->type) || istype(n->left->type, TSTRING))
n->addable = n->left->addable;
break;
case OCAP:
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
case OITAB:
n->addable = n->left->addable;
break;
}
if(complexop(n, res)) {
complexgen(n, res);
goto ret;
}
// if both are addressable, move
if(n->addable) {
if(n->op == OREGISTER || res->op == OREGISTER) {
gmove(n, res);
} else {
regalloc(&n1, n->type, N);
gmove(n, &n1);
cgen(&n1, res);
regfree(&n1);
}
goto ret;
}
nl = n->left;
nr = n->right;
if(nl != N && nl->ullman >= UINF)
if(nr != N && nr->ullman >= UINF) {
tempname(&n1, nl->type);
cgen(nl, &n1);
n2 = *n;
n2.left = &n1;
cgen(&n2, res);
goto ret;
}
if(!iscomplex[n->type->etype]) {
a = optoas(OAS, n->type);
if(sudoaddable(a, n, &addr)) {
if(res->op == OREGISTER) {
p1 = gins(a, N, res);
p1->from = addr;
} else {
regalloc(&n2, n->type, N);
p1 = gins(a, N, &n2);
p1->from = addr;
gins(a, &n2, res);
regfree(&n2);
}
sudoclean();
goto ret;
}
}
// TODO(minux): we shouldn't reverse FP comparisons, but then we need to synthesize
// OGE, OLE, and ONE ourselves.
// if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) goto flt;
switch(n->op) {
default:
dump("cgen", n);
fatal("cgen: unknown op %+hN", 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(ABR, T, 0);
p2 = pc;
gmove(nodbool(1), res);
p3 = gbranch(ABR, T, 0);
patch(p1, pc);
bgen(n, 1, 0, p2);
gmove(nodbool(0), 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:
if(isfloat[nl->type->etype]) {
nr = nodintconst(-1);
convlit(&nr, n->type);
a = optoas(OMUL, nl->type);
goto sbop;
}
a = optoas(n->op, nl->type);
goto uop;
// symmetric binary
case OAND:
case OOR:
case OXOR:
case OADD:
case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
goto sbop;
// asymmetric binary
case OSUB:
a = optoas(n->op, nl->type);
goto abop;
case OHMUL:
cgen_hmul(nl, nr, res);
break;
case OCONV:
if(n->type->width > nl->type->width) {
// If loading from memory, do conversion during load,
// so as to avoid use of 8-bit register in, say, int(*byteptr).
switch(nl->op) {
case ODOT:
case ODOTPTR:
case OINDEX:
case OIND:
case ONAME:
igen(nl, &n1, res);
regalloc(&n2, n->type, res);
gmove(&n1, &n2);
gmove(&n2, res);
regfree(&n2);
regfree(&n1);
goto ret;
}
}
regalloc(&n1, nl->type, res);
regalloc(&n2, n->type, &n1);
cgen(nl, &n1);
// if we do the conversion n1 -> n2 here
// reusing the register, then gmove won't
// have to allocate its own register.
gmove(&n1, &n2);
gmove(&n2, res);
regfree(&n2);
regfree(&n1);
break;
case ODOT:
case ODOTPTR:
case OINDEX:
case OIND:
case ONAME: // PHEAP or PPARAMREF var
igen(n, &n1, res);
gmove(&n1, res);
regfree(&n1);
break;
case OITAB:
// interface table is first word of interface value
igen(nl, &n1, res);
n1.type = n->type;
gmove(&n1, res);
regfree(&n1);
break;
case OSPTR:
// pointer is the first word of string or slice.
if(isconst(nl, CTSTR)) {
regalloc(&n1, types[tptr], res);
p1 = gins(AMOVD, N, &n1);
datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
gmove(&n1, res);
regfree(&n1);
break;
}
igen(nl, &n1, res);
n1.type = n->type;
gmove(&n1, res);
regfree(&n1);
break;
case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map and chan have len in the first int-sized word.
// a zero pointer means zero length
regalloc(&n1, types[tptr], res);
cgen(nl, &n1);
nodconst(&n2, types[tptr], 0);
gins(optoas(OCMP, types[tptr]), &n1, &n2);
p1 = gbranch(optoas(OEQ, types[tptr]), T, 0);
n2 = n1;
n2.op = OINDREG;
n2.type = types[simtype[TINT]];
gmove(&n2, &n1);
patch(p1, pc);
gmove(&n1, res);
regfree(&n1);
break;
}
if(istype(nl->type, TSTRING) || isslice(nl->type)) {
// both slice and string have len one pointer into the struct.
// a zero pointer means zero length
igen(nl, &n1, res);
n1.type = types[simtype[TUINT]];
n1.xoffset += Array_nel;
gmove(&n1, res);
regfree(&n1);
break;
}
fatal("cgen: OLEN: unknown type %lT", nl->type);
break;
case OCAP:
if(istype(nl->type, TCHAN)) {
// chan has cap in the second int-sized word.
// a zero pointer means zero length
regalloc(&n1, types[tptr], res);
cgen(nl, &n1);
nodconst(&n2, types[tptr], 0);
gins(optoas(OCMP, types[tptr]), &n1, &n2);
p1 = gbranch(optoas(OEQ, types[tptr]), T, 0);
n2 = n1;
n2.op = OINDREG;
n2.xoffset = widthint;
n2.type = types[simtype[TINT]];
gmove(&n2, &n1);
patch(p1, pc);
gmove(&n1, res);
regfree(&n1);
break;
}
if(isslice(nl->type)) {
igen(nl, &n1, res);
n1.type = types[simtype[TUINT]];
n1.xoffset += Array_cap;
gmove(&n1, res);
regfree(&n1);
break;
}
fatal("cgen: OCAP: unknown type %lT", nl->type);
break;
case OADDR:
if(n->bounded) // let race detector avoid nil checks
disable_checknil++;
agen(nl, res);
if(n->bounded)
disable_checknil--;
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 OCALLFUNC:
cgen_call(n, 0);
cgen_callret(n, res);
break;
case OMOD:
case ODIV:
if(isfloat[n->type->etype]) {
a = optoas(n->op, nl->type);
goto abop;
}
if(nl->ullman >= nr->ullman) {
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
cgen_div(n->op, &n1, nr, res);
regfree(&n1);
} else {
if(!smallintconst(nr)) {
regalloc(&n2, nr->type, res);
cgen(nr, &n2);
} else {
n2 = *nr;
}
cgen_div(n->op, nl, &n2, res);
if(n2.op != OLITERAL)
regfree(&n2);
}
break;
case OLSH:
case ORSH:
case OLROT:
cgen_shift(n->op, n->bounded, nl, nr, res);
break;
}
goto ret;
sbop: // symmetric binary
/*
* put simplest on right - we'll generate into left
* and then adjust it using the computation of right.
* constants and variables have the same ullman
* count, so look for constants specially.
*
* an integer constant we can use as an immediate
* is simpler than a variable - we can use the immediate
* in the adjustment instruction directly - so it goes
* on the right.
*
* other constants, like big integers or floating point
* constants, require a mov into a register, so those
* might as well go on the left, so we can reuse that
* register for the computation.
*/
if(nl->ullman < nr->ullman ||
(nl->ullman == nr->ullman &&
(smallintconst(nl) || (nr->op == OLITERAL && !smallintconst(nr))))) {
r = nl;
nl = nr;
nr = r;
}
abop: // asymmetric binary
if(nl->ullman >= nr->ullman) {
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
/*
* This generates smaller code - it avoids a MOV - but it's
* easily 10% slower due to not being able to
* optimize/manipulate the move.
* To see, run: go test -bench . crypto/md5
* with and without.
*
if(sudoaddable(a, nr, &addr)) {
p1 = gins(a, N, &n1);
p1->from = addr;
gmove(&n1, res);
sudoclean();
regfree(&n1);
goto ret;
}
*
*/
// TODO(minux): enable using constants directly in certain instructions.
//if(smallintconst(nr))
// n2 = *nr;
//else {
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
//}
} else {
//if(smallintconst(nr))
// n2 = *nr;
//else {
regalloc(&n2, nr->type, res);
cgen(nr, &n2);
//}
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
}
gins(a, &n2, &n1);
// Normalize result for types smaller than word.
if(n->type->width < widthreg) {
switch(n->op) {
case OADD:
case OSUB:
case OMUL:
case OLSH:
gins(optoas(OAS, n->type), &n1, &n1);
break;
}
}
gmove(&n1, res);
regfree(&n1);
if(n2.op != OLITERAL)
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:
;
}
/*
* allocate a register (reusing res if possible) and generate
* a = n
* The caller must call regfree(a).
*/
void
cgenr(Node *n, Node *a, Node *res)
{
Node n1;
if(debug['g'])
dump("cgenr-n", n);
if(isfat(n->type))
fatal("cgenr on fat node");
if(n->addable) {
regalloc(a, n->type, res);
gmove(n, a);
return;
}
switch(n->op) {
case ONAME:
case ODOT:
case ODOTPTR:
case OINDEX:
case OCALLFUNC:
case OCALLMETH:
case OCALLINTER:
igen(n, &n1, res);
regalloc(a, types[tptr], &n1);
gmove(&n1, a);
regfree(&n1);
break;
default:
regalloc(a, n->type, res);
cgen(n, a);
break;
}
}
/*
* allocate a register (reusing res if possible) and generate
* a = &n
* The caller must call regfree(a).
* The generated code checks that the result is not nil.
*/
void
agenr(Node *n, Node *a, Node *res)
{
Node *nl, *nr;
Node n1, n2, n3, n4, tmp;
Prog *p1, *p2;
uint32 w;
uint64 v;
if(debug['g'])
dump("agenr-n", n);
nl = n->left;
nr = n->right;
switch(n->op) {
case ODOT:
case ODOTPTR:
case OCALLFUNC:
case OCALLMETH:
case OCALLINTER:
igen(n, &n1, res);
regalloc(a, types[tptr], &n1);
agen(&n1, a);
regfree(&n1);
break;
case OIND:
cgenr(n->left, a, res);
cgen_checknil(a);
break;
case OINDEX:
p2 = nil; // to be patched to panicindex.
w = n->type->width;
//bounded = debug['B'] || n->bounded;
if(nr->addable) {
if(!isconst(nr, CTINT))
tempname(&tmp, types[TINT64]);
if(!isconst(nl, CTSTR))
agenr(nl, &n3, res);
if(!isconst(nr, CTINT)) {
cgen(nr, &tmp);
regalloc(&n1, tmp.type, N);
gmove(&tmp, &n1);
}
} else if(nl->addable) {
if(!isconst(nr, CTINT)) {
tempname(&tmp, types[TINT64]);
cgen(nr, &tmp);
regalloc(&n1, tmp.type, N);
gmove(&tmp, &n1);
}
if(!isconst(nl, CTSTR)) {
agenr(nl, &n3, res);
}
} else {
tempname(&tmp, types[TINT64]);
cgen(nr, &tmp);
nr = &tmp;
if(!isconst(nl, CTSTR))
agenr(nl, &n3, res);
regalloc(&n1, tmp.type, N);
gins(optoas(OAS, tmp.type), &tmp, &n1);
}
// &a is in &n3 (allocated in res)
// i is in &n1 (if not constant)
// w is width
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
fatal("constant string constant index");
v = mpgetfix(nr->val.u.xval);
if(isslice(nl->type) || nl->type->etype == TSTRING) {
if(!debug['B'] && !n->bounded) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = Array_nel;
regalloc(&n4, n1.type, N);
gmove(&n1, &n4);
nodconst(&n2, types[TUINT64], v);
gins(optoas(OCMP, types[TUINT64]), &n4, &n2);
regfree(&n4);
p1 = gbranch(optoas(OGT, types[TUINT64]), T, +1);
ginscall(panicindex, 0);
patch(p1, pc);
}
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = Array_array;
gmove(&n1, &n3);
}
if (v*w != 0) {
nodconst(&n2, types[tptr], v*w);
gins(optoas(OADD, types[tptr]), &n2, &n3);
}
*a = n3;
break;
}
regalloc(&n2, types[TINT64], &n1); // i
gmove(&n1, &n2);
regfree(&n1);
if(!debug['B'] && !n->bounded) {
// check bounds
if(isconst(nl, CTSTR)) {
nodconst(&n4, types[TUINT64], nl->val.u.sval->len);
} else if(isslice(nl->type) || nl->type->etype == TSTRING) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = Array_nel;
regalloc(&n4, types[TUINT64], N);
gmove(&n1, &n4);
} else {
if(nl->type->bound < (1<<15)-1)
nodconst(&n4, types[TUINT64], nl->type->bound);
else {
regalloc(&n4, types[TUINT64], N);
p1 = gins(AMOVD, N, &n4);
p1->from.type = D_CONST;
p1->from.offset = nl->type->bound;
}
}
gins(optoas(OCMP, types[TUINT64]), &n2, &n4);
if(n4.op == OREGISTER)
regfree(&n4);
p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1);
if(p2)
patch(p2, pc);
ginscall(panicindex, 0);
patch(p1, pc);
}
if(isconst(nl, CTSTR)) {
regalloc(&n3, types[tptr], res);
p1 = gins(AMOVD, N, &n3);
datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
p1->from.type = D_CONST;
} else if(isslice(nl->type) || nl->type->etype == TSTRING) {
n1 = n3;
n1.op = OINDREG;
n1.type = types[tptr];
n1.xoffset = Array_array;
gmove(&n1, &n3);
}
if(w == 0) {
// nothing to do
} else if(w == 1) {
/* w already scaled */
gins(optoas(OADD, types[tptr]), &n2, &n3);
} /* else if(w == 2 || w == 4 || w == 8) {
// TODO(minux): scale using shift
} */ else {
regalloc(&n4, types[TUINT64], N);
nodconst(&n1, types[TUINT64], w);
gmove(&n1, &n4);
gins(optoas(OMUL, types[TUINT64]), &n4, &n2);
gins(optoas(OADD, types[tptr]), &n2, &n3);
regfree(&n4);
}
*a = n3;
regfree(&n2);
break;
default:
regalloc(a, types[tptr], res);
agen(n, a);
break;
}
}
/*
* generate:
* res = &n;
* The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
{
Node *nl, *nr;
Node n1, n2, n3;
if(debug['g']) {
dump("\nagen-res", res);
dump("agen-r", n);
}
if(n == N || n->type == T)
return;
while(n->op == OCONVNOP)
n = n->left;
if(isconst(n, CTNIL) && n->type->width > widthptr) {
// Use of a nil interface or nil slice.
// Create a temporary we can take the address of and read.
// The generated code is just going to panic, so it need not
// be terribly efficient. See issue 3670.
tempname(&n1, n->type);
gvardef(&n1);
clearfat(&n1);
regalloc(&n2, types[tptr], res);
memset(&n3, 0, sizeof n3);
n3.op = OADDR;
n3.left = &n1;
gins(AMOVD, &n3, &n2);
gmove(&n2, res);
regfree(&n2);
goto ret;
}
if(n->addable) {
memset(&n1, 0, sizeof n1);
n1.op = OADDR;
n1.left = n;
regalloc(&n2, types[tptr], res);
gins(AMOVD, &n1, &n2);
gmove(&n2, res);
regfree(&n2);
goto ret;
}
nl = n->left;
nr = n->right;
USED(nr);
switch(n->op) {
default:
fatal("agen: unknown op %+hN", n);
break;
case OCALLMETH:
// TODO(minux): 5g has this: Release res so that it is available for cgen_call.
// Pick it up again after the call for OCALLMETH and OCALLFUNC.
cgen_callmeth(n, 0);
cgen_aret(n, res);
break;
case OCALLINTER:
cgen_callinter(n, res, 0);
cgen_aret(n, res);
break;
case OCALLFUNC:
cgen_call(n, 0);
cgen_aret(n, res);
break;
case OSLICE:
case OSLICEARR:
case OSLICESTR:
case OSLICE3:
case OSLICE3ARR:
tempname(&n1, n->type);
cgen_slice(n, &n1);
agen(&n1, res);
break;
case OEFACE:
tempname(&n1, n->type);
cgen_eface(n, &n1);
agen(&n1, res);
break;
case OINDEX:
agenr(n, &n1, res);
gmove(&n1, res);
regfree(&n1);
break;
case ONAME:
// should only get here with names in this func.
if(n->funcdepth > 0 && n->funcdepth != funcdepth) {
dump("bad agen", n);
fatal("agen: bad ONAME funcdepth %d != %d",
n->funcdepth, funcdepth);
}
// should only get here for heap vars or paramref
if(!(n->class & PHEAP) && n->class != PPARAMREF) {
dump("bad agen", n);
fatal("agen: bad ONAME class %#x", n->class);
}
cgen(n->heapaddr, res);
if(n->xoffset != 0) {
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
}
break;
case OIND:
cgen(nl, res);
cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
if(n->xoffset != 0) {
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
}
break;
case ODOTPTR:
cgen(nl, res);
cgen_checknil(res);
if(n->xoffset != 0) {
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
}
break;
}
ret:
;
}
/*
* generate:
* newreg = &n;
* res = newreg
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
* The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
{
Type *fp;
Iter flist;
Node n1;
if(debug['g']) {
dump("\nigen-n", n);
}
switch(n->op) {
case ONAME:
if((n->class&PHEAP) || n->class == PPARAMREF)
break;
*a = *n;
return;
case OINDREG:
// Increase the refcount of the register so that igen's caller
// has to call regfree.
if(n->val.u.reg != D_R0+REGSP)
reg[n->val.u.reg]++;
*a = *n;
return;
case ODOT:
igen(n->left, a, res);
a->xoffset += n->xoffset;
a->type = n->type;
fixlargeoffset(a);
return;
case ODOTPTR:
cgenr(n->left, a, res);
cgen_checknil(a);
a->op = OINDREG;
a->xoffset += n->xoffset;
a->type = n->type;
fixlargeoffset(a);
return;
case OCALLFUNC:
case OCALLMETH:
case OCALLINTER:
switch(n->op) {
case OCALLFUNC:
cgen_call(n, 0);
break;
case OCALLMETH:
cgen_callmeth(n, 0);
break;
case OCALLINTER:
cgen_callinter(n, N, 0);
break;
}
fp = structfirst(&flist, getoutarg(n->left->type));
memset(a, 0, sizeof *a);
a->op = OINDREG;
a->val.u.reg = D_R0+REGSP;
a->addable = 1;
a->xoffset = fp->width + widthptr; // +widthptr: saved lr at 0(SP)
a->type = n->type;
return;
case OINDEX:
// Index of fixed-size array by constant can
// put the offset in the addressing.
// Could do the same for slice except that we need
// to use the real index for the bounds checking.
if(isfixedarray(n->left->type) ||
(isptr[n->left->type->etype] && isfixedarray(n->left->left->type)))
if(isconst(n->right, CTINT)) {
// Compute &a.
if(!isptr[n->left->type->etype])
igen(n->left, a, res);
else {
igen(n->left, &n1, res);
cgen_checknil(&n1);
regalloc(a, types[tptr], res);
gmove(&n1, a);
regfree(&n1);
a->op = OINDREG;
}
// Compute &a[i] as &a + i*width.
a->type = n->type;
a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width;
fixlargeoffset(a);
return;
}
break;
}
agenr(n, a, res);
a->op = OINDREG;
a->type = n->type;
}
/*
* generate:
* if(n == true) goto to;
*/
void
bgen(Node *n, int true, int likely, Prog *to)
{
int et, a;
Node *nl, *nr, *l, *r;
Node n1, n2, tmp;
NodeList *ll;
Prog *p1, *p2;
if(debug['g']) {
dump("\nbgen", n);
}
if(n == N)
n = nodbool(1);
if(n->ninit != nil)
genlist(n->ninit);
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;
}
nr = N;
while(n->op == OCONVNOP) {
n = n->left;
if(n->ninit != nil)
genlist(n->ninit);
}
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 = ABNE;
if(!true)
a = ABEQ;
patch(gbranch(a, n->type, likely), to);
regfree(&n1);
goto ret;
case OLITERAL:
// need to ask if it is bool?
if(!true == !n->val.u.bval)
patch(gbranch(ABR, T, likely), to);
goto ret;
case OANDAND:
if(!true)
goto caseor;
caseand:
p1 = gbranch(ABR, T, 0);
p2 = gbranch(ABR, T, 0);
patch(p1, pc);
bgen(n->left, !true, -likely, p2);
bgen(n->right, !true, -likely, p2);
p1 = gbranch(ABR, T, 0);
patch(p1, to);
patch(p2, pc);
goto ret;
case OOROR:
if(!true)
goto caseand;
caseor:
bgen(n->left, true, likely, to);
bgen(n->right, true, likely, 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;
break;
}
switch(n->op) {
case ONOT:
bgen(nl, !true, likely, to);
goto ret;
case OEQ:
case ONE:
case OLT:
case OGT:
case OLE:
case OGE:
a = n->op;
if(!true) {
if(isfloat[nr->type->etype]) {
// brcom is not valid on floats when NaN is involved.
p1 = gbranch(ABR, T, 0);
p2 = gbranch(ABR, T, 0);
patch(p1, pc);
ll = n->ninit; // avoid re-genning ninit
n->ninit = nil;
bgen(n, 1, -likely, p2);
n->ninit = ll;
patch(gbranch(ABR, T, 0), to);
patch(p2, pc);
goto ret;
}
a = brcom(a);
true = !true;
}
// make simplest on right
if(nl->op == OLITERAL || (nl->ullman < nr->ullman && nl->ullman < UINF)) {
a = brrev(a);
r = nl;
nl = nr;
nr = r;
}
if(isslice(nl->type)) {
// front end should only leave cmp to literal nil
if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
yyerror("illegal slice comparison");
break;
}
a = optoas(a, types[tptr]);
igen(nl, &n1, N);
n1.xoffset += Array_array;
n1.type = types[tptr];
nodconst(&tmp, types[tptr], 0);
regalloc(&n2, types[tptr], &n1);
gmove(&n1, &n2);
gins(optoas(OCMP, types[tptr]), &n2, &tmp);
regfree(&n2);
patch(gbranch(a, types[tptr], likely), to);
regfree(&n1);
break;
}
if(isinter(nl->type)) {
// front end should only leave cmp to literal nil
if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
yyerror("illegal interface comparison");
break;
}
a = optoas(a, types[tptr]);
igen(nl, &n1, N);
n1.type = types[tptr];
nodconst(&tmp, types[tptr], 0);
regalloc(&n2, types[tptr], &n1);
gmove(&n1, &n2);
gins(optoas(OCMP, types[tptr]), &n2, &tmp);
regfree(&n2);
patch(gbranch(a, types[tptr], likely), to);
regfree(&n1);
break;
}
if(iscomplex[nl->type->etype]) {
complexbool(a, nl, nr, true, likely, to);
break;
}
if(nr->ullman >= UINF) {
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
tempname(&tmp, nl->type);
gmove(&n1, &tmp);
regfree(&n1);
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
regalloc(&n1, nl->type, N);
cgen(&tmp, &n1);
goto cmp;
}
regalloc(&n1, nl->type, N);
cgen(nl, &n1);
// TODO(minux): cmpi does accept 16-bit signed immediate as p->to.
// and cmpli accepts 16-bit unsigned immediate.
//if(smallintconst(nr)) {
// gins(optoas(OCMP, nr->type), &n1, nr);
// patch(gbranch(optoas(a, nr->type), nr->type, likely), to);
// regfree(&n1);
// break;
//}
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
cmp:
l = &n1;
r = &n2;
gins(optoas(OCMP, nr->type), l, r);
// TODO(minux): determine the reason for failed test/floatcmp.go.
// we might need to specially handle floating point comparisons.
/*if(isfloat[nr->type->etype] && (n->op == OEQ || n->op == ONE)) {
} else*/
patch(gbranch(optoas(a, nr->type), nr->type, likely), to);
regfree(&n1);
regfree(&n2);
break;
}
goto ret;
ret:
;
}
/*
* n is on stack, either local variable
* or return value from function call.
* return n's offset from SP.
*/
int64
stkof(Node *n)
{
Type *t;
Iter flist;
int64 off;
switch(n->op) {
case OINDREG:
return n->xoffset;
case ODOT:
t = n->left->type;
if(isptr[t->etype])
break;
off = stkof(n->left);
if(off == -1000 || off == 1000)
return off;
return off + n->xoffset;
case OINDEX:
t = n->left->type;
if(!isfixedarray(t))
break;
off = stkof(n->left);
if(off == -1000 || off == 1000)
return off;
if(isconst(n->right, CTINT))
return off + t->type->width * mpgetfix(n->right->val.u.xval);
return 1000;
case OCALLMETH:
case OCALLINTER:
case OCALLFUNC:
t = n->left->type;
if(isptr[t->etype])
t = t->type;
t = structfirst(&flist, getoutarg(t));
if(t != T)
return t->width + widthptr; // +widthptr: correct for saved LR
break;
}
// botch - probably failing to recognize address
// arithmetic on the above. eg INDEX and DOT
return -1000;
}
/*
* block copy:
* memmove(&ns, &n, w);
*/
void
sgen(Node *n, Node *ns, int64 w)
{
Node dst, src, tmp;
int32 c, odst, osrc;
int dir, align, op;
Prog *p;
NodeList *l;
Node *res = ns;
if(debug['g']) {
print("\nsgen w=%lld\n", w);
dump("r", n);
dump("res", ns);
}
if(n->ullman >= UINF && ns->ullman >= UINF)
fatal("sgen UINF");
if(w < 0)
fatal("sgen copy %lld", w);
// If copying .args, that's all the results, so record definition sites
// for them for the liveness analysis.
if(ns->op == ONAME && strcmp(ns->sym->name, ".args") == 0)
for(l = curfn->dcl; l != nil; l = l->next)
if(l->n->class == PPARAMOUT)
gvardef(l->n);
// Avoid taking the address for simple enough types.
//if(componentgen(n, ns))
// return;
if(w == 0) {
// evaluate side effects only.
regalloc(&dst, types[tptr], N);
agen(res, &dst);
agen(n, &dst);
regfree(&dst);
return;
}
// determine alignment.
// want to avoid unaligned access, so have to use
// smaller operations for less aligned types.
// for example moving [4]byte must use 4 MOVB not 1 MOVW.
align = n->type->align;
switch(align) {
default:
fatal("sgen: invalid alignment %d for %T", align, n->type);
case 1:
op = AMOVBU;
break;
case 2:
op = AMOVHU;
break;
case 4:
op = AMOVWZU; // there is no lwau, only lwaux
break;
case 8:
op = AMOVDU;
break;
}
if(w%align)
fatal("sgen: unaligned size %lld (align=%d) for %T", w, align, n->type);
c = w / align;
// offset on the stack
osrc = stkof(n);
odst = stkof(res);
if(osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000)) {
// osrc and odst both on stack, and at least one is in
// an unknown position. Could generate code to test
// for forward/backward copy, but instead just copy
// to a temporary location first.
tempname(&tmp, n->type);
sgen(n, &tmp, w);
sgen(&tmp, res, w);
return;
}
if(osrc%align != 0 || odst%align != 0)
fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align);
// if we are copying forward on the stack and
// the src and dst overlap, then reverse direction
dir = align;
if(osrc < odst && odst < osrc+w)
dir = -dir;
if(n->ullman >= res->ullman) {
agenr(n, &dst, res); // temporarily use dst
regalloc(&src, types[tptr], N);
gins(AMOVD, &dst, &src);
if(res->op == ONAME)
gvardef(res);
agen(res, &dst);
} else {
if(res->op == ONAME)
gvardef(res);
agenr(res, &dst, res);
agenr(n, &src, N);
}
regalloc(&tmp, types[tptr], N);
// set up end marker
//memset(&nend, 0, sizeof nend);
//if(c >= 4) {
// regalloc(&nend, types[tptr], N);
// p = gins(AMOVD, &src, &nend);
// p->from.type = D_CONST;
// if(dir < 0)
// p->from.offset = dir;
// else
// p->from.offset = w;
//}
// move src and dest to the end of block if necessary
if(dir < 0) {
p = gins(AADD, N, &src);
p->from.type = D_CONST;
p->from.offset = w;
p = gins(AADD, N, &dst);
p->from.type = D_CONST;
p->from.offset = w;
} else {
p = gins(AADD, N, &src);
p->from.type = D_CONST;
p->from.offset = -dir;
p = gins(AADD, N, &dst);
p->from.type = D_CONST;
p->from.offset = -dir;
}
// move
// TODO: enable loops and duffcopy for larger copies.
/*if(c >= 4) {
p = gins(op, &src, &tmp);
p->from.type = D_OREG;
p->from.offset = dir;
ploop = p;
p = gins(op, &tmp, &dst);
p->to.type = D_OREG;
p->to.offset = dir;
p = gins(ACMP, &src, N);
raddr(&nend, p);
patch(gbranch(ABNE, T, 0), ploop);
regfree(&nend);
} else*/ {
while(c-- > 0) {
p = gins(op, &src, &tmp);
p->from.type = D_OREG;
p->from.offset = dir;
p = gins(op, &tmp, &dst);
p->to.type = D_OREG;
p->to.offset = dir;
}
}
regfree(&dst);
regfree(&src);
regfree(&tmp);
}
static int
cadable(Node *n)
{
if(!n->addable) {
// dont know how it happens,
// but it does
return 0;
}
switch(n->op) {
case ONAME:
return 1;
}
return 0;
}
/*
* copy a composite value by moving its individual components.
* Slices, strings and interfaces are supported.
* Small structs or arrays with elements of basic type are
* also supported.
* nr is N when assigning a zero value.
* return 1 if can do, 0 if can't.
*/
int
componentgen(Node *nr, Node *nl)
{
Node nodl, nodr;
Type *t;
int freel, freer;
vlong fldcount;
vlong loffset, roffset;
freel = 0;
freer = 0;
switch(nl->type->etype) {
default:
goto no;
case TARRAY:
t = nl->type;
// Slices are ok.
if(isslice(t))
break;
// Small arrays are ok.
if(t->bound > 0 && t->bound <= 3 && !isfat(t->type))
break;
goto no;
case TSTRUCT:
// Small structs with non-fat types are ok.
// Zero-sized structs are treated separately elsewhere.
fldcount = 0;
for(t=nl->type->type; t; t=t->down) {
if(isfat(t->type))
goto no;
if(t->etype != TFIELD)
fatal("componentgen: not a TFIELD: %lT", t);
fldcount++;
}
if(fldcount == 0 || fldcount > 4)
goto no;
break;
case TSTRING:
case TINTER:
break;
}
nodl = *nl;
if(!cadable(nl)) {
if(nr == N || !cadable(nr))
goto no;
igen(nl, &nodl, N);
freel = 1;
}
if(nr != N) {
nodr = *nr;
if(!cadable(nr)) {
igen(nr, &nodr, N);
freer = 1;
}
}
// nl and nr are 'cadable' which basically means they are names (variables) now.
// If they are the same variable, don't generate any code, because the
// VARDEF we generate will mark the old value as dead incorrectly.
// (And also the assignments are useless.)
if(nr != N && nl->op == ONAME && nr->op == ONAME && nl == nr)
goto yes;
switch(nl->type->etype) {
case TARRAY:
// componentgen for arrays.
if(nl->op == ONAME)
gvardef(nl);
t = nl->type;
if(!isslice(t)) {
nodl.type = t->type;
nodr.type = nodl.type;
for(fldcount=0; fldcount < t->bound; fldcount++) {
if(nr == N)
clearslim(&nodl);
else
gmove(&nodr, &nodl);
nodl.xoffset += t->type->width;
nodr.xoffset += t->type->width;
}
goto yes;
}
// componentgen for slices.
nodl.xoffset += Array_array;
nodl.type = ptrto(nl->type->type);
if(nr != N) {
nodr.xoffset += Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
nodl.xoffset += Array_nel-Array_array;
nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_nel-Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
nodl.xoffset += Array_cap-Array_nel;
nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_cap-Array_nel;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
goto yes;
case TSTRING:
if(nl->op == ONAME)
gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
if(nr != N) {
nodr.xoffset += Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
nodl.xoffset += Array_nel-Array_array;
nodl.type = types[simtype[TUINT]];
if(nr != N) {
nodr.xoffset += Array_nel-Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
goto yes;
case TINTER:
if(nl->op == ONAME)
gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
if(nr != N) {
nodr.xoffset += Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
nodl.xoffset += Array_nel-Array_array;
nodl.type = ptrto(types[TUINT8]);
if(nr != N) {
nodr.xoffset += Array_nel-Array_array;
nodr.type = nodl.type;
} else
nodconst(&nodr, nodl.type, 0);
gmove(&nodr, &nodl);
goto yes;
case TSTRUCT:
if(nl->op == ONAME)
gvardef(nl);
loffset = nodl.xoffset;
roffset = nodr.xoffset;
// funarg structs may not begin at offset zero.
if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type)
loffset -= nl->type->type->width;
if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type)
roffset -= nr->type->type->width;
for(t=nl->type->type; t; t=t->down) {
nodl.xoffset = loffset + t->width;
nodl.type = t->type;
if(nr == N)
clearslim(&nodl);
else {
nodr.xoffset = roffset + t->width;
nodr.type = nodl.type;
gmove(&nodr, &nodl);
}
}
goto yes;
}
no:
if(freer)
regfree(&nodr);
if(freel)
regfree(&nodl);
return 0;
yes:
if(freer)
regfree(&nodr);
if(freel)
regfree(&nodl);
return 1;
}
// 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 <u.h>
#include <libc.h>
#include "gg.h"
int thechar = '9';
char* thestring = "power64";
LinkArch* thelinkarch;
void
linkarchinit(void)
{
thestring = getgoarch();
if(strcmp(thestring, "power64le") == 0)
thelinkarch = &linkpower64le;
else
thelinkarch = &linkpower64;
}
vlong MAXWIDTH = 1LL<<50;
/*
* go declares several platform-specific type aliases:
* int, uint, float, and uintptr
*/
Typedef typedefs[] =
{
{"int", TINT, TINT64},
{"uint", TUINT, TUINT64},
{"uintptr", TUINTPTR, TUINT64},
{0}
};
void
betypeinit(void)
{
widthptr = 8;
widthint = 8;
widthreg = 8;
zprog.link = P;
zprog.as = AGOK;
zprog.reg = NREG;
zprog.from.name = D_NONE;
zprog.from.type = D_NONE;
zprog.from.reg = NREG;
zprog.to = zprog.from;
zprog.from3 = zprog.from;
listinit9();
}
// Copyright 2014 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.
#ifndef EXTERN
#define EXTERN extern
#endif
#include "../gc/go.h"
#include "../9l/9.out.h"
// TODO(minux): Remove when no longer used.
#define noimpl sysfatal("%s not implemented (%s:%d).", __func__, __FILE__, __LINE__)
#define TEXTFLAG reg
EXTERN int32 dynloc;
EXTERN uchar reg[NREG+NFREG];
EXTERN int32 pcloc; // instruction counter
EXTERN Strlit emptystring;
EXTERN Prog zprog;
EXTERN Node* newproc;
EXTERN Node* deferproc;
EXTERN Node* deferreturn;
EXTERN Node* panicindex;
EXTERN Node* panicslice;
EXTERN Node* panicdiv;
EXTERN Node* throwreturn;
extern vlong unmappedzero;
/*
* ggen.c
*/
void compile(Node*);
void gen(Node*);
Node* lookdot(Node*, Node*, int);
void cgen_as(Node*, Node*);
void cgen_callmeth(Node*, int);
void cgen_callinter(Node*, Node*, int);
void cgen_proc(Node*, int);
void cgen_callret(Node*, Node*);
void cgen_div(int, Node*, Node*, Node*);
void cgen_hmul(Node*, Node*, Node*);
void cgen_shift(int, int, Node*, Node*, Node*);
void cgen_dcl(Node*);
int needconvert(Type*, Type*);
void genconv(Type*, Type*);
void allocparams(void);
void checklabels(void);
void ginscall(Node*, int);
int gen_as_init(Node*);
void clearslim(Node*);
/*
* cgen.c
*/
void agen(Node*, Node*);
void agenr(Node*, Node*, Node*);
void cgenr(Node*, Node*, Node*);
void igen(Node*, Node*, Node*);
vlong fieldoffset(Type*, Node*);
void sgen(Node*, Node*, int64);
void gmove(Node*, Node*);
Prog* gins(int, Node*, Node*);
int samaddr(Node*, Node*);
void naddr(Node*, Addr*, int);
void cgen_aret(Node*, Node*);
int componentgen(Node*, Node*);
/*
* gsubr.c
*/
void clearp(Prog*);
Prog* gbranch(int, Type*, int);
Prog* prog(int);
void gconv(int, int);
int conv2pt(Type*);
vlong convvtox(vlong, int);
void fnparam(Type*, int, int);
Prog* gop(int, Node*, Node*, Node*);
int optoas(int, Type*);
void ginit(void);
void gclean(void);
void regalloc(Node*, Type*, Node*);
void regfree(Node*);
Node* nodarg(Type*, int);
void nodreg(Node*, Type*, int);
void nodindreg(Node*, Type*, int);
void ginscon(int, vlong, Node*);
void buildtxt(void);
Plist* newplist(void);
int isfat(Type*);
void sudoclean(void);
int sudoaddable(int, Node*, Addr*);
void afunclit(Addr*, Node*);
void nodfconst(Node*, Type*, Mpflt*);
void gtrack(Sym*);
void gargsize(vlong);
void fixlargeoffset(Node *n);
/*
* cplx.c
*/
int complexop(Node*, Node*);
void complexmove(Node*, Node*);
void complexgen(Node*, Node*);
/*
* gobj.c
*/
void datastring(char*, int, Addr*);
void datagostring(Strlit*, Addr*);
/*
* list.c
*/
void listinit(void);
void zaddr(Biobuf*, Addr*, int, int);
// 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.
#undef EXTERN
#define EXTERN
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
//static Prog *appendpp(Prog*, int, int, vlong, int, vlong);
static Prog *zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *ax);
void
defframe(Prog *ptxt)
{
uint32 frame, r0;
Prog *p;
vlong hi, lo;
NodeList *l;
Node *n;
// fill in argument size
ptxt->to.offset = rnd(curfn->type->argwid, widthptr);
// fill in final stack size
ptxt->to.offset <<= 32;
frame = rnd(stksize+maxarg, widthreg);
ptxt->to.offset |= frame;
// insert code to zero ambiguously live variables
// so that the garbage collector only sees initialized values
// when it looks for pointers.
p = ptxt;
lo = hi = 0;
r0 = 0;
// iterate through declarations - they are sorted in decreasing xoffset order.
for(l=curfn->dcl; l != nil; l = l->next) {
n = l->n;
if(!n->needzero)
continue;
if(n->class != PAUTO)
fatal("needzero class %d", n->class);
if(n->type->width % widthptr != 0 || n->xoffset % widthptr != 0 || n->type->width == 0)
fatal("var %lN has size %d offset %d", n, (int)n->type->width, (int)n->xoffset);
if(lo != hi && n->xoffset + n->type->width >= lo - 2*widthreg) {
// merge with range we already have
lo = n->xoffset;
continue;
}
// zero old range
p = zerorange(p, frame, lo, hi, &r0);
// set new range
hi = n->xoffset + n->type->width;
lo = n->xoffset;
}
// zero final range
zerorange(p, frame, lo, hi, &r0);
}
static Prog*
zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *r0)
{
vlong cnt/*, i*/;
cnt = hi - lo;
if(cnt == 0)
return p;
fprint(2, "zerorange TODO: %P, frame:%lld, lo:%lld, hi:%lld, r0: %p (%d)\n", p, frame, lo, hi, r0, *r0);
return p;
}
/*static*/ Prog*
appendpp(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.
void
markautoused(Prog *p)
{
for (; p; p = p->link) {
if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL)
continue;
if (p->from.node)
p->from.node->used = 1;
if (p->to.node)
p->to.node->used = 1;
}
}
// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
void
fixautoused(Prog *p)
{
Prog **lp;
for (lp=&p; (p=*lp) != P; ) {
if (p->as == ATYPE && p->from.node && p->from.name == D_AUTO && !p->from.node->used) {
*lp = p->link;
continue;
}
if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !p->to.node->used) {
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
// VARDEFs are interspersed with other code, and a jump might be using the
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
// the no-ops.
p->to.type = D_NONE;
p->to.node = N;
p->as = ANOP;
continue;
}
if (p->from.name == D_AUTO && p->from.node)
p->from.offset += p->from.node->stkdelta;
if (p->to.name == D_AUTO && p->to.node)
p->to.offset += p->to.node->stkdelta;
lp = &p->link;
}
}
/*
* generate: BL reg, f
* where both reg and f are registers.
* On power, f must be moved to CTR first.
*/
static void
ginsBL(Node *reg, Node *f)
{
Prog *p;
p = gins(AMOVD, f, N);
p->to.type = D_SPR;
p->to.offset = D_CTR;
p = gins(ABL, reg, N);
p->to.type = D_SPR;
p->to.offset = D_CTR;
}
/*
* generate:
* call f
* proc=-1 normal call but no return
* proc=0 normal call
* proc=1 goroutine run in new proc
* proc=2 defer call save away stack
* proc=3 normal call to C pointer (not Go func value)
*/
void
ginscall(Node *f, int proc)
{
int32 arg;
Prog *p;
Node reg, con, reg2;
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 += 3*widthptr;
}
if(arg != -1)
gargsize(arg);
switch(proc) {
default:
fatal("ginscall: bad proc %d", proc);
break;
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 a Power64 NOP that we will have the right line number.
// Power64 NOP is really or r0, r0, r0; use that description
// because the NOP pseudo-instruction would be removed by
// the linker.
nodreg(&reg, types[TINT], D_R0);
gins(AOR, &reg, &reg);
}
p = gins(ABL, N, f);
afunclit(&p->to, f);
if(proc == -1 || noreturn(p))
gins(AUNDEF, N, N);
break;
}
nodreg(&reg, types[tptr], D_R0+REGENV);
nodreg(&r1, types[tptr], D_R0+3);
gmove(f, &reg);
reg.op = OINDREG;
gmove(&reg, &r1);
reg.op = OREGISTER;
ginsBL(&reg, &r1);
break;
case 3: // normal call of c function pointer
ginsBL(N, f);
break;
case 1: // call in new proc (go)
case 2: // deferred call (defer)
nodconst(&con, types[TINT64], argsize(f->type));
nodreg(&reg, types[TINT64], D_R0+3);
nodreg(&reg2, types[TINT64], D_R0+4);
gmove(f, &reg);
p = gins(ASUB, N, N);
p->from.type = D_CONST;
p->from.offset = 3 * 8;
p->to.type = D_REG;
p->to.reg = REGSP;
gmove(&con, &reg2);
p = gins(AMOVW, &reg2, N);
p->to.type = D_OREG;
p->to.reg = REGSP;
p->to.offset = 8;
p = gins(AMOVD, &reg, N);
p->to.type = D_OREG;
p->to.reg = REGSP;
p->to.offset = 16;
if(proc == 1)
ginscall(newproc, 0);
else {
if(!hasdefer)
fatal("hasdefer=0 but has defer");
ginscall(deferproc, 0);
}
p = gins(AADD, N, N);
p->from.type = D_CONST;
p->from.offset = 3 * 8;
p->to.type = D_REG;
p->to.reg = REGSP;
if(proc == 2) {
nodreg(&reg, types[TINT64], D_R0+3);
p = gins(ACMP, &reg, N);
p->to.type = D_REG;
p->to.reg = D_R0;
p = gbranch(ABEQ, T, +1);
cgen_ret(N);
patch(p, pc);
}
break;
}
if(arg != -1)
gargsize(-1);
}
/*
* n is call to interface method.
* generate res = n.
*/
void
cgen_callinter(Node *n, Node *res, int proc)
{
Node *i, *f;
Node tmpi, nodi, nodo, nodr, nodsp;
Prog *p;
i = n->left;
if(i->op != ODOTINTER)
fatal("cgen_callinter: not ODOTINTER %O", i->op);
f = i->right; // field
if(f->op != ONAME)
fatal("cgen_callinter: not ONAME %O", f->op);
i = i->left; // interface
if(!i->addable) {
tempname(&tmpi, i->type);
cgen(i, &tmpi);
i = &tmpi;
}
genlist(n->list); // assign the args
// i is now addable, prepare an indirected
// register to hold its address.
igen(i, &nodi, res); // REG = &inter
nodindreg(&nodsp, types[tptr], D_R0+REGSP);
nodsp.xoffset = widthptr;
nodi.type = types[tptr];
nodi.xoffset += widthptr;
cgen(&nodi, &nodsp); // 0(SP) = 8(REG) -- i.data
regalloc(&nodo, types[tptr], res);
nodi.type = types[tptr];
nodi.xoffset -= widthptr;
cgen(&nodi, &nodo); // REG = 0(REG) -- i.tab
regfree(&nodi);
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) {
// plain call: use direct c function pointer - more efficient
cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f]
proc = 3;
} else {
// go/defer. generate go func value.
p = gins(AMOVD, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f]
p->from.type = D_CONST;
}
nodr.type = n->left->type;
ginscall(&nodr, proc);
regfree(&nodr);
regfree(&nodo);
}
/*
* generate function call;
* 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)
{
Type *t;
Node nod, afun;
if(n == N)
return;
if(n->left->ullman >= UINF) {
// if name involves a fn call
// precompute the address of the fn
tempname(&afun, types[tptr]);
cgen(n->left, &afun);
}
genlist(n->list); // assign the args
t = n->left->type;
// call tempname pointer
if(n->left->ullman >= UINF) {
regalloc(&nod, types[tptr], N);
cgen_as(&nod, &afun);
nod.type = t;
ginscall(&nod, proc);
regfree(&nod);
return;
}
// call pointer
if(n->left->op != ONAME || n->left->class != PFUNC) {
regalloc(&nod, types[tptr], N);
cgen_as(&nod, n->left);
nod.type = t;
ginscall(&nod, proc);
regfree(&nod);
return;
}
// call direct
n->left->method = 1;
ginscall(n->left, proc);
}
/*
* call to n has already been generated.
* generate:
* res = return value from call.
*/
void
cgen_callret(Node *n, Node *res)
{
Node nod;
Type *fp, *t;
Iter flist;
t = n->left->type;
if(t->etype == TPTR32 || t->etype == TPTR64)
t = t->type;
fp = structfirst(&flist, getoutarg(t));
if(fp == T)
fatal("cgen_callret: nil");
memset(&nod, 0, sizeof(nod));
nod.op = OINDREG;
nod.val.u.reg = D_R0+REGSP;
nod.addable = 1;
nod.xoffset = fp->width + widthptr; // +widthptr: saved LR at 0(R1)
nod.type = fp->type;
cgen_as(res, &nod);
}
/*
* call to n has already been generated.
* generate:
* res = &return value from call.
*/
void
cgen_aret(Node *n, Node *res)
{
Node nod1, nod2;
Type *fp, *t;
Iter flist;
t = n->left->type;
if(isptr[t->etype])
t = t->type;
fp = structfirst(&flist, getoutarg(t));
if(fp == T)
fatal("cgen_aret: nil");
memset(&nod1, 0, sizeof(nod1));
nod1.op = OINDREG;
nod1.val.u.reg = D_R0 + REGSP;
nod1.addable = 1;
nod1.xoffset = fp->width + widthptr; // +widthptr: saved lr at 0(SP)
nod1.type = fp->type;
if(res->op != OREGISTER) {
regalloc(&nod2, types[tptr], res);
agen(&nod1, &nod2);
gins(AMOVD, &nod2, res);
regfree(&nod2);
} else
agen(&nod1, res);
}
/*
* generate return.
* n->left is assignments to return values.
*/
void
cgen_ret(Node *n)
{
Prog *p;
if(n != N)
genlist(n->list); // copy out args
if(hasdefer)
ginscall(deferreturn, 0);
genlist(curfn->exit);
p = gins(ARET, N, N);
if(n != N && n->op == ORETJMP) {
p->to.name = D_EXTERN;
p->to.type = D_CONST;
p->to.sym = linksym(n->left->sym);
}
}
void
cgen_asop(Node *n)
{
USED(n);
fatal("cgen_asop"); // no longer used
}
int
samereg(Node *a, Node *b)
{
if(a == N || b == N)
return 0;
if(a->op != OREGISTER)
return 0;
if(b->op != OREGISTER)
return 0;
if(a->val.u.reg != b->val.u.reg)
return 0;
return 1;
}
/*
* generate division.
* generates one of:
* res = nl / nr
* res = nl % nr
* according to op.
*/
void
dodiv(int op, Node *nl, Node *nr, Node *res)
{
int a, check;
Type *t, *t0;
Node tl, tr, tl2, tr2, nm1, nz, tm;
Prog *p1, *p2;
// Have to be careful about handling
// most negative int divided by -1 correctly.
// The hardware will generate undefined result.
// Also need to explicitly trap on division on zero,
// the hardware will silently generate undefined result.
// DIVW will leave unpredicable result in higher 32-bit,
// so always use DIVD/DIVDU.
t = nl->type;
t0 = t;
check = 0;
if(issigned[t->etype]) {
check = 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;
}
if(t->width < 8) {
if(issigned[t->etype])
t = types[TINT64];
else
t = types[TUINT64];
check = 0;
}
a = optoas(ODIV, t);
regalloc(&tl, t0, N);
regalloc(&tr, t0, N);
if(nl->ullman >= nr->ullman) {
cgen(nl, &tl);
cgen(nr, &tr);
} else {
cgen(nr, &tr);
cgen(nl, &tl);
}
if(t != t0) {
// Convert
tl2 = tl;
tr2 = tr;
tl.type = t;
tr.type = t;
gmove(&tl2, &tl);
gmove(&tr2, &tr);
}
// Handle divide-by-zero panic.
p1 = gins(optoas(OCMP, t), &tr, N);
p1->to.type = D_REG;
p1->to.reg = REGZERO;
p1 = gbranch(optoas(ONE, t), T, +1);
if(panicdiv == N)
panicdiv = sysfunc("panicdivide");
ginscall(panicdiv, -1);
patch(p1, pc);
if(check) {
nodconst(&nm1, t, -1);
gins(optoas(OCMP, t), &tr, &nm1);
p1 = gbranch(optoas(ONE, t), T, +1);
if(op == ODIV) {
// a / (-1) is -a.
gins(optoas(OMINUS, t), N, &tl);
gmove(&tl, res);
} else {
// a % (-1) is 0.
nodconst(&nz, t, 0);
gmove(&nz, res);
}
p2 = gbranch(AJMP, T, 0);
patch(p1, pc);
}
p1 = gins(a, &tr, &tl);
if(op == ODIV) {
regfree(&tr);
gmove(&tl, res);
} else {
// A%B = A-(A/B*B)
regalloc(&tm, t, N);
// patch div to use the 3 register form
// TODO(minux): add gins3?
p1->reg = p1->to.reg;
p1->to.reg = tm.val.u.reg;
gins(optoas(OMUL, t), &tr, &tm);
regfree(&tr);
gins(optoas(OSUB, t), &tm, &tl);
regfree(&tm);
gmove(&tl, res);
}
regfree(&tl);
if(check)
patch(p2, pc);
}
/*
* generate division according to op, one of:
* res = nl / nr
* res = nl % nr
*/
void
cgen_div(int op, Node *nl, Node *nr, Node *res)
{
Node n1, n2, n3;
int w, a;
Magic m;
// TODO(minux): enable division by magic multiply (also need to fix longmod below)
//if(nr->op != OLITERAL)
goto longdiv;
w = nl->type->width*8;
// Front end handled 32-bit division. We only need to handle 64-bit.
// try to do division by multiply by (2^w)/d
// see hacker's delight chapter 10
switch(simtype[nl->type->etype]) {
default:
goto longdiv;
case TUINT64:
m.w = w;
m.ud = mpgetfix(nr->val.u.xval);
umagic(&m);
if(m.bad)
break;
if(op == OMOD)
goto longmod;
cgenr(nl, &n1, N);
nodconst(&n2, nl->type, m.um);
regalloc(&n3, nl->type, res);
cgen_hmul(&n1, &n2, &n3);
if(m.ua) {
// need to add numerator accounting for overflow
gins(optoas(OADD, nl->type), &n1, &n3);
nodconst(&n2, nl->type, 1);
gins(optoas(ORROTC, nl->type), &n2, &n3);
nodconst(&n2, nl->type, m.s-1);
gins(optoas(ORSH, nl->type), &n2, &n3);
} else {
nodconst(&n2, nl->type, m.s);
gins(optoas(ORSH, nl->type), &n2, &n3); // shift dx
}
gmove(&n3, res);
regfree(&n1);
regfree(&n3);
return;
case TINT64:
m.w = w;
m.sd = mpgetfix(nr->val.u.xval);
smagic(&m);
if(m.bad)
break;
if(op == OMOD)
goto longmod;
cgenr(nl, &n1, res);
nodconst(&n2, nl->type, m.sm);
regalloc(&n3, nl->type, N);
cgen_hmul(&n1, &n2, &n3);
if(m.sm < 0) {
// need to add numerator
gins(optoas(OADD, nl->type), &n1, &n3);
}
nodconst(&n2, nl->type, m.s);
gins(optoas(ORSH, nl->type), &n2, &n3); // shift n3
nodconst(&n2, nl->type, w-1);
gins(optoas(ORSH, nl->type), &n2, &n1); // -1 iff num is neg
gins(optoas(OSUB, nl->type), &n1, &n3); // added
if(m.sd < 0) {
// this could probably be removed
// by factoring it into the multiplier
gins(optoas(OMINUS, nl->type), N, &n3);
}
gmove(&n3, res);
regfree(&n1);
regfree(&n3);
return;
}
goto longdiv;
longdiv:
// division and mod using (slow) hardware instruction
dodiv(op, nl, nr, res);
return;
longmod:
// mod using formula A%B = A-(A/B*B) but
// we know that there is a fast algorithm for A/B
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
regalloc(&n2, nl->type, N);
cgen_div(ODIV, &n1, nr, &n2);
a = optoas(OMUL, nl->type);
if(w == 8) {
// use 2-operand 16-bit multiply
// because there is no 2-operand 8-bit multiply
//a = AIMULW;
}
if(!smallintconst(nr)) {
regalloc(&n3, nl->type, N);
cgen(nr, &n3);
gins(a, &n3, &n2);
regfree(&n3);
} else
gins(a, nr, &n2);
gins(optoas(OSUB, nl->type), &n2, &n1);
gmove(&n1, res);
regfree(&n1);
regfree(&n2);
}
/*
* generate high multiply:
* res = (nl*nr) >> width
*/
void
cgen_hmul(Node *nl, Node *nr, Node *res)
{
int w;
Node n1, n2, *tmp;
Type *t;
Prog *p;
// largest ullman on left.
if(nl->ullman < nr->ullman) {
tmp = nl;
nl = nr;
nr = tmp;
}
t = nl->type;
w = t->width * 8;
cgenr(nl, &n1, res);
cgenr(nr, &n2, N);
switch(simtype[t->etype]) {
case TINT8:
case TINT16:
case TINT32:
gins(optoas(OMUL, t), &n2, &n1);
p = gins(ASRAD, N, &n1);
p->from.type = D_CONST;
p->from.offset = w;
break;
case TUINT8:
case TUINT16:
case TUINT32:
gins(optoas(OMUL, t), &n2, &n1);
p = gins(ASRD, N, &n1);
p->from.type = D_CONST;
p->from.offset = w;
break;
case TINT64:
case TUINT64:
if(issigned[t->etype])
p = gins(AMULHD, &n2, &n1);
else
p = gins(AMULHDU, &n2, &n1);
break;
default:
fatal("cgen_hmul %T", t);
break;
}
cgen(&n1, res);
regfree(&n1);
regfree(&n2);
}
/*
* generate shift according to op, one of:
* res = nl << nr
* res = nl >> nr
*/
void
cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res)
{
Node n1, n2, n3, n4, n5;
int a;
Prog *p1;
uvlong sc;
Type *tcount;
a = optoas(op, nl->type);
if(nr->op == OLITERAL) {
regalloc(&n1, nl->type, res);
cgen(nl, &n1);
sc = mpgetfix(nr->val.u.xval);
if(sc >= nl->type->width*8) {
// large shift gets 2 shifts by width-1
nodconst(&n3, types[TUINT32], nl->type->width*8-1);
gins(a, &n3, &n1);
gins(a, &n3, &n1);
} else
gins(a, nr, &n1);
gmove(&n1, res);
regfree(&n1);
goto ret;
}
if(nl->ullman >= UINF) {
tempname(&n4, nl->type);
cgen(nl, &n4);
nl = &n4;
}
if(nr->ullman >= UINF) {
tempname(&n5, nr->type);
cgen(nr, &n5);
nr = &n5;
}
// Allow either uint32 or uint64 as shift type,
// to avoid unnecessary conversion from uint32 to uint64
// just to do the comparison.
tcount = types[simtype[nr->type->etype]];
if(tcount->etype < TUINT32)
tcount = types[TUINT32];
regalloc(&n1, nr->type, N); // to hold the shift type in CX
regalloc(&n3, tcount, &n1); // to clear high bits of CX
regalloc(&n2, nl->type, res);
if(nl->ullman >= nr->ullman) {
cgen(nl, &n2);
cgen(nr, &n1);
gmove(&n1, &n3);
} else {
cgen(nr, &n1);
gmove(&n1, &n3);
cgen(nl, &n2);
}
regfree(&n3);
// test and fix up large shifts
if(!bounded) {
nodconst(&n3, tcount, nl->type->width*8);
gins(optoas(OCMP, tcount), &n1, &n3);
p1 = gbranch(optoas(OLT, tcount), T, +1);
if(op == ORSH && issigned[nl->type->etype]) {
nodconst(&n3, types[TUINT32], nl->type->width*8-1);
gins(a, &n3, &n2);
} else {
nodconst(&n3, nl->type, 0);
gmove(&n3, &n2);
}
patch(p1, pc);
}
gins(a, &n1, &n2);
gmove(&n2, res);
regfree(&n1);
regfree(&n2);
ret:
;
}
void
clearfat(Node *nl)
{
uint64 w, c, q, t;
Node dst, end, r0, *f;
Prog *p, *pl;
/* clear a fat object */
if(debug['g']) {
print("clearfat %N (%T, size: %lld)\n", nl, nl->type, nl->type->width);
}
w = nl->type->width;
// Avoid taking the address for simple enough types.
//if(componentgen(N, nl))
// return;
c = w % 8; // bytes
q = w / 8; // dwords
if(reg[REGRT1] > 0)
fatal("R%d in use during clearfat", REGRT1);
nodreg(&r0, types[TUINT64], 0); // r0 is always zero
nodreg(&dst, types[tptr], D_R0+REGRT1);
reg[REGRT1]++;
agen(nl, &dst);
if(q > 128) {
p = gins(ASUB, N, &dst);
p->from.type = D_CONST;
p->from.offset = 8;
regalloc(&end, types[tptr], N);
p = gins(AMOVD, &dst, &end);
p->from.type = D_CONST;
p->from.offset = q*8;
p = gins(AMOVDU, &r0, &dst);
p->to.type = D_OREG;
p->to.offset = 8;
pl = p;
p = gins(ACMP, &dst, &end);
patch(gbranch(ABNE, T, 0), pl);
regfree(&end);
} else if(q >= 4) {
p = gins(ASUB, N, &dst);
p->from.type = D_CONST;
p->from.offset = 8;
f = sysfunc("duffzero");
p = gins(ADUFFZERO, N, f);
afunclit(&p->to, f);
// 4 and 128 = magic constants: see ../../pkg/runtime/asm_power64x.s
p->to.offset = 4*(128-q);
} else
for(t = 0; t < q; t++) {
p = gins(AMOVD, &r0, &dst);
p->to.type = D_OREG;
p->to.offset = 8*t;
}
for(t = 0; t < c; t++) {
p = gins(AMOVB, &r0, &dst);
p->to.type = D_OREG;
p->to.offset = t;
}
reg[REGRT1]--;
}
// 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(debug_checknil && ctxt->debugvlog)
print("expandchecks: %P\n", p);
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\n", p);
/*
// check is
// TD $4, R0, arg (R0 is always zero)
// eqv. to:
// tdeq r0, arg
// NOTE: this needs special runtime support to make SIGTRAP recoverable.
reg = p->from.reg;
p->as = ATD;
p->from = p->to = p->from3 = zprog.from;
p->from.type = D_CONST;
p->from.offset = 4;
p->from.reg = NREG;
p->reg = 0;
p->to.type = D_REG;
p->to.reg = reg;
*/
// check is
// CMP arg, R0
// BNE 2(PC) [likely]
// MOVD R0, 0(R0)
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->pc = 9999;
p2->pc = 9999;
p->as = ACMP;
p->to.type = D_REG;
p->to.reg = REGZERO;
p1->as = ABNE;
//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.
p2->as = AMOVD;
p2->from.type = D_REG;
p2->from.reg = 0;
p2->to.type = D_OREG;
p2->to.reg = 0;
p2->to.offset = 0;
}
}
// Derived from Inferno utils/6c/swt.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/swt.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include "gg.h"
int
dsname(Sym *s, int off, char *t, int n)
{
Prog *p;
p = gins(ADATA, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.offset = off;
p->from.reg = NREG;
p->from.sym = linksym(s);
p->reg = n;
p->to.type = D_SCONST;
p->to.name = D_NONE;
p->to.reg = NREG;
p->to.offset = 0;
memmove(p->to.u.sval, t, n);
return off + n;
}
/*
* make a refer to the data s, s+len
* emitting DATA if needed.
*/
void
datastring(char *s, int len, Addr *a)
{
Sym *sym;
sym = stringsym(s, len);
a->type = D_OREG;
a->name = D_EXTERN;
a->etype = simtype[TINT];
a->offset = widthptr+widthint; // skip header
a->reg = NREG;
a->sym = linksym(sym);
a->node = sym->def;
}
/*
* make a refer to the string sval,
* emitting DATA if needed.
*/
void
datagostring(Strlit *sval, Addr *a)
{
Sym *sym;
sym = stringsym(sval->s, sval->len);
a->type = D_OREG;
a->name = D_EXTERN;
a->sym = linksym(sym);
a->reg = NREG;
a->node = sym->def;
a->offset = 0; // header
a->etype = TINT32;
}
void
gdata(Node *nam, Node *nr, int wid)
{
Prog *p;
if(nr->op == OLITERAL) {
switch(nr->val.ctype) {
case CTCPLX:
gdatacomplex(nam, nr->val.u.cval);
return;
case CTSTR:
gdatastring(nam, nr->val.u.sval);
return;
}
}
p = gins(ADATA, nam, nr);
p->reg = wid;
}
void
gdatacomplex(Node *nam, Mpcplx *cval)
{
Prog *p;
int w;
w = cplxsubtype(nam->type->etype);
w = types[w]->width;
p = gins(ADATA, nam, N);
p->reg = w;
p->to.type = D_FCONST;
p->to.u.dval = mpgetflt(&cval->real);
p = gins(ADATA, nam, N);
p->reg = w;
p->from.offset += w;
p->to.type = D_FCONST;
p->to.u.dval = mpgetflt(&cval->imag);
}
void
gdatastring(Node *nam, Strlit *sval)
{
Prog *p;
Node nod1;
p = gins(ADATA, nam, N);
datastring(sval->s, sval->len, &p->to);
p->reg = types[tptr]->width;
p->to.type = D_CONST;
p->to.etype = simtype[tptr];
nodconst(&nod1, types[TINT], sval->len);
p = gins(ADATA, nam, &nod1);
p->reg = widthint;
p->from.offset += widthptr;
}
int
dstringptr(Sym *s, int off, char *str)
{
Prog *p;
off = rnd(off, widthptr);
p = gins(ADATA, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = linksym(s);
p->from.offset = off;
p->reg = widthptr;
datastring(str, strlen(str)+1, &p->to);
p->to.type = D_CONST;
p->to.etype = simtype[TINT];
off += widthptr;
return off;
}
int
dgostrlitptr(Sym *s, int off, Strlit *lit)
{
Prog *p;
if(lit == nil)
return duintptr(s, off, 0);
off = rnd(off, widthptr);
p = gins(ADATA, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = linksym(s);
p->from.offset = off;
p->reg = widthptr;
datagostring(lit, &p->to);
p->to.type = D_CONST;
p->to.etype = simtype[TINT];
off += widthptr;
return off;
}
int
dgostringptr(Sym *s, int off, char *str)
{
int n;
Strlit *lit;
if(str == nil)
return duintptr(s, off, 0);
n = strlen(str);
lit = mal(sizeof *lit + n);
strcpy(lit->s, str);
lit->len = n;
return dgostrlitptr(s, off, lit);
}
int
dsymptr(Sym *s, int off, Sym *x, int xoff)
{
Prog *p;
off = rnd(off, widthptr);
p = gins(ADATA, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = linksym(s);
p->from.offset = off;
p->reg = widthptr;
p->to.type = D_CONST;
p->to.name = D_EXTERN;
p->to.sym = linksym(x);
p->to.offset = xoff;
off += widthptr;
return off;
}
void
nopout(Prog *p)
{
p->as = ANOP;
}
// Derived from Inferno utils/6c/txt.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "../../pkg/runtime/funcdata.h"
// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 6l for all GOOS.
// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
vlong unmappedzero = 4096;
void
clearp(Prog *p)
{
*p = zprog;
p->as = AEND;
p->pc = pcloc;
pcloc++;
}
static int ddumped;
static Prog *dfirst;
static Prog *dpc;
/*
* generate and return proc with p->as = as,
* linked into program. pc is next instruction.
*/
Prog*
prog(int as)
{
Prog *p;
if(as == ADATA || as == AGLOBL) {
if(ddumped)
fatal("already dumped data");
if(dpc == nil) {
dpc = mal(sizeof(*dpc));
dfirst = dpc;
}
p = dpc;
dpc = mal(sizeof(*dpc));
p->link = dpc;
p->reg = 0; // used for flags
} else {
p = pc;
pc = mal(sizeof(*pc));
clearp(pc);
p->link = pc;
}
if(lineno == 0) {
if(debug['K'])
warn("prog: line 0");
}
p->as = as;
p->lineno = lineno;
return p;
}
void
dumpdata(void)
{
ddumped = 1;
if(dfirst == nil)
return;
newplist();
*pc = *dfirst;
pc = dpc;
clearp(pc);
}
/*
* generate a branch.
* t is ignored.
* likely values are for branch prediction:
* -1 unlikely
* 0 no opinion
* +1 likely
*/
Prog*
gbranch(int as, Type *t, int likely)
{
Prog *p;
USED(t);
p = prog(as);
p->to.type = D_BRANCH;
p->to.u.branch = P;
// TODO(minux): Enable this code.
// Note: liblink used Bcc CR0, label form, so we need another way
// to set likely/unlikely flag. Also note the y bit is not exactly
// likely/unlikely bit.
if(0 && as != ABR && likely != 0) {
p->from.type = D_CONST;
p->from.offset = likely > 0;
}
return p;
}
/*
* patch previous branch to jump to to.
*/
void
patch(Prog *p, Prog *to)
{
if(p->to.type != D_BRANCH)
fatal("patch: not a branch");
p->to.u.branch = to;
p->to.offset = to->pc;
}
Prog*
unpatch(Prog *p)
{
Prog *q;
if(p->to.type != D_BRANCH)
fatal("unpatch: not a branch");
q = p->to.u.branch;
p->to.u.branch = P;
p->to.offset = 0;
return q;
}
/*
* start a new Prog list.
*/
Plist*
newplist(void)
{
Plist *pl;
pl = linknewplist(ctxt);
pc = mal(sizeof(*pc));
clearp(pc);
pl->firstpc = pc;
return pl;
}
void
gused(Node *n)
{
gins(ANOP, n, N); // used
}
Prog*
gjmp(Prog *to)
{
Prog *p;
p = gbranch(ABR, T, 0);
if(to != P)
patch(p, to);
return p;
}
void
ggloblnod(Node *nam)
{
Prog *p;
p = gins(AGLOBL, nam, N);
p->lineno = nam->lineno;
p->from.sym->gotype = linksym(ngotype(nam));
p->to.sym = nil;
p->to.type = D_CONST;
p->to.offset = nam->type->width;
if(nam->readonly)
p->reg = RODATA;
if(nam->type != T && !haspointers(nam->type))
p->reg |= NOPTR;
}
void
gtrack(Sym *s)
{
Prog *p;
p = gins(AUSEFIELD, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = linksym(s);
}
void
gargsize(vlong size)
{
Node n1, n2;
nodconst(&n1, types[TINT32], PCDATA_ArgSize);
nodconst(&n2, types[TINT32], size);
gins(APCDATA, &n1, &n2);
}
void
ggloblsym(Sym *s, int32 width, int8 flags)
{
Prog *p;
p = gins(AGLOBL, N, N);
p->from.type = D_OREG;
p->from.name = D_EXTERN;
p->from.sym = linksym(s);
p->to.type = D_CONST;
p->to.name = D_NONE;
p->to.offset = width;
p->reg = flags;
}
int
isfat(Type *t)
{
if(t != T)
switch(t->etype) {
case TSTRUCT:
case TARRAY:
case TSTRING:
case TINTER: // maybe remove later
return 1;
}
return 0;
}
/*
* naddr of func generates code for address of func.
* if using opcode that can take address implicitly,
* call afunclit to fix up the argument.
*/
void
afunclit(Addr *a, Node *n)
{
if(a->type == D_CONST && a->name == D_EXTERN) {
a->type = D_OREG;
a->sym = linksym(n->sym);
}
}
static int resvd[] =
{
REGZERO,
REGSP, // reserved for SP, XXX: not reserved in 9c.
30, // for g
REGTMP, // REGTMP
FREGCVI+NREG,
FREGZERO+NREG,
FREGHALF+NREG,
FREGONE+NREG,
FREGTWO+NREG,
};
void
ginit(void)
{
int i;
for(i=0; i<nelem(reg); i++)
reg[i] = 1;
for(i=0; i<NREG; i++)
reg[i] = 0;
for(i=NREG; i<NREG+NREG; i++)
reg[i] = 0;
for(i=0; i<nelem(resvd); i++)
reg[resvd[i]]++;
}
static uintptr regpc[nelem(reg)];
void
gclean(void)
{
int i;
for(i=0; i<nelem(resvd); i++)
reg[resvd[i]]--;
for(i=0; i<nelem(reg); i++)
if(reg[i])
yyerror("reg %R left allocated, %p\n", i, regpc[i]);
}
int32
anyregalloc(void)
{
int i, j;
for(i=0; i<nelem(reg); i++) {
if(reg[i] == 0)
goto ok;
for(j=0; j<nelem(resvd); j++)
if(resvd[j] == i)
goto ok;
return 1;
ok:;
}
return 0;
}
/*
* allocate register of type t, leave in n.
* if o != N, o is desired fixed register.
* caller must regfree(n).
*/
void
regalloc(Node *n, Type *t, Node *o)
{
int i, et;
int fixfree, fltfree;
if(t == T)
fatal("regalloc: t nil");
et = simtype[t->etype];
if(debug['r']) {
fixfree = 0;
fltfree = 0;
for(i = D_R0; i < D_F0+NREG; i++)
if(reg[i] == 0) {
if(i < D_F0)
fixfree++;
else
fltfree++;
}
print("regalloc fix %d flt %d free\n", fixfree, fltfree);
}
switch(et) {
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TPTR32:
case TPTR64:
case TBOOL:
if(o != N && o->op == OREGISTER) {
i = o->val.u.reg;
if(i >= D_R0+REGMIN && i <= D_R0+REGMAX)
goto out;
}
for(i=D_R0+REGMIN; i<=D_R0+REGMAX; i++)
if(reg[i] == 0) {
regpc[i] = (uintptr)getcallerpc(&n);
goto out;
}
flusherrors();
for(i=D_R0; i<D_R0+NREG; i++)
print("R%d %p\n", i, regpc[i]);
fatal("out of fixed registers");
case TFLOAT32:
case TFLOAT64:
if(o != N && o->op == OREGISTER) {
i = o->val.u.reg;
if(i >= D_F0+FREGMIN && i <= D_F0+FREGMAX)
goto out;
}
for(i=D_F0+FREGMIN; i<=D_F0+FREGMAX; i++)
if(reg[i] == 0) {
regpc[i] = (uintptr)getcallerpc(&n);
goto out;
}
flusherrors();
for(i=D_F0; i<D_F0+NREG; i++)
print("F%d %p\n", i, regpc[i]);
fatal("out of floating registers");
case TCOMPLEX64:
case TCOMPLEX128:
tempname(n, t);
return;
}
fatal("regalloc: unknown type %T", t);
return;
out:
reg[i]++;
nodreg(n, t, i);
}
void
regfree(Node *n)
{
int i;
if(n->op == ONAME)
return;
if(n->op != OREGISTER && n->op != OINDREG)
fatal("regfree: not a register");
i = n->val.u.reg;
if(i == D_R0 + REGSP)
return;
if(i < 0 || i >= nelem(reg))
fatal("regfree: reg out of range");
if(reg[i] <= 0)
fatal("regfree: reg not allocated");
reg[i]--;
if(reg[i] == 0)
regpc[i] = 0;
}
/*
* initialize n to be register r of type t.
*/
void
nodreg(Node *n, Type *t, int r)
{
if(t == T)
fatal("nodreg: t nil");
memset(n, 0, sizeof(*n));
n->op = OREGISTER;
n->addable = 1;
ullmancalc(n);
n->val.u.reg = r;
n->type = t;
}
/*
* initialize n to be indirect of register r; n is type t.
*/
void
nodindreg(Node *n, Type *t, int r)
{
nodreg(n, t, r);
n->op = OINDREG;
}
Node*
nodarg(Type *t, int fp)
{
Node *n;
NodeList *l;
Type *first;
Iter savet;
// entire argument struct, not just one arg
if(t->etype == TSTRUCT && t->funarg) {
n = nod(ONAME, N, N);
n->sym = lookup(".args");
n->type = t;
first = structfirst(&savet, &t);
if(first == nil)
fatal("nodarg: bad struct");
if(first->width == BADWIDTH)
fatal("nodarg: offset not computed for %T", t);
n->xoffset = first->width;
n->addable = 1;
goto fp;
}
if(t->etype != TFIELD)
fatal("nodarg: not field %T", t);
if(fp == 1) {
for(l=curfn->dcl; l; l=l->next) {
n = l->n;
if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym)
return n;
}
}
n = nod(ONAME, N, N);
n->type = t->type;
n->sym = t->sym;
if(t->width == BADWIDTH)
fatal("nodarg: offset not computed for %T", t);
n->xoffset = t->width;
n->addable = 1;
n->orig = t->nname;
fp:
// Rewrite argument named _ to __,
// or else the assignment to _ will be
// discarded during code generation.
if(isblank(n))
n->sym = lookup("__");
switch(fp) {
default:
fatal("nodarg %T %d", t, fp);
case 0: // output arg for calling another function
n->op = OINDREG;
n->val.u.reg = D_R0+REGSP;
n->xoffset += 8;
break;
case 1: // input arg to current function
n->class = PPARAM;
break;
case 2: // offset output arg
fatal("shouldn't be used");
n->op = OINDREG;
n->val.u.reg = D_R0 + REGSP;
n->xoffset += types[tptr]->width;
break;
}
n->typecheck = 1;
return n;
}
/*
* generate
* as $c, n
*/
void
ginscon(int as, vlong c, Node *n2)
{
Node n1, ntmp;
nodconst(&n1, types[TINT64], c);
if(as != AMOVD && (c < -BIG || c > BIG)) {
// cannot have more than 16-bit of immediate in ADD, etc.
// instead, MOV into register first.
regalloc(&ntmp, types[TINT64], N);
gins(AMOVD, &n1, &ntmp);
gins(as, &ntmp, n2);
regfree(&ntmp);
return;
}
gins(as, &n1, n2);
}
#define CASE(a,b) (((a)<<16)|((b)<<0))
/*c2go int CASE(int, int); */
/*
* Is this node a memory operand?
*/
int
ismem(Node *n)
{
switch(n->op) {
case OITAB:
case OSPTR:
case OLEN:
case OCAP:
case OINDREG:
case ONAME:
case OPARAM:
case OCLOSUREVAR:
return 1;
case OADDR:
//if(flag_largemodel)
return 1;
break;
}
return 0;
}
/*
* set up nodes representing 2^63
*/
Node bigi;
Node bigf;
void
bignodes(void)
{
static int did;
if(did)
return;
did = 1;
nodconst(&bigi, types[TUINT64], 1);
mpshiftfix(bigi.val.u.xval, 63);
bigf = bigi;
bigf.type = types[TFLOAT64];
bigf.val.ctype = CTFLT;
bigf.val.u.fval = mal(sizeof *bigf.val.u.fval);
mpmovefixflt(bigf.val.u.fval, bigi.val.u.xval);
}
/*
* generate move:
* t = f
* hard part is conversions.
*/
void
gmove(Node *f, Node *t)
{
int a, ft, tt;
Type *cvt;
Node r1, r2, r3, con;
Prog *p1, *p2;
if(debug['M'])
print("gmove %lN -> %lN\n", f, t);
ft = simsimtype(f->type);
tt = simsimtype(t->type);
cvt = t->type;
if(iscomplex[ft] || iscomplex[tt]) {
complexmove(f, t);
return;
}
// cannot have two memory operands
if(ismem(f) && ismem(t))
goto hard;
// convert constant to desired type
if(f->op == OLITERAL) {
switch(tt) {
default:
convconst(&con, t->type, &f->val);
break;
case TINT32:
case TINT16:
case TINT8:
convconst(&con, types[TINT64], &f->val);
regalloc(&r1, con.type, t);
gins(AMOVD, &con, &r1);
gmove(&r1, t);
regfree(&r1);
return;
case TUINT32:
case TUINT16:
case TUINT8:
convconst(&con, types[TUINT64], &f->val);
regalloc(&r1, con.type, t);
gins(AMOVD, &con, &r1);
gmove(&r1, t);
regfree(&r1);
return;
}
f = &con;
ft = tt; // so big switch will choose a simple mov
// constants can't move directly to memory.
if(ismem(t)) {
goto hard;
// float constants come from memory.
//if(isfloat[tt])
// goto hard;
// 64-bit immediates are also from memory.
//if(isint[tt])
// goto hard;
//// 64-bit immediates are really 32-bit sign-extended
//// unless moving into a register.
//if(isint[tt]) {
// if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0)
// goto hard;
// if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0)
// goto hard;
//}
}
}
// value -> value copy, only one memory operand.
// figure out the instruction to use.
// break out of switch for one-instruction gins.
// goto rdst for "destination must be register".
// goto hard for "convert to cvt type first".
// otherwise handle and return.
switch(CASE(ft, tt)) {
default:
fatal("gmove %lT -> %lT", f->type, t->type);
/*
* integer copy and truncate
*/
case CASE(TINT8, TINT8): // same size
case CASE(TUINT8, TINT8):
case CASE(TINT16, TINT8): // truncate
case CASE(TUINT16, TINT8):
case CASE(TINT32, TINT8):
case CASE(TUINT32, TINT8):
case CASE(TINT64, TINT8):
case CASE(TUINT64, TINT8):
a = AMOVB;
break;
case CASE(TINT8, TUINT8): // same size
case CASE(TUINT8, TUINT8):
case CASE(TINT16, TUINT8): // truncate
case CASE(TUINT16, TUINT8):
case CASE(TINT32, TUINT8):
case CASE(TUINT32, TUINT8):
case CASE(TINT64, TUINT8):
case CASE(TUINT64, TUINT8):
a = AMOVBZ;
break;
case CASE(TINT16, TINT16): // same size
case CASE(TUINT16, TINT16):
case CASE(TINT32, TINT16): // truncate
case CASE(TUINT32, TINT16):
case CASE(TINT64, TINT16):
case CASE(TUINT64, TINT16):
a = AMOVH;
case CASE(TINT16, TUINT16): // same size
case CASE(TUINT16, TUINT16):
case CASE(TINT32, TUINT16): // truncate
case CASE(TUINT32, TUINT16):
case CASE(TINT64, TUINT16):
case CASE(TUINT64, TUINT16):
a = AMOVHZ;
break;
case CASE(TINT32, TINT32): // same size
case CASE(TUINT32, TINT32):
case CASE(TINT64, TINT32): // truncate
case CASE(TUINT64, TINT32):
a = AMOVW;
break;
case CASE(TINT32, TUINT32): // same size
case CASE(TUINT32, TUINT32):
case CASE(TINT64, TUINT32):
case CASE(TUINT64, TUINT32):
a = AMOVWZ;
break;
case CASE(TINT64, TINT64): // same size
case CASE(TINT64, TUINT64):
case CASE(TUINT64, TINT64):
case CASE(TUINT64, TUINT64):
a = AMOVD;
break;
/*
* integer up-conversions
*/
case CASE(TINT8, TINT16): // sign extend int8
case CASE(TINT8, TUINT16):
case CASE(TINT8, TINT32):
case CASE(TINT8, TUINT32):
case CASE(TINT8, TINT64):
case CASE(TINT8, TUINT64):
a = AMOVB;
goto rdst;
case CASE(TUINT8, TINT16): // zero extend uint8
case CASE(TUINT8, TUINT16):
case CASE(TUINT8, TINT32):
case CASE(TUINT8, TUINT32):
case CASE(TUINT8, TINT64):
case CASE(TUINT8, TUINT64):
a = AMOVBZ;
goto rdst;
case CASE(TINT16, TINT32): // sign extend int16
case CASE(TINT16, TUINT32):
case CASE(TINT16, TINT64):
case CASE(TINT16, TUINT64):
a = AMOVH;
goto rdst;
case CASE(TUINT16, TINT32): // zero extend uint16
case CASE(TUINT16, TUINT32):
case CASE(TUINT16, TINT64):
case CASE(TUINT16, TUINT64):
a = AMOVHZ;
goto rdst;
case CASE(TINT32, TINT64): // sign extend int32
case CASE(TINT32, TUINT64):
a = AMOVW;
goto rdst;
case CASE(TUINT32, TINT64): // zero extend uint32
case CASE(TUINT32, TUINT64):
a = AMOVWZ;
goto rdst;
/*
* float to integer
*/
case CASE(TFLOAT32, TINT32):
case CASE(TFLOAT64, TINT32):
case CASE(TFLOAT32, TINT64):
case CASE(TFLOAT64, TINT64):
case CASE(TFLOAT32, TINT16):
case CASE(TFLOAT32, TINT8):
case CASE(TFLOAT32, TUINT16):
case CASE(TFLOAT32, TUINT8):
case CASE(TFLOAT64, TINT16):
case CASE(TFLOAT64, TINT8):
case CASE(TFLOAT64, TUINT16):
case CASE(TFLOAT64, TUINT8):
case CASE(TFLOAT32, TUINT32):
case CASE(TFLOAT64, TUINT32):
case CASE(TFLOAT32, TUINT64):
case CASE(TFLOAT64, TUINT64):
//warn("gmove: convert float to int not implemented: %N -> %N\n", f, t);
//return;
// algorithm is:
// if small enough, use native float64 -> int64 conversion.
// otherwise, subtract 2^63, convert, and add it back.
bignodes();
regalloc(&r1, types[ft], f);
gmove(f, &r1);
if(tt == TUINT64) {
regalloc(&r2, types[TFLOAT64], N);
gmove(&bigf, &r2);
gins(AFCMPU, &r1, &r2);
p1 = gbranch(optoas(OLT, types[TFLOAT64]), T, +1);
gins(AFSUB, &r2, &r1);
patch(p1, pc);
regfree(&r2);
}
regalloc(&r2, types[TFLOAT64], N);
regalloc(&r3, types[TINT64], t);
gins(AFCTIDZ, &r1, &r2);
p1 = gins(AFMOVD, &r2, N);
p1->to.type = D_OREG;
p1->to.reg = REGSP;
p1->to.offset = -8;
p1 = gins(AMOVD, N, &r3);
p1->from.type = D_OREG;
p1->from.reg = REGSP;
p1->from.offset = -8;
regfree(&r2);
regfree(&r1);
if(tt == TUINT64) {
p1 = gbranch(optoas(OLT, types[TFLOAT64]), T, +1); // use CR0 here again
nodreg(&r1, types[TINT64], D_R0+REGTMP);
gins(AMOVD, &bigi, &r1);
gins(AADD, &r1, &r3);
patch(p1, pc);
}
gmove(&r3, t);
regfree(&r3);
return;
/*
* integer to float
*/
case CASE(TINT32, TFLOAT32):
case CASE(TINT32, TFLOAT64):
case CASE(TINT64, TFLOAT32):
case CASE(TINT64, TFLOAT64):
case CASE(TINT16, TFLOAT32):
case CASE(TINT16, TFLOAT64):
case CASE(TINT8, TFLOAT32):
case CASE(TINT8, TFLOAT64):
case CASE(TUINT16, TFLOAT32):
case CASE(TUINT16, TFLOAT64):
case CASE(TUINT8, TFLOAT32):
case CASE(TUINT8, TFLOAT64):
case CASE(TUINT32, TFLOAT32):
case CASE(TUINT32, TFLOAT64):
case CASE(TUINT64, TFLOAT32):
case CASE(TUINT64, TFLOAT64):
//warn("gmove: convert int to float not implemented: %N -> %N\n", f, t);
//return;
// algorithm is:
// if small enough, use native int64 -> uint64 conversion.
// otherwise, halve (rounding to odd?), convert, and double.
bignodes();
regalloc(&r1, types[TINT64], N);
gmove(f, &r1);
if(ft == TUINT64) {
nodreg(&r2, types[TUINT64], D_R0+REGTMP);
gmove(&bigi, &r2);
gins(ACMPU, &r1, &r2);
p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1);
p2 = gins(ASRD, N, &r1);
p2->from.type = D_CONST;
p2->from.offset = 1;
patch(p1, pc);
}
regalloc(&r2, types[TFLOAT64], t);
p1 = gins(AMOVD, &r1, N);
p1->to.type = D_OREG;
p1->to.reg = REGSP;
p1->to.offset = -8;
p1 = gins(AFMOVD, N, &r2);
p1->from.type = D_OREG;
p1->from.reg = REGSP;
p1->from.offset = -8;
gins(AFCFID, &r2, &r2);
regfree(&r1);
if(ft == TUINT64) {
p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1); // use CR0 here again
nodreg(&r1, types[TFLOAT64], D_F0+FREGTWO);
gins(AFMUL, &r1, &r2);
patch(p1, pc);
}
gmove(&r2, t);
regfree(&r2);
return;
/*
* float to float
*/
case CASE(TFLOAT32, TFLOAT32):
a = AFMOVS;
break;
case CASE(TFLOAT64, TFLOAT64):
a = AFMOVD;
break;
case CASE(TFLOAT32, TFLOAT64):
a = AFMOVS;
goto rdst;
case CASE(TFLOAT64, TFLOAT32):
a = AFRSP;
goto rdst;
}
gins(a, f, t);
return;
rdst:
// requires register destination
regalloc(&r1, t->type, t);
gins(a, f, &r1);
gmove(&r1, t);
regfree(&r1);
return;
hard:
// requires register intermediate
regalloc(&r1, cvt, t);
gmove(f, &r1);
gmove(&r1, t);
regfree(&r1);
return;
}
int
samaddr(Node *f, Node *t)
{
if(f->op != t->op)
return 0;
switch(f->op) {
case OREGISTER:
if(f->val.u.reg != t->val.u.reg)
break;
return 1;
}
return 0;
}
/*
* generate one instruction:
* as f, t
*/
Prog*
gins(int as, Node *f, Node *t)
{
//int32 w;
Prog *p;
Addr af, at;
switch(as) {
case AMOVW:
case AMOVD:
case AFMOVS:
case AFMOVD:
if(f != N && t != N && samaddr(f, t))
return nil;
break;
}
memset(&af, 0, sizeof af);
memset(&at, 0, sizeof at);
if(f != N)
naddr(f, &af, 1);
if(t != N)
naddr(t, &at, 1);
p = prog(as);
if(f != N)
p->from = af;
if(t != N)
p->to = at;
if(debug['g'])
print("%P\n", p);
// TODO(minux): enable these.
// right now it fails on MOVD $type."".TypeAssertionError(SB) [width=1], R7 [width=8]
/*
w = 0;
switch(as) {
case AMOVB:
case AMOVBU:
case AMOVBZ:
case AMOVBZU:
w = 1;
break;
case AMOVH:
case AMOVHU:
case AMOVHZ:
case AMOVHZU:
w = 2;
break;
case AMOVW:
case AMOVWU:
case AMOVWZ:
case AMOVWZU:
w = 4;
break;
case AMOVD:
case AMOVDU:
w = 8;
break;
}
if(w != 0 && ((f != N && af.width < w) || (t != N && at.width > w))) {
dump("f", f);
dump("t", t);
fatal("bad width: %P (%d, %d)\n", p, af.width, at.width);
}
*/
return p;
}
void
fixlargeoffset(Node *n)
{
Node a;
if(n == N)
return;
if(n->op != OINDREG)
return;
if(n->val.u.reg == D_R0+REGSP) // stack offset cannot be large
return;
if(n->xoffset != (int32)n->xoffset) {
// TODO(minux): offset too large, move into R31 and add to R31 instead.
// this is used only in test/fixedbugs/issue6036.go.
print("offset too large: %N\n", n);
noimpl;
a = *n;
a.op = OREGISTER;
a.type = types[tptr];
a.xoffset = 0;
cgen_checknil(&a);
ginscon(optoas(OADD, types[tptr]), n->xoffset, &a);
n->xoffset = 0;
}
}
/*
* generate code to compute n;
* make a refer to result.
*/
void
naddr(Node *n, Addr *a, int canemitcode)
{
Sym *s;
a->type = D_NONE;
a->name = D_NONE;
a->reg = NREG;
a->gotype = nil;
a->node = N;
a->etype = 0;
a->width = 0;
if(n == N)
return;
if(n->type != T && n->type->etype != TIDEAL) {
dowidth(n->type);
a->width = n->type->width;
}
switch(n->op) {
default:
fatal("naddr: bad %O %D", n->op, a);
break;
case ONAME:
a->etype = 0;
a->width = 0;
a->reg = NREG;
if(n->type != T) {
a->etype = simtype[n->type->etype];
a->width = n->type->width;
}
a->offset = n->xoffset;
s = n->sym;
a->node = n->orig;
//if(a->node >= (Node*)&n)
// fatal("stack node");
if(s == S)
s = lookup(".noname");
if(n->method) {
if(n->type != T)
if(n->type->sym != S)
if(n->type->sym->pkg != nil)
s = pkglookup(s->name, n->type->sym->pkg);
}
a->type = D_OREG;
switch(n->class) {
default:
fatal("naddr: ONAME class %S %d\n", n->sym, n->class);
case PEXTERN:
a->name = D_EXTERN;
break;
case PAUTO:
a->name = D_AUTO;
break;
case PPARAM:
case PPARAMOUT:
a->name = D_PARAM;
break;
case PFUNC:
a->name = D_EXTERN;
a->type = D_CONST;
a->width = widthptr;
s = funcsym(s);
break;
}
a->sym = linksym(s);
break;
case OLITERAL:
switch(n->val.ctype) {
default:
fatal("naddr: const %lT", n->type);
break;
case CTFLT:
a->type = D_FCONST;
a->u.dval = mpgetflt(n->val.u.fval);
break;
case CTINT:
case CTRUNE:
a->sym = nil;
a->type = D_CONST;
a->offset = mpgetfix(n->val.u.xval);
break;
case CTSTR:
datagostring(n->val.u.sval, a);
break;
case CTBOOL:
a->sym = nil;
a->type = D_CONST;
a->offset = n->val.u.bval;
break;
case CTNIL:
a->sym = nil;
a->type = D_CONST;
a->offset = 0;
break;
}
break;
case OREGISTER:
if(n->val.u.reg < D_F0) {
a->type = D_REG;
a->reg = n->val.u.reg;
} else {
a->type = D_FREG;
a->reg = n->val.u.reg - D_F0;
}
a->sym = nil;
break;
case OINDREG:
a->type = D_OREG;
a->reg = n->val.u.reg;
a->sym = linksym(n->sym);
a->offset = n->xoffset;
if(a->offset != (int32)a->offset)
yyerror("offset %lld too large for OINDREG", a->offset);
break;
case OPARAM:
// n->left is PHEAP ONAME for stack parameter.
// compute address of actual parameter on stack.
a->etype = simtype[n->left->type->etype];
a->width = n->left->type->width;
a->offset = n->xoffset;
a->sym = linksym(n->left->sym);
a->type = D_OREG;
a->name = D_PARAM;
a->node = n->left->orig;
break;
case OCLOSUREVAR:
if(!curfn->needctxt)
fatal("closurevar without needctxt");
a->type = D_OREG;
a->reg = REGENV;
a->offset = n->xoffset;
a->sym = nil;
break;
case OCFUNC:
naddr(n->left, a, canemitcode);
a->sym = linksym(n->left->sym);
break;
case OITAB:
// itable of interface value
naddr(n->left, a, canemitcode);
a->etype = simtype[tptr];
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
break;
case OSPTR:
// pointer in a string or slice
naddr(n->left, a, canemitcode);
if(a->type == D_CONST && a->offset == 0)
break; // ptr(nil)
a->etype = simtype[tptr];
a->offset += Array_array;
a->width = widthptr;
break;
case OLEN:
// len of string or slice
naddr(n->left, a, canemitcode);
a->etype = simtype[TINT];
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
a->offset += Array_nel;
break;
case OCAP:
// cap of string or slice
naddr(n->left, a, canemitcode);
a->etype = simtype[TINT];
if(a->type == D_CONST && a->offset == 0)
break; // cap(nil)
a->offset += Array_cap;
break;
case OADDR:
naddr(n->left, a, canemitcode);
a->etype = tptr;
switch(a->type) {
case D_OREG:
a->type = D_CONST;
break;
case D_REG:
case D_CONST:
break;
default:
fatal("naddr: OADDR %d\n", a->type);
}
}
}
/*
* return Axxx for Oxxx on type t.
*/
int
optoas(int op, Type *t)
{
int a;
if(t == T)
fatal("optoas: t is nil");
a = AGOK;
switch(CASE(op, simtype[t->etype])) {
default:
fatal("optoas: no entry for op=%O type=%T", op, t);
break;
case CASE(OEQ, TBOOL):
case CASE(OEQ, TINT8):
case CASE(OEQ, TUINT8):
case CASE(OEQ, TINT16):
case CASE(OEQ, TUINT16):
case CASE(OEQ, TINT32):
case CASE(OEQ, TUINT32):
case CASE(OEQ, TINT64):
case CASE(OEQ, TUINT64):
case CASE(OEQ, TPTR32):
case CASE(OEQ, TPTR64):
case CASE(OEQ, TFLOAT32):
case CASE(OEQ, TFLOAT64):
a = ABEQ;
break;
case CASE(ONE, TBOOL):
case CASE(ONE, TINT8):
case CASE(ONE, TUINT8):
case CASE(ONE, TINT16):
case CASE(ONE, TUINT16):
case CASE(ONE, TINT32):
case CASE(ONE, TUINT32):
case CASE(ONE, TINT64):
case CASE(ONE, TUINT64):
case CASE(ONE, TPTR32):
case CASE(ONE, TPTR64):
case CASE(ONE, TFLOAT32):
case CASE(ONE, TFLOAT64):
a = ABNE;
break;
case CASE(OLT, TINT8): // ACMP
case CASE(OLT, TINT16):
case CASE(OLT, TINT32):
case CASE(OLT, TINT64):
case CASE(OLT, TUINT8): // ACMPU
case CASE(OLT, TUINT16):
case CASE(OLT, TUINT32):
case CASE(OLT, TUINT64):
case CASE(OLT, TFLOAT32): // AFCMPU
case CASE(OLT, TFLOAT64):
a = ABLT;
break;
case CASE(OLE, TINT8): // ACMP
case CASE(OLE, TINT16):
case CASE(OLE, TINT32):
case CASE(OLE, TINT64):
case CASE(OLE, TUINT8): // ACMPU
case CASE(OLE, TUINT16):
case CASE(OLE, TUINT32):
case CASE(OLE, TUINT64):
case CASE(OLE, TFLOAT32): // AFCMPU
case CASE(OLE, TFLOAT64):
a = ABLE;
break;
case CASE(OGT, TINT8):
case CASE(OGT, TINT16):
case CASE(OGT, TINT32):
case CASE(OGT, TINT64):
case CASE(OGT, TUINT8):
case CASE(OGT, TUINT16):
case CASE(OGT, TUINT32):
case CASE(OGT, TUINT64):
case CASE(OGT, TFLOAT32):
case CASE(OGT, TFLOAT64):
a = ABGT;
break;
case CASE(OGE, TINT8):
case CASE(OGE, TINT16):
case CASE(OGE, TINT32):
case CASE(OGE, TINT64):
case CASE(OGE, TUINT8):
case CASE(OGE, TUINT16):
case CASE(OGE, TUINT32):
case CASE(OGE, TUINT64):
case CASE(OGE, TFLOAT32):
case CASE(OGE, TFLOAT64):
a = ABGE;
break;
case CASE(OCMP, TBOOL):
case CASE(OCMP, TINT8):
case CASE(OCMP, TINT16):
case CASE(OCMP, TINT32):
case CASE(OCMP, TPTR32):
case CASE(OCMP, TINT64):
a = ACMP;
break;
case CASE(OCMP, TUINT8):
case CASE(OCMP, TUINT16):
case CASE(OCMP, TUINT32):
case CASE(OCMP, TUINT64):
case CASE(OCMP, TPTR64):
a = ACMPU;
break;
case CASE(OCMP, TFLOAT32):
case CASE(OCMP, TFLOAT64):
a = AFCMPU;
break;
case CASE(OAS, TBOOL):
case CASE(OAS, TINT8):
a = AMOVB;
break;
case CASE(OAS, TUINT8):
a = AMOVBZ;
break;
case CASE(OAS, TINT16):
a = AMOVH;
break;
case CASE(OAS, TUINT16):
a = AMOVHZ;
break;
case CASE(OAS, TINT32):
a = AMOVW;
break;
case CASE(OAS, TUINT32):
case CASE(OAS, TPTR32):
a = AMOVWZ;
break;
case CASE(OAS, TINT64):
case CASE(OAS, TUINT64):
case CASE(OAS, TPTR64):
a = AMOVD;
break;
case CASE(OAS, TFLOAT32):
a = AFMOVS;
break;
case CASE(OAS, TFLOAT64):
a = AFMOVD;
break;
case CASE(OADD, TINT8):
case CASE(OADD, TUINT8):
case CASE(OADD, TINT16):
case CASE(OADD, TUINT16):
case CASE(OADD, TINT32):
case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32):
case CASE(OADDPTR, TPTR32):
case CASE(OADD, TINT64):
case CASE(OADD, TUINT64):
case CASE(OADD, TPTR64):
case CASE(OADDPTR, TPTR64):
a = AADD;
break;
case CASE(OADD, TFLOAT32):
a = AFADDS;
break;
case CASE(OADD, TFLOAT64):
a = AFADD;
break;
case CASE(OSUB, TINT8):
case CASE(OSUB, TUINT8):
case CASE(OSUB, TINT16):
case CASE(OSUB, TUINT16):
case CASE(OSUB, TINT32):
case CASE(OSUB, TUINT32):
case CASE(OSUB, TPTR32):
case CASE(OSUB, TINT64):
case CASE(OSUB, TUINT64):
case CASE(OSUB, TPTR64):
a = ASUB;
break;
case CASE(OSUB, TFLOAT32):
a = AFSUBS;
break;
case CASE(OSUB, TFLOAT64):
a = AFSUB;
break;
case CASE(OMINUS, TINT8):
case CASE(OMINUS, TUINT8):
case CASE(OMINUS, TINT16):
case CASE(OMINUS, TUINT16):
case CASE(OMINUS, TINT32):
case CASE(OMINUS, TUINT32):
case CASE(OMINUS, TPTR32):
case CASE(OMINUS, TINT64):
case CASE(OMINUS, TUINT64):
case CASE(OMINUS, TPTR64):
a = ANEG;
break;
case CASE(OAND, TINT8):
case CASE(OAND, TUINT8):
case CASE(OAND, TINT16):
case CASE(OAND, TUINT16):
case CASE(OAND, TINT32):
case CASE(OAND, TUINT32):
case CASE(OAND, TPTR32):
case CASE(OAND, TINT64):
case CASE(OAND, TUINT64):
case CASE(OAND, TPTR64):
a = AAND;
break;
case CASE(OOR, TINT8):
case CASE(OOR, TUINT8):
case CASE(OOR, TINT16):
case CASE(OOR, TUINT16):
case CASE(OOR, TINT32):
case CASE(OOR, TUINT32):
case CASE(OOR, TPTR32):
case CASE(OOR, TINT64):
case CASE(OOR, TUINT64):
case CASE(OOR, TPTR64):
a = AOR;
break;
case CASE(OXOR, TINT8):
case CASE(OXOR, TUINT8):
case CASE(OXOR, TINT16):
case CASE(OXOR, TUINT16):
case CASE(OXOR, TINT32):
case CASE(OXOR, TUINT32):
case CASE(OXOR, TPTR32):
case CASE(OXOR, TINT64):
case CASE(OXOR, TUINT64):
case CASE(OXOR, TPTR64):
a = AXOR;
break;
// TODO(minux): handle rotates
//case CASE(OLROT, TINT8):
//case CASE(OLROT, TUINT8):
//case CASE(OLROT, TINT16):
//case CASE(OLROT, TUINT16):
//case CASE(OLROT, TINT32):
//case CASE(OLROT, TUINT32):
//case CASE(OLROT, TPTR32):
//case CASE(OLROT, TINT64):
//case CASE(OLROT, TUINT64):
//case CASE(OLROT, TPTR64):
// a = 0//???; RLDC?
// break;
case CASE(OLSH, TINT8):
case CASE(OLSH, TUINT8):
case CASE(OLSH, TINT16):
case CASE(OLSH, TUINT16):
case CASE(OLSH, TINT32):
case CASE(OLSH, TUINT32):
case CASE(OLSH, TPTR32):
case CASE(OLSH, TINT64):
case CASE(OLSH, TUINT64):
case CASE(OLSH, TPTR64):
a = ASLD;
break;
case CASE(ORSH, TUINT8):
case CASE(ORSH, TUINT16):
case CASE(ORSH, TUINT32):
case CASE(ORSH, TPTR32):
case CASE(ORSH, TUINT64):
case CASE(ORSH, TPTR64):
a = ASRD;
break;
case CASE(ORSH, TINT8):
case CASE(ORSH, TINT16):
case CASE(ORSH, TINT32):
case CASE(ORSH, TINT64):
a = ASRAD;
break;
// TODO(minux): handle rotates
//case CASE(ORROTC, TINT8):
//case CASE(ORROTC, TUINT8):
//case CASE(ORROTC, TINT16):
//case CASE(ORROTC, TUINT16):
//case CASE(ORROTC, TINT32):
//case CASE(ORROTC, TUINT32):
//case CASE(ORROTC, TINT64):
//case CASE(ORROTC, TUINT64):
// a = 0//??? RLDC??
// break;
case CASE(OHMUL, TINT64):
a = AMULHD;
break;
case CASE(OHMUL, TUINT64):
case CASE(OHMUL, TPTR64):
a = AMULHDU;
break;
case CASE(OMUL, TINT8):
case CASE(OMUL, TINT16):
case CASE(OMUL, TINT32):
case CASE(OMUL, TINT64):
a = AMULLD;
break;
case CASE(OMUL, TUINT8):
case CASE(OMUL, TUINT16):
case CASE(OMUL, TUINT32):
case CASE(OMUL, TPTR32):
// don't use word multiply, the high 32-bit are undefined.
// fallthrough
case CASE(OMUL, TUINT64):
case CASE(OMUL, TPTR64):
a = AMULLD; // for 64-bit multiplies, signedness doesn't matter.
break;
case CASE(OMUL, TFLOAT32):
a = AFMULS;
break;
case CASE(OMUL, TFLOAT64):
a = AFMUL;
break;
case CASE(ODIV, TINT8):
case CASE(ODIV, TINT16):
case CASE(ODIV, TINT32):
case CASE(ODIV, TINT64):
a = ADIVD;
break;
case CASE(ODIV, TUINT8):
case CASE(ODIV, TUINT16):
case CASE(ODIV, TUINT32):
case CASE(ODIV, TPTR32):
case CASE(ODIV, TUINT64):
case CASE(ODIV, TPTR64):
a = ADIVDU;
break;
case CASE(ODIV, TFLOAT32):
a = AFDIVS;
break;
case CASE(ODIV, TFLOAT64):
a = AFDIV;
break;
}
return a;
}
enum
{
ODynam = 1<<0,
OAddable = 1<<1,
};
int
xgen(Node *n, Node *a, int o)
{
// TODO(minux)
USED(n); USED(a); USED(o);
return -1;
}
void
sudoclean(void)
{
return;
}
/*
* generate code to compute address of n,
* a reference to a (perhaps nested) field inside
* an array or struct.
* return 0 on failure, 1 on success.
* on success, leaves usable address in a.
*
* caller is responsible for calling sudoclean
* after successful sudoaddable,
* to release the register used for a.
*/
int
sudoaddable(int as, Node *n, Addr *a)
{
// TODO(minux)
USED(as); USED(n); USED(a);
return 0;
}
// Derived from Inferno utils/6c/gc.h
// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "../gc/popt.h"
#define Z N
#define Adr Addr
#define BLOAD(r) band(bnot(r->refbehind), r->refahead)
#define BSTORE(r) band(bnot(r->calbehind), r->calahead)
#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z])
#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z])
#define CLOAD 5
#define CREF 5
#define CINF 1000
#define LOOP 3
typedef struct Reg Reg;
typedef struct Rgn Rgn;
/*c2go
extern Node *Z;
enum
{
CLOAD = 5,
CREF = 5,
CINF = 1000,
LOOP = 3,
};
uint32 BLOAD(Reg*);
uint32 BSTORE(Reg*);
uint32 LOAD(Reg*);
uint32 STORE(Reg*);
*/
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
// r->prog->opt points back to r.
struct Reg
{
Flow f;
Bits set; // variables written by this instruction.
Bits use1; // variables read by prog->from.
Bits use2; // variables read by prog->to.
Bits refbehind;
Bits refahead;
Bits calbehind;
Bits calahead;
Bits regdiff;
Bits act;
int32 regu; // register used bitmap
};
#define R ((Reg*)0)
/*c2go extern Reg *R; */
#define NRGN 600
/*c2go enum { NRGN = 600 }; */
struct Rgn
{
Reg* enter;
short cost;
short varno;
short regno;
};
EXTERN int32 exregoffset; // not set
EXTERN int32 exfregoffset; // not set
EXTERN Reg zreg;
EXTERN Rgn region[NRGN];
EXTERN Rgn* rgp;
EXTERN int nregion;
EXTERN int nvar;
EXTERN int32 regbits;
EXTERN int32 exregbits;
EXTERN Bits externs;
EXTERN Bits params;
EXTERN Bits consts;
EXTERN Bits addrs;
EXTERN Bits ivar;
EXTERN Bits ovar;
EXTERN int change;
EXTERN int32 maxnr;
EXTERN struct
{
int32 ncvtreg;
int32 nspill;
int32 nreload;
int32 ndelmov;
int32 nvar;
int32 naddr;
} ostats;
/*
* reg.c
*/
int rcmp(const void*, const void*);
void regopt(Prog*);
void addmove(Reg*, int, int, int);
Bits mkvar(Reg*, Adr*);
void prop(Reg*, Bits, Bits);
void synch(Reg*, Bits);
uint32 allreg(uint32, Rgn*);
void paint1(Reg*, int);
uint32 paint2(Reg*, int);
void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
void dumpone(Flow*, int);
void dumpit(char*, Flow*, int);
/*
* peep.c
*/
void peep(Prog*);
void excise(Flow*);
int copyu(Prog*, Adr*, Adr*);
uint64 RtoB(int);
uint64 FtoB(int);
int BtoR(uint64);
int BtoF(uint64);
/*
* prog.c
*/
typedef struct ProgInfo ProgInfo;
struct ProgInfo
{
uint32 flags; // the bits below
uint64 reguse; // required registers used by this instruction
uint64 regset; // required registers set by this instruction
uint64 regindex; // registers used by addressing mode
};
enum
{
// Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA.
Pseudo = 1<<1,
// There's nothing to say about the instruction,
// but it's still okay to see.
OK = 1<<2,
// Size of right-side write, or right-side read if no write.
SizeB = 1<<3,
SizeW = 1<<4,
SizeL = 1<<5,
SizeQ = 1<<6,
SizeF = 1<<7, // float aka float32
SizeD = 1<<8, // double aka float64
// Left side: address taken, read, write.
LeftAddr = 1<<9,
LeftRead = 1<<10,
LeftWrite = 1<<11,
// Register in middle; never written.
RegRead = 1<<12,
CanRegRead = 1<<13,
// Right side: address taken, read, write.
RightAddr = 1<<14,
RightRead = 1<<15,
RightWrite = 1<<16,
PostInc = 1<<17,
// Instruction kinds
Move = 1<<18, // straight move
Conv = 1<<19, // size conversion
Cjmp = 1<<20, // conditional jump
Break = 1<<21, // breaks control flow (no fallthrough)
Call = 1<<22, // function call
Jump = 1<<23, // jump
Skip = 1<<24, // data instruction
};
void proginfo(ProgInfo*, Prog*);
// To allow use of AJMP, ACALL, ARET in ../gc/popt.c.
enum
{
AJMP = ABR,
ACALL = ABL,
ARET = ARETURN,
};
// Derived from Inferno utils/6c/peep.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
void
peep(Prog *p)
{
USED(p);
// TODO(minux)
return;
}
void
excise(Flow *r)
{
Prog *p;
p = r->prog;
if(debug['P'] && debug['v'])
print("%P ===delete===\n", p);
*p = zprog;
p->as = ANOP;
ostats.ndelmov++;
}
int
regtyp(Adr *a)
{
switch(a->type) {
default:
return 0;
case D_REG:
case D_FREG:
return 1;
}
}
int
sameaddr(Addr *a, Addr *v)
{
if(a->type != v->type)
return 0;
if(regtyp(v) && a->reg == v->reg)
return 1;
if(v->type == D_AUTO || v->type == D_PARAM)
if(v->offset == a->offset)
return 1;
return 0;
}
int
smallindir(Addr *a, Addr *reg)
{
return reg->type == D_REG && a->type == D_OREG &&
a->reg == reg->reg &&
0 <= a->offset && a->offset < 4096;
}
int
stackaddr(Addr *a)
{
return a->type == D_REG && a->reg == REGSP;
}
// Copyright 2014 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 <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
enum {
LeftRdwr = LeftRead | LeftWrite,
RightRdwr = RightRead | RightWrite,
};
// This table gives the basic information about instruction
// generated by the compiler and processed in the optimizer.
// See opt.h for bit definitions.
//
// Instructions not generated need not be listed.
// As an exception to that rule, we typically write down all the
// size variants of an operation even if we just use a subset.
//
// The table is formatted for 8-space tabs.
static ProgInfo progtable[ALAST] = {
[ATYPE]= {Pseudo | Skip},
[ATEXT]= {Pseudo},
[AFUNCDATA]= {Pseudo},
[APCDATA]= {Pseudo},
[AUNDEF]= {Break},
[AUSEFIELD]= {OK},
[ACHECKNIL]= {LeftRead},
[AVARDEF]= {Pseudo | RightWrite},
[AVARKILL]= {Pseudo | RightWrite},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.
[ANOP]= {LeftRead | RightWrite},
// Integer
[AADD]= {SizeQ | LeftRead | RegRead | RightWrite},
[ASUB]= {SizeQ | LeftRead | RegRead | RightWrite},
[ANEG]= {SizeQ | LeftRead | RegRead | RightWrite},
[AAND]= {SizeQ | LeftRead | RegRead | RightWrite},
[AOR]= {SizeQ | LeftRead | RegRead | RightWrite},
[AXOR]= {SizeQ | LeftRead | RegRead | RightWrite},
[AMULLD]= {SizeQ | LeftRead | RegRead | RightWrite},
[AMULLW]= {SizeL | LeftRead | RegRead | RightWrite},
[AMULHD]= {SizeL | LeftRead | RegRead | RightWrite},
[AMULHDU]= {SizeL | LeftRead | RegRead | RightWrite},
[ADIVD]= {SizeQ | LeftRead | RegRead | RightWrite},
[ADIVDU]= {SizeQ | LeftRead | RegRead | RightWrite},
[ASLD]= {SizeQ | LeftRead | RegRead | RightWrite},
[ASRD]= {SizeQ | LeftRead | RegRead | RightWrite},
[ASRAD]= {SizeQ | LeftRead | RegRead | RightWrite},
[ACMP]= {SizeQ | LeftRead | RightRead},
[ACMPU]= {SizeQ | LeftRead | RightRead},
[ATD]= {SizeQ | RightRead},
// Floating point.
[AFADD]= {SizeD | LeftRead | RegRead | RightWrite},
[AFADDS]= {SizeF | LeftRead | RegRead | RightWrite},
[AFSUB]= {SizeD | LeftRead | RegRead | RightWrite},
[AFSUBS]= {SizeF | LeftRead | RegRead | RightWrite},
[AFMUL]= {SizeD | LeftRead | RegRead | RightWrite},
[AFMULS]= {SizeF | LeftRead | RegRead | RightWrite},
[AFDIV]= {SizeD | LeftRead | RegRead | RightWrite},
[AFDIVS]= {SizeF | LeftRead | RegRead | RightWrite},
[AFCTIDZ]= {SizeF | LeftRead | RegRead | RightWrite},
[AFCFID]= {SizeF | LeftRead | RegRead | RightWrite},
[AFCMPU]= {SizeD | LeftRead | RightRead},
[AFRSP]= {SizeD | LeftRead | RightWrite | Conv},
// Moves
[AMOVB]= {SizeB | LeftRead | RightWrite | Move | Conv},
[AMOVBU]= {SizeB | LeftRead | RightWrite | Move | Conv | PostInc},
[AMOVBZ]= {SizeB | LeftRead | RightWrite | Move | Conv},
[AMOVH]= {SizeW | LeftRead | RightWrite | Move | Conv},
[AMOVHU]= {SizeW | LeftRead | RightWrite | Move | Conv | PostInc},
[AMOVHZ]= {SizeW | LeftRead | RightWrite | Move | Conv},
[AMOVW]= {SizeL | LeftRead | RightWrite | Move | Conv},
// there is no AMOVWU.
[AMOVWZU]= {SizeL | LeftRead | RightWrite | Move | Conv | PostInc},
[AMOVWZ]= {SizeL | LeftRead | RightWrite | Move | Conv},
[AMOVD]= {SizeQ | LeftRead | RightWrite | Move},
[AMOVDU]= {SizeQ | LeftRead | RightWrite | Move | PostInc},
[AFMOVS]= {SizeF | LeftRead | RightWrite | Move | Conv},
[AFMOVD]= {SizeD | LeftRead | RightWrite | Move},
// Jumps
[ABR]= {Jump | Break},
[ABL]= {Call},
[ABEQ]= {Cjmp},
[ABNE]= {Cjmp},
[ABGE]= {Cjmp},
[ABLT]= {Cjmp},
[ABGT]= {Cjmp},
[ABLE]= {Cjmp},
[ARETURN]= {Break},
// In addtion, duffzero reads R0,R2 and writes R2. This fact must be
// encoded in peep.c (TODO)
[ADUFFZERO]= {Call},
// In addtion, duffcopy reads R0,R2,R3 and writes R2,R3. This fact must be
// encoded in peep.c (TODO)
[ADUFFCOPY]= {Call},
};
void
proginfo(ProgInfo *info, Prog *p)
{
*info = progtable[p->as];
if(info->flags == 0) {
*info = progtable[AADD];
fatal("proginfo: unknown instruction %P", p);
}
if((info->flags & RegRead) && p->reg == NREG) {
info->flags &= ~RegRead;
info->flags |= /*CanRegRead |*/ RightRead;
}
if(p->from.type == D_OREG && p->from.reg != NREG) {
info->reguse |= RtoB(p->from.reg);
if(info->flags & PostInc) {
info->regset |= RtoB(p->from.reg);
}
}
if(p->to.type == D_OREG && p->to.reg != NREG) {
info->reguse |= RtoB(p->to.reg);
if(info->flags & PostInc) {
info->regset |= RtoB(p->to.reg);
}
}
if(p->from.type == D_CONST && p->from.sym != nil && (info->flags & LeftRead)) {
info->flags &= ~LeftRead;
info->flags |= LeftAddr;
}
}
// Derived from Inferno utils/6c/reg.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
void
regopt(Prog *p)
{
USED(p);
// TODO(minux)
return;
}
/*
* track register variables including external registers:
* bit reg
* 0 R0
* 1 R1
* ... ...
* 31 R31
* 32+0 F0
* 32+1 F1
* ... ...
* 32+31 F31
*/
uint64
RtoB(int r)
{
if(r >= D_R0 && r <= D_R0+31)
return 1ULL << (r - D_R0);
return 0;
}
int
BtoR(uint64 b)
{
b &= 0xffffffff;
if(b == 0)
return 0;
return bitno(b) + D_R0;
}
uint64
FtoB(int r)
{
if(r >= D_F0 && r <= D_F0+31)
return 1ULL << (32 + r - D_F0);
return 0;
}
int
BtoF(uint64 b)
{
b >>= 32;
if(b == 0)
return 0;
return bitno(b) + D_F0;
}
void
dumpone(Flow *f, int isreg)
{
int z;
Bits bit;
Reg *r;
print("%d:%P", f->loop, f->prog);
if(isreg) {
r = (Reg*)f;
for(z=0; z<BITS; z++)
bit.b[z] =
r->set.b[z] |
r->use1.b[z] |
r->use2.b[z] |
r->refbehind.b[z] |
r->refahead.b[z] |
r->calbehind.b[z] |
r->calahead.b[z] |
r->regdiff.b[z] |
r->act.b[z] |
0;
if(bany(&bit)) {
print("\t");
if(bany(&r->set))
print(" s:%Q", r->set);
if(bany(&r->use1))
print(" u1:%Q", r->use1);
if(bany(&r->use2))
print(" u2:%Q", r->use2);
if(bany(&r->refbehind))
print(" rb:%Q ", r->refbehind);
if(bany(&r->refahead))
print(" ra:%Q ", r->refahead);
if(bany(&r->calbehind))
print(" cb:%Q ", r->calbehind);
if(bany(&r->calahead))
print(" ca:%Q ", r->calahead);
if(bany(&r->regdiff))
print(" d:%Q ", r->regdiff);
if(bany(&r->act))
print(" a:%Q ", r->act);
}
}
print("\n");
}
void
dumpit(char *str, Flow *r0, int isreg)
{
Flow *r, *r1;
print("\n%s\n", str);
for(r = r0; r != nil; r = r->link) {
dumpone(r, isreg);
r1 = r->p2;
if(r1 != nil) {
print(" pred:");
for(; r1 != nil; r1 = r1->p2link)
print(" %.4ud", (int)r1->prog->pc);
print("\n");
}
// r1 = r->s1;
// if(r1 != R) {
// print(" succ:");
// for(; r1 != R; r1 = r1->s1)
// print(" %.4ud", (int)r1->prog->pc);
// print("\n");
// }
}
}
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