From 0120ea190c2ef6385374aaa0ff1eb33b5bf949c5 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 10 Oct 2018 23:47:56 +0200 Subject: [PATCH] - remove the need to do any VARF_Native runtime checks by making native functions use the same calling convention as the script version --- src/scripting/vm/jit.cpp | 8 ++ src/scripting/vm/jit_call.cpp | 135 +++------------------------------- src/scripting/vm/jitintern.h | 15 +--- src/scripting/vm/vm.h | 11 ++- src/scripting/vm/vmexec.cpp | 2 +- src/scripting/vm/vmexec.h | 7 +- src/scripting/vm/vmframe.cpp | 39 ++++++++-- src/scripting/vm/vmintern.h | 8 +- 8 files changed, 68 insertions(+), 157 deletions(-) diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index fcf353cee0..7231f8e590 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -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(); diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index ec921de89a..a8fd19f3da 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -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(&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(&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(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; - } -} diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index 471b789342..4ebef501d8 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -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 void EmitComparisonOpcode(Func jmpFunc) { diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 2cd86cdcf1..2ceeebae97 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -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 &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*/); diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index bd6c2f5f53..5b58180a8e 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -134,7 +134,7 @@ struct VMExec_Unchecked #undef assert #include -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 diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index eec5009e9c..8f17b24b37 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -710,7 +710,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret) } else { - VMCalls[0]++; auto sfunc = static_cast(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(call); return sfunc->ScriptCall(sfunc, reg.param + f->NumParam - B, B, ret, numret); } @@ -2046,10 +2044,11 @@ static void SetReturn(const VMRegisters ®, 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(func)); VMFillParams(params, newf, numparams); try { diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 9e0aa26c7e..c1049a4b45 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -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(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(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; diff --git a/src/scripting/vm/vmintern.h b/src/scripting/vm/vmintern.h index b372829155..4291eb5c24 100644 --- a/src/scripting/vm/vmintern.h +++ b/src/scripting/vm/vmintern.h @@ -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 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; };