Samsung / walrus

WebAssembly Lightweight RUntime
Apache License 2.0
40 stars 10 forks source link

Replace exception throws with boolean return #93

Closed gergocs closed 1 year ago

gergocs commented 1 year ago

Replaced exception throws with boolean return for better performance for JIT callbacks.

Signed-off-by: Gergo Csizi gergocs@inf.u-szeged.hu

zherczeg commented 1 year ago

The aim of this patch to make helper functions jit friendly (by not throwing exceptions).

clover2123 commented 1 year ago

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?

zherczeg commented 1 year ago

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?

Yes.

ksh8281 commented 1 year ago

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

zherczeg commented 1 year ago

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.

clover2123 commented 1 year ago

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

ksh8281 commented 1 year ago

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;
  }
}
zherczeg commented 1 year ago

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?

ksh8281 commented 1 year ago

I prefer B way. what is your opinion?

zherczeg commented 1 year ago

I would like to move away from a), but no other preferences. We can do any option, so it is totally up to you.

clover2123 commented 1 year ago

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?

zherczeg commented 1 year ago

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.