- 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++)
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();
frameF = cc.newIntPtr();
frameS = cc.newIntPtr();

View file

@ -106,7 +106,6 @@ void JitCompiler::EmitPARAM()
I_FatalError("Unknown REGT value passed to EmitPARAM\n");
break;
}
}
void JitCompiler::EmitPARAMI()
@ -126,33 +125,29 @@ void JitCompiler::EmitRESULT()
void JitCompiler::EmitCALL()
{
EmitDoCall(regA[A], CallType::Unknown);
EmitDoCall(regA[A]);
}
void JitCompiler::EmitCALL_K()
{
VMFunction *func = (VMFunction*)konsta[A].o;
auto ptr = newTempIntPtr();
cc.mov(ptr, asmjit::imm_ptr(func));
EmitDoCall(ptr, (func->VarFlags & VARF_Native) ? CallType::Native : CallType::Script);
cc.mov(ptr, asmjit::imm_ptr(konsta[A].o));
EmitDoCall(ptr);
}
void JitCompiler::EmitTAIL()
{
EmitDoTail(regA[A], CallType::Unknown);
EmitDoTail(regA[A]);
}
void JitCompiler::EmitTAIL_K()
{
VMFunction *func = (VMFunction*)konsta[A].o;
auto ptr = newTempIntPtr();
cc.mov(ptr, asmjit::imm_ptr(func));
EmitDoTail(ptr, (func->VarFlags & VARF_Native) ? CallType::Native : CallType::Script);
cc.mov(ptr, asmjit::imm_ptr(konsta[A].o));
EmitDoTail(ptr);
}
void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, CallType calltype)
void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc)
{
using namespace asmjit;
@ -173,28 +168,7 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, CallType calltype)
paramsptr = params;
}
if (calltype == CallType::Script)
{
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);
}
EmitScriptCall(vmfunc, paramsptr);
LoadInOuts(B);
LoadReturns(pc + 1, C);
@ -207,14 +181,6 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr)
{
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();
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));
}
void JitCompiler::EmitNativeCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr)
{
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)
void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc)
{
// 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.
@ -268,28 +221,7 @@ void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc, CallType calltype)
auto result = newResultInt32();
if (calltype == CallType::Script)
{
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);
}
EmitScriptTailCall(vmfunc, result, paramsptr);
EmitPopFrame();
cc.ret(result);
@ -302,14 +234,6 @@ void JitCompiler::EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result,
{
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();
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);
}
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)
{
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);
}
}
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 EmitPopFrame();
enum class CallType
{
Unknown,
Script,
Native
};
void EmitDoCall(asmjit::X86Gp ptr, CallType calltype);
void EmitDoCall(asmjit::X86Gp ptr);
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 EmitNativeTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr);
void StoreInOuts(int b);
void LoadInOuts(int b);
@ -63,8 +54,6 @@ private:
void FillReturns(const VMOP *retval, int numret);
void LoadCallResult(const VMOP &opdata, bool addrof);
static int DoNativeCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns);
template <typename Func>
void EmitComparisonOpcode(Func jmpFunc)
{

View file

@ -330,6 +330,8 @@ public:
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)
{
AllFunctions.Push(this);
@ -361,12 +363,15 @@ public:
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.
VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; }
VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; }
VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; }
VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
// Return value is the number of results.
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*/);

View file

@ -134,7 +134,7 @@ struct VMExec_Unchecked
#undef assert
#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
VMExec_Unchecked::Exec
#else

View file

@ -710,7 +710,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
}
else
{
VMCalls[0]++;
auto sfunc = static_cast<VMScriptFunction *>(call);
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
{ // FIXME: Not a true tail call
VMCalls[0]++;
auto sfunc = static_cast<VMScriptFunction *>(call);
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;
VMFrame *newf = stack->AllocFrame(func);
VMFrame *newf = stack->AllocFrame(static_cast<VMScriptFunction*>(func));
VMFillParams(params, newf, numparams);
try
{

View file

@ -74,6 +74,7 @@ VMScriptFunction::VMScriptFunction(FName name)
NumKonstA = 0;
MaxParam = 0;
NumArgs = 0;
ScriptCall = &VMScriptFunction::FirstScriptCall;
}
VMScriptFunction::~VMScriptFunction()
@ -213,17 +214,44 @@ int VMScriptFunction::PCToLine(const VMOP *pc)
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);
if (!func->ScriptCall)
func->ScriptCall = VMExec;
VMScriptFunction *sfunc = static_cast<VMScriptFunction*>(func);
sfunc->ScriptCall = JitCompile(sfunc);
if (!sfunc->ScriptCall)
sfunc->ScriptCall = VMExec;
else
func->FunctionJitted = true;
sfunc->FunctionJitted = true;
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
@ -500,7 +528,6 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results,
else
{
VMCycles[0].Clock();
VMCalls[0]++;
JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo;
JitExceptionInfo newExceptInfo;

View file

@ -429,7 +429,7 @@ enum EVMEngine
};
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 VMDumpConstants(FILE *out, const VMScriptFunction *func);
@ -450,7 +450,7 @@ extern thread_local JitExceptionInfo *CurrentJitExceptInfo;
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
{
@ -482,15 +482,13 @@ public:
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
int(*ScriptCall)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = &VMScriptFunction::FirstScriptCall;
void InitExtra(void *addr);
void DestroyExtra(void *addr);
int AllocExtraStack(PType *type);
int PCToLine(const VMOP *pc);
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;
};