#include "cc.h"

/*
* this is machine dependent, but it is totally
* common on all of the 64-bit symulating machines.
*/

#define FNX     100     /* botch -- redefinition */

Node*   nodaddv;
Node*   nodsubv;
Node*   nodmulv;
Node*   noddivv;
Node*   noddivvu;
Node*   nodmodv;
Node*   nodmodvu;
Node*   nodlshv;
Node*   nodrshav;
Node*   nodrshlv;
Node*   nodandv;
Node*   nodorv;
Node*   nodxorv;
Node*   nodnegv;
Node*   nodcomv;

Node*   nodtestv;
Node*   nodeqv;
Node*   nodnev;
Node*   nodlev;
Node*   nodltv;
Node*   nodgev;
Node*   nodgtv;
Node*   nodhiv;
Node*   nodhsv;
Node*   nodlov;
Node*   nodlsv;

Node*   nodf2v;
Node*   nodd2v;
Node*   nodp2v;
Node*   nodsi2v;
Node*   nodui2v;
Node*   nodsl2v;
Node*   nodul2v;
Node*   nodsh2v;
Node*   noduh2v;
Node*   nodsc2v;
Node*   noduc2v;

Node*   nodv2f;
Node*   nodv2d;
Node*   noduv2f;
Node*   noduv2d;
Node*   nodv2ui;
Node*   nodv2si;
Node*   nodv2ul;
Node*   nodv2sl;
Node*   nodv2uh;
Node*   nodv2sh;
Node*   nodv2uc;
Node*   nodv2sc;

Node*   nodvpp;
Node*   nodppv;
Node*   nodvmm;
Node*   nodmmv;

Node*   nodaddd;
Node*   nodsubd;
Node*   nodmuld;
Node*   noddivd;

Node*   nodvasop;

char    etconv[NTYPE];  /* for _vasop */
Init    initetconv[] =
{
       TCHAR,          1,      0,
       TUCHAR,         2,      0,
       TSHORT,         3,      0,
       TUSHORT,        4,      0,
       TLONG,          5,      0,
       TULONG,         6,      0,
       TVLONG,         7,      0,
       TUVLONG,        8,      0,
       TINT,           9,      0,
       TUINT,          10,     0,
       -1,             0,      0,
};

Node*
fvn(char *name, int type)
{
       Node *n;

       n = new(ONAME, Z, Z);
       n->sym = slookup(name);
       n->sym->sig = SIGINTERN;
       if(fntypes[type] == 0)
               fntypes[type] = typ(TFUNC, types[type]);
       n->type = fntypes[type];
       n->etype = type;
       n->class = CGLOBL;
       n->addable = 10;
       n->complex = 0;
       return n;
}

