- remove the need to do any VARF_Native runtime checks by making native functions use the same calling convention as the script version

This commit is contained in:
Magnus Norddahl 2018-10-10 23:47:56 +02:00
parent b6bc06e568
commit 0120ea190c
8 changed files with 68 additions and 157 deletions

View file

@ -250,6 +250,14 @@ void JitCompiler::Setup()
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
labels[i] = cc.newLabel(); labels[i] = cc.newLabel();
// VMCalls[0]++
auto vmcallsptr = newTempIntPtr();
auto vmcalls = newTempInt32();
cc.mov(vmcallsptr, imm_ptr(VMCalls));
cc.mov(vmcalls, x86::dword_ptr(vmcallsptr));
cc.add(vmcalls, (int)1);
cc.mov(x86::dword_ptr(vmcallsptr), vmcalls);
frameD = cc.newIntPtr(); frameD = cc.newIntPtr();
frameF = cc.newIntPtr(); frameF = cc.newIntPtr();
frameS = cc.newIntPtr(); frameS = cc.newIntPtr();

View file

@ -106,7 +106,6 @@ void JitCompiler::EmitPARAM()
I_FatalError("Unknown REGT value passed to EmitPARAM\n"); I_FatalError("Unknown REGT value passed to EmitPARAM\n");
break; break;
} }
} }
void JitCompiler::EmitPARAMI() void JitCompiler::EmitPARAMI()
@ -126,33 +125,29 @@ void JitCompiler::EmitRESULT()
void JitCompiler::EmitCALL() void JitCompiler::EmitCALL()
{ {
EmitDoCall(regA[A], CallType::Unknown); EmitDoCall(regA[A]);
} }
void JitCompiler::EmitCALL_K() void JitCompiler::EmitCALL_K()
{ {
VMFunction *func = (VMFunction*)konsta[A].o;
auto ptr = newTempIntPtr(); auto ptr = newTempIntPtr();
cc.mov(ptr, asmjit::imm_ptr(func)); cc.mov(ptr, asmjit::imm_ptr(konsta[A].o));
EmitDoCall(ptr, (func->VarFlags & VARF_Native) ? CallType::Native : CallType::Script); EmitDoCall(ptr);
} }
void JitCompiler::EmitTAIL() void JitCompiler::EmitTAIL()
{ {
EmitDoTail(regA[A], CallType::Unknown); EmitDoTail(regA[A]);
} }
void JitCompiler::EmitTAIL_K() void JitCompiler::EmitTAIL_K()
{ {
VMFunction *func = (VMFunction*)konsta[A].o;
auto ptr = newTempIntPtr(); auto ptr = newTempIntPtr();
cc.mov(ptr, asmjit::imm_ptr(func)); cc.mov(ptr, asmjit::imm_ptr(konsta[A].o));
EmitDoTail(ptr, (func->VarFlags & VARF_Native) ? CallType::Native : CallType::Script); EmitDoTail(ptr);
} }
void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, CallType calltype) void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc)
{ {
using namespace asmjit; using namespace asmjit;
@ -173,28 +168,7 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, CallType calltype)
paramsptr = params; paramsptr = params;
} }
if (calltype == CallType::Script)
{
EmitScriptCall(vmfunc, paramsptr); EmitScriptCall(vmfunc, paramsptr);
}
else if (calltype == CallType::Native)
{
EmitNativeCall(vmfunc, paramsptr);
}
else
{
auto nativecall = cc.newLabel();
auto endcall = cc.newLabel();
auto varflags = newTempInt32();
cc.mov(varflags, x86::dword_ptr(vmfunc, offsetof(VMFunction, VarFlags)));
cc.test(varflags, (int)VARF_Native);
cc.jnz(nativecall);
EmitScriptCall(vmfunc, paramsptr);
cc.jmp(endcall);
cc.bind(nativecall);
EmitNativeCall(vmfunc, paramsptr);
cc.bind(endcall);
}
LoadInOuts(B); LoadInOuts(B);
LoadReturns(pc + 1, C); LoadReturns(pc + 1, C);
@ -207,14 +181,6 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr)
{ {
using namespace asmjit; using namespace asmjit;
// VMCalls[0]++
auto vmcallsptr = newTempIntPtr();
auto vmcalls = newTempInt32();
cc.mov(vmcallsptr, imm_ptr(VMCalls));
cc.mov(vmcalls, x86::dword_ptr(vmcallsptr));
cc.add(vmcalls, (int)1);
cc.mov(x86::dword_ptr(vmcallsptr), vmcalls);
auto scriptcall = newTempIntPtr(); auto scriptcall = newTempIntPtr();
cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall))); cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall)));
@ -228,20 +194,7 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr)
call->setArg(4, Imm(C)); call->setArg(4, Imm(C));
} }
void JitCompiler::EmitNativeCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc)
{
using namespace asmjit;
auto result = newResultInt32();
auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*>(&JitCompiler::DoNativeCall);
call->setRet(0, result);
call->setArg(0, vmfunc);
call->setArg(1, Imm(B));
call->setArg(2, Imm(C));
call->setArg(3, paramsptr);
call->setArg(4, callReturns);
}
void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc, CallType calltype)
{ {
// Whereas the CALL instruction uses its third operand to specify how many return values // Whereas the CALL instruction uses its third operand to specify how many return values
// it expects, TAIL ignores its third operand and uses whatever was passed to this Exec call. // it expects, TAIL ignores its third operand and uses whatever was passed to this Exec call.
@ -268,28 +221,7 @@ void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc, CallType calltype)
auto result = newResultInt32(); auto result = newResultInt32();
if (calltype == CallType::Script)
{
EmitScriptTailCall(vmfunc, result, paramsptr); EmitScriptTailCall(vmfunc, result, paramsptr);
}
else if (calltype == CallType::Native)
{
EmitNativeTailCall(vmfunc, result, paramsptr);
}
else
{
auto nativecall = cc.newLabel();
auto endcall = cc.newLabel();
auto varflags = newTempInt32();
cc.mov(varflags, x86::dword_ptr(vmfunc, offsetof(VMFunction, VarFlags)));
cc.test(varflags, (int)VARF_Native);
cc.jnz(nativecall);
EmitScriptTailCall(vmfunc, result, paramsptr);
cc.jmp(endcall);
cc.bind(nativecall);
EmitNativeTailCall(vmfunc, result, paramsptr);
cc.bind(endcall);
}
EmitPopFrame(); EmitPopFrame();
cc.ret(result); cc.ret(result);
@ -302,14 +234,6 @@ void JitCompiler::EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result,
{ {
using namespace asmjit; using namespace asmjit;
// VMCalls[0]++
auto vmcallsptr = newTempIntPtr();
auto vmcalls = newTempInt32();
cc.mov(vmcallsptr, imm_ptr(VMCalls));
cc.mov(vmcalls, x86::dword_ptr(vmcallsptr));
cc.add(vmcalls, (int)1);
cc.mov(x86::dword_ptr(vmcallsptr), vmcalls);
auto scriptcall = newTempIntPtr(); auto scriptcall = newTempIntPtr();
cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall))); cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall)));
@ -322,19 +246,6 @@ void JitCompiler::EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result,
call->setArg(4, numret); call->setArg(4, numret);
} }
void JitCompiler::EmitNativeTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr)
{
using namespace asmjit;
auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*>(&JitCompiler::DoNativeCall);
call->setRet(0, result);
call->setArg(0, vmfunc);
call->setArg(1, Imm(B));
call->setArg(2, numret);
call->setArg(3, paramsptr);
call->setArg(4, ret);
}
void JitCompiler::StoreInOuts(int b) void JitCompiler::StoreInOuts(int b)
{ {
using namespace asmjit; using namespace asmjit;
@ -497,29 +408,3 @@ void JitCompiler::FillReturns(const VMOP *retval, int numret)
cc.mov(x86::byte_ptr(callReturns, i * sizeof(VMReturn) + offsetof(VMReturn, RegType)), type); cc.mov(x86::byte_ptr(callReturns, i * sizeof(VMReturn) + offsetof(VMReturn, RegType)), type);
} }
} }
int JitCompiler::DoNativeCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns)
{
try
{
assert((call->VarFlags & VARF_Native) && "DoNativeCall must only be called for native functions");
VMCycles[0].Unclock();
int numret = static_cast<VMNativeFunction *>(call)->NativeCall(param, call->DefaultArgs, b, returns, c);
VMCycles[0].Clock();
return numret;
}
catch (CVMAbortException &err)
{
err.MaybePrintMessage();
err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars());
VMThrowException(std::current_exception());
return 0;
}
catch (...)
{
VMThrowException(std::current_exception());
return 0;
}
}

