mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-27 14:32:26 +00:00
2ffdf3d0e1
Mainly quaternion math and sound system cleanup.
1739 lines
39 KiB
C++
1739 lines
39 KiB
C++
|
|
#include "jitintern.h"
|
|
#include "basics.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// String instructions.
|
|
|
|
static void ConcatString(FString* to, FString* first, FString* second)
|
|
{
|
|
*to = *first + *second;
|
|
}
|
|
|
|
void JitCompiler::EmitCONCAT()
|
|
{
|
|
auto rc = CheckRegS(C, A);
|
|
auto call = CreateCall<void, FString*, FString*, FString*>(ConcatString);
|
|
call->setArg(0, regS[A]);
|
|
call->setArg(1, regS[B]);
|
|
call->setArg(2, rc);
|
|
}
|
|
|
|
static int StringLength(FString* str)
|
|
{
|
|
return static_cast<int>(str->Len());
|
|
}
|
|
|
|
void JitCompiler::EmitLENS()
|
|
{
|
|
auto result = newResultInt32();
|
|
auto call = CreateCall<int, FString*>(StringLength);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regS[B]);
|
|
cc.mov(regD[A], result);
|
|
}
|
|
|
|
static int StringCompareNoCase(FString* first, FString* second)
|
|
{
|
|
return first->CompareNoCase(*second);
|
|
}
|
|
|
|
static int StringCompare(FString* first, FString* second)
|
|
{
|
|
return first->Compare(*second);
|
|
}
|
|
|
|
void JitCompiler::EmitCMPS()
|
|
{
|
|
EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) {
|
|
|
|
auto call = CreateCall<int, FString*, FString*>(static_cast<bool>(A & CMP_APPROX) ? StringCompareNoCase : StringCompare);
|
|
|
|
auto result = newResultInt32();
|
|
call->setRet(0, result);
|
|
|
|
if (static_cast<bool>(A & CMP_BK)) call->setArg(0, asmjit::imm_ptr(&konsts[B]));
|
|
else call->setArg(0, regS[B]);
|
|
|
|
if (static_cast<bool>(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
auto konstTmp = newTempIntPtr();
|
|
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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
auto konstTmp = newTempIntPtr();
|
|
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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
auto konstTmp = newTempIntPtr();
|
|
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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
auto konstTmp = newTempIntPtr();
|
|
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 = newTempInt32();
|
|
auto tmp1 = newTempInt32();
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
cc.test(regD[C], regD[C]);
|
|
cc.je(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 rc = CheckRegD(C, A);
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovl(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMIN_RK()
|
|
{
|
|
auto rc = newTempInt32();
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.mov(rc, asmjit::imm(konstd[C]));
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovl(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMAX_RR()
|
|
{
|
|
auto rc = CheckRegD(C, A);
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovg(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMAX_RK()
|
|
{
|
|
auto rc = newTempInt32();
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.mov(rc, asmjit::imm(konstd[C]));
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovg(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMINU_RR()
|
|
{
|
|
auto rc = CheckRegD(C, A);
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovb(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMINU_RK()
|
|
{
|
|
auto rc = newTempInt32();
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.mov(rc, asmjit::imm(konstd[C]));
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmovb(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMAXU_RR()
|
|
{
|
|
auto rc = CheckRegD(C, A);
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmova(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMAXU_RK()
|
|
{
|
|
auto rc = newTempInt32();
|
|
if (A != B)
|
|
cc.mov(regD[A], regD[B]);
|
|
cc.mov(rc, asmjit::imm(konstd[C]));
|
|
cc.cmp(rc, regD[A]);
|
|
cc.cmova(regD[A], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitABS()
|
|
{
|
|
auto srcB = CheckRegD(B, A);
|
|
auto tmp = newTempInt32();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
auto zero = newTempXmmSd();
|
|
cc.xorpd(zero, zero);
|
|
cc.ucomisd(regF[C], zero);
|
|
cc.je(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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[B]));
|
|
cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
|
cc.divsd(regF[A], rc);
|
|
}
|
|
|
|
static double DoubleModF(double a, double b)
|
|
{
|
|
return a - floor(a / b) * b;
|
|
}
|
|
|
|
void JitCompiler::EmitMODF_RR()
|
|
{
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
auto zero = newTempXmmSd();
|
|
cc.xorpd(zero, zero);
|
|
cc.ucomisd(regF[C], zero);
|
|
cc.je(label);
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(DoubleModF);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regF[B]);
|
|
call->setArg(1, regF[C]);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
|
|
void JitCompiler::EmitMODF_RK()
|
|
{
|
|
if (konstf[C] == 0.)
|
|
{
|
|
EmitThrowException(X_DIVISION_BY_ZERO);
|
|
}
|
|
else
|
|
{
|
|
auto tmpPtr = newTempIntPtr();
|
|
cc.mov(tmpPtr, asmjit::imm_ptr(&konstf[C]));
|
|
|
|
auto tmp = newTempXmmSd();
|
|
cc.movsd(tmp, asmjit::x86::qword_ptr(tmpPtr));
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(DoubleModF);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regF[B]);
|
|
call->setArg(1, tmp);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
}
|
|
|
|
void JitCompiler::EmitMODF_KR()
|
|
{
|
|
using namespace asmjit;
|
|
|
|
auto label = EmitThrowExceptionLabel(X_DIVISION_BY_ZERO);
|
|
auto zero = newTempXmmSd();
|
|
cc.xorpd(zero, zero);
|
|
cc.ucomisd(regF[C], zero);
|
|
cc.je(label);
|
|
|
|
auto tmp = newTempXmmSd();
|
|
cc.movsd(tmp, x86::ptr(ToMemAddress(&konstf[B])));
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(DoubleModF);
|
|
call->setRet(0, result);
|
|
call->setArg(0, tmp);
|
|
call->setArg(1, regF[C]);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
|
|
void JitCompiler::EmitPOWF_RR()
|
|
{
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(g_pow);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regF[B]);
|
|
call->setArg(1, regF[C]);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
|
|
void JitCompiler::EmitPOWF_RK()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
auto tmp2 = newTempXmmSd();
|
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[C]));
|
|
cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp));
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(g_pow);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regF[B]);
|
|
call->setArg(1, tmp2);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
|
|
void JitCompiler::EmitPOWF_KR()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
auto tmp2 = newTempXmmSd();
|
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[B]));
|
|
cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp));
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(g_pow);
|
|
call->setRet(0, result);
|
|
call->setArg(0, tmp2);
|
|
call->setArg(1, regF[C]);
|
|
cc.movsd(regF[A], result);
|
|
}
|
|
|
|
void JitCompiler::EmitMINF_RR()
|
|
{
|
|
auto rc = CheckRegF(C, A);
|
|
if (A != B)
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.minpd(regF[A], rc); // minsd requires SSE 4.1
|
|
}
|
|
|
|
void JitCompiler::EmitMINF_RK()
|
|
{
|
|
auto rb = CheckRegF(B, A);
|
|
auto tmp = newTempIntPtr();
|
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[C]));
|
|
cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
|
cc.minpd(regF[A], rb); // minsd requires SSE 4.1
|
|
}
|
|
|
|
void JitCompiler::EmitMAXF_RR()
|
|
{
|
|
auto rc = CheckRegF(C, A);
|
|
if (A != B)
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.maxpd(regF[A], rc); // maxsd requires SSE 4.1
|
|
}
|
|
|
|
void JitCompiler::EmitMAXF_RK()
|
|
{
|
|
auto rb = CheckRegF(B, A);
|
|
auto tmp = newTempIntPtr();
|
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[C]));
|
|
cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
|
cc.maxpd(regF[A], rb); // maxsd requires SSE 4.1
|
|
}
|
|
|
|
void JitCompiler::EmitATAN2()
|
|
{
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double, double>(g_atan2);
|
|
call->setRet(0, result);
|
|
call->setArg(0, regF[B]);
|
|
call->setArg(1, regF[C]);
|
|
cc.movsd(regF[A], result);
|
|
|
|
static const double constant = 180 / M_PI;
|
|
auto tmp = newTempIntPtr();
|
|
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 = newTempXmmSd();
|
|
cc.movsd(maskXmm, mask);
|
|
if (A != B)
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.xorpd(regF[A], maskXmm);
|
|
}
|
|
else
|
|
{
|
|
auto v = newTempXmmSd();
|
|
cc.movsd(v, regF[B]);
|
|
|
|
if (C == FLOP_TAN_DEG)
|
|
{
|
|
static const double constant = M_PI / 180;
|
|
auto tmp = newTempIntPtr();
|
|
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_Error("Unknown OP_FLOP subfunction"); break;
|
|
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;
|
|
case FLOP_ROUND: func = round; break;
|
|
}
|
|
|
|
auto result = newResultXmmSd();
|
|
auto call = CreateCall<double, double>(func);
|
|
call->setRet(0, result);
|
|
call->setArg(0, v);
|
|
cc.movsd(regF[A], result);
|
|
|
|
if (C == FLOP_ACOS_DEG || C == FLOP_ASIN_DEG || C == FLOP_ATAN_DEG)
|
|
{
|
|
static const double constant = 180 / M_PI;
|
|
auto tmp = newTempIntPtr();
|
|
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<bool>(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 = newTempXmmSd();
|
|
|
|
const int64_t absMaskInt = 0x7FFFFFFFFFFFFFFF;
|
|
auto absMask = cc.newDoubleConst(asmjit::kConstScopeLocal, reinterpret_cast<const double&>(absMaskInt));
|
|
auto absMaskXmm = newTempXmmPd();
|
|
|
|
auto epsilon = cc.newDoubleConst(asmjit::kConstScopeLocal, VM_EPSILON);
|
|
auto epsilonXmm = newTempXmmSd();
|
|
|
|
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<bool>(A & CMP_APPROX);
|
|
if (!approx) {
|
|
auto konstTmp = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
auto subTmp = newTempXmmSd();
|
|
|
|
const int64_t absMaskInt = 0x7FFFFFFFFFFFFFFF;
|
|
auto absMask = cc.newDoubleConst(kConstScopeLocal, reinterpret_cast<const double&>(absMaskInt));
|
|
auto absMaskXmm = newTempXmmPd();
|
|
|
|
auto epsilon = cc.newDoubleConst(kConstScopeLocal, VM_EPSILON);
|
|
auto epsilonXmm = newTempXmmSd();
|
|
|
|
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<bool>(A & CMP_APPROX)) I_Error("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<bool>(A & CMP_APPROX)) I_Error("CMP_APPROX not implemented for LTF_RK.\n");
|
|
|
|
auto constTmp = newTempIntPtr();
|
|
auto xmmTmp = newTempXmmSd();
|
|
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<bool>(A & CMP_APPROX)) I_Error("CMP_APPROX not implemented for LTF_KR.\n");
|
|
|
|
auto tmp = newTempIntPtr();
|
|
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<bool>(A & CMP_APPROX)) I_Error("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<bool>(A & CMP_APPROX)) I_Error("CMP_APPROX not implemented for LEF_RK.\n");
|
|
|
|
auto constTmp = newTempIntPtr();
|
|
auto xmmTmp = newTempXmmSd();
|
|
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<bool>(A & CMP_APPROX)) I_Error("CMP_APPROX not implemented for LEF_KR.\n");
|
|
|
|
auto tmp = newTempIntPtr();
|
|
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 = newTempXmmSd();
|
|
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.addsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
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.subsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.subsd(regF[A + 1], rc1);
|
|
}
|
|
|
|
void JitCompiler::EmitDOTV2_RR()
|
|
{
|
|
auto rc0 = CheckRegF(C, A);
|
|
auto rc1 = CheckRegF(C + 1, A);
|
|
auto tmp = newTempXmmSd();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempXmmSd();
|
|
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) {
|
|
EmitVectorComparison<2> (check, fail, success);
|
|
});
|
|
}
|
|
|
|
void JitCompiler::EmitEQV2_K()
|
|
{
|
|
I_Error("EQV2_K is not used.");
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Vector math. (3D)
|
|
|
|
void JitCompiler::EmitNEGV3()
|
|
{
|
|
auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0);
|
|
auto maskXmm = newTempXmmSd();
|
|
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.addsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.addsd(regF[A + 1], rc1);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
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.subsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.subsd(regF[A + 1], rc1);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
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 = newTempXmmSd();
|
|
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 = newTempXmmSd();
|
|
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempXmmSd();
|
|
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) {
|
|
EmitVectorComparison<3> (check, fail, success);
|
|
});
|
|
}
|
|
|
|
void JitCompiler::EmitEQV3_K()
|
|
{
|
|
I_Error("EQV3_K is not used.");
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Vector math. (4D/Quaternion)
|
|
|
|
void JitCompiler::EmitNEGV4()
|
|
{
|
|
auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0);
|
|
auto maskXmm = newTempXmmSd();
|
|
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);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
cc.xorpd(regF[A + 3], maskXmm);
|
|
}
|
|
|
|
void JitCompiler::EmitADDV4_RR()
|
|
{
|
|
auto rc0 = CheckRegF(C, A);
|
|
auto rc1 = CheckRegF(C + 1, A + 1);
|
|
auto rc2 = CheckRegF(C + 2, A + 2);
|
|
auto rc3 = CheckRegF(C + 3, A + 3);
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.addsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.addsd(regF[A + 1], rc1);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.addsd(regF[A + 2], rc2);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
cc.addsd(regF[A + 3], rc3);
|
|
}
|
|
|
|
void JitCompiler::EmitSUBV4_RR()
|
|
{
|
|
auto rc0 = CheckRegF(C, A);
|
|
auto rc1 = CheckRegF(C + 1, A + 1);
|
|
auto rc2 = CheckRegF(C + 2, A + 2);
|
|
auto rc3 = CheckRegF(C + 3, A + 3);
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.subsd(regF[A], rc0);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.subsd(regF[A + 1], rc1);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.subsd(regF[A + 2], rc2);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
cc.subsd(regF[A + 3], rc3);
|
|
}
|
|
|
|
void JitCompiler::EmitDOTV4_RR()
|
|
{
|
|
auto rb1 = CheckRegF(B + 1, A);
|
|
auto rb2 = CheckRegF(B + 2, A);
|
|
auto rb3 = CheckRegF(B + 3, A);
|
|
auto rc0 = CheckRegF(C, A);
|
|
auto rc1 = CheckRegF(C + 1, A);
|
|
auto rc2 = CheckRegF(C + 2, A);
|
|
auto rc3 = CheckRegF(C + 3, A);
|
|
auto tmp = newTempXmmSd();
|
|
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);
|
|
cc.movsd(tmp, rb3);
|
|
cc.mulsd(tmp, rc3);
|
|
cc.addsd(regF[A], tmp);
|
|
}
|
|
|
|
void JitCompiler::EmitMULVF4_RR()
|
|
{
|
|
auto rc = CheckRegF(C, A, A + 1, A + 2, A + 3);
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
cc.mulsd(regF[A], rc);
|
|
cc.mulsd(regF[A + 1], rc);
|
|
cc.mulsd(regF[A + 2], rc);
|
|
cc.mulsd(regF[A + 3], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitMULVF4_RK()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
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));
|
|
cc.mulsd(regF[A + 3], asmjit::x86::qword_ptr(tmp));
|
|
}
|
|
|
|
void JitCompiler::EmitDIVVF4_RR()
|
|
{
|
|
auto rc = CheckRegF(C, A, A + 1, A + 2, A + 3);
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
cc.divsd(regF[A], rc);
|
|
cc.divsd(regF[A + 1], rc);
|
|
cc.divsd(regF[A + 2], rc);
|
|
cc.divsd(regF[A + 3], rc);
|
|
}
|
|
|
|
void JitCompiler::EmitDIVVF4_RK()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
cc.movsd(regF[A], regF[B]);
|
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
|
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));
|
|
cc.divsd(regF[A + 3], asmjit::x86::qword_ptr(tmp));
|
|
}
|
|
|
|
void JitCompiler::EmitLENV4()
|
|
{
|
|
auto rb1 = CheckRegF(B + 1, A);
|
|
auto rb2 = CheckRegF(B + 2, A);
|
|
auto rb3 = CheckRegF(B + 3, A);
|
|
auto tmp = newTempXmmSd();
|
|
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);
|
|
cc.movsd(tmp, rb3);
|
|
cc.mulsd(tmp, rb3);
|
|
cc.addsd(regF[A], tmp);
|
|
CallSqrt(regF[A], regF[A]);
|
|
}
|
|
|
|
void JitCompiler::EmitEQV4_R()
|
|
{
|
|
EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) {
|
|
EmitVectorComparison<4> (check, fail, success);
|
|
});
|
|
}
|
|
|
|
void JitCompiler::EmitEQV4_K()
|
|
{
|
|
I_Error("EQV4_K is not used.");
|
|
}
|
|
|
|
// Quaternion ops
|
|
void FuncMULQQ(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz, double bw)
|
|
{
|
|
*reinterpret_cast<DQuaternion*>(result) = DQuaternion(ax, ay, az, aw) * DQuaternion(bx, by, bz, bw);
|
|
}
|
|
|
|
void FuncMULQV3(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz)
|
|
{
|
|
*reinterpret_cast<DVector3*>(result) = DQuaternion(ax, ay, az, aw) * DVector3(bx, by, bz);
|
|
}
|
|
|
|
void JitCompiler::EmitMULQQ_RR()
|
|
{
|
|
auto stack = GetTemporaryVectorStackStorage();
|
|
auto tmp = newTempIntPtr();
|
|
cc.lea(tmp, stack);
|
|
|
|
auto call = CreateCall<void, void*, double, double, double, double, double, double, double, double>(FuncMULQQ);
|
|
call->setArg(0, tmp);
|
|
call->setArg(1, regF[B + 0]);
|
|
call->setArg(2, regF[B + 1]);
|
|
call->setArg(3, regF[B + 2]);
|
|
call->setArg(4, regF[B + 3]);
|
|
call->setArg(5, regF[C + 0]);
|
|
call->setArg(6, regF[C + 1]);
|
|
call->setArg(7, regF[C + 2]);
|
|
call->setArg(8, regF[C + 3]);
|
|
|
|
cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0));
|
|
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
|
|
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
|
cc.movsd(regF[A + 3], asmjit::x86::qword_ptr(tmp, 24));
|
|
}
|
|
|
|
void JitCompiler::EmitMULQV3_RR()
|
|
{
|
|
auto stack = GetTemporaryVectorStackStorage();
|
|
auto tmp = newTempIntPtr();
|
|
cc.lea(tmp, stack);
|
|
|
|
auto call = CreateCall<void, void*, double, double, double, double, double, double, double>(FuncMULQV3);
|
|
call->setArg(0, tmp);
|
|
call->setArg(1, regF[B + 0]);
|
|
call->setArg(2, regF[B + 1]);
|
|
call->setArg(3, regF[B + 2]);
|
|
call->setArg(4, regF[B + 3]);
|
|
call->setArg(5, regF[C + 0]);
|
|
call->setArg(6, regF[C + 1]);
|
|
call->setArg(7, regF[C + 2]);
|
|
|
|
cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0));
|
|
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
|
|
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Pointer math.
|
|
|
|
void JitCompiler::EmitADDA_RR()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
cc.mov(tmpptr, regD[C]);
|
|
cc.add(tmp, tmpptr);
|
|
|
|
cc.bind(label);
|
|
cc.mov(regA[A], tmp);
|
|
}
|
|
|
|
void JitCompiler::EmitADDA_RK()
|
|
{
|
|
auto tmp = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 = newTempIntPtr();
|
|
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 result = newResultXmmSd();
|
|
auto call = CreateCall<double, double>(g_sqrt);
|
|
call->setRet(0, result);
|
|
call->setArg(0, b);
|
|
cc.movsd(a, result);
|
|
}
|