| // 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" | |
| /* | |
| * 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(); | |
| } | |