qzdoom/src/scripting/vm/jit_flow.cpp

436 lines
9.6 KiB
C++
Raw Normal View History

#include "jitintern.h"
void JitCompiler::EmitTEST()
{
int i = (int)(ptrdiff_t)(pc - sfunc->Code);
cc.cmp(regD[A], BC);
cc.jne(GetLabel(i + 2));
}
void JitCompiler::EmitTESTN()
{
int bc = BC;
int i = (int)(ptrdiff_t)(pc - sfunc->Code);
cc.cmp(regD[A], -bc);
cc.jne(GetLabel(i + 2));
}
void JitCompiler::EmitJMP()
{
auto dest = pc + JMPOFS(pc) + 1;
int i = (int)(ptrdiff_t)(dest - sfunc->Code);
cc.jmp(GetLabel(i));
}
void JitCompiler::EmitIJMP()
{
// This uses the whole function as potential jump targets. Can the range be reduced?
int i = (int)(ptrdiff_t)(pc - sfunc->Code);
2018-10-07 07:02:28 +00:00
auto val = newTempInt32();
cc.mov(val, regD[A]);
cc.add(val, i + (int)BCs + 1);
int size = sfunc->CodeSize;
for (i = 0; i < size; i++)
{
if (sfunc->Code[i].op == OP_JMP)
{
int target = i + JMPOFS(&sfunc->Code[i]) + 1;
cc.cmp(val, i);
cc.je(GetLabel(target));
}
}
// This should never happen. It means we are jumping to something that is not a JMP instruction!
EmitThrowException(X_OTHER);
}
static VMFunction *GetVirtual(DObject *o, int c)
{
auto p = o->GetClass();
assert(c < (int)p->Virtuals.Size());
return p->Virtuals[c];
}
void JitCompiler::EmitVTBL()
{
auto label = EmitThrowExceptionLabel(X_READ_NIL);
cc.test(regA[B], regA[B]);
cc.jz(label);
2018-10-07 07:02:28 +00:00
auto result = newResultIntPtr();
auto call = CreateCall<VMFunction*, DObject*, int>(GetVirtual);
call->setRet(0, result);
2018-09-14 22:28:34 +00:00
call->setArg(0, regA[B]);
call->setArg(1, asmjit::Imm(C));
cc.mov(regA[A], result);
}
static void ValidateCall(DObject *o, VMFunction *f, int b)
{
try
{
FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1);
}
catch (...)
{
VMThrowException(std::current_exception());
}
}
void JitCompiler::EmitSCOPE()
{
auto label = EmitThrowExceptionLabel(X_READ_NIL);
cc.test(regA[A], regA[A]);
cc.jz(label);
2018-10-07 07:02:28 +00:00
auto f = newTempIntPtr();
cc.mov(f, asmjit::imm_ptr(konsta[C].v));
typedef int(*FuncPtr)(DObject*, VMFunction*, int);
auto call = CreateCall<void, DObject*, VMFunction*, int>(ValidateCall);
call->setArg(0, regA[A]);
call->setArg(1, f);
call->setArg(2, asmjit::Imm(B));
}
static void SetString(VMReturn* ret, FString* str)
{
ret->SetString(*str);
}
void JitCompiler::EmitRET()
{
using namespace asmjit;
if (B == REGT_NIL)
{
EmitPopFrame();
2018-10-07 07:02:28 +00:00
X86Gp vReg = newTempInt32();
cc.mov(vReg, 0);
cc.ret(vReg);
}
else
{
int a = A;
int retnum = a & ~RET_FINAL;
2018-10-07 07:02:28 +00:00
X86Gp reg_retnum = newTempInt32();
X86Gp location = newTempIntPtr();
Label L_endif = cc.newLabel();
cc.mov(reg_retnum, retnum);
2018-09-14 22:12:12 +00:00
cc.cmp(reg_retnum, numret);
cc.jge(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)
{
2018-10-07 07:02:28 +00:00
auto tmp = newTempInt64();
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
{
2018-10-07 07:02:28 +00:00
auto ptr = newTempIntPtr();
2018-09-14 17:20:31 +00:00
cc.mov(ptr, ret);
2018-09-14 21:38:57 +00:00
cc.add(ptr, (int)(retnum * sizeof(VMReturn)));
auto call = CreateCall<void, VMReturn*, FString*>(SetString);
2018-09-14 17:20:31 +00:00
call->setArg(0, ptr);
if (regtype & REGT_KONST) call->setArg(1, asmjit::imm_ptr(&konsts[regnum]));
else call->setArg(1, regS[regnum]);
break;
2018-09-14 17:20:31 +00:00
}
case REGT_POINTER:
2018-10-07 18:55:06 +00:00
if (cc.is64Bit())
{
if (regtype & REGT_KONST)
cc.mov(x86::qword_ptr(location), asmjit::imm_ptr(konsta[regnum].v));
else
cc.mov(x86::qword_ptr(location), regA[regnum]);
}
else
2018-10-07 18:55:06 +00:00
{
if (regtype & REGT_KONST)
cc.mov(x86::dword_ptr(location), asmjit::imm_ptr(konsta[regnum].v));
else
cc.mov(x86::dword_ptr(location), regA[regnum]);
}
break;
}
if (a & RET_FINAL)
{
cc.add(reg_retnum, 1);
EmitPopFrame();
cc.ret(reg_retnum);
}
cc.bind(L_endif);
if (a & RET_FINAL)
{
EmitPopFrame();
cc.ret(numret);
}
}
}
void JitCompiler::EmitRETI()
{
using namespace asmjit;
int a = A;
int retnum = a & ~RET_FINAL;
2018-10-07 07:02:28 +00:00
X86Gp reg_retnum = newTempInt32();
X86Gp location = newTempIntPtr();
Label L_endif = cc.newLabel();
cc.mov(reg_retnum, retnum);
2018-09-14 22:12:12 +00:00
cc.cmp(reg_retnum, numret);
cc.jge(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);
EmitPopFrame();
cc.ret(reg_retnum);
}
cc.bind(L_endif);
if (a & RET_FINAL)
{
EmitPopFrame();
cc.ret(numret);
}
}
static DObject* CreateNew(PClass *cls, int c)
{
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 (...)
{
VMThrowException(std::current_exception());
return nullptr;
}
}
void JitCompiler::EmitNEW()
{
auto result = newResultIntPtr();
auto call = CreateCall<DObject*, PClass*, int>(CreateNew);
call->setRet(0, result);
call->setArg(0, regA[B]);
call->setArg(1, asmjit::Imm(C));
cc.mov(regA[A], result);
}
static void ThrowNewK(PClass *cls, int c)
{
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'");
}
}
catch (...)
{
VMThrowException(std::current_exception());
}
}
static DObject *CreateNewK(PClass *cls, int c)
{
try
{
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
return cls->CreateNew();
}
catch (...)
{
VMThrowException(std::current_exception());
return nullptr;
}
}
void JitCompiler::EmitNEW_K()
{
PClass *cls = (PClass*)konsta[B].v;
2018-11-10 18:52:41 +00:00
auto regcls = newTempIntPtr();
cc.mov(regcls, asmjit::imm_ptr(cls));
if (!cls->ConstructNative || cls->bAbstract || cls->IsDescendantOf(NAME_Actor))
{
auto call = CreateCall<void, PClass*, int>(ThrowNewK);
2018-11-10 18:52:41 +00:00
call->setArg(0, regcls);
}
else
{
2018-10-07 07:02:28 +00:00
auto result = newResultIntPtr();
auto call = CreateCall<DObject*, PClass*, int>(CreateNewK);
call->setRet(0, result);
call->setArg(0, regcls);
call->setArg(1, asmjit::Imm(C));
cc.mov(regA[A], result);
}
}
void JitCompiler::EmitTHROW()
{
EmitThrowException(EVMAbortException(BC));
}
void JitCompiler::EmitBOUND()
{
auto cursor = cc.getCursor();
2018-11-10 18:52:41 +00:00
auto label = cc.newLabel();
cc.bind(label);
2018-11-10 18:52:41 +00:00
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
call->setArg(0, asmjit::imm_ptr(sfunc));
call->setArg(1, asmjit::imm_ptr(pc));
call->setArg(2, regD[A]);
call->setArg(3, asmjit::imm(BC));
cc.setCursor(cursor);
2018-11-10 18:52:41 +00:00
cc.cmp(regD[A], (int)BC);
cc.jae(label);
}
void JitCompiler::EmitBOUND_K()
{
auto cursor = cc.getCursor();
2018-11-10 18:52:41 +00:00
auto label = cc.newLabel();
cc.bind(label);
2018-11-10 18:52:41 +00:00
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
call->setArg(0, asmjit::imm_ptr(sfunc));
call->setArg(1, asmjit::imm_ptr(pc));
call->setArg(2, regD[A]);
call->setArg(3, asmjit::imm(konstd[BC]));
cc.setCursor(cursor);
2018-11-10 18:52:41 +00:00
cc.cmp(regD[A], (int)konstd[BC]);
cc.jae(label);
}
void JitCompiler::EmitBOUND_R()
{
auto cursor = cc.getCursor();
2018-11-10 18:52:41 +00:00
auto label = cc.newLabel();
cc.bind(label);
2018-11-10 18:52:41 +00:00
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
call->setArg(0, asmjit::imm_ptr(sfunc));
call->setArg(1, asmjit::imm_ptr(pc));
call->setArg(2, regD[A]);
call->setArg(3, regD[B]);
cc.setCursor(cursor);
2018-11-10 18:52:41 +00:00
cc.cmp(regD[A], regD[B]);
cc.jae(label);
2018-11-10 18:52:41 +00:00
}
void JitCompiler::ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int index, int size)
{
try
{
2018-11-10 18:56:54 +00:00
if (index >= size)
2018-11-10 18:52:41 +00:00
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", size, index);
}
else
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", index);
}
}
catch (...)
{
VMThrowException(std::current_exception());
}
}