- switch to using setjmp/longjmp for exception handling

This commit is contained in:
Magnus Norddahl 2018-10-09 16:30:55 +02:00
parent 2b05e75656
commit 884e185db0
6 changed files with 102 additions and 125 deletions

View file

@ -152,18 +152,18 @@ void JitCompiler::Setup()
funcname.Format("Function: %s", sfunc->PrintableName.GetChars()); funcname.Format("Function: %s", sfunc->PrintableName.GetChars());
cc.comment(funcname.GetChars(), funcname.Len()); cc.comment(funcname.GetChars(), funcname.Len());
auto unusedFunc = cc.newIntPtr("func"); // VMFunction*
args = cc.newIntPtr("args"); // VMValue *params args = cc.newIntPtr("args"); // VMValue *params
numargs = cc.newInt32("numargs"); // int numargs numargs = cc.newInt32("numargs"); // int numargs
ret = cc.newIntPtr("ret"); // VMReturn *ret ret = cc.newIntPtr("ret"); // VMReturn *ret
numret = cc.newInt32("numret"); // int numret numret = cc.newInt32("numret"); // int numret
exceptInfo = cc.newIntPtr("exceptinfo"); // JitExceptionInfo *exceptInfo
cc.addFunc(FuncSignature5<int, void *, int, void *, int, void *>()); cc.addFunc(FuncSignature5<int, VMFunction *, void *, int, void *, int>());
cc.setArg(0, args); cc.setArg(0, unusedFunc);
cc.setArg(1, numargs); cc.setArg(1, args);
cc.setArg(2, ret); cc.setArg(2, numargs);
cc.setArg(3, numret); cc.setArg(3, ret);
cc.setArg(4, exceptInfo); cc.setArg(4, numret);
auto stackalloc = cc.newStack(sizeof(VMReturn) * MAX_RETURNS, alignof(VMReturn)); auto stackalloc = cc.newStack(sizeof(VMReturn) * MAX_RETURNS, alignof(VMReturn));
callReturns = cc.newIntPtr("callReturns"); 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, MaxParam)), sfunc->MaxParam);
cc.mov(x86::word_ptr(vmframe, offsetof(VMFrame, NumParam)), 0); cc.mov(x86::word_ptr(vmframe, offsetof(VMFrame, NumParam)), 0);
auto fillParams = CreateCall<void, VMFrame *, VMValue *, int, JitExceptionInfo *>([](VMFrame *newf, VMValue *args, int numargs, JitExceptionInfo *exceptinfo) { auto fillParams = CreateCall<void, VMFrame *, VMValue *, int>([](VMFrame *newf, VMValue *args, int numargs) {
try try
{ {
VMFillParams(args, newf, numargs); VMFillParams(args, newf, numargs);
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
} }
}); });
fillParams->setArg(0, vmframe); fillParams->setArg(0, vmframe);
fillParams->setArg(1, args); fillParams->setArg(1, args);
fillParams->setArg(2, numargs); fillParams->setArg(2, numargs);
fillParams->setArg(3, exceptInfo);
EmitCheckForException();
for (int i = 0; i < sfunc->NumRegD; i++) for (int i = 0; i < sfunc->NumRegD; i++)
cc.mov(regD[i], x86::dword_ptr(frameD, i * sizeof(int32_t))); cc.mov(regD[i], x86::dword_ptr(frameD, i * sizeof(int32_t)));
@ -331,18 +328,18 @@ void JitCompiler::Setup()
else else
{ {
stack = cc.newIntPtr("stack"); stack = cc.newIntPtr("stack");
auto allocFrame = CreateCall<VMFrameStack *, VMScriptFunction *, VMValue *, int, JitExceptionInfo *>([](VMScriptFunction *func, VMValue *args, int numargs, JitExceptionInfo *exceptinfo) -> VMFrameStack* { auto allocFrame = CreateCall<VMFrameStack *, VMScriptFunction *, VMValue *, int>([](VMScriptFunction *func, VMValue *args, int numargs) -> VMFrameStack* {
try try
{ {
VMFrameStack *stack = &GlobalVMStack; VMFrameStack *stack = &GlobalVMStack;
VMFrame *newf = stack->AllocFrame(func); VMFrame *newf = stack->AllocFrame(func);
CurrentJitExceptInfo->vmframes++;
VMFillParams(args, newf, numargs); VMFillParams(args, newf, numargs);
return stack; return stack;
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
return nullptr; return nullptr;
} }
}); });
@ -350,8 +347,6 @@ void JitCompiler::Setup()
allocFrame->setArg(0, imm_ptr(sfunc)); allocFrame->setArg(0, imm_ptr(sfunc));
allocFrame->setArg(1, args); allocFrame->setArg(1, args);
allocFrame->setArg(2, numargs); allocFrame->setArg(2, numargs);
allocFrame->setArg(3, exceptInfo);
EmitCheckForException();
cc.mov(vmframe, x86::ptr(stack)); // stack->Blocks cc.mov(vmframe, x86::ptr(stack)); // stack->Blocks
cc.mov(vmframe, x86::ptr(vmframe, VMFrameStack::OffsetLastFrame())); // Blocks->LastFrame 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) if (sfunc->SpecialInits.Size() != 0 || sfunc->NumRegS != 0)
{ {
auto popFrame = CreateCall<void, VMFrameStack *, JitExceptionInfo *>([](VMFrameStack *stack, JitExceptionInfo *exceptinfo) { auto popFrame = CreateCall<void, VMFrameStack *>([](VMFrameStack *stack) {
try try
{ {
stack->PopFrame(); stack->PopFrame();
CurrentJitExceptInfo->vmframes--;
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
} }
}); });
popFrame->setArg(0, stack); popFrame->setArg(0, stack);
popFrame->setArg(1, exceptInfo);
} }
} }
@ -411,53 +405,25 @@ void JitCompiler::EmitNullPointerThrow(int index, EVMAbortException reason)
void JitCompiler::EmitThrowException(EVMAbortException reason) void JitCompiler::EmitThrowException(EVMAbortException reason)
{ {
using namespace asmjit; auto call = CreateCall<void, VMScriptFunction *, VMOP *, int>([](VMScriptFunction *func, VMOP *line, int r) {
try
// Update JitExceptionInfo struct {
cc.mov(x86::dword_ptr(exceptInfo, 0 * 4), (int32_t)reason); ThrowAbortException(func, line, (EVMAbortException)r, nullptr);
if (cc.is64Bit()) }
cc.mov(x86::qword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); catch (...)
else {
cc.mov(x86::dword_ptr(exceptInfo, 4 * 4), imm_ptr(pc)); VMThrowException(std::current_exception());
}
// Return from function });
EmitPopFrame(); call->setArg(0, asmjit::imm_ptr(sfunc));
X86Gp vReg = newTempInt32(); call->setArg(1, asmjit::imm_ptr(pc));
cc.mov(vReg, 0); call->setArg(2, asmjit::imm(reason));
cc.ret(vReg);
} }
void JitCompiler::EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1) void JitCompiler::EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1)
{ {
using namespace asmjit; // To do: fix throw message and use arg1
EmitThrowException(reason);
// 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);
} }
asmjit::X86Gp JitCompiler::CheckRegD(int r0, int r1) asmjit::X86Gp JitCompiler::CheckRegD(int r0, int r1)

