#include "jitintern.h" ///////////////////////////////////////////////////////////////////////////// // String instructions. void JitCompiler::EmitCONCAT() { auto rc = CheckRegS(C, A); auto call = CreateCall([](FString* to, FString* first, FString* second) { *to = *first + *second; }); call->setArg(0, regS[A]); call->setArg(1, regS[B]); call->setArg(2, rc); } void JitCompiler::EmitLENS() { auto call = CreateCall([](FString* str) -> int { return static_cast(str->Len()); }); call->setRet(0, regD[A]); call->setArg(0, regS[B]); } void JitCompiler::EmitCMPS() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto compareNoCaseLambda = [](FString* first, FString* second) -> int { return first->CompareNoCase(*second); }; auto compareLambda = [](FString* first, FString* second) -> int { return first->Compare(*second); }; auto call = static_cast(A & CMP_APPROX) ? CreateCall(compareNoCaseLambda) : CreateCall(compareLambda); auto result = cc.newInt32(); call->setRet(0, result); if (static_cast(A & CMP_BK)) call->setArg(0, asmjit::imm_ptr(&konsts[B])); else call->setArg(0, regS[B]); if (static_cast(A & CMP_CK)) call->setArg(1, asmjit::imm_ptr(&konsts[C])); else call->setArg(1, regS[C]); int method = A & CMP_METHOD_MASK; if (method == CMP_EQ) { cc.test(result, result); if (check) cc.jz(fail); else cc.jnz(fail); } else if (method == CMP_LT) { cc.cmp(result, 0); if (check) cc.jl(fail); else cc.jnl(fail); } else { cc.cmp(result, 0); if (check) cc.jle(fail); else cc.jnle(fail); } }); } ///////////////////////////////////////////////////////////////////////////// // Integer math. void JitCompiler::EmitSLL_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.shl(regD[A], rc); } void JitCompiler::EmitSLL_RI() { if (A != B) cc.mov(regD[A], regD[B]); cc.shl(regD[A], C); } void JitCompiler::EmitSLL_KR() { auto rc = CheckRegD(C, A); cc.mov(regD[A], konstd[B]); cc.shl(regD[A], rc); } void JitCompiler::EmitSRL_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.shr(regD[A], rc); } void JitCompiler::EmitSRL_RI() { if (A != B) cc.mov(regD[A], regD[B]); cc.shr(regD[A], C); } void JitCompiler::EmitSRL_KR() { auto rc = CheckRegD(C, A); cc.mov(regD[A], konstd[B]); cc.shr(regD[A], rc); } void JitCompiler::EmitSRA_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.sar(regD[A], rc); } void JitCompiler::EmitSRA_RI() { if (A != B) cc.mov(regD[A], regD[B]); cc.sar(regD[A], C); } void JitCompiler::EmitSRA_KR() { auto rc = CheckRegD(C, A); cc.mov(regD[A], konstd[B]); cc.sar(regD[A], rc); } void JitCompiler::EmitADD_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.add(regD[A], rc); } void JitCompiler::EmitADD_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.add(regD[A], konstd[C]); } void JitCompiler::EmitADDI() { if (A != B) cc.mov(regD[A], regD[B]); cc.add(regD[A], Cs); } void JitCompiler::EmitSUB_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.sub(regD[A], rc); } void JitCompiler::EmitSUB_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.sub(regD[A], konstd[C]); } void JitCompiler::EmitSUB_KR() { auto rc = CheckRegD(C, A); cc.mov(regD[A], konstd[B]); cc.sub(regD[A], rc); } void JitCompiler::EmitMUL_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.imul(regD[A], rc); } void JitCompiler::EmitMUL_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.imul(regD[A], konstd[C]); } void JitCompiler::EmitDIV_RR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, regD[B]); cc.cdq(tmp1, tmp0); cc.idiv(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp0); } void JitCompiler::EmitDIV_RK() { if (konstd[C] != 0) { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto konstTmp = cc.newIntPtr(); cc.mov(tmp0, regD[B]); cc.cdq(tmp1, tmp0); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.idiv(tmp1, tmp0, asmjit::x86::ptr(konstTmp)); cc.mov(regD[A], tmp0); } else EmitThrowException(X_DIVISION_BY_ZERO); } void JitCompiler::EmitDIV_KR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, konstd[B]); cc.cdq(tmp1, tmp0); cc.idiv(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp0); } void JitCompiler::EmitDIVU_RR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, regD[B]); cc.mov(tmp1, 0); cc.div(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp0); } void JitCompiler::EmitDIVU_RK() { if (konstd[C] != 0) { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto konstTmp = cc.newIntPtr(); cc.mov(tmp0, regD[B]); cc.mov(tmp1, 0); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.div(tmp1, tmp0, asmjit::x86::ptr(konstTmp)); cc.mov(regD[A], tmp0); } else EmitThrowException(X_DIVISION_BY_ZERO); } void JitCompiler::EmitDIVU_KR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, konstd[B]); cc.mov(tmp1, 0); cc.div(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp0); } void JitCompiler::EmitMOD_RR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, regD[B]); cc.cdq(tmp1, tmp0); cc.idiv(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp1); } void JitCompiler::EmitMOD_RK() { if (konstd[C] != 0) { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto konstTmp = cc.newIntPtr(); cc.mov(tmp0, regD[B]); cc.cdq(tmp1, tmp0); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.idiv(tmp1, tmp0, asmjit::x86::ptr(konstTmp)); cc.mov(regD[A], tmp1); } else EmitThrowException(X_DIVISION_BY_ZERO); } void JitCompiler::EmitMOD_KR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, konstd[B]); cc.cdq(tmp1, tmp0); cc.idiv(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp1); } void JitCompiler::EmitMODU_RR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, regD[B]); cc.mov(tmp1, 0); cc.div(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp1); } void JitCompiler::EmitMODU_RK() { if (konstd[C] != 0) { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto konstTmp = cc.newIntPtr(); cc.mov(tmp0, regD[B]); cc.mov(tmp1, 0); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.div(tmp1, tmp0, asmjit::x86::ptr(konstTmp)); cc.mov(regD[A], tmp1); } else EmitThrowException(X_DIVISION_BY_ZERO); } void JitCompiler::EmitMODU_KR() { auto tmp0 = cc.newInt32(); auto tmp1 = cc.newInt32(); auto label = cc.newLabel(); cc.test(regD[C], regD[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); cc.mov(tmp0, konstd[B]); cc.mov(tmp1, 0); cc.div(tmp1, tmp0, regD[C]); cc.mov(regD[A], tmp1); } void JitCompiler::EmitAND_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.and_(regD[A], rc); } void JitCompiler::EmitAND_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.and_(regD[A], konstd[C]); } void JitCompiler::EmitOR_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.or_(regD[A], rc); } void JitCompiler::EmitOR_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.or_(regD[A], konstd[C]); } void JitCompiler::EmitXOR_RR() { auto rc = CheckRegD(C, A); if (A != B) cc.mov(regD[A], regD[B]); cc.xor_(regD[A], rc); } void JitCompiler::EmitXOR_RK() { if (A != B) cc.mov(regD[A], regD[B]); cc.xor_(regD[A], konstd[C]); } void JitCompiler::EmitMIN_RR() { auto tmp0 = cc.newXmmSs(); auto tmp1 = cc.newXmmSs(); cc.movd(tmp0, regD[B]); cc.movd(tmp1, regD[C]); cc.pminsd(tmp0, tmp1); cc.movd(regD[A], tmp0); } void JitCompiler::EmitMIN_RK() { auto tmp0 = cc.newXmmSs(); auto tmp1 = cc.newXmmSs(); auto konstTmp = cc.newIntPtr(); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.movd(tmp0, regD[B]); cc.movss(tmp1, asmjit::x86::dword_ptr(konstTmp)); cc.pminsd(tmp0, tmp1); cc.movd(regD[A], tmp0); } void JitCompiler::EmitMAX_RR() { auto tmp0 = cc.newXmmSs(); auto tmp1 = cc.newXmmSs(); cc.movd(tmp0, regD[B]); cc.movd(tmp1, regD[C]); cc.pmaxsd(tmp0, tmp1); cc.movd(regD[A], tmp0); } void JitCompiler::EmitMAX_RK() { auto tmp0 = cc.newXmmSs(); auto tmp1 = cc.newXmmSs(); auto konstTmp = cc.newIntPtr(); cc.mov(konstTmp, asmjit::imm_ptr(&konstd[C])); cc.movd(tmp0, regD[B]); cc.movss(tmp1, asmjit::x86::dword_ptr(konstTmp)); cc.pmaxsd(tmp0, tmp1); cc.movd(regD[A], tmp0); } void JitCompiler::EmitABS() { auto srcB = CheckRegD(B, A); auto tmp = cc.newInt32(); cc.mov(tmp, regD[B]); cc.sar(tmp, 31); cc.mov(regD[A], tmp); cc.xor_(regD[A], srcB); cc.sub(regD[A], tmp); } void JitCompiler::EmitNEG() { auto srcB = CheckRegD(B, A); cc.xor_(regD[A], regD[A]); cc.sub(regD[A], srcB); } void JitCompiler::EmitNOT() { if (A != B) cc.mov(regD[A], regD[B]); cc.not_(regD[A]); } void JitCompiler::EmitEQ_R() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], regD[C]); if (check) cc.je(fail); else cc.jne(fail); }); } void JitCompiler::EmitEQ_K() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], konstd[C]); if (check) cc.je(fail); else cc.jne(fail); }); } void JitCompiler::EmitLT_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], regD[C]); if (check) cc.jl(fail); else cc.jnl(fail); }); } void JitCompiler::EmitLT_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], konstd[C]); if (check) cc.jl(fail); else cc.jnl(fail); }); } void JitCompiler::EmitLT_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstd[B])); cc.cmp(asmjit::x86::ptr(tmp), regD[C]); if (check) cc.jl(fail); else cc.jnl(fail); }); } void JitCompiler::EmitLE_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], regD[C]); if (check) cc.jle(fail); else cc.jnle(fail); }); } void JitCompiler::EmitLE_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], konstd[C]); if (check) cc.jle(fail); else cc.jnle(fail); }); } void JitCompiler::EmitLE_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstd[B])); cc.cmp(asmjit::x86::ptr(tmp), regD[C]); if (check) cc.jle(fail); else cc.jnle(fail); }); } void JitCompiler::EmitLTU_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], regD[C]); if (check) cc.jb(fail); else cc.jnb(fail); }); } void JitCompiler::EmitLTU_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], konstd[C]); if (check) cc.jb(fail); else cc.jnb(fail); }); } void JitCompiler::EmitLTU_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstd[B])); cc.cmp(asmjit::x86::ptr(tmp), regD[C]); if (check) cc.jb(fail); else cc.jnb(fail); }); } void JitCompiler::EmitLEU_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], regD[C]); if (check) cc.jbe(fail); else cc.jnbe(fail); }); } void JitCompiler::EmitLEU_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regD[B], konstd[C]); if (check) cc.jbe(fail); else cc.jnbe(fail); }); } void JitCompiler::EmitLEU_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstd[B])); cc.cmp(asmjit::x86::ptr(tmp), regD[C]); if (check) cc.jbe(fail); else cc.jnbe(fail); }); } ///////////////////////////////////////////////////////////////////////////// // Double-precision floating point math. void JitCompiler::EmitADDF_RR() { auto rc = CheckRegF(C, A); if (A != B) cc.movsd(regF[A], regF[B]); cc.addsd(regF[A], rc); } void JitCompiler::EmitADDF_RK() { auto tmp = cc.newIntPtr(); if (A != B) cc.movsd(regF[A], regF[B]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.addsd(regF[A], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitSUBF_RR() { auto rc = CheckRegF(C, A); if (A != B) cc.movsd(regF[A], regF[B]); cc.subsd(regF[A], rc); } void JitCompiler::EmitSUBF_RK() { auto tmp = cc.newIntPtr(); if (A != B) cc.movsd(regF[A], regF[B]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.subsd(regF[A], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitSUBF_KR() { auto rc = CheckRegF(C, A); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[B])); cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.subsd(regF[A], rc); } void JitCompiler::EmitMULF_RR() { auto rc = CheckRegF(C, A); if (A != B) cc.movsd(regF[A], regF[B]); cc.mulsd(regF[A], rc); } void JitCompiler::EmitMULF_RK() { auto tmp = cc.newIntPtr(); if (A != B) cc.movsd(regF[A], regF[B]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitDIVF_RR() { auto label = cc.newLabel(); cc.ptest(regF[C], regF[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); auto rc = CheckRegF(C, A); cc.movsd(regF[A], regF[B]); cc.divsd(regF[A], rc); } void JitCompiler::EmitDIVF_RK() { if (konstf[C] == 0.) { EmitThrowException(X_DIVISION_BY_ZERO); } else { auto tmp = cc.newIntPtr(); cc.movsd(regF[A], regF[B]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.divsd(regF[A], asmjit::x86::qword_ptr(tmp)); } } void JitCompiler::EmitDIVF_KR() { auto rc = CheckRegF(C, A); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[B])); cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.divsd(regF[A], rc); } void JitCompiler::EmitMODF_RR() { auto label = cc.newLabel(); cc.ptest(regF[C], regF[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); auto call = CreateCall([](double a, double b) -> double { return a - floor(a / b) * b; }); call->setRet(0, regF[A]); call->setArg(0, regF[B]); call->setArg(1, regF[C]); } void JitCompiler::EmitMODF_RK() { auto label = cc.newLabel(); cc.ptest(regF[C], regF[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); auto tmp = cc.newXmm(); cc.movsd(tmp, asmjit::x86::ptr(ToMemAddress(&konstf[C]))); auto call = CreateCall([](double a, double b) -> double { return a - floor(a / b) * b; }); call->setRet(0, regF[A]); call->setArg(0, regF[B]); call->setArg(1, tmp); } void JitCompiler::EmitMODF_KR() { using namespace asmjit; auto label = cc.newLabel(); cc.ptest(regF[C], regF[C]); cc.jne(label); EmitThrowException(X_DIVISION_BY_ZERO); cc.bind(label); auto tmp = cc.newXmm(); cc.movsd(tmp, x86::ptr(ToMemAddress(&konstf[B]))); auto call = CreateCall([](double a, double b) -> double { return a - floor(a / b) * b; }); call->setRet(0, regF[A]); call->setArg(0, tmp); call->setArg(1, regF[C]); } void JitCompiler::EmitPOWF_RR() { auto call = CreateCall(g_pow); call->setRet(0, regF[A]); call->setArg(0, regF[B]); call->setArg(1, regF[C]); } void JitCompiler::EmitPOWF_RK() { auto tmp = cc.newIntPtr(); auto tmp2 = cc.newXmm(); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); auto call = CreateCall(g_pow); call->setRet(0, regF[A]); call->setArg(0, regF[B]); call->setArg(1, tmp2); } void JitCompiler::EmitPOWF_KR() { auto tmp = cc.newIntPtr(); auto tmp2 = cc.newXmm(); cc.mov(tmp, asmjit::imm_ptr(&konstf[B])); cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); auto call = CreateCall(g_pow); call->setRet(0, regF[A]); call->setArg(0, tmp2); call->setArg(1, regF[C]); } void JitCompiler::EmitMINF_RR() { auto rc = CheckRegF(C, A); if (A != B) cc.movsd(regF[A], regF[B]); cc.minsd(regF[A], rc); } void JitCompiler::EmitMINF_RK() { auto rb = CheckRegF(B, A); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.minsd(regF[A], rb); } void JitCompiler::EmitMAXF_RR() { auto rc = CheckRegF(C, A); if (A != B) cc.movsd(regF[A], regF[B]); cc.maxsd(regF[A], rc); } void JitCompiler::EmitMAXF_RK() { auto rb = CheckRegF(B, A); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.maxsd(regF[A], rb); } void JitCompiler::EmitATAN2() { auto call = CreateCall(g_atan2); call->setRet(0, regF[A]); call->setArg(0, regF[B]); call->setArg(1, regF[C]); static const double constant = 180 / M_PI; auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&constant)); cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitFLOP() { if (C == FLOP_NEG) { auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0); auto maskXmm = cc.newXmmSd(); cc.movsd(maskXmm, mask); if (A != B) cc.movsd(regF[A], regF[B]); cc.xorpd(regF[A], maskXmm); } else { auto v = cc.newXmm(); cc.movsd(v, regF[B]); if (C == FLOP_TAN_DEG) { static const double constant = M_PI / 180; auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&constant)); cc.mulsd(v, asmjit::x86::qword_ptr(tmp)); } typedef double(*FuncPtr)(double); FuncPtr func = nullptr; switch (C) { default: I_FatalError("Unknown OP_FLOP subfunction"); case FLOP_ABS: func = fabs; break; case FLOP_EXP: func = g_exp; break; case FLOP_LOG: func = g_log; break; case FLOP_LOG10: func = g_log10; break; case FLOP_SQRT: func = g_sqrt; break; case FLOP_CEIL: func = ceil; break; case FLOP_FLOOR: func = floor; break; case FLOP_ACOS: func = g_acos; break; case FLOP_ASIN: func = g_asin; break; case FLOP_ATAN: func = g_atan; break; case FLOP_COS: func = g_cos; break; case FLOP_SIN: func = g_sin; break; case FLOP_TAN: func = g_tan; break; case FLOP_ACOS_DEG: func = g_acos; break; case FLOP_ASIN_DEG: func = g_asin; break; case FLOP_ATAN_DEG: func = g_atan; break; case FLOP_COS_DEG: func = g_cosdeg; break; case FLOP_SIN_DEG: func = g_sindeg; break; case FLOP_TAN_DEG: func = g_tan; break; case FLOP_COSH: func = g_cosh; break; case FLOP_SINH: func = g_sinh; break; case FLOP_TANH: func = g_tanh; break; } auto call = CreateCall(func); call->setRet(0, regF[A]); call->setArg(0, v); if (C == FLOP_ACOS_DEG || C == FLOP_ASIN_DEG || C == FLOP_ATAN_DEG) { static const double constant = 180 / M_PI; auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&constant)); cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp)); } } } void JitCompiler::EmitEQF_R() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { bool approx = static_cast(A & CMP_APPROX); if (!approx) { cc.ucomisd(regF[B], regF[C]); if (check) { cc.jp(success); cc.je(fail); } else { cc.jp(fail); cc.jne(fail); } } else { auto tmp = cc.newXmmSd(); const int64_t absMaskInt = 0x7FFFFFFFFFFFFFFF; auto absMask = cc.newDoubleConst(asmjit::kConstScopeLocal, reinterpret_cast(absMaskInt)); auto absMaskXmm = cc.newXmmPd(); auto epsilon = cc.newDoubleConst(asmjit::kConstScopeLocal, VM_EPSILON); auto epsilonXmm = cc.newXmmSd(); cc.movsd(tmp, regF[B]); cc.subsd(tmp, regF[C]); cc.movsd(absMaskXmm, absMask); cc.andpd(tmp, absMaskXmm); cc.movsd(epsilonXmm, epsilon); cc.ucomisd(epsilonXmm, tmp); if (check) cc.ja(fail); else cc.jna(fail); } }); } void JitCompiler::EmitEQF_K() { using namespace asmjit; EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { bool approx = static_cast(A & CMP_APPROX); if (!approx) { auto konstTmp = cc.newIntPtr(); cc.mov(konstTmp, asmjit::imm_ptr(&konstf[C])); cc.ucomisd(regF[B], x86::qword_ptr(konstTmp)); if (check) { cc.jp(success); cc.je(fail); } else { cc.jp(fail); cc.jne(fail); } } else { auto konstTmp = cc.newIntPtr(); auto subTmp = cc.newXmmSd(); const int64_t absMaskInt = 0x7FFFFFFFFFFFFFFF; auto absMask = cc.newDoubleConst(kConstScopeLocal, reinterpret_cast(absMaskInt)); auto absMaskXmm = cc.newXmmPd(); auto epsilon = cc.newDoubleConst(kConstScopeLocal, VM_EPSILON); auto epsilonXmm = cc.newXmmSd(); cc.mov(konstTmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(subTmp, regF[B]); cc.subsd(subTmp, x86::qword_ptr(konstTmp)); cc.movsd(absMaskXmm, absMask); cc.andpd(subTmp, absMaskXmm); cc.movsd(epsilonXmm, epsilon); cc.ucomisd(epsilonXmm, subTmp); if (check) cc.ja(fail); else cc.jna(fail); } }); } void JitCompiler::EmitLTF_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LTF_RR.\n"); cc.ucomisd(regF[C], regF[B]); if (check) cc.ja(fail); else cc.jna(fail); }); } void JitCompiler::EmitLTF_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LTF_RK.\n"); auto constTmp = cc.newIntPtr(); auto xmmTmp = cc.newXmmSd(); cc.mov(constTmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(xmmTmp, asmjit::x86::qword_ptr(constTmp)); cc.ucomisd(xmmTmp, regF[B]); if (check) cc.ja(fail); else cc.jna(fail); }); } void JitCompiler::EmitLTF_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LTF_KR.\n"); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[B])); cc.ucomisd(regF[C], asmjit::x86::qword_ptr(tmp)); if (check) cc.ja(fail); else cc.jna(fail); }); } void JitCompiler::EmitLEF_RR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LEF_RR.\n"); cc.ucomisd(regF[C], regF[B]); if (check) cc.jae(fail); else cc.jnae(fail); }); } void JitCompiler::EmitLEF_RK() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LEF_RK.\n"); auto constTmp = cc.newIntPtr(); auto xmmTmp = cc.newXmmSd(); cc.mov(constTmp, asmjit::imm_ptr(&konstf[C])); cc.movsd(xmmTmp, asmjit::x86::qword_ptr(constTmp)); cc.ucomisd(xmmTmp, regF[B]); if (check) cc.jae(fail); else cc.jnae(fail); }); } void JitCompiler::EmitLEF_KR() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for LEF_KR.\n"); auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(&konstf[B])); cc.ucomisd(regF[C], asmjit::x86::qword_ptr(tmp)); if (check) cc.jae(fail); else cc.jnae(fail); }); } ///////////////////////////////////////////////////////////////////////////// // Vector math. (2D) void JitCompiler::EmitNEGV2() { auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0); auto maskXmm = cc.newXmmSd(); cc.movsd(maskXmm, mask); cc.movsd(regF[A], regF[B]); cc.xorpd(regF[A], maskXmm); cc.movsd(regF[A + 1], regF[B + 1]); cc.xorpd(regF[A + 1], maskXmm); } void JitCompiler::EmitADDV2_RR() { auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A + 1); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.addsd(regF[A], rc0); cc.addsd(regF[A + 1], rc1); } void JitCompiler::EmitSUBV2_RR() { auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A + 1); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.subsd(regF[A], rc0); cc.subsd(regF[A + 1], rc1); } void JitCompiler::EmitDOTV2_RR() { auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A); auto tmp = cc.newXmmSd(); cc.movsd(regF[A], regF[B]); cc.mulsd(regF[A], rc0); cc.movsd(tmp, regF[B + 1]); cc.mulsd(tmp, rc1); cc.addsd(regF[A], tmp); } void JitCompiler::EmitMULVF2_RR() { auto rc = CheckRegF(C, A, A + 1); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.mulsd(regF[A], rc); cc.mulsd(regF[A + 1], rc); } void JitCompiler::EmitMULVF2_RK() { auto tmp = cc.newIntPtr(); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.mulsd(regF[A + 1], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitDIVVF2_RR() { auto rc = CheckRegF(C, A, A + 1); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.divsd(regF[A], rc); cc.divsd(regF[A + 1], rc); } void JitCompiler::EmitDIVVF2_RK() { auto tmp = cc.newIntPtr(); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.divsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.divsd(regF[A + 1], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitLENV2() { auto rb0 = CheckRegF(B, A); auto rb1 = CheckRegF(B + 1, A); auto tmp = cc.newXmmSd(); cc.movsd(regF[A], regF[B]); cc.mulsd(regF[A], rb0); cc.movsd(tmp, rb1); cc.mulsd(tmp, rb1); cc.addsd(regF[A], tmp); CallSqrt(regF[A], regF[A]); } void JitCompiler::EmitEQV2_R() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for EQV2_R.\n"); cc.ucomisd(regF[B], regF[C]); if (check) { cc.jp(success); cc.jne(success); } else { cc.jp(fail); cc.jne(fail); } cc.ucomisd(regF[B + 1], regF[C + 1]); if (check) { cc.jp(success); cc.je(fail); } else { cc.jp(fail); cc.jne(fail); } }); } void JitCompiler::EmitEQV2_K() { I_FatalError("EQV2_K is not used."); } ///////////////////////////////////////////////////////////////////////////// // Vector math. (3D) void JitCompiler::EmitNEGV3() { auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0); auto maskXmm = cc.newXmmSd(); cc.movsd(maskXmm, mask); cc.movsd(regF[A], regF[B]); cc.xorpd(regF[A], maskXmm); cc.movsd(regF[A + 1], regF[B + 1]); cc.xorpd(regF[A + 1], maskXmm); cc.movsd(regF[A + 2], regF[B + 2]); cc.xorpd(regF[A + 2], maskXmm); } void JitCompiler::EmitADDV3_RR() { auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A + 1); auto rc2 = CheckRegF(C + 2, A + 2); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.addsd(regF[A], rc0); cc.addsd(regF[A + 1], rc1); cc.addsd(regF[A + 2], rc2); } void JitCompiler::EmitSUBV3_RR() { auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A + 1); auto rc2 = CheckRegF(C + 2, A + 2); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.subsd(regF[A], rc0); cc.subsd(regF[A + 1], rc1); cc.subsd(regF[A + 2], rc2); } void JitCompiler::EmitDOTV3_RR() { auto rb1 = CheckRegF(B + 1, A); auto rb2 = CheckRegF(B + 2, A); auto rc0 = CheckRegF(C, A); auto rc1 = CheckRegF(C + 1, A); auto rc2 = CheckRegF(C + 2, A); auto tmp = cc.newXmmSd(); cc.movsd(regF[A], regF[B]); cc.mulsd(regF[A], rc0); cc.movsd(tmp, rb1); cc.mulsd(tmp, rc1); cc.addsd(regF[A], tmp); cc.movsd(tmp, rb2); cc.mulsd(tmp, rc2); cc.addsd(regF[A], tmp); } void JitCompiler::EmitCROSSV_RR() { auto tmp = cc.newXmmSd(); auto a0 = CheckRegF(B, A); auto a1 = CheckRegF(B + 1, A + 1); auto a2 = CheckRegF(B + 2, A + 2); auto b0 = CheckRegF(C, A); auto b1 = CheckRegF(C + 1, A + 1); auto b2 = CheckRegF(C + 2, A + 2); // r0 = a1b2 - a2b1 cc.movsd(regF[A], a1); cc.mulsd(regF[A], b2); cc.movsd(tmp, a2); cc.mulsd(tmp, b1); cc.subsd(regF[A], tmp); // r1 = a2b0 - a0b2 cc.movsd(regF[A + 1], a2); cc.mulsd(regF[A + 1], b0); cc.movsd(tmp, a0); cc.mulsd(tmp, b2); cc.subsd(regF[A + 1], tmp); // r2 = a0b1 - a1b0 cc.movsd(regF[A + 2], a0); cc.mulsd(regF[A + 2], b1); cc.movsd(tmp, a1); cc.mulsd(tmp, b0); cc.subsd(regF[A + 2], tmp); } void JitCompiler::EmitMULVF3_RR() { auto rc = CheckRegF(C, A, A + 1, A + 2); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.mulsd(regF[A], rc); cc.mulsd(regF[A + 1], rc); cc.mulsd(regF[A + 2], rc); } void JitCompiler::EmitMULVF3_RK() { auto tmp = cc.newIntPtr(); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.mulsd(regF[A + 1], asmjit::x86::qword_ptr(tmp)); cc.mulsd(regF[A + 2], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitDIVVF3_RR() { auto rc = CheckRegF(C, A, A + 1, A + 2); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.divsd(regF[A], rc); cc.divsd(regF[A + 1], rc); cc.divsd(regF[A + 2], rc); } void JitCompiler::EmitDIVVF3_RK() { auto tmp = cc.newIntPtr(); cc.movsd(regF[A], regF[B]); cc.movsd(regF[A + 1], regF[B + 1]); cc.movsd(regF[A + 2], regF[B + 2]); cc.mov(tmp, asmjit::imm_ptr(&konstf[C])); cc.divsd(regF[A], asmjit::x86::qword_ptr(tmp)); cc.divsd(regF[A + 1], asmjit::x86::qword_ptr(tmp)); cc.divsd(regF[A + 2], asmjit::x86::qword_ptr(tmp)); } void JitCompiler::EmitLENV3() { auto rb1 = CheckRegF(B + 1, A); auto rb2 = CheckRegF(B + 2, A); auto tmp = cc.newXmmSd(); cc.movsd(regF[A], regF[B]); cc.mulsd(regF[A], regF[B]); cc.movsd(tmp, rb1); cc.mulsd(tmp, rb1); cc.addsd(regF[A], tmp); cc.movsd(tmp, rb2); cc.mulsd(tmp, rb2); cc.addsd(regF[A], tmp); CallSqrt(regF[A], regF[A]); } void JitCompiler::EmitEQV3_R() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { if (static_cast(A & CMP_APPROX)) I_FatalError("CMP_APPROX not implemented for EQV3_R.\n"); cc.ucomisd(regF[B], regF[C]); if (check) { cc.jp(success); cc.jne(success); } else { cc.jp(fail); cc.jne(fail); } cc.ucomisd(regF[B + 1], regF[C + 1]); if (check) { cc.jp(success); cc.jne(success); } else { cc.jp(fail); cc.jne(fail); } cc.ucomisd(regF[B + 2], regF[C + 2]); if (check) { cc.jp(success); cc.je(fail); } else { cc.jp(fail); cc.jne(fail); } }); } void JitCompiler::EmitEQV3_K() { I_FatalError("EQV3_K is not used."); } ///////////////////////////////////////////////////////////////////////////// // Pointer math. void JitCompiler::EmitADDA_RR() { auto tmp = cc.newIntPtr(); auto label = cc.newLabel(); cc.mov(tmp, regA[B]); // Check if zero, the first operand is zero, if it is, don't add. cc.cmp(tmp, 0); cc.je(label); auto tmpptr = cc.newIntPtr(); cc.mov(tmpptr, regD[C]); cc.add(tmp, tmpptr); cc.bind(label); cc.mov(regA[A], tmp); } void JitCompiler::EmitADDA_RK() { auto tmp = cc.newIntPtr(); auto label = cc.newLabel(); cc.mov(tmp, regA[B]); // Check if zero, the first operand is zero, if it is, don't add. cc.cmp(tmp, 0); cc.je(label); cc.add(tmp, konstd[C]); cc.bind(label); cc.mov(regA[A], tmp); } void JitCompiler::EmitSUBA() { auto tmp = cc.newIntPtr(); cc.mov(tmp, regA[B]); cc.sub(tmp, regD[C]); cc.mov(regA[A], tmp); } void JitCompiler::EmitEQA_R() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { cc.cmp(regA[B], regA[C]); if (check) cc.je(fail); else cc.jne(fail); }); } void JitCompiler::EmitEQA_K() { EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) { auto tmp = cc.newIntPtr(); cc.mov(tmp, asmjit::imm_ptr(konsta[C].v)); cc.cmp(regA[B], tmp); if (check) cc.je(fail); else cc.jne(fail); }); } void JitCompiler::CallSqrt(const asmjit::X86Xmm &a, const asmjit::X86Xmm &b) { auto call = CreateCall(g_sqrt); call->setRet(0, a); call->setArg(0, b); }