void
com64init(void)
{
       Init *p;

       nodaddv = fvn("_addv", TVLONG);
       nodsubv = fvn("_subv", TVLONG);
       nodmulv = fvn("_mulv", TVLONG);
       noddivv = fvn("_divv", TVLONG);
       noddivvu = fvn("_divvu", TVLONG);
       nodmodv = fvn("_modv", TVLONG);
       nodmodvu = fvn("_modvu", TVLONG);
       nodlshv = fvn("_lshv", TVLONG);
       nodrshav = fvn("_rshav", TVLONG);
       nodrshlv = fvn("_rshlv", TVLONG);
       nodandv = fvn("_andv", TVLONG);
       nodorv = fvn("_orv", TVLONG);
       nodxorv = fvn("_xorv", TVLONG);
       nodnegv = fvn("_negv", TVLONG);
       nodcomv = fvn("_comv", TVLONG);

       nodtestv = fvn("_testv", TLONG);
       nodeqv = fvn("_eqv", TLONG);
       nodnev = fvn("_nev", TLONG);
       nodlev = fvn("_lev", TLONG);
       nodltv = fvn("_ltv", TLONG);
       nodgev = fvn("_gev", TLONG);
       nodgtv = fvn("_gtv", TLONG);
       nodhiv = fvn("_hiv", TLONG);
       nodhsv = fvn("_hsv", TLONG);
       nodlov = fvn("_lov", TLONG);
       nodlsv = fvn("_lsv", TLONG);

       nodf2v = fvn("_f2v", TVLONG);
       nodd2v = fvn("_d2v", TVLONG);
       nodp2v = fvn("_p2v", TVLONG);
       nodsi2v = fvn("_si2v", TVLONG);
       nodui2v = fvn("_ui2v", TVLONG);
       nodsl2v = fvn("_sl2v", TVLONG);
       nodul2v = fvn("_ul2v", TVLONG);
       nodsh2v = fvn("_sh2v", TVLONG);
       noduh2v = fvn("_uh2v", TVLONG);
       nodsc2v = fvn("_sc2v", TVLONG);
       noduc2v = fvn("_uc2v", TVLONG);

       nodv2f = fvn("_v2f", TFLOAT);
       nodv2d = fvn("_v2d", TDOUBLE);
       noduv2f = fvn("_uv2f", TFLOAT);
       noduv2d = fvn("_uv2d", TDOUBLE);
       nodv2sl = fvn("_v2sl", TLONG);
       nodv2ul = fvn("_v2ul", TULONG);
       nodv2si = fvn("_v2si", TINT);
       nodv2ui = fvn("_v2ui", TUINT);
       nodv2sh = fvn("_v2sh", TSHORT);
       nodv2uh = fvn("_v2ul", TUSHORT);
       nodv2sc = fvn("_v2sc", TCHAR);
       nodv2uc = fvn("_v2uc", TUCHAR);

       nodvpp = fvn("_vpp", TVLONG);
       nodppv = fvn("_ppv", TVLONG);
       nodvmm = fvn("_vmm", TVLONG);
       nodmmv = fvn("_mmv", TVLONG);

       nodaddd = fvn("_vasaddd", TVLONG);
       nodsubd = fvn("_vassubd", TVLONG);
       nodmuld = fvn("_vasmuld", TVLONG);
       noddivd = fvn("_vasdivd", TVLONG);

       nodvasop = fvn("_vasop", TVLONG);

       for(p = initetconv; p->code >= 0; p++)
               etconv[p->code] = p->value;
}