View file

@ -170,16 +170,13 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp ptr)
} }
auto result = newResultInt32(); auto result = newResultInt32();
auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*, JitExceptionInfo*>(&JitCompiler::DoCall); auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*>(&JitCompiler::DoCall);
call->setRet(0, result); call->setRet(0, result);
call->setArg(0, ptr); call->setArg(0, ptr);
call->setArg(1, Imm(B)); call->setArg(1, Imm(B));
call->setArg(2, Imm(C)); call->setArg(2, Imm(C));
call->setArg(3, paramsptr); call->setArg(3, paramsptr);
call->setArg(4, callReturns); call->setArg(4, callReturns);
call->setArg(5, exceptInfo);
EmitCheckForException();
LoadInOuts(B); LoadInOuts(B);
LoadReturns(pc + 1, C); LoadReturns(pc + 1, C);
@ -214,14 +211,13 @@ void JitCompiler::EmitDoTail(asmjit::X86Gp ptr)
} }
auto result = newResultInt32(); auto result = newResultInt32();
auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*, JitExceptionInfo*>(&JitCompiler::DoCall); auto call = CreateCall<int, VMFunction*, int, int, VMValue*, VMReturn*>(&JitCompiler::DoCall);
call->setRet(0, result); call->setRet(0, result);
call->setArg(0, ptr); call->setArg(0, ptr);
call->setArg(1, Imm(B)); call->setArg(1, Imm(B));
call->setArg(2, numret); call->setArg(2, numret);
call->setArg(3, paramsptr); call->setArg(3, paramsptr);
call->setArg(4, ret); call->setArg(4, ret);
call->setArg(5, exceptInfo);
EmitPopFrame(); EmitPopFrame();
cc.ret(result); 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 try
{ {
@ -424,8 +420,7 @@ int JitCompiler::DoCall(VMFunction *call, int b, int c, VMValue *param, VMReturn
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
return 0; return 0;
} }
} }

