summaryrefslogtreecommitdiff
path: root/src/cmd/5g/cgen64.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/5g/cgen64.c')
-rw-r--r--src/cmd/5g/cgen64.c716
1 files changed, 716 insertions, 0 deletions
diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c
new file mode 100644
index 000000000..b56df765b
--- /dev/null
+++ b/src/cmd/5g/cgen64.c
@@ -0,0 +1,716 @@
+// 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, s, n1, creg;
+ Prog *p1, *p2, *p3, *p4, *p5, *p6;
+
+ uint64 v;
+
+ if(res->op != OINDREG && res->op != ONAME) {
+ dump("n", n);
+ dump("res", res);
+ fatal("cgen64 %O of %O", n->op, res->op);
+ }
+
+ l = n->left;
+ if(!l->addable) {
+ tempname(&t1, l->type);
+ cgen(l, &t1);
+ l = &t1;
+ }
+
+ split64(l, &lo1, &hi1);
+ switch(n->op) {
+ default:
+ fatal("cgen64 %O", n->op);
+
+ case OMINUS:
+ split64(res, &lo2, &hi2);
+
+ regalloc(&t1, lo1.type, N);
+ regalloc(&al, lo1.type, N);
+ regalloc(&ah, hi1.type, N);
+
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+
+ gmove(ncon(0), &t1);
+ p1 = gins(ASUB, &al, &t1);
+ p1->scond |= C_SBIT;
+ gins(AMOVW, &t1, &lo2);
+
+ gmove(ncon(0), &t1);
+ gins(ASBC, &ah, &t1);
+ gins(AMOVW, &t1, &hi2);
+
+ regfree(&t1);
+ regfree(&al);
+ regfree(&ah);
+ splitclean();
+ splitclean();
+ return;
+
+ case OCOM:
+ regalloc(&t1, lo1.type, N);
+ gmove(ncon(-1), &t1);
+
+ split64(res, &lo2, &hi2);
+ regalloc(&n1, lo1.type, N);
+
+ gins(AMOVW, &lo1, &n1);
+ gins(AEOR, &t1, &n1);
+ gins(AMOVW, &n1, &lo2);
+
+ gins(AMOVW, &hi1, &n1);
+ gins(AEOR, &t1, &n1);
+ gins(AMOVW, &n1, &hi2);
+
+ regfree(&t1);
+ regfree(&n1);
+ splitclean();
+ splitclean();
+ return;
+
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OOR:
+ case OXOR:
+ // binary operators.
+ // common setup below.
+ break;
+ }
+
+ // setup for binary operators
+ r = n->right;
+ if(r != N && !r->addable) {
+ tempname(&t2, r->type);
+ cgen(r, &t2);
+ r = &t2;
+ }
+ 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);
+ p1 = gins(AADD, &bl, &al);
+ p1->scond |= C_SBIT;
+ gins(AADC, &bh, &ah);
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+ case OSUB:
+ // TODO: Constants.
+ regalloc(&bl, types[TPTR32], N);
+ regalloc(&bh, types[TPTR32], N);
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+ gins(AMOVW, &lo2, &bl);
+ gins(AMOVW, &hi2, &bh);
+ p1 = gins(ASUB, &bl, &al);
+ p1->scond |= C_SBIT;
+ gins(ASBC, &bh, &ah);
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+ case OMUL:
+ // TODO(kaib): this can be done with 4 regs and does not need 6
+ regalloc(&bl, types[TPTR32], N);
+ regalloc(&bh, types[TPTR32], N);
+ regalloc(&cl, types[TPTR32], N);
+ regalloc(&ch, 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 -> ah al
+ 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 = ah.val.u.reg;
+ p1->to.offset = al.val.u.reg;
+//print("%P\n", p1);
+
+ // bl * ch + ah -> ah
+ p1 = gins(AMULA, N, N);
+ p1->from.type = D_REG;
+ p1->from.reg = bl.val.u.reg;
+ p1->reg = ch.val.u.reg;
+ p1->to.type = D_REGREG;
+ p1->to.reg = ah.val.u.reg;
+ p1->to.offset = ah.val.u.reg;
+//print("%P\n", p1);
+
+ // bh * cl + ah -> ah
+ p1 = gins(AMULA, N, N);
+ p1->from.type = D_REG;
+ p1->from.reg = bh.val.u.reg;
+ p1->reg = cl.val.u.reg;
+ p1->to.type = D_REGREG;
+ p1->to.reg = ah.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:
+ regalloc(&bl, lo1.type, N);
+ regalloc(&bh, hi1.type, N);
+ gins(AMOVW, &hi1, &bh);
+ gins(AMOVW, &lo1, &bl);
+
+ 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);
+ } else
+ if(v > 32) {
+ gins(AEOR, &al, &al);
+ // MOVW bl<<(v-32), ah
+ gshift(AMOVW, &bl, SHIFT_LL, (v-32), &ah);
+ } else
+ if(v == 32) {
+ gins(AEOR, &al, &al);
+ gins(AMOVW, &bl, &ah);
+ } else
+ if(v > 0) {
+ // MOVW bl<<v, al
+ gshift(AMOVW, &bl, SHIFT_LL, v, &al);
+
+ // MOVW bh<<v, ah
+ gshift(AMOVW, &bh, SHIFT_LL, v, &ah);
+
+ // OR bl>>(32-v), ah
+ gshift(AORR, &bl, SHIFT_LR, 32-v, &ah);
+ } else {
+ gins(AMOVW, &bl, &al);
+ gins(AMOVW, &bh, &ah);
+ }
+ goto olsh_break;
+ }
+
+ regalloc(&s, types[TUINT32], N);
+ regalloc(&creg, types[TUINT32], N);
+ if (is64(r->type)) {
+ // shift is >= 1<<32
+ split64(r, &cl, &ch);
+ gmove(&ch, &s);
+ p1 = gins(ATST, &s, N);
+ p6 = gbranch(ABNE, T);
+ gmove(&cl, &s);
+ splitclean();
+ } else {
+ gmove(r, &s);
+ p6 = P;
+ }
+ p1 = gins(ATST, &s, N);
+
+ // shift == 0
+ p1 = gins(AMOVW, &bl, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bh, &ah);
+ p1->scond = C_SCOND_EQ;
+ p2 = gbranch(ABEQ, T);
+
+ // shift is < 32
+ nodconst(&n1, types[TUINT32], 32);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO bl<<s, al
+ p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &al);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW.LO bh<<s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_LL, &s, &ah);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO s, creg
+ p1 = gins(ASUB, &s, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // OR.LO bl>>creg, ah
+ p1 = gregshift(AORR, &bl, SHIFT_LR, &creg, &ah);
+ p1->scond = C_SCOND_LO;
+
+ // BLO end
+ p3 = gbranch(ABLO, T);
+
+ // shift == 32
+ p1 = gins(AEOR, &al, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bl, &ah);
+ p1->scond = C_SCOND_EQ;
+ p4 = gbranch(ABEQ, T);
+
+ // shift is < 64
+ nodconst(&n1, types[TUINT32], 64);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // EOR.LO al, al
+ p1 = gins(AEOR, &al, &al);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW.LO creg>>1, creg
+ p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO creg, s
+ p1 = gins(ASUB, &creg, &s);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW bl<<s, ah
+ p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &ah);
+ p1->scond = C_SCOND_LO;
+
+ p5 = gbranch(ABLO, T);
+
+ // shift >= 64
+ if (p6 != P) patch(p6, pc);
+ gins(AEOR, &al, &al);
+ gins(AEOR, &ah, &ah);
+
+ patch(p2, pc);
+ patch(p3, pc);
+ patch(p4, pc);
+ patch(p5, pc);
+ regfree(&s);
+ regfree(&creg);
+
+olsh_break:
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+
+ case ORSH:
+ regalloc(&bl, lo1.type, N);
+ regalloc(&bh, hi1.type, N);
+ gins(AMOVW, &hi1, &bh);
+ gins(AMOVW, &lo1, &bl);
+
+ if(r->op == OLITERAL) {
+ v = mpgetfix(r->val.u.xval);
+ if(v >= 64) {
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, al
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &al);
+
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ gins(AEOR, &al, &al);
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if(v > 32) {
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->(v-32), al
+ gshift(AMOVW, &bh, SHIFT_AR, v-32, &al);
+
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ // MOVW bh>>(v-32), al
+ gshift(AMOVW, &bh, SHIFT_LR, v-32, &al);
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if(v == 32) {
+ gins(AMOVW, &bh, &al);
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if( v > 0) {
+ // MOVW bl>>v, al
+ gshift(AMOVW, &bl, SHIFT_LR, v, &al);
+
+ // OR bh<<(32-v), al
+ gshift(AORR, &bh, SHIFT_LL, 32-v, &al);
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->v, ah
+ gshift(AMOVW, &bh, SHIFT_AR, v, &ah);
+ } else {
+ // MOVW bh>>v, ah
+ gshift(AMOVW, &bh, SHIFT_LR, v, &ah);
+ }
+ } else {
+ gins(AMOVW, &bl, &al);
+ gins(AMOVW, &bh, &ah);
+ }
+ goto orsh_break;
+ }
+
+ regalloc(&s, types[TUINT32], N);
+ regalloc(&creg, types[TUINT32], N);
+ if(is64(r->type)) {
+ // shift is >= 1<<32
+ split64(r, &cl, &ch);
+ gmove(&ch, &s);
+ gins(ATST, &s, N);
+ if(bh.type->etype == TINT32)
+ p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ else
+ p1 = gins(AEOR, &ah, &ah);
+ p1->scond = C_SCOND_NE;
+ p6 = gbranch(ABNE, T);
+ gmove(&cl, &s);
+ splitclean();
+ } else {
+ gmove(r, &s);
+ p6 = P;
+ }
+ p1 = gins(ATST, &s, N);
+
+ // shift == 0
+ p1 = gins(AMOVW, &bl, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bh, &ah);
+ p1->scond = C_SCOND_EQ;
+ p2 = gbranch(ABEQ, T);
+
+ // check if shift is < 32
+ nodconst(&n1, types[TUINT32], 32);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO bl>>s, al
+ p1 = gregshift(AMOVW, &bl, SHIFT_LR, &s, &al);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO s,creg
+ p1 = gins(ASUB, &s, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // OR.LO bh<<(32-s), al
+ p1 = gregshift(AORR, &bh, SHIFT_LL, &creg, &al);
+ p1->scond = C_SCOND_LO;
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &ah);
+ } else {
+ // MOVW bh>>s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &ah);
+ }
+ p1->scond = C_SCOND_LO;
+
+ // BLO end
+ p3 = gbranch(ABLO, T);
+
+ // shift == 32
+ p1 = gins(AMOVW, &bh, &al);
+ p1->scond = C_SCOND_EQ;
+ if(bh.type->etype == TINT32)
+ p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ else
+ p1 = gins(AEOR, &ah, &ah);
+ p4 = gbranch(ABEQ, T);
+
+ // check if shift is < 64
+ nodconst(&n1, types[TUINT32], 64);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO creg>>1, creg
+ p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO creg, s
+ p1 = gins(ASUB, &creg, &s);
+ p1->scond = C_SCOND_LO;
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->(s-32), al
+ p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &al);
+ p1->scond = C_SCOND_LO;
+ } else {
+ // MOVW bh>>(v-32), al
+ p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &al);
+ p1->scond = C_SCOND_LO;
+ }
+
+ // BLO end
+ p5 = gbranch(ABLO, T);
+
+ // s >= 64
+ if(p6 != P)
+ patch(p6, pc);
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, al
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &al);
+ } else {
+ gins(AEOR, &al, &al);
+ }
+
+ patch(p2, pc);
+ patch(p3, pc);
+ patch(p4, pc);
+ patch(p5, pc);
+ regfree(&s);
+ regfree(&creg);
+
+
+orsh_break:
+ regfree(&bl);
+ regfree(&bh);
+ 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;
+// }
+ regalloc(&n1, lo1.type, N);
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+ gins(AMOVW, &lo2, &n1);
+ gins(optoas(n->op, lo1.type), &n1, &al);
+ gins(AMOVW, &hi2, &n1);
+ gins(optoas(n->op, lo1.type), &n1, &ah);
+ regfree(&n1);
+ break;
+ }
+ if(is64(r->type))
+ splitclean();
+ splitclean();
+
+ split64(res, &lo1, &hi1);
+ gins(AMOVW, &al, &lo1);
+ gins(AMOVW, &ah, &hi1);
+ splitclean();
+
+//out:
+ 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)
+{
+ Node lo1, hi1, lo2, hi2, r1, r2;
+ 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;
+ regalloc(&r1, types[TINT32], N);
+ regalloc(&r2, types[TINT32], N);
+ gins(AMOVW, &hi1, &r1);
+ gins(AMOVW, &hi2, &r2);
+ gcmp(ACMP, &r1, &r2);
+ regfree(&r1);
+ regfree(&r2);
+
+ br = P;
+ switch(op) {
+ default:
+ fatal("cmp64 %O %T", op, t);
+ case OEQ:
+ // cmp hi
+ // bne L
+ // cmp lo
+ // beq to
+ // L:
+ br = gbranch(ABNE, T);
+ break;
+ case ONE:
+ // cmp hi
+ // bne to
+ // cmp lo
+ // bne to
+ patch(gbranch(ABNE, T), to);
+ break;
+ case OGE:
+ case OGT:
+ // cmp hi
+ // bgt to
+ // blt L
+ // cmp lo
+ // bge to (or bgt to)
+ // L:
+ patch(gbranch(optoas(OGT, t), T), to);
+ br = gbranch(optoas(OLT, t), T);
+ break;
+ case OLE:
+ case OLT:
+ // cmp hi
+ // blt to
+ // bgt L
+ // cmp lo
+ // ble 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;
+ regalloc(&r1, types[TINT32], N);
+ regalloc(&r2, types[TINT32], N);
+ gins(AMOVW, &lo1, &r1);
+ gins(AMOVW, &lo2, &r2);
+ gcmp(ACMP, &r1, &r2);
+ regfree(&r1);
+ regfree(&r2);
+
+ // jump again
+ patch(gbranch(optoas(op, t), T), to);
+
+ // point first branch down here if appropriate
+ if(br != P)
+ patch(br, pc);
+
+ splitclean();
+ splitclean();
+}