Closed gergocs closed 1 year ago
The aim of this patch to make helper functions jit friendly (by not throwing exceptions).
It seems that throwing exceptions by Interpreter
is fine,
but JIT
will handle exceptions differently because its hard to catch throws in JIT code. Am I right?
It seems that throwing exceptions by
Interpreter
is fine, butJIT
will handle exceptions differently because its hard to catch throws in JIT code. Am I right?
Yes.
IMHO we can support c++ exception on JIT. User can raise c++ exception in user callback layer but if we don't support, the program will crash.
throw "user exception"
--------------------
walrus-user-callback
---------
walrus
-----------
user-layer want to catch exception
if we want to replace c++ throw into true/false, we should add exception check on many site ex) after function call
Our aim is not fully replace c++ try-catch. Only doing it in a few helper functions, which used by both interpreter and jit. The scope is small.
Then how about adding JIT helper functions that return boolean instead of throwing exceptions? JIT invokes not-throw functions if necessary while Interpreter is not changed at all IMO this approach seems more clear although code size will be increased little
If you want to replace some functions, IMHO we can divide 1 function into 2 functions eg)
class Memory {
friend class JIT;
private:
ExceptionEnum Memory::read(...) // for jit
public:
int Memory::read(ExcutionState, ....) // for general purpose
{
if (read(...) == hadException) {
state.throwexception(...)
}
return result;
}
}
a) The current jit implementation uses helpers, which call interpreter helpers enclosed in try-catch blocks. This is inefficient for jit. b) Move common (does not throw exceptions) code into helpers, and add interpret / jit variants to the code, which handles the "check" parts differently. Kind of a balanced approach. c) Make duplicated code for interpreter and jit. This way interpreter performance is unaffected but harder to maintain.
Which one you prefer?
I prefer B way. what is your opinion?
I would like to move away from a), but no other preferences. We can do any option, so it is totally up to you.
I wonder the exact exception handling approach in JIT. 1) When a helper function returns an exception (enum value or false), JIT code first checks the result. 2) JIT code finds that there was an exception, it calls another helper function which handles the exception value After then, how to propagate the exception value to the caller side? What if caller and callee both are JIT code and an exception occurred during the execution of callee? Or what about other case where one is interpreter mode and another is in JIT mode?
In JIT exceptions are enums stored in the ErrorCodes error;
field:
https://github.com/Samsung/walrus/blob/jit_next/src/runtime/JITExec.h#L45
In the ExecutionContext
we can store extra data about the exception (future work).
In the original jit branch exceptions are propagated with "returnTo" instruction, which allows you to return to the exception handler of the caller function, rather than the normal return address. This is a powerful operation, which does not exists in C++. So error codes are never checked when a function is returned normally.
C++ helpers return with error codes, and when they do it, jit jumps to the exception handler code path. C++ helpers are complex functions, the extra check has negligible overhead.
I have no idea how to implement calls efficiently in the jit_next branch, Actually calls / throws are the only remaining opcodes which we need to implement. I could use a C++ helper for supporting calls, but that is slow. I don't see any other option though.
Replaced exception throws with boolean return for better performance for JIT callbacks.
Signed-off-by: Gergo Csizi gergocs@inf.u-szeged.hu