/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' and 'long long' cases. */ void gen_cvt_itof(int t) { save_reg(TREG_ST0); gv(RC_INT); if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { /* signed long long to float/double/long double (unsigned case is handled generically) */ o(0x50 + vtop->r2); /* push r2 */ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x242cdf); /* fildll (%esp) */ o(0x08c483); /* add $8, %esp */ } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { /* unsigned int to float/double/long double */ o(0x6a); /* push $0 */ g(0x00); o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x242cdf); /* fildll (%esp) */ o(0x08c483); /* add $8, %esp */ } else { /* int to float/double/long double */ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x2404db); /* fildl (%esp) */ o(0x04c483); /* add $4, %esp */ } vtop->r = TREG_ST0; }
// Generate an integer binary operation void gen_opi(int op) { int r, fr, opc, c; switch (op) { case '+': case TOK_ADDC1: // Add with carry generation opc = 0; gen_op8: if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { // Constant case vswap(); r = gv(RC_INT); vswap(); c = vtop->c.i; if (c == (char) c) { // Optimize +/- 1 case with inc and dec if (op == '+' && c == 1 || op == '-' && c == -1) { o(0x40 | r); // inc r } else if (op == '-' && c == 1 || op == '+' && c == -1) { o(0x48 | r); // dec r } else { o(0x83); o(0xc0 | (opc << 3) | r); g(c); } } else { o(0x81); oad(0xc0 | (opc << 3) | r, c); } } else { gv2(RC_INT, RC_INT); r = vtop[-1].r; fr = vtop[0].r; o((opc << 3) | 0x01); o(0xc0 + r + fr * 8); } vtop--; if (op >= TOK_ULT && op <= TOK_GT) { vtop->r = VT_CMP; vtop->c.i = op; } break; case '-': case TOK_SUBC1: // Subtract with carry generation opc = 5; goto gen_op8; case TOK_ADDC2: // Add with carry use opc = 2; goto gen_op8; case TOK_SUBC2: // Subtract with carry use opc = 3; goto gen_op8; case '&': opc = 4; goto gen_op8; case '^': opc = 6; goto gen_op8; case '|': opc = 1; goto gen_op8; case '*': gv2(RC_INT, RC_INT); r = vtop[-1].r; fr = vtop[0].r; vtop--; o(0xaf0f); // imul fr, r o(0xc0 + fr + r * 8); break; case TOK_SHL: opc = 4; goto gen_shift; case TOK_SHR: opc = 5; goto gen_shift; case TOK_SAR: opc = 7; gen_shift: opc = 0xc0 | (opc << 3); if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { // Constant case vswap(); r = gv(RC_INT); vswap(); c = vtop->c.i & 0x1f; o(0xc1); // shl/shr/sar $xxx, r o(opc | r); g(c); } else { // Generate the shift in ecx gv2(RC_INT, RC_ECX); r = vtop[-1].r; o(0xd3); // shl/shr/sar %cl, r o(opc | r); } vtop--; break; case '/': case TOK_UDIV: case TOK_PDIV: case '%': case TOK_UMOD: case TOK_UMULL: // First operand must be in eax // TODO: need better constraint for second operand gv2(RC_EAX, RC_ECX); r = vtop[-1].r; fr = vtop[0].r; vtop--; save_reg(TREG_EDX); if (op == TOK_UMULL) { o(0xf7); // mul fr o(0xe0 + fr); vtop->r2 = TREG_EDX; r = TREG_EAX; } else { if (op == TOK_UDIV || op == TOK_UMOD) { o(0xf7d231); // xor %edx, %edx, div fr, %eax o(0xf0 + fr); } else { o(0xf799); // cltd, idiv fr, %eax o(0xf8 + fr); } if (op == '%' || op == TOK_UMOD) { r = TREG_EDX; } else { r = TREG_EAX; } } vtop->r = r; break; default: opc = 7; goto gen_op8; } }
// Generate a floating point operation 'v = t1 op t2' instruction. The // two operands are guaranted to have the same floating point type // TODO: need to use ST1 too void gen_opf(int op) { int a, ft, fc, swapped, r; // Convert constants to memory references if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { vswap(); gv(RC_FLOAT); vswap(); } if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) gv(RC_FLOAT); // Must put at least one value in the floating point register if ((vtop[-1].r & VT_LVAL) && (vtop[0].r & VT_LVAL)) { vswap(); gv(RC_FLOAT); vswap(); } swapped = 0; // Swap the stack if needed so that t1 is the register and t2 is the memory reference if (vtop[-1].r & VT_LVAL) { vswap(); swapped = 1; } if (op >= TOK_ULT && op <= TOK_GT) { // Load on stack second operand load(TREG_ST0, vtop); save_reg(TREG_EAX); // eax is used by FP comparison code if (op == TOK_GE || op == TOK_GT) { swapped = !swapped; } else if (op == TOK_EQ || op == TOK_NE) { swapped = 0; } if (swapped) o(0xc9d9); // fxch %st(1) o(0xe9da); // fucompp o(0xe0df); // fnstsw %ax if (op == TOK_EQ) { o(0x45e480); // and $0x45, %ah o(0x40fC80); // cmp $0x40, %ah } else if (op == TOK_NE) { o(0x45e480); // and $0x45, %ah o(0x40f480); // xor $0x40, %ah op = TOK_NE; } else if (op == TOK_GE || op == TOK_LE) { o(0x05c4f6); // test $0x05, %ah op = TOK_EQ; } else { o(0x45c4f6); // test $0x45, %ah op = TOK_EQ; } vtop--; vtop->r = VT_CMP; vtop->c.i = op; } else { // No memory reference possible for long double operations if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { load(TREG_ST0, vtop); swapped = !swapped; } switch (op) { case '+': a = 0; break; case '-': a = 4; if (swapped) a++; break; case '*': a = 1; break; case '/': a = 6; if (swapped) a++; break; default: a = 0; } ft = vtop->type.t; fc = vtop->c.ul; if ((ft & VT_BTYPE) == VT_LDOUBLE) { o(0xde); // fxxxp %st, %st(1) o(0xc1 + (a << 3)); } else { // If saved lvalue, then we must reload it r = vtop->r; if ((r & VT_VALMASK) == VT_LLOCAL) { SValue v1; r = get_reg(RC_INT); v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; load(r, &v1); fc = 0; } if ((ft & VT_BTYPE) == VT_DOUBLE) { o(0xdc); } else { o(0xd8); } gen_modrm(a, r, vtop->sym, fc); } vtop--; } }
/* generate an integer binary operation */ void gen_opi(int op) { int r, fr, opc, c; switch(op) { case '+': case TOK_ADDC1: /* add with carry generation */ opc = 0; gen_op8: if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ vswap(); r = gv(RC_INT); vswap(); c = vtop->c.i; if (c == (char)c) { /* XXX: generate inc and dec for smaller code ? */ o(0x83); o(0xc0 | (opc << 3) | r); g(c); } else { o(0x81); oad(0xc0 | (opc << 3) | r, c); } } else { gv2(RC_INT, RC_INT); r = vtop[-1].r; fr = vtop[0].r; o((opc << 3) | 0x01); o(0xc0 + r + fr * 8); } vtop--; if (op >= TOK_ULT && op <= TOK_GT) { vtop->r = VT_CMP; vtop->c.i = op; } break; case '-': case TOK_SUBC1: /* sub with carry generation */ opc = 5; goto gen_op8; case TOK_ADDC2: /* add with carry use */ opc = 2; goto gen_op8; case TOK_SUBC2: /* sub with carry use */ opc = 3; goto gen_op8; case '&': opc = 4; goto gen_op8; case '^': opc = 6; goto gen_op8; case '|': opc = 1; goto gen_op8; case '*': gv2(RC_INT, RC_INT); r = vtop[-1].r; fr = vtop[0].r; vtop--; o(0xaf0f); /* imul fr, r */ o(0xc0 + fr + r * 8); break; case TOK_SHL: opc = 4; goto gen_shift; case TOK_SHR: opc = 5; goto gen_shift; case TOK_SAR: opc = 7; gen_shift: opc = 0xc0 | (opc << 3); if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ vswap(); r = gv(RC_INT); vswap(); c = vtop->c.i & 0x1f; o(0xc1); /* shl/shr/sar $xxx, r */ o(opc | r); g(c); } else { /* we generate the shift in ecx */ gv2(RC_INT, RC_ECX); r = vtop[-1].r; o(0xd3); /* shl/shr/sar %cl, r */ o(opc | r); } vtop--; break; case '/': case TOK_UDIV: case TOK_PDIV: case '%': case TOK_UMOD: case TOK_UMULL: /* first operand must be in eax */ /* XXX: need better constraint for second operand */ gv2(RC_EAX, RC_ECX); r = vtop[-1].r; fr = vtop[0].r; vtop--; save_reg(TREG_EDX); if (op == TOK_UMULL) { o(0xf7); /* mul fr */ o(0xe0 + fr); vtop->r2 = TREG_EDX; r = TREG_EAX; } else { if (op == TOK_UDIV || op == TOK_UMOD) { o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ o(0xf0 + fr); } else { o(0xf799); /* cltd, idiv fr, %eax */ o(0xf8 + fr); } if (op == '%' || op == TOK_UMOD) r = TREG_EDX; else r = TREG_EAX; } vtop->r = r; break; default: opc = 7; goto gen_op8; } }