blob: 05e49fa7320aa70bd7c745db452ef3298f4490d7 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "gg.h"
/*
* attempt to generate 64-bit
* res = n
* return 1 on success, 0 if op not handled.
*/
void
cgen64(Node *n, Node *res)
{
Node t1, t2, *l, *r;
Node lo1, lo2, hi1, hi2;
Node al, ah, bl, bh, cl, ch; //, s1, s2;
Prog *p1;
//, *p2;
uint64 v;
// uint32 lv, hv;
if(res->op != OINDREG && res->op != ONAME) {
dump("n", n);
dump("res", res);
fatal("cgen64 %O of %O", n->op, res->op);
}
switch(n->op) {
default:
fatal("cgen64 %O", n->op);
// case OMINUS:
// cgen(n->left, res);
// split64(res, &lo1, &hi1);
// gins(ANEGL, N, &lo1);
// gins(AADCL, ncon(0), &hi1);
// gins(ANEGL, N, &hi1);
// splitclean();
// return;
// case OCOM:
// cgen(n->left, res);
// split64(res, &lo1, &hi1);
// gins(ANOTL, N, &lo1);
// gins(ANOTL, N, &hi1);
// splitclean();
// return;
case OADD:
case OSUB:
case OMUL:
case OLSH:
case ORSH:
case OAND:
case OOR:
case OXOR:
// binary operators.
// common setup below.
break;
}
l = n->left;
r = n->right;
if(!l->addable) {
tempalloc(&t1, l->type);
cgen(l, &t1);
l = &t1;
}
if(r != N && !r->addable) {
tempalloc(&t2, r->type);
cgen(r, &t2);
r = &t2;
}
// Setup for binary operation.
split64(l, &lo1, &hi1);
if(is64(r->type))
split64(r, &lo2, &hi2);
regalloc(&al, lo1.type, N);
regalloc(&ah, hi1.type, N);
// Do op. Leave result in ah:al.
switch(n->op) {
default:
fatal("cgen64: not implemented: %N\n", n);
case OADD:
// TODO: Constants
regalloc(&bl, types[TPTR32], N);
regalloc(&bh, types[TPTR32], N);
gins(AMOVW, &hi1, &ah);
gins(AMOVW, &lo1, &al);
gins(AMOVW, &hi2, &bh);
gins(AMOVW, &lo2, &bl);
gins(AADD, &bl, &al);
gins(AADC, &bh, &ah);
regfree(&bl);
regfree(&bh);
break;
case OSUB:
// TODO: Constants.
gins(AMOVW, &lo1, &al);
gins(AMOVW, &hi1, &ah);
gins(ASUB, &lo2, &al);
gins(ASBC, &hi2, &ah);
break;
case OMUL:
// TODO(kaib): this can be done with 4 regs and does not need 6
regalloc(&bh, types[TPTR32], N);
regalloc(&bl, types[TPTR32], N);
regalloc(&ch, types[TPTR32], N);
regalloc(&cl, types[TPTR32], N);
// load args into bh:bl and bh:bl.
gins(AMOVW, &hi1, &bh);
gins(AMOVW, &lo1, &bl);
gins(AMOVW, &hi2, &ch);
gins(AMOVW, &lo2, &cl);
// bl * cl
p1 = gins(AMULLU, N, N);
p1->from.type = D_REG;
p1->from.reg = bl.val.u.reg;
p1->reg = cl.val.u.reg;
p1->to.type = D_REGREG;
p1->to.reg = al.val.u.reg;
p1->to.offset = ah.val.u.reg;
//print("%P\n", p1);
// bl * ch
p1 = gins(AMULALU, N, N);
p1->from.type = D_REG;
p1->from.reg = ah.val.u.reg;
p1->reg = bl.val.u.reg;
p1->to.type = D_REGREG;
p1->to.reg = ch.val.u.reg;
p1->to.offset = ah.val.u.reg;
//print("%P\n", p1);
// bh * cl
p1 = gins(AMULALU, N, N);
p1->from.type = D_REG;
p1->from.reg = ah.val.u.reg;
p1->reg = bh.val.u.reg;
p1->to.type = D_REGREG;
p1->to.reg = cl.val.u.reg;
p1->to.offset = ah.val.u.reg;
//print("%P\n", p1);
regfree(&bh);
regfree(&bl);
regfree(&ch);
regfree(&cl);
break;
case OLSH:
if(r->op == OLITERAL) {
v = mpgetfix(r->val.u.xval);
if(v >= 64) {
// TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al)
// here and below (verify it optimizes to EOR)
gins(AEOR, &al, &al);
gins(AEOR, &ah, &ah);
break;
}
if(v >= 32) {
gins(AEOR, &al, &al);
// MOVW lo1<<(v-32), ah
p1 = gins(AMOVW, &lo1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LL | (v-32)<<7 | lo1.val.u.reg;
p1->from.reg = NREG;
break;
}
// general literal left shift
// MOVW lo1<<v, al
p1 = gins(AMOVW, &lo1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LL | v<<7 | lo1.val.u.reg;
p1->from.reg = NREG;
break;
// MOVW hi1<<v, ah
p1 = gins(AMOVW, &hi1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LL | v<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
break;
// OR lo1>>(32-v), ah
p1 = gins(AORR, &lo1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LR | (32-v)<<7 | lo1.val.u.reg;
p1->from.reg = NREG;
break;
}
fatal("cgen64 OLSH, !OLITERAL not implemented");
break;
case ORSH:
if(r->op == OLITERAL) {
v = mpgetfix(r->val.u.xval);
if(v >= 64) {
if(hi1.type->etype == TINT32) {
// MOVW hi1->31, al
p1 = gins(AMOVW, &hi1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_AR | 31 << 7 | hi1.val.u.reg;
p1->from.reg = NREG;
// MOVW hi1->31, ah
p1 = gins(AMOVW, &hi1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_AR | 31 << 7 | hi1.val.u.reg;
p1->from.reg = NREG;
} else {
gins(AEOR, &al, &al);
gins(AEOR, &ah, &ah);
}
break;
}
if(v >= 32) {
if(hi1.type->etype == TINT32) {
// MOVW hi1->(v-32), al
p1 = gins(AMOVW, &hi1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_AR | (v-32)<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
// MOVW hi1->31, ah
p1 = gins(AMOVW, &hi1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_AR | 31<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
} else {
// MOVW hi1>>(v-32), al
p1 = gins(AMOVW, &hi1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LR | (v-32)<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
gins(AEOR, &ah, &ah);
}
break;
}
// general literal right shift
// MOVW lo1>>v, al
p1 = gins(AMOVW, &lo1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LR | v<<7 | lo1.val.u.reg;
p1->from.reg = NREG;
// OR hi1<<(32-v), al, al
p1 = gins(AORR, &hi1, &al);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LL | (32-v)<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
p1->reg = al.val.u.reg;
if(hi1.type->etype == TINT32) {
// MOVW hi1->v, ah
p1 = gins(AMOVW, &hi1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_AR | v<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
} else {
// MOVW hi1>>v, ah
p1 = gins(AMOVW, &hi1, &ah);
p1->from.type = D_SHIFT;
p1->from.offset = SHIFT_LR | v<<7 | hi1.val.u.reg;
p1->from.reg = NREG;
}
break;
}
fatal("cgen64 ORSH, !OLITERAL not implemented");
// // load value into DX:AX.
// gins(AMOVL, &lo1, &ax);
// gins(AMOVL, &hi1, &dx);
// // load shift value into register.
// // if high bits are set, zero value.
// p1 = P;
// if(is64(r->type)) {
// gins(ACMPL, &hi2, ncon(0));
// p1 = gbranch(AJNE, T);
// gins(AMOVL, &lo2, &cx);
// } else
// gins(AMOVL, r, &cx);
// // if shift count is >=64, zero or sign-extend value
// gins(ACMPL, &cx, ncon(64));
// p2 = gbranch(optoas(OLT, types[TUINT32]), T);
// if(p1 != P)
// patch(p1, pc);
// if(hi1.type->etype == TINT32) {
// gins(ASARL, ncon(31), &dx);
// gins(AMOVL, &dx, &ax);
// } else {
// gins(AXORL, &dx, &dx);
// gins(AXORL, &ax, &ax);
// }
// patch(p2, pc);
// // if shift count is >= 32, sign-extend hi.
// gins(ACMPL, &cx, ncon(32));
// p1 = gbranch(optoas(OLT, types[TUINT32]), T);
// gins(AMOVL, &dx, &ax);
// if(hi1.type->etype == TINT32) {
// gins(ASARL, &cx, &ax); // SARL only uses bottom 5 bits of count
// gins(ASARL, ncon(31), &dx);
// } else {
// gins(ASHRL, &cx, &ax);
// gins(AXORL, &dx, &dx);
// }
// p2 = gbranch(AJMP, T);
// patch(p1, pc);
// // general shift
// p1 = gins(ASHRL, &cx, &ax);
// p1->from.index = D_DX; // double-width shift
// p1->from.scale = 0;
// gins(optoas(ORSH, hi1.type), &cx, &dx);
// patch(p2, pc);
// break;
case OXOR:
case OAND:
case OOR:
// TODO(kaib): literal optimizations
// make constant the right side (it usually is anyway).
// if(lo1.op == OLITERAL) {
// nswap(&lo1, &lo2);
// nswap(&hi1, &hi2);
// }
// if(lo2.op == OLITERAL) {
// // special cases for constants.
// lv = mpgetfix(lo2.val.u.xval);
// hv = mpgetfix(hi2.val.u.xval);
// splitclean(); // right side
// split64(res, &lo2, &hi2);
// switch(n->op) {
// case OXOR:
// gmove(&lo1, &lo2);
// gmove(&hi1, &hi2);
// switch(lv) {
// case 0:
// break;
// case 0xffffffffu:
// gins(ANOTL, N, &lo2);
// break;
// default:
// gins(AXORL, ncon(lv), &lo2);
// break;
// }
// switch(hv) {
// case 0:
// break;
// case 0xffffffffu:
// gins(ANOTL, N, &hi2);
// break;
// default:
// gins(AXORL, ncon(hv), &hi2);
// break;
// }
// break;
// case OAND:
// switch(lv) {
// case 0:
// gins(AMOVL, ncon(0), &lo2);
// break;
// default:
// gmove(&lo1, &lo2);
// if(lv != 0xffffffffu)
// gins(AANDL, ncon(lv), &lo2);
// break;
// }
// switch(hv) {
// case 0:
// gins(AMOVL, ncon(0), &hi2);
// break;
// default:
// gmove(&hi1, &hi2);
// if(hv != 0xffffffffu)
// gins(AANDL, ncon(hv), &hi2);
// break;
// }
// break;
// case OOR:
// switch(lv) {
// case 0:
// gmove(&lo1, &lo2);
// break;
// case 0xffffffffu:
// gins(AMOVL, ncon(0xffffffffu), &lo2);
// break;
// default:
// gmove(&lo1, &lo2);
// gins(AORL, ncon(lv), &lo2);
// break;
// }
// switch(hv) {
// case 0:
// gmove(&hi1, &hi2);
// break;
// case 0xffffffffu:
// gins(AMOVL, ncon(0xffffffffu), &hi2);
// break;
// default:
// gmove(&hi1, &hi2);
// gins(AORL, ncon(hv), &hi2);
// break;
// }
// break;
// }
// splitclean();
// splitclean();
// goto out;
// }
gins(AMOVW, &lo1, &al);
gins(AMOVW, &hi1, &ah);
gins(optoas(n->op, lo1.type), &lo2, &al);
gins(optoas(n->op, lo1.type), &hi2, &ah);
break;
}
if(is64(r->type))
splitclean();
splitclean();
split64(res, &lo1, &hi1);
gins(AMOVW, &al, &lo1);
gins(AMOVW, &ah, &hi1);
splitclean();
//out:
if(r == &t2)
tempfree(&t2);
if(l == &t1)
tempfree(&t1);
regfree(&al);
regfree(&ah);
}
/*
* generate comparison of nl, nr, both 64-bit.
* nl is memory; nr is constant or memory.
*/
void
cmp64(Node *nl, Node *nr, int op, Prog *to)
{
fatal("cmp64 not implemented");
// Node lo1, hi1, lo2, hi2, rr;
// Prog *br;
// Type *t;
// split64(nl, &lo1, &hi1);
// split64(nr, &lo2, &hi2);
// // compare most significant word;
// // if they differ, we're done.
// t = hi1.type;
// if(nl->op == OLITERAL || nr->op == OLITERAL)
// gins(ACMPL, &hi1, &hi2);
// else {
// regalloc(&rr, types[TINT32], N);
// gins(AMOVL, &hi1, &rr);
// gins(ACMPL, &rr, &hi2);
// regfree(&rr);
// }
// br = P;
// switch(op) {
// default:
// fatal("cmp64 %O %T", op, t);
// case OEQ:
// // cmp hi
// // jne L
// // cmp lo
// // jeq to
// // L:
// br = gbranch(AJNE, T);
// break;
// case ONE:
// // cmp hi
// // jne to
// // cmp lo
// // jne to
// patch(gbranch(AJNE, T), to);
// break;
// case OGE:
// case OGT:
// // cmp hi
// // jgt to
// // jlt L
// // cmp lo
// // jge to (or jgt to)
// // L:
// patch(gbranch(optoas(OGT, t), T), to);
// br = gbranch(optoas(OLT, t), T);
// break;
// case OLE:
// case OLT:
// // cmp hi
// // jlt to
// // jgt L
// // cmp lo
// // jle to (or jlt to)
// // L:
// patch(gbranch(optoas(OLT, t), T), to);
// br = gbranch(optoas(OGT, t), T);
// break;
// }
// // compare least significant word
// t = lo1.type;
// if(nl->op == OLITERAL || nr->op == OLITERAL)
// gins(ACMPL, &lo1, &lo2);
// else {
// regalloc(&rr, types[TINT32], N);
// gins(AMOVL, &lo1, &rr);
// gins(ACMPL, &rr, &lo2);
// regfree(&rr);
// }
// // jump again
// patch(gbranch(optoas(op, t), T), to);
// // point first branch down here if appropriate
// if(br != P)
// patch(br, pc);
// splitclean();
// splitclean();
}