int
com64(Node *n)
{
       Node *l, *r, *a, *t;
       int lv, rv;

       if(n->type == 0)
               return 0;

       l = n->left;
       r = n->right;

       lv = 0;
       if(l && l->type && typev[l->type->etype])
               lv = 1;
       rv = 0;
       if(r && r->type && typev[r->type->etype])
               rv = 1;

       if(lv) {
               switch(n->op) {
               case OEQ:
                       a = nodeqv;
                       goto setbool;
               case ONE:
                       a = nodnev;
                       goto setbool;
               case OLE:
                       a = nodlev;
                       goto setbool;
               case OLT:
                       a = nodltv;
                       goto setbool;
               case OGE:
                       a = nodgev;
                       goto setbool;
               case OGT:
                       a = nodgtv;
                       goto setbool;
               case OHI:
                       a = nodhiv;
                       goto setbool;
               case OHS:
                       a = nodhsv;
                       goto setbool;
               case OLO:
                       a = nodlov;
                       goto setbool;
               case OLS:
                       a = nodlsv;
                       goto setbool;

               case OANDAND:
               case OOROR:
                       if(machcap(n))
                               return 1;

                       if(rv) {
                               r = new(OFUNC, nodtestv, r);
                               n->right = r;
                               r->complex = FNX;
                               r->op = OFUNC;
                               r->type = types[TLONG];
                       }

               case OCOND:
               case ONOT:
                       if(machcap(n))
                               return 1;

                       l = new(OFUNC, nodtestv, l);
                       n->left = l;
                       l->complex = FNX;
                       l->op = OFUNC;
                       l->type = types[TLONG];
                       n->complex = FNX;
                       return 1;
               }
       }

       if(rv) {
               if(machcap(n))
                       return 1;
               switch(n->op) {
               case OANDAND:
               case OOROR:
                       r = new(OFUNC, nodtestv, r);
                       n->right = r;
                       r->complex = FNX;
                       r->op = OFUNC;
                       r->type = types[TLONG];
                       return 1;
               case OCOND:
                       return 1;
               }
       }

       if(typev[n->type->etype]) {
               if(machcap(n))
                       return 1;
               switch(n->op) {
               default:
                       diag(n, "unknown vlong %O", n->op);
               case OFUNC:
                       n->complex = FNX;
               case ORETURN:
               case OAS:
               case OIND:
               case OLIST:
               case OCOMMA:
                       return 1;
               case OADD:
                       a = nodaddv;
                       goto setbop;
               case OSUB:
                       a = nodsubv;
                       goto setbop;
               case OMUL:
               case OLMUL:
                       a = nodmulv;
                       goto setbop;
               case ODIV:
                       a = noddivv;
                       goto setbop;
               case OLDIV:
                       a = noddivvu;
                       goto setbop;
               case OMOD:
                       a = nodmodv;
                       goto setbop;
               case OLMOD:
                       a = nodmodvu;
                       goto setbop;
               case OASHL:
                       a = nodlshv;
                       goto setbop;
               case OASHR:
                       a = nodrshav;
                       goto setbop;
               case OLSHR:
                       a = nodrshlv;
                       goto setbop;
               case OAND:
                       a = nodandv;
                       goto setbop;
               case OOR:
                       a = nodorv;
                       goto setbop;
               case OXOR:
                       a = nodxorv;
                       goto setbop;
               case OPOSTINC:
                       a = nodvpp;
                       goto setvinc;
               case OPOSTDEC:
                       a = nodvmm;
                       goto setvinc;
               case OPREINC:
                       a = nodppv;
                       goto setvinc;
               case OPREDEC:
                       a = nodmmv;
                       goto setvinc;
               case ONEG:
                       a = nodnegv;
                       goto setfnx;
               case OCOM:
                       a = nodcomv;
                       goto setfnx;
               case OCAST:
                       switch(l->type->etype) {
                       case TCHAR:
                               a = nodsc2v;
                               goto setfnxl;
                       case TUCHAR:
                               a = noduc2v;
                               goto setfnxl;
                       case TSHORT:
                               a = nodsh2v;
                               goto setfnxl;
                       case TUSHORT:
                               a = noduh2v;
                               goto setfnxl;
                       case TINT:
                               a = nodsi2v;
                               goto setfnx;
                       case TUINT:
                               a = nodui2v;
                               goto setfnx;
                       case TLONG:
                               a = nodsl2v;
                               goto setfnx;
                       case TULONG:
                               a = nodul2v;
                               goto setfnx;
                       case TFLOAT:
                               a = nodf2v;
                               goto setfnx;
                       case TDOUBLE:
                               a = nodd2v;
                               goto setfnx;
                       case TIND:
                               a = nodp2v;
                               goto setfnx;
                       }
                       diag(n, "unknown %T->vlong cast", l->type);
                       return 1;
               case OASADD:
                       a = nodaddv;
                       goto setasop;
               case OASSUB:
                       a = nodsubv;
                       goto setasop;
               case OASMUL:
               case OASLMUL:
                       a = nodmulv;
                       goto setasop;
               case OASDIV:
                       a = noddivv;
                       goto setasop;
               case OASLDIV:
                       a = noddivvu;
                       goto setasop;
               case OASMOD:
                       a = nodmodv;
                       goto setasop;
               case OASLMOD:
                       a = nodmodvu;
                       goto setasop;
               case OASASHL:
                       a = nodlshv;
                       goto setasop;
               case OASASHR:
                       a = nodrshav;
                       goto setasop;
               case OASLSHR:
                       a = nodrshlv;
                       goto setasop;
               case OASAND:
                       a = nodandv;
                       goto setasop;
               case OASOR:
                       a = nodorv;
                       goto setasop;
               case OASXOR:
                       a = nodxorv;
                       goto setasop;
               }
       }

       if(n->op == OCAST) {
               if(l->type && typev[l->type->etype]) {
                       if(machcap(n))
                               return 1;
                       switch(n->type->etype) {
                       case TDOUBLE:
                               if(l->type->etype == TUVLONG)
                                       a = noduv2d;
                               else
                                       a = nodv2d;
                               goto setfnx;
                       case TFLOAT:
                               if(l->type->etype == TUVLONG)
                                       a = noduv2f;
                               else
                                       a = nodv2f;
                               goto setfnx;
                       case TLONG:
                               a = nodv2sl;
                               goto setfnx;
                       case TULONG:
                               a = nodv2ul;
                               goto setfnx;
                       case TINT:
                               a = nodv2si;
                               goto setfnx;
                       case TUINT:
                               a = nodv2ui;
                               goto setfnx;
                       case TSHORT:
                               a = nodv2sh;
                               goto setfnx;
                       case TUSHORT:
                               a = nodv2uh;
                               goto setfnx;
                       case TCHAR:
                               a = nodv2sc;
                               goto setfnx;
                       case TUCHAR:
                               a = nodv2uc;
                               goto setfnx;
                       case TIND:      // small pun here
                               a = nodv2ul;
                               goto setfnx;
                       }
                       diag(n, "unknown vlong->%T cast", n->type);
                       return 1;
               }
       }

       return 0;

setbop:
       n->left = a;
       n->right = new(OLIST, l, r);
       n->complex = FNX;
       n->op = OFUNC;
       return 1;

setfnxl:
       l = new(OCAST, l, 0);
       l->type = types[TLONG];
       l->complex = l->left->complex;

setfnx:
       n->left = a;
       n->right = l;
       n->complex = FNX;
       n->op = OFUNC;
       return 1;

setvinc:
       n->left = a;
       l = new(OADDR, l, Z);
       l->type = typ(TIND, l->left->type);
       l->complex = l->left->complex;
       n->right = new(OLIST, l, r);
       n->complex = FNX;
       n->op = OFUNC;
       return 1;

setbool:
       if(machcap(n))
               return 1;
       n->left = a;
       n->right = new(OLIST, l, r);
       n->complex = FNX;
       n->op = OFUNC;
       n->type = types[TLONG];
       return 1;

setasop:
       while(l->op == OFUNC)
               l = l->right;

       if(mixedasop(n->left->type, n->right->type)) {
               if(n->right->type->etype != TDOUBLE) {
                       r = new(OCAST, r, 0);
                       r->type = types[TDOUBLE];
               }

               if(l->type->etype == TUVLONG)
                       a = noduv2d;
               else
                       a = nodv2d;

               t = new(OADDR, a, 0);
               t->type = typ(TIND, a->type);
               r = new(OLIST, t, r);

               t = new(OADDR, l, 0);
               t->type = typ(TIND, l->type);
               t->complex = l->complex;
               r = new(OLIST, t, r);

               switch(n->op){
               default:        diag(n, "mixed vlong/double %O not implemented", n->op);
               case OASADD:    a = nodaddd; break;
               case OASSUB:    a = nodsubd; break;
               case OASMUL:    a = nodmuld; break;
               case OASDIV:    a = noddivd; break;
               }

               n->left = a;
               n->right = r;
               n->complex = FNX;
               n->op = OFUNC;

               return 1;
       }

       t = new(OCONST, 0, 0);
       t->vconst = etconv[l->type->etype];
       t->type = types[TLONG];
       t->addable = 20;
       r = new(OLIST, t, r);

       t = new(OADDR, a, 0);
       t->type = typ(TIND, a->type);
       r = new(OLIST, t, r);

       t = new(OADDR, l, 0);
       t->type = typ(TIND, l->type);
       t->complex = l->complex;
       r = new(OLIST, t, r);

       n->left = nodvasop;
       n->right = r;
       n->complex = FNX;
       n->op = OFUNC;

       return 1;
}

void
bool64(Node *n)
{
       Node *n1;

       if(machcap(Z))
               return;
       if(typev[n->type->etype]) {
               n1 = new(OXXX, 0, 0);
               *n1 = *n;

               n->right = n1;
               n->left = nodtestv;
               n->complex = FNX;
               n->addable = 0;
               n->op = OFUNC;
               n->type = types[TLONG];
       }
}

/*
* more machine depend stuff.
* this is common for 8,16,32,64 bit machines.
* this is common for ieee machines.
*/
double
convvtof(vlong v)
{
       double d;

       d = v;          /* BOTCH */
       return d;
}

vlong
convftov(double d)
{
       vlong v;


       v = d;          /* BOTCH */
       return v;
}

double
convftox(double d, int et)
{

       if(!typefd[et])
               diag(Z, "bad type in castftox %s", tnames[et]);
       return d;
}

vlong
convvtox(vlong c, int et)
{
       int n;

       n = 8 * ewidth[et];
       c &= MASK(n);
       if(!typeu[et])
               if(c & SIGN(n))
                       c |= ~MASK(n);
       return c;
}