View file

@ -80,23 +80,19 @@ void JitCompiler::EmitSCOPE()
cc.mov(f, asmjit::imm_ptr(konsta[C].v)); cc.mov(f, asmjit::imm_ptr(konsta[C].v));
typedef int(*FuncPtr)(DObject*, VMFunction*, int); typedef int(*FuncPtr)(DObject*, VMFunction*, int);
auto call = CreateCall<void, DObject*, VMFunction*, int, JitExceptionInfo *>([](DObject *o, VMFunction *f, int b, JitExceptionInfo *exceptinfo) { auto call = CreateCall<void, DObject*, VMFunction*, int>([](DObject *o, VMFunction *f, int b) {
try try
{ {
FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1); FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1);
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
} }
}); });
call->setArg(0, regA[A]); call->setArg(0, regA[A]);
call->setArg(1, f); call->setArg(1, f);
call->setArg(2, asmjit::Imm(B)); call->setArg(2, asmjit::Imm(B));
call->setArg(3, exceptInfo);
EmitCheckForException();
} }
void JitCompiler::EmitRET() void JitCompiler::EmitRET()
@ -265,7 +261,7 @@ void JitCompiler::EmitRETI()
void JitCompiler::EmitNEW() void JitCompiler::EmitNEW()
{ {
auto result = newResultIntPtr(); auto result = newResultIntPtr();
auto call = CreateCall<DObject*, PClass*, int, JitExceptionInfo *>([](PClass *cls, int c, JitExceptionInfo *exceptinfo) -> DObject* { auto call = CreateCall<DObject*, PClass*, int>([](PClass *cls, int c) -> DObject* {
try try
{ {
if (!cls->ConstructNative) if (!cls->ConstructNative)
@ -287,17 +283,13 @@ void JitCompiler::EmitNEW()
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
return nullptr; return nullptr;
} }
}); });
call->setRet(0, result); call->setRet(0, result);
call->setArg(0, regA[B]); call->setArg(0, regA[B]);
call->setArg(1, asmjit::Imm(C)); call->setArg(1, asmjit::Imm(C));
call->setArg(2, exceptInfo);
EmitCheckForException();
cc.mov(regA[A], result); cc.mov(regA[A], result);
} }
@ -322,7 +314,7 @@ void JitCompiler::EmitNEW_K()
auto result = newResultIntPtr(); auto result = newResultIntPtr();
auto regcls = newTempIntPtr(); auto regcls = newTempIntPtr();
cc.mov(regcls, asmjit::imm_ptr(konsta[B].v)); cc.mov(regcls, asmjit::imm_ptr(konsta[B].v));
auto call = CreateCall<DObject*, PClass*, int, JitExceptionInfo *>([](PClass *cls, int c, JitExceptionInfo *exceptinfo) -> DObject* { auto call = CreateCall<DObject*, PClass*, int>([](PClass *cls, int c) -> DObject* {
try try
{ {
if (c) FScopeBarrier::ValidateNew(cls, c - 1); if (c) FScopeBarrier::ValidateNew(cls, c - 1);
@ -330,17 +322,13 @@ void JitCompiler::EmitNEW_K()
} }
catch (...) catch (...)
{ {
exceptinfo->reason = X_OTHER; VMThrowException(std::current_exception());
exceptinfo->cppException = std::current_exception();
return nullptr; return nullptr;
} }
}); });
call->setRet(0, result); call->setRet(0, result);
call->setArg(0, regcls); call->setArg(0, regcls);
call->setArg(1, asmjit::Imm(C)); call->setArg(1, asmjit::Imm(C));
call->setArg(2, exceptInfo);
EmitCheckForException();
cc.mov(regA[A], result); cc.mov(regA[A], result);
} }

