2018-09-13 00:29:04 +00:00
|
|
|
|
|
|
|
#include "jitintern.h"
|
|
|
|
|
|
|
|
void JitCompiler::EmitTEST()
|
|
|
|
{
|
|
|
|
int i = (int)(ptrdiff_t)(pc - sfunc->Code);
|
|
|
|
cc.cmp(regD[A], BC);
|
|
|
|
cc.jne(labels[i + 2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitTESTN()
|
|
|
|
{
|
|
|
|
int bc = BC;
|
|
|
|
int i = (int)(ptrdiff_t)(pc - sfunc->Code);
|
|
|
|
cc.cmp(regD[A], -bc);
|
|
|
|
cc.jne(labels[i + 2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitJMP()
|
|
|
|
{
|
|
|
|
auto dest = pc + JMPOFS(pc) + 1;
|
|
|
|
int i = (int)(ptrdiff_t)(dest - sfunc->Code);
|
|
|
|
cc.jmp(labels[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitIJMP()
|
|
|
|
{
|
|
|
|
I_FatalError("EmitIJMP not implemented\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitVTBL()
|
|
|
|
{
|
|
|
|
auto notnull = cc.newLabel();
|
|
|
|
cc.test(regA[B], regA[B]);
|
|
|
|
cc.jnz(notnull);
|
|
|
|
EmitThrowException(X_READ_NIL);
|
|
|
|
cc.bind(notnull);
|
|
|
|
|
|
|
|
auto result = cc.newInt32();
|
|
|
|
typedef VMFunction*(*FuncPtr)(DObject*, int);
|
|
|
|
auto call = cc.call(ToMemAddress(reinterpret_cast<const void*>(static_cast<FuncPtr>([](DObject *o, int c) -> VMFunction* {
|
|
|
|
auto p = o->GetClass();
|
|
|
|
assert(c < (int)p->Virtuals.Size());
|
|
|
|
return p->Virtuals[c];
|
|
|
|
}))), asmjit::FuncSignature2<void*, void*, int>());
|
|
|
|
call->setRet(0, result);
|
|
|
|
call->setArg(0, regA[A]);
|
|
|
|
call->setArg(1, asmjit::Imm(C));
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitSCOPE()
|
|
|
|
{
|
|
|
|
auto notnull = cc.newLabel();
|
|
|
|
cc.test(regA[A], regA[A]);
|
|
|
|
cc.jnz(notnull);
|
|
|
|
EmitThrowException(X_READ_NIL);
|
|
|
|
cc.bind(notnull);
|
|
|
|
|
|
|
|
auto f = cc.newIntPtr();
|
|
|
|
cc.mov(f, ToMemAddress(konsta[C].v));
|
|
|
|
|
|
|
|
auto result = cc.newInt32();
|
|
|
|
typedef int(*FuncPtr)(DObject*, VMFunction*, int);
|
|
|
|
auto call = cc.call(ToMemAddress(reinterpret_cast<const void*>(static_cast<FuncPtr>([](DObject *o, VMFunction *f, int b) -> int {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
catch (const CVMAbortException &)
|
|
|
|
{
|
|
|
|
// To do: pass along the exception info
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}))), asmjit::FuncSignature3<int, void*, void*, int>());
|
|
|
|
call->setRet(0, result);
|
|
|
|
call->setArg(0, regA[A]);
|
|
|
|
call->setArg(1, f);
|
|
|
|
call->setArg(2, asmjit::Imm(B));
|
|
|
|
|
|
|
|
auto notzero = cc.newLabel();
|
|
|
|
cc.test(result, result);
|
|
|
|
cc.jnz(notzero);
|
|
|
|
EmitThrowException(X_OTHER);
|
|
|
|
cc.bind(notzero);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitRET()
|
|
|
|
{
|
|
|
|
using namespace asmjit;
|
|
|
|
if (B == REGT_NIL)
|
|
|
|
{
|
|
|
|
X86Gp vReg = cc.newInt32();
|
|
|
|
cc.mov(vReg, 0);
|
|
|
|
cc.ret(vReg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int a = A;
|
|
|
|
int retnum = a & ~RET_FINAL;
|
|
|
|
|
|
|
|
X86Gp reg_retnum = cc.newInt32();
|
|
|
|
X86Gp location = cc.newIntPtr();
|
|
|
|
Label L_endif = cc.newLabel();
|
|
|
|
|
|
|
|
cc.mov(reg_retnum, retnum);
|
|
|
|
cc.test(reg_retnum, numret);
|
|
|
|
cc.jg(L_endif);
|
|
|
|
|
|
|
|
cc.mov(location, x86::ptr(ret, retnum * sizeof(VMReturn)));
|
|
|
|
|
|
|
|
int regtype = B;
|
|
|
|
int regnum = C;
|
|
|
|
switch (regtype & REGT_TYPE)
|
|
|
|
{
|
|
|
|
case REGT_INT:
|
|
|
|
if (regtype & REGT_KONST)
|
|
|
|
cc.mov(x86::dword_ptr(location), konstd[regnum]);
|
|
|
|
else
|
|
|
|
cc.mov(x86::dword_ptr(location), regD[regnum]);
|
|
|
|
break;
|
|
|
|
case REGT_FLOAT:
|
|
|
|
if (regtype & REGT_KONST)
|
|
|
|
{
|
|
|
|
auto tmp = cc.newInt64();
|
|
|
|
if (regtype & REGT_MULTIREG3)
|
|
|
|
{
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum]));
|
|
|
|
cc.mov(x86::qword_ptr(location), tmp);
|
|
|
|
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum + 1]));
|
|
|
|
cc.mov(x86::qword_ptr(location, 8), tmp);
|
|
|
|
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum + 2]));
|
|
|
|
cc.mov(x86::qword_ptr(location, 16), tmp);
|
|
|
|
}
|
|
|
|
else if (regtype & REGT_MULTIREG2)
|
|
|
|
{
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum]));
|
|
|
|
cc.mov(x86::qword_ptr(location), tmp);
|
|
|
|
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum + 1]));
|
|
|
|
cc.mov(x86::qword_ptr(location, 8), tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cc.mov(tmp, (((int64_t *)konstf)[regnum]));
|
|
|
|
cc.mov(x86::qword_ptr(location), tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (regtype & REGT_MULTIREG3)
|
|
|
|
{
|
|
|
|
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
|
|
|
cc.movsd(x86::qword_ptr(location, 8), regF[regnum + 1]);
|
|
|
|
cc.movsd(x86::qword_ptr(location, 16), regF[regnum + 2]);
|
|
|
|
}
|
|
|
|
else if (regtype & REGT_MULTIREG2)
|
|
|
|
{
|
|
|
|
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
|
|
|
cc.movsd(x86::qword_ptr(location, 8), regF[regnum + 1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case REGT_STRING:
|
2018-09-14 17:20:31 +00:00
|
|
|
{
|
|
|
|
auto setRetStringLamdba = [](VMReturn* ret, FString* str) -> void {
|
|
|
|
ret->SetString(*str);
|
|
|
|
};
|
|
|
|
auto ptr = cc.newIntPtr();
|
|
|
|
cc.mov(ptr, ret);
|
|
|
|
cc.add(ptr, retnum * sizeof(VMReturn));
|
|
|
|
auto call = cc.call(ToMemAddress(reinterpret_cast<void*>(static_cast<void(*)(VMReturn*, FString*)>(setRetStringLamdba))),
|
|
|
|
asmjit::FuncSignature2<void, VMReturn*, FString*>(asmjit::CallConv::kIdHostCDecl));
|
|
|
|
call->setArg(0, ptr);
|
|
|
|
if (regtype & REGT_KONST) call->setArg(1, asmjit::imm_ptr(&konsts[regnum]));
|
|
|
|
else call->setArg(1, regS[regnum]);
|
2018-09-13 00:29:04 +00:00
|
|
|
break;
|
2018-09-14 17:20:31 +00:00
|
|
|
}
|
2018-09-13 00:29:04 +00:00
|
|
|
case REGT_POINTER:
|
|
|
|
#ifdef ASMJIT_ARCH_X64
|
|
|
|
if (regtype & REGT_KONST)
|
|
|
|
cc.mov(x86::qword_ptr(location), ToMemAddress(konsta[regnum].v));
|
|
|
|
else
|
|
|
|
cc.mov(x86::qword_ptr(location), regA[regnum]);
|
|
|
|
#else
|
|
|
|
if (regtype & REGT_KONST)
|
|
|
|
cc.mov(x86::dword_ptr(location), ToMemAddress(konsta[regnum].v));
|
|
|
|
else
|
|
|
|
cc.mov(x86::dword_ptr(location), regA[regnum]);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a & RET_FINAL)
|
|
|
|
{
|
|
|
|
cc.add(reg_retnum, 1);
|
|
|
|
cc.ret(reg_retnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
cc.bind(L_endif);
|
|
|
|
if (a & RET_FINAL)
|
|
|
|
cc.ret(numret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitRETI()
|
|
|
|
{
|
|
|
|
using namespace asmjit;
|
|
|
|
|
|
|
|
int a = A;
|
|
|
|
int retnum = a & ~RET_FINAL;
|
|
|
|
|
|
|
|
X86Gp reg_retnum = cc.newInt32();
|
|
|
|
X86Gp location = cc.newIntPtr();
|
|
|
|
Label L_endif = cc.newLabel();
|
|
|
|
|
|
|
|
cc.mov(reg_retnum, retnum);
|
|
|
|
cc.test(reg_retnum, numret);
|
|
|
|
cc.jg(L_endif);
|
|
|
|
|
|
|
|
cc.mov(location, x86::ptr(ret, retnum * sizeof(VMReturn)));
|
|
|
|
cc.mov(x86::dword_ptr(location), BCs);
|
|
|
|
|
|
|
|
if (a & RET_FINAL)
|
|
|
|
{
|
|
|
|
cc.add(reg_retnum, 1);
|
|
|
|
cc.ret(reg_retnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
cc.bind(L_endif);
|
|
|
|
if (a & RET_FINAL)
|
|
|
|
cc.ret(numret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitNEW()
|
|
|
|
{
|
|
|
|
auto result = cc.newIntPtr();
|
|
|
|
typedef DObject*(*FuncPtr)(PClass*, int);
|
|
|
|
auto call = cc.call(ToMemAddress(reinterpret_cast<const void*>(static_cast<FuncPtr>([](PClass *cls, int c) -> DObject* {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (!cls->ConstructNative)
|
|
|
|
{
|
|
|
|
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
else if (cls->bAbstract)
|
|
|
|
{
|
|
|
|
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
else if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited
|
|
|
|
{
|
|
|
|
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
|
|
|
}
|
|
|
|
|
|
|
|
// [ZZ] validate readonly and between scope construction
|
|
|
|
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
|
|
|
return cls->CreateNew();
|
|
|
|
}
|
|
|
|
catch (const CVMAbortException &)
|
|
|
|
{
|
|
|
|
// To do: pass along the exception info
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}))), asmjit::FuncSignature2<void*, void*, int>());
|
|
|
|
call->setRet(0, result);
|
|
|
|
call->setArg(0, regA[B]);
|
|
|
|
call->setArg(1, asmjit::Imm(C));
|
|
|
|
|
|
|
|
auto notnull = cc.newLabel();
|
|
|
|
cc.test(result, result);
|
|
|
|
cc.jnz(notnull);
|
|
|
|
EmitThrowException(X_OTHER);
|
|
|
|
cc.bind(notnull);
|
|
|
|
cc.mov(regA[A], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitNEW_K()
|
|
|
|
{
|
|
|
|
PClass *cls = (PClass*)konsta[B].v;
|
|
|
|
if (!cls->ConstructNative)
|
|
|
|
{
|
|
|
|
EmitThrowException(X_OTHER); // "Class %s requires native construction", cls->TypeName.GetChars()
|
|
|
|
}
|
|
|
|
else if (cls->bAbstract)
|
|
|
|
{
|
|
|
|
EmitThrowException(X_OTHER); // "Cannot instantiate abstract class %s", cls->TypeName.GetChars()
|
|
|
|
}
|
|
|
|
else if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited
|
|
|
|
{
|
|
|
|
EmitThrowException(X_OTHER); // "Cannot create actors with 'new'"
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto result = cc.newIntPtr();
|
|
|
|
auto regcls = cc.newIntPtr();
|
|
|
|
cc.mov(regcls, ToMemAddress(konsta[B].v));
|
|
|
|
typedef DObject*(*FuncPtr)(PClass*, int);
|
|
|
|
auto call = cc.call(ToMemAddress(reinterpret_cast<const void*>(static_cast<FuncPtr>([](PClass *cls, int c) -> DObject* {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
|
|
|
return cls->CreateNew();
|
|
|
|
}
|
|
|
|
catch (const CVMAbortException &)
|
|
|
|
{
|
|
|
|
// To do: pass along the exception info
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}))), asmjit::FuncSignature2<void*, void*, int>());
|
|
|
|
call->setRet(0, result);
|
|
|
|
call->setArg(0, regcls);
|
|
|
|
call->setArg(1, asmjit::Imm(C));
|
|
|
|
|
|
|
|
auto notnull = cc.newLabel();
|
|
|
|
cc.test(result, result);
|
|
|
|
cc.jnz(notnull);
|
|
|
|
EmitThrowException(X_OTHER);
|
|
|
|
cc.bind(notnull);
|
|
|
|
cc.mov(regA[A], result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitTHROW()
|
|
|
|
{
|
|
|
|
EmitThrowException(EVMAbortException(BC));
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitBOUND()
|
|
|
|
{
|
|
|
|
auto label1 = cc.newLabel();
|
|
|
|
auto label2 = cc.newLabel();
|
|
|
|
cc.cmp(regD[A], (int)BC);
|
|
|
|
cc.jl(label1);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Max.index = %u, current index = %u\n", BC, reg.d[A]
|
|
|
|
cc.bind(label1);
|
|
|
|
cc.cmp(regD[A], (int)0);
|
|
|
|
cc.jge(label2);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Negative current index = %i\n", reg.d[A]
|
|
|
|
cc.bind(label2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitBOUND_K()
|
|
|
|
{
|
|
|
|
auto label1 = cc.newLabel();
|
|
|
|
auto label2 = cc.newLabel();
|
|
|
|
cc.cmp(regD[A], (int)konstd[BC]);
|
|
|
|
cc.jl(label1);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Max.index = %u, current index = %u\n", konstd[BC], reg.d[A]
|
|
|
|
cc.bind(label1);
|
|
|
|
cc.cmp(regD[A], (int)0);
|
|
|
|
cc.jge(label2);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Negative current index = %i\n", reg.d[A]
|
|
|
|
cc.bind(label2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitCompiler::EmitBOUND_R()
|
|
|
|
{
|
|
|
|
auto label1 = cc.newLabel();
|
|
|
|
auto label2 = cc.newLabel();
|
|
|
|
cc.cmp(regD[A], regD[B]);
|
|
|
|
cc.jl(label1);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Max.index = %u, current index = %u\n", reg.d[B], reg.d[A]
|
|
|
|
cc.bind(label1);
|
|
|
|
cc.cmp(regD[A], (int)0);
|
|
|
|
cc.jge(label2);
|
|
|
|
EmitThrowException(X_ARRAY_OUT_OF_BOUNDS); // "Negative current index = %i\n", reg.d[A]
|
|
|
|
cc.bind(label2);
|
|
|
|
}
|