- 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:
parent
b26b16e4b7
commit
42b9a41421
6 changed files with 110 additions and 172 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue