- removed the longjmp based exception catch/rethrow mechanism and instead force-terminate in case a user exception is thrown while the VM is executing JITed code on a non-Windows system

On Windows none of this is needed, because we can generate a proper unwind frame for the JITed functions, but even on Linux, it would require manual additions to each single piece of native code that ever gets called from inside a JIT compiled function.
This is an utterly prohibitive proposition because it makes direct native calls a virtual impossibility
So, in order to get the thrown error properly presented both I_Error and ThrowAbortException will now forward to I_FatalError if it is called from inside a JIT context.
This commit is contained in:
Christoph Oelckers 2018-11-25 12:36:00 +01:00
commit 42b9a41421
6 changed files with 110 additions and 172 deletions

View file

@ -58,6 +58,17 @@ CUSTOM_CVAR(Bool, vm_jit, true, CVAR_NOINITCALL)
CVAR(Bool, vm_jit, false, CVAR_NOINITCALL|CVAR_NOSET)
#endif
// On Windows we can rely on the system's unwinder to deal with JITed code.
// On Linux we can not and need to be able to tell the error management that we have some JITed code in the call chain.
#ifdef _WIN32
inline void beginVM() { }
inline void endVM() { }
#else
thread_local int jit_frames;
inline void beginVM() { if (vm_jit) jit_frames++; }
inline void endVM() { if (vm_jit) jit_frames--; }
#endif
cycle_t VMCycles[10];
int VMCalls[10];
@ -314,13 +325,7 @@ int VMNativeFunction::NativeScriptCall(VMFunction *func, VMValue *params, int nu
{
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;
throw;
}
}
@ -534,32 +539,6 @@ 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
@ -601,25 +580,12 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results,
{
VMCycles[0].Clock();
JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo;
JitExceptionInfo newExceptInfo;
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);
return 0;
}
auto sfunc = static_cast<VMScriptFunction *>(func);
beginVM();
int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults);
endVM();
VMCycles[0].Unclock();
return numret;
}
}
}
@ -733,7 +699,18 @@ void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException
{
va_list ap;
va_start(ap, moreinfo);
CVMAbortException err(reason, moreinfo, ap);
#ifndef _WIN32)
// Without a usable unwinder, aborting is the only real option here. :(
if (jit_frames)
{
I_FatalError("VM error: %s", err.what());
}
#endif
err.stacktrace.AppendFormat("Called from %s at %s, line %d\n", sfunc->PrintableName.GetChars(), sfunc->SourceFileName.GetChars(), sfunc->PCToLine(line));
throw err;
va_end(ap);