diff options
Diffstat (limited to 'src/cmd/5g/cgen64.c')
-rw-r--r-- | src/cmd/5g/cgen64.c | 716 |
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(); +} |