// 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, ax, dx, cx, ex, fx, *l, *r; Node lo1, lo2, hi1, hi2; 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) { tempname(&t1, l->type); cgen(l, &t1); l = &t1; } if(r != N && !r->addable) { tempname(&t2, r->type); cgen(r, &t2); r = &t2; } nodreg(&ax, types[TINT32], D_AX); nodreg(&cx, types[TINT32], D_CX); nodreg(&dx, types[TINT32], D_DX); // Setup for binary operation. split64(l, &lo1, &hi1); if(is64(r->type)) split64(r, &lo2, &hi2); // Do op. Leave result in DX:AX. switch(n->op) { case OADD: // TODO: Constants gins(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); gins(AADDL, &lo2, &ax); gins(AADCL, &hi2, &dx); break; case OSUB: // TODO: Constants. gins(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); gins(ASUBL, &lo2, &ax); gins(ASBBL, &hi2, &dx); break; case OMUL: // let's call the next two EX and FX. regalloc(&ex, types[TPTR32], N); regalloc(&fx, types[TPTR32], N); // load args into DX:AX and EX:CX. gins(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); gins(AMOVL, &lo2, &cx); gins(AMOVL, &hi2, &ex); // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. gins(AMOVL, &dx, &fx); gins(AORL, &ex, &fx); p1 = gbranch(AJNE, T); gins(AMULL, &cx, N); // implicit &ax p2 = gbranch(AJMP, T); patch(p1, pc); // full 64x64 -> 64, from 32x32 -> 64. gins(AIMULL, &cx, &dx); gins(AMOVL, &ax, &fx); gins(AIMULL, &ex, &fx); gins(AADDL, &dx, &fx); gins(AMOVL, &cx, &dx); gins(AMULL, &dx, N); // implicit &ax gins(AADDL, &fx, &dx); patch(p2, pc); regfree(&ex); regfree(&fx); break; case OLSH: if(r->op == OLITERAL) { v = mpgetfix(r->val.u.xval); if(v >= 64) { if(is64(r->type)) splitclean(); splitclean(); split64(res, &lo2, &hi2); gins(AMOVL, ncon(0), &lo2); gins(AMOVL, ncon(0), &hi2); splitclean(); goto out; } if(v >= 32) { if(is64(r->type)) splitclean(); split64(res, &lo2, &hi2); gmove(&lo1, &hi2); if(v > 32) { gins(ASHLL, ncon(v - 32), &hi2); } gins(AMOVL, ncon(0), &lo2); splitclean(); splitclean(); goto out; } // general shift gins(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); p1 = gins(ASHLL, ncon(v), &dx); p1->from.index = D_AX; // double-width shift p1->from.scale = 0; gins(ASHLL, ncon(v), &ax); break; } // 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 { cx.type = types[TUINT32]; gmove(r, &cx); } // if shift count is >=64, zero value gins(ACMPL, &cx, ncon(64)); p2 = gbranch(optoas(OLT, types[TUINT32]), T); if(p1 != P) patch(p1, pc); gins(AXORL, &dx, &dx); gins(AXORL, &ax, &ax); patch(p2, pc); // if shift count is >= 32, zero low. gins(ACMPL, &cx, ncon(32)); p1 = gbranch(optoas(OLT, types[TUINT32]), T); gins(AMOVL, &ax, &dx); gins(ASHLL, &cx, &dx); // SHLL only uses bottom 5 bits of count gins(AXORL, &ax, &ax); p2 = gbranch(AJMP, T); patch(p1, pc); // general shift p1 = gins(ASHLL, &cx, &dx); p1->from.index = D_AX; // double-width shift p1->from.scale = 0; gins(ASHLL, &cx, &ax); patch(p2, pc); break; case ORSH: if(r->op == OLITERAL) { v = mpgetfix(r->val.u.xval); if(v >= 64) { if(is64(r->type)) splitclean(); splitclean(); split64(res, &lo2, &hi2); if(hi1.type->etype == TINT32) { gmove(&hi1, &lo2); gins(ASARL, ncon(31), &lo2); gmove(&hi1, &hi2); gins(ASARL, ncon(31), &hi2); } else { gins(AMOVL, ncon(0), &lo2); gins(AMOVL, ncon(0), &hi2); } splitclean(); goto out; } if(v >= 32) { if(is64(r->type)) splitclean(); split64(res, &lo2, &hi2); gmove(&hi1, &lo2); if(v > 32) gins(optoas(ORSH, hi1.type), ncon(v-32), &lo2); if(hi1.type->etype == TINT32) { gmove(&hi1, &hi2); gins(ASARL, ncon(31), &hi2); } else gins(AMOVL, ncon(0), &hi2); splitclean(); splitclean(); goto out; } // general shift gins(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); p1 = gins(ASHRL, ncon(v), &ax); p1->from.index = D_DX; // double-width shift p1->from.scale = 0; gins(optoas(ORSH, hi1.type), ncon(v), &dx); break; } // 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 { cx.type = types[TUINT32]; gmove(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: // 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(AMOVL, &lo1, &ax); gins(AMOVL, &hi1, &dx); gins(optoas(n->op, lo1.type), &lo2, &ax); gins(optoas(n->op, lo1.type), &hi2, &dx); break; } if(is64(r->type)) splitclean(); splitclean(); split64(res, &lo1, &hi1); gins(AMOVL, &ax, &lo1); gins(AMOVL, &dx, &hi1); splitclean(); out:; } /* * 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, 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(); }