View file

@ -42,20 +42,11 @@ private:
void EmitOpcode(); void EmitOpcode();
void EmitPopFrame(); void EmitPopFrame();
enum class CallType void EmitDoCall(asmjit::X86Gp ptr);
{
Unknown,
Script,
Native
};
void EmitDoCall(asmjit::X86Gp ptr, CallType calltype);
void EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr); void EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr);
void EmitNativeCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr);
void EmitDoTail(asmjit::X86Gp ptr, CallType calltype); void EmitDoTail(asmjit::X86Gp ptr);
void EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr); void EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr);
void EmitNativeTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr);
void StoreInOuts(int b); void StoreInOuts(int b);
void LoadInOuts(int b); void LoadInOuts(int b);
@ -63,8 +54,6 @@ private:
void FillReturns(const VMOP *retval, int numret); void FillReturns(const VMOP *retval, int numret);
void LoadCallResult(const VMOP &opdata, bool addrof); void LoadCallResult(const VMOP &opdata, bool addrof);
static int DoNativeCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns);
template <typename Func> template <typename Func>
void EmitComparisonOpcode(Func jmpFunc) void EmitComparisonOpcode(Func jmpFunc)
{ {

View file

@ -330,6 +330,8 @@ public:
class PPrototype *Proto; class PPrototype *Proto;
int(*ScriptCall)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = nullptr;
VMFunction(FName name = NAME_None) : ImplicitArgs(0), Name(name), Proto(NULL) VMFunction(FName name = NAME_None) : ImplicitArgs(0), Name(name), Proto(NULL)
{ {
AllFunctions.Push(this); AllFunctions.Push(this);
@ -361,12 +363,15 @@ public:
typedef int (*NativeCallType)(VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret); typedef int (*NativeCallType)(VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret);
// 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype. // 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype.
VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; } VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; } VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; } VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
// Return value is the number of results. // Return value is the number of results.
NativeCallType NativeCall; NativeCallType NativeCall;
private:
static int NativeScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
}; };
int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/); int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/);