View file

@ -49,7 +49,7 @@ private:
void LoadReturns(const VMOP *retval, int numret); void LoadReturns(const VMOP *retval, int numret);
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 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 <typename Func> template <typename Func>
void EmitComparisonOpcode(Func jmpFunc) void EmitComparisonOpcode(Func jmpFunc)
@ -131,7 +131,6 @@ private:
void EmitNullPointerThrow(int index, EVMAbortException reason); void EmitNullPointerThrow(int index, EVMAbortException reason);
void EmitThrowException(EVMAbortException reason); void EmitThrowException(EVMAbortException reason);
void EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1); void EmitThrowException(EVMAbortException reason, asmjit::X86Gp arg1);
void EmitCheckForException();
asmjit::X86Gp CheckRegD(int r0, int r1); asmjit::X86Gp CheckRegD(int r0, int r1);
asmjit::X86Xmm CheckRegF(int r0, int r1); asmjit::X86Xmm CheckRegF(int r0, int r1);
@ -147,7 +146,6 @@ private:
asmjit::X86Gp numargs; asmjit::X86Gp numargs;
asmjit::X86Gp ret; asmjit::X86Gp ret;
asmjit::X86Gp numret; asmjit::X86Gp numret;
asmjit::X86Gp exceptInfo;
asmjit::X86Gp stack; asmjit::X86Gp stack;
int offsetExtra; int offsetExtra;

View file

@ -78,10 +78,10 @@ VMScriptFunction::VMScriptFunction(FName name)
VMScriptFunction::~VMScriptFunction() VMScriptFunction::~VMScriptFunction()
{ {
if (JitFunc) if (FunctionJitted)
{ {
JitCleanUp(this); JitCleanUp(this);
JitFunc = nullptr; FunctionJitted = false;
} }
if (Code != NULL) 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) int VMScriptFunction::FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret)
{ {
func->JitFunc = JitCompile(func); func->ScriptCall = JitCompile(func);
if (func->JitFunc) if (!func->ScriptCall)
func->ScriptCall = &VMScriptFunction::JitCall;
else
func->ScriptCall = VMExec; func->ScriptCall = VMExec;
else
func->FunctionJitted = true;
return func->ScriptCall(func, params, numparams, ret, numret); 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 // VMFrame :: InitRegS
@ -449,6 +434,32 @@ VMFrame *VMFrameStack::PopFrame()
return parent; 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 // VMFrameStack :: Call
@ -490,10 +501,25 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results,
{ {
VMCycles[0].Clock(); VMCycles[0].Clock();
VMCalls[0]++; VMCalls[0]++;
auto sfunc = static_cast<VMScriptFunction *>(func);
int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults); JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo;
VMCycles[0].Unclock(); JitExceptionInfo newExceptInfo;
return numret; CurrentJitExceptInfo = &newExceptInfo;
if (setjmp(CurrentJitExceptInfo->sjljbuf) == 0)
{
auto sfunc = static_cast<VMScriptFunction *>(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);
}
} }
} }
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "vm.h" #include "vm.h"
#include <csetjmp>
class VMScriptFunction; class VMScriptFunction;
@ -440,13 +441,16 @@ typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
struct JitExceptionInfo struct JitExceptionInfo
{ {
int32_t reason; // EVMAbortException
int32_t args[3];
VMOP* pcOnJitAbort;
std::exception_ptr cppException; 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 class VMScriptFunction : public VMFunction
{ {
@ -487,6 +491,6 @@ public:
private: private:
static int FirstScriptCall(VMScriptFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret); 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;
}; };