From 884e185db0e985969d28c67a4c9868d1bf4d5b17 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 9 Oct 2018 16:30:55 +0200 Subject: [PATCH] - switch to using setjmp/longjmp for exception handling --- src/scripting/vm/jit.cpp | 94 +++++++++++------------------------ src/scripting/vm/jit_call.cpp | 13 ++--- src/scripting/vm/jit_flow.cpp | 24 +++------ src/scripting/vm/jitintern.h | 4 +- src/scripting/vm/vmframe.cpp | 76 ++++++++++++++++++---------- src/scripting/vm/vmintern.h | 16 +++--- 6 files changed, 102 insertions(+), 125 deletions(-) diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index 47ab70f20..7f68f88d8 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -152,18 +152,18 @@ void JitCompiler::Setup() funcname.Format("Function: %s", sfunc->PrintableName.GetChars()); cc.comment(funcname.GetChars(), funcname.Len()); + auto unusedFunc = cc.newIntPtr("func"); // VMFunction* args = cc.newIntPtr("args"); // VMValue *params numargs = cc.newInt32("numargs"); // int numargs ret = cc.newIntPtr("ret"); // VMReturn *ret numret = cc.newInt32("numret"); // int numret - exceptInfo = cc.newIntPtr("exceptinfo"); // JitExceptionInfo *exceptInfo - cc.addFunc(FuncSignature5()); - cc.setArg(0, args); - cc.setArg(1, numargs); - cc.setArg(2, ret); - cc.setArg(3, numret); - cc.setArg(4, exceptInfo); + cc.addFunc(FuncSignature5()); + cc.setArg(0, unusedFunc); + cc.setArg(1, args); + cc.setArg(2, numargs); + cc.setArg(3, ret); + cc.setArg(4, numret); auto stackalloc = cc.newStack(sizeof(VMReturn) * MAX_RETURNS, alignof(VMReturn)); callReturns = cc.newIntPtr("callReturns"); @@ -297,22 +297,19 @@ void JitCompiler::Setup() cc.mov(x86::word_ptr(vmframe, offsetof(VMFrame, MaxParam)), sfunc->MaxParam); cc.mov(x86::word_ptr(vmframe, offsetof(VMFrame, NumParam)), 0); - auto fillParams = CreateCall([](VMFrame *newf, VMValue *args, int numargs, JitExceptionInfo *exceptinfo) { + auto fillParams = CreateCall([](VMFrame *newf, VMValue *args, int numargs) { try { VMFillParams(args, newf, numargs); } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); } }); fillParams->setArg(0, vmframe); fillParams->setArg(1, args); fillParams->setArg(2, numargs); - fillParams->setArg(3, exceptInfo); - EmitCheckForException(); for (int i = 0; i < sfunc->NumRegD; i++) cc.mov(regD[i], x86::dword_ptr(frameD, i * sizeof(int32_t))); @@ -331,18 +328,18 @@ void JitCompiler::Setup() else { stack = cc.newIntPtr("stack"); - auto allocFrame = CreateCall([](VMScriptFunction *func, VMValue *args, int numargs, JitExceptionInfo *exceptinfo) -> VMFrameStack* { + auto allocFrame = CreateCall([](VMScriptFunction *func, VMValue *args, int numargs) -> VMFrameStack* { try { VMFrameStack *stack = &GlobalVMStack; VMFrame *newf = stack->AllocFrame(func); + CurrentJitExceptInfo->vmframes++; VMFillParams(args, newf, numargs); return stack; } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); return nullptr; } }); @@ -350,8 +347,6 @@ void JitCompiler::Setup() allocFrame->setArg(0, imm_ptr(sfunc)); allocFrame->setArg(1, args); allocFrame->setArg(2, numargs); - allocFrame->setArg(3, exceptInfo); - EmitCheckForException(); cc.mov(vmframe, x86::ptr(stack)); // stack->Blocks cc.mov(vmframe, x86::ptr(vmframe, VMFrameStack::OffsetLastFrame())); // Blocks->LastFrame @@ -384,19 +379,18 @@ void JitCompiler::EmitPopFrame() { if (sfunc->SpecialInits.Size() != 0 || sfunc->NumRegS != 0) { - auto popFrame = CreateCall([](VMFrameStack *stack, JitExceptionInfo *exceptinfo) { + auto popFrame = CreateCall([](VMFrameStack *stack) { try { stack->PopFrame(); + CurrentJitExceptInfo->vmframes--; } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); } }); popFrame->setArg(0, stack); - popFrame->setArg(1, exceptInfo); } } @@ -411,53 +405,25 @@ void JitCompiler::EmitNullPointerThrow(int index, EVMAbortException reason) void JitCompiler::EmitThrowException(EVMAbortException reason) { - using namespace asmjit; - - // Update JitExceptionInfo struct - cc.mov(x86::dword_ptr(exceptInfo, 0 * 4), (int32_t)reason); - if (cc.is64Bit()) - cc.mov(x86::qword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); - else - cc.mov(x86::dword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); - - // Return from function - EmitPopFrame(); - X86Gp vReg = newTempInt32(); - cc.mov(vReg, 0); - cc.ret(vReg); + auto call = CreateCall([](VMScriptFunction *func, VMOP *line, int r) { + try + { + ThrowAbortException(func, line, (EVMAbortException)r, nullptr); + } + catch (...) + { + VMThrowException(std::current_exception()); + } + }); + call->setArg(0, asmjit::imm_ptr(sfunc)); + call->setArg(1, asmjit::imm_ptr(pc)); + call->setArg(2, asmjit::imm(reason)); } void JitCompiler::EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1) { - using namespace asmjit; - - // Update JitExceptionInfo struct - cc.mov(x86::dword_ptr(exceptInfo, 0 * 4), (int32_t)reason); - cc.mov(x86::dword_ptr(exceptInfo, 1 * 4), arg1); - if (cc.is64Bit()) - cc.mov(x86::qword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); - else - cc.mov(x86::dword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); - - // Return from function - EmitPopFrame(); - X86Gp vReg = newTempInt32(); - cc.mov(vReg, 0); - cc.ret(vReg); -} - -void JitCompiler::EmitCheckForException() -{ - auto noexception = cc.newLabel(); - auto exceptResult = newTempInt32(); - cc.mov(exceptResult, asmjit::x86::dword_ptr(exceptInfo, 0 * 4)); - cc.cmp(exceptResult, (int)-1); - cc.je(noexception); - EmitPopFrame(); - asmjit::X86Gp vReg = newTempInt32(); - cc.mov(vReg, 0); - cc.ret(vReg); - cc.bind(noexception); + // To do: fix throw message and use arg1 + EmitThrowException(reason); } asmjit::X86Gp JitCompiler::CheckRegD(int r0, int r1) diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index 7401c42fb..b5531bd16 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -170,16 +170,13 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp ptr) } auto result = newResultInt32(); - auto call = CreateCall(&JitCompiler::DoCall); + auto call = CreateCall(&JitCompiler::DoCall); call->setRet(0, result); call->setArg(0, ptr); call->setArg(1, Imm(B)); call->setArg(2, Imm(C)); call->setArg(3, paramsptr); call->setArg(4, callReturns); - call->setArg(5, exceptInfo); - - EmitCheckForException(); LoadInOuts(B); LoadReturns(pc + 1, C); @@ -214,14 +211,13 @@ void JitCompiler::EmitDoTail(asmjit::X86Gp ptr) } auto result = newResultInt32(); - auto call = CreateCall(&JitCompiler::DoCall); + auto call = CreateCall(&JitCompiler::DoCall); call->setRet(0, result); call->setArg(0, ptr); call->setArg(1, Imm(B)); call->setArg(2, numret); call->setArg(3, paramsptr); call->setArg(4, ret); - call->setArg(5, exceptInfo); EmitPopFrame(); cc.ret(result); @@ -393,7 +389,7 @@ void JitCompiler::FillReturns(const VMOP *retval, int numret) } } -int JitCompiler::DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns, JitExceptionInfo *exceptinfo) +int JitCompiler::DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns) { try { @@ -424,8 +420,7 @@ int JitCompiler::DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); return 0; } } diff --git a/src/scripting/vm/jit_flow.cpp b/src/scripting/vm/jit_flow.cpp index da11b1222..be74bc940 100644 --- a/src/scripting/vm/jit_flow.cpp +++ b/src/scripting/vm/jit_flow.cpp @@ -80,23 +80,19 @@ void JitCompiler::EmitSCOPE() cc.mov(f, asmjit::imm_ptr(konsta[C].v)); typedef int(*FuncPtr)(DObject*, VMFunction*, int); - auto call = CreateCall([](DObject *o, VMFunction *f, int b, JitExceptionInfo *exceptinfo) { + auto call = CreateCall([](DObject *o, VMFunction *f, int b) { try { FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1); } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); } }); call->setArg(0, regA[A]); call->setArg(1, f); call->setArg(2, asmjit::Imm(B)); - call->setArg(3, exceptInfo); - - EmitCheckForException(); } void JitCompiler::EmitRET() @@ -265,7 +261,7 @@ void JitCompiler::EmitRETI() void JitCompiler::EmitNEW() { auto result = newResultIntPtr(); - auto call = CreateCall([](PClass *cls, int c, JitExceptionInfo *exceptinfo) -> DObject* { + auto call = CreateCall([](PClass *cls, int c) -> DObject* { try { if (!cls->ConstructNative) @@ -287,17 +283,13 @@ void JitCompiler::EmitNEW() } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); return nullptr; } }); call->setRet(0, result); call->setArg(0, regA[B]); call->setArg(1, asmjit::Imm(C)); - call->setArg(2, exceptInfo); - - EmitCheckForException(); cc.mov(regA[A], result); } @@ -322,7 +314,7 @@ void JitCompiler::EmitNEW_K() auto result = newResultIntPtr(); auto regcls = newTempIntPtr(); cc.mov(regcls, asmjit::imm_ptr(konsta[B].v)); - auto call = CreateCall([](PClass *cls, int c, JitExceptionInfo *exceptinfo) -> DObject* { + auto call = CreateCall([](PClass *cls, int c) -> DObject* { try { if (c) FScopeBarrier::ValidateNew(cls, c - 1); @@ -330,17 +322,13 @@ void JitCompiler::EmitNEW_K() } catch (...) { - exceptinfo->reason = X_OTHER; - exceptinfo->cppException = std::current_exception(); + VMThrowException(std::current_exception()); return nullptr; } }); call->setRet(0, result); call->setArg(0, regcls); call->setArg(1, asmjit::Imm(C)); - call->setArg(2, exceptInfo); - - EmitCheckForException(); cc.mov(regA[A], result); } diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index d8ad24ffb..17761c2f9 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -49,7 +49,7 @@ private: void LoadReturns(const VMOP *retval, int numret); void FillReturns(const VMOP *retval, int numret); void LoadCallResult(const VMOP &opdata, bool addrof); - static int DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns, JitExceptionInfo *exceptinfo); + static int DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn *returns); template void EmitComparisonOpcode(Func jmpFunc) @@ -131,7 +131,6 @@ private: void EmitNullPointerThrow(int index, EVMAbortException reason); void EmitThrowException(EVMAbortException reason); void EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1); - void EmitCheckForException(); asmjit::X86Gp CheckRegD(int r0, int r1); asmjit::X86Xmm CheckRegF(int r0, int r1); @@ -147,7 +146,6 @@ private: asmjit::X86Gp numargs; asmjit::X86Gp ret; asmjit::X86Gp numret; - asmjit::X86Gp exceptInfo; asmjit::X86Gp stack; int offsetExtra; diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 805b6a291..a5a682040 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -78,10 +78,10 @@ VMScriptFunction::VMScriptFunction(FName name) VMScriptFunction::~VMScriptFunction() { - if (JitFunc) + if (FunctionJitted) { JitCleanUp(this); - JitFunc = nullptr; + FunctionJitted = false; } if (Code != NULL) @@ -215,30 +215,15 @@ int VMScriptFunction::PCToLine(const VMOP *pc) int VMScriptFunction::FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) { - func->JitFunc = JitCompile(func); - if (func->JitFunc) - func->ScriptCall = &VMScriptFunction::JitCall; - else + func->ScriptCall = JitCompile(func); + if (!func->ScriptCall) func->ScriptCall = VMExec; + else + func->FunctionJitted = true; return func->ScriptCall(func, params, numparams, ret, numret); } -int VMScriptFunction::JitCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) -{ - JitExceptionInfo exceptInfo; - exceptInfo.reason = -1; - int result = func->JitFunc(params, numparams, ret, numret, &exceptInfo); - if (exceptInfo.reason != -1) - { - if (exceptInfo.cppException) - std::rethrow_exception(exceptInfo.cppException); - else - ThrowAbortException(func, exceptInfo.pcOnJitAbort, (EVMAbortException)exceptInfo.reason, nullptr); - } - return result; -} - //=========================================================================== // // VMFrame :: InitRegS @@ -449,6 +434,32 @@ VMFrame *VMFrameStack::PopFrame() return parent; } +//=========================================================================== +// +// The jitted code does not implement C++ exception handling. +// Catch them, longjmp out of the jitted functions, perform vmframe cleanup +// then rethrow the C++ exception +// +//=========================================================================== + +thread_local JitExceptionInfo *CurrentJitExceptInfo; + +void VMThrowException(std::exception_ptr cppException) +{ + CurrentJitExceptInfo->cppException = cppException; + longjmp(CurrentJitExceptInfo->sjljbuf, 1); +} + +static void VMRethrowException(JitExceptionInfo *exceptInfo) +{ + int c = exceptInfo->vmframes; + VMFrameStack *stack = &GlobalVMStack; + for (int i = 0; i < c; i++) + stack->PopFrame(); + + std::rethrow_exception(exceptInfo->cppException); +} + //=========================================================================== // // VMFrameStack :: Call @@ -490,10 +501,25 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, { VMCycles[0].Clock(); VMCalls[0]++; - auto sfunc = static_cast(func); - int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults); - VMCycles[0].Unclock(); - return numret; + + JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo; + JitExceptionInfo newExceptInfo; + CurrentJitExceptInfo = &newExceptInfo; + if (setjmp(CurrentJitExceptInfo->sjljbuf) == 0) + { + auto sfunc = static_cast(func); + int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults); + CurrentJitExceptInfo = prevExceptInfo; + VMCycles[0].Unclock(); + return numret; + } + else + { + VMCycles[0].Unclock(); + auto exceptInfo = CurrentJitExceptInfo; + CurrentJitExceptInfo = prevExceptInfo; + VMRethrowException(exceptInfo); + } } } } diff --git a/src/scripting/vm/vmintern.h b/src/scripting/vm/vmintern.h index a8a610578..b37282915 100644 --- a/src/scripting/vm/vmintern.h +++ b/src/scripting/vm/vmintern.h @@ -1,6 +1,7 @@ #pragma once #include "vm.h" +#include class VMScriptFunction; @@ -440,13 +441,16 @@ typedef std::pair FTypeAndOffset; struct JitExceptionInfo { - int32_t reason; // EVMAbortException - int32_t args[3]; - VMOP* pcOnJitAbort; std::exception_ptr cppException; + std::jmp_buf sjljbuf; + int vmframes = 0; }; -typedef int(*JitFuncPtr)(VMValue *params, int numparams, VMReturn *ret, int numret, JitExceptionInfo *exceptInfo); +extern thread_local JitExceptionInfo *CurrentJitExceptInfo; + +void VMThrowException(std::exception_ptr cppException); + +typedef int(*JitFuncPtr)(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); class VMScriptFunction : public VMFunction { @@ -487,6 +491,6 @@ public: private: static int FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); - static int JitCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); - JitFuncPtr JitFunc = nullptr; + + bool FunctionJitted = false; };