View file

@ -134,7 +134,7 @@ struct VMExec_Unchecked
#undef assert #undef assert
#include <assert.h> #include <assert.h>
int (*VMExec)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = int (*VMExec)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) =
#ifdef NDEBUG #ifdef NDEBUG
VMExec_Unchecked::Exec VMExec_Unchecked::Exec
#else #else

View file

@ -710,7 +710,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
} }
else else
{ {
VMCalls[0]++;
auto sfunc = static_cast<VMScriptFunction *>(call); auto sfunc = static_cast<VMScriptFunction *>(call);
numret = sfunc->ScriptCall(sfunc, reg.param + f->NumParam - b, b, returns, C); numret = sfunc->ScriptCall(sfunc, reg.param + f->NumParam - b, b, returns, C);
} }
@ -753,7 +752,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
} }
else else
{ // FIXME: Not a true tail call { // FIXME: Not a true tail call
VMCalls[0]++;
auto sfunc = static_cast<VMScriptFunction *>(call); auto sfunc = static_cast<VMScriptFunction *>(call);
return sfunc->ScriptCall(sfunc, reg.param + f->NumParam - B, B, ret, numret); return sfunc->ScriptCall(sfunc, reg.param + f->NumParam - B, B, ret, numret);
} }
@ -2046,10 +2044,11 @@ static void SetReturn(const VMRegisters &reg, VMFrame *frame, VMReturn *ret, VM_
} }
} }
static int Exec(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) static int Exec(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret)
{ {
VMCalls[0]++;
VMFrameStack *stack = &GlobalVMStack; VMFrameStack *stack = &GlobalVMStack;
VMFrame *newf = stack->AllocFrame(func); VMFrame *newf = stack->AllocFrame(static_cast<VMScriptFunction*>(func));
VMFillParams(params, newf, numparams); VMFillParams(params, newf, numparams);
try try
{ {

View file

@ -74,6 +74,7 @@ VMScriptFunction::VMScriptFunction(FName name)
NumKonstA = 0; NumKonstA = 0;
MaxParam = 0; MaxParam = 0;
NumArgs = 0; NumArgs = 0;
ScriptCall = &VMScriptFunction::FirstScriptCall;
} }
VMScriptFunction::~VMScriptFunction() VMScriptFunction::~VMScriptFunction()
@ -213,17 +214,44 @@ int VMScriptFunction::PCToLine(const VMOP *pc)
return -1; return -1;
} }
int VMScriptFunction::FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) int VMScriptFunction::FirstScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret)
{ {
func->ScriptCall = JitCompile(func); VMScriptFunction *sfunc = static_cast<VMScriptFunction*>(func);
if (!func->ScriptCall) sfunc->ScriptCall = JitCompile(sfunc);
func->ScriptCall = VMExec; if (!sfunc->ScriptCall)
sfunc->ScriptCall = VMExec;
else else
func->FunctionJitted = true; sfunc->FunctionJitted = true;
return func->ScriptCall(func, params, numparams, ret, numret); return func->ScriptCall(func, params, numparams, ret, numret);
} }
int VMNativeFunction::NativeScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *returns, int numret)
{
try
{
assert((call->VarFlags & VARF_Native) && "DoNativeCall must only be called for native functions");
VMCycles[0].Unclock();
numret = static_cast<VMNativeFunction *>(func)->NativeCall(params, func->DefaultArgs, numparams, returns, numret);
VMCycles[0].Clock();
return numret;
}
catch (CVMAbortException &err)
{
err.MaybePrintMessage();
err.stacktrace.AppendFormat("Called from %s\n", func->PrintableName.GetChars());
VMThrowException(std::current_exception());
return 0;
}
catch (...)
{
VMThrowException(std::current_exception());
return 0;
}
}
//=========================================================================== //===========================================================================
// //
// VMFrame :: InitRegS // VMFrame :: InitRegS
@ -500,7 +528,6 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results,
else else
{ {
VMCycles[0].Clock(); VMCycles[0].Clock();
VMCalls[0]++;
JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo; JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo;
JitExceptionInfo newExceptInfo; JitExceptionInfo newExceptInfo;

View file

@ -429,7 +429,7 @@ enum EVMEngine
}; };
void VMSelectEngine(EVMEngine engine); void VMSelectEngine(EVMEngine engine);
extern int (*VMExec)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); extern int (*VMExec)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
void VMFillParams(VMValue *params, VMFrame *callee, int numparam); void VMFillParams(VMValue *params, VMFrame *callee, int numparam);
void VMDumpConstants(FILE *out, const VMScriptFunction *func); void VMDumpConstants(FILE *out, const VMScriptFunction *func);
@ -450,7 +450,7 @@ extern thread_local JitExceptionInfo *CurrentJitExceptInfo;
void VMThrowException(std::exception_ptr cppException); void VMThrowException(std::exception_ptr cppException);
typedef int(*JitFuncPtr)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); typedef int(*JitFuncPtr)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
class VMScriptFunction : public VMFunction class VMScriptFunction : public VMFunction
{ {
@ -482,15 +482,13 @@ public:
VM_UBYTE NumArgs; // Number of arguments this function takes VM_UBYTE NumArgs; // Number of arguments this function takes
TArray<FTypeAndOffset> SpecialInits; // list of all contents on the extra stack which require construction and destruction TArray<FTypeAndOffset> SpecialInits; // list of all contents on the extra stack which require construction and destruction
int(*ScriptCall)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = &VMScriptFunction::FirstScriptCall;
void InitExtra(void *addr); void InitExtra(void *addr);
void DestroyExtra(void *addr); void DestroyExtra(void *addr);
int AllocExtraStack(PType *type); int AllocExtraStack(PType *type);
int PCToLine(const VMOP *pc); int PCToLine(const VMOP *pc);
private: private:
static int FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); static int FirstScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
bool FunctionJitted = false; bool FunctionJitted = false;
}; };