[LLVMdev] Proper way to handle JIT codegen exceptions?

Török Edwin edwintorok at gmail.com
Fri Oct 28 00:31:44 PDT 2011


On 10/28/2011 09:00 AM, Gregory Junker wrote:
> I am looking through the source and Support/ErrorHandling.cpp/.h says, in so
> many words, "don't throw exceptions from inside an installed fatal error
> handling routine". 
> 
> So how are people handling errors during JIT? For command-line compilers,
> calling exit() or abort() isn't as big a deal -- the compiler is probably
> going to exit anyway. But say, for instance, the host application is Firefox
> -- my guess is that just letting exit() handle the situation isn't the
> solution.
> 
> So what is the "proper" way to handle exceptional LLVM conditions ("cannot
> select", for instance)? Yes, I know this is something that needs to be
> brought to the programmer's attention, but having the user's application
> crash out on them isn't the way I expect most actual LLVM-JIT-based
> applications are handling these things...

I use setjmp/longjmp in ClamAV. Would be better if instead of throwing a fatal error
the code generators would simply return an error code, but that would be too much work.

Although this might leak memory in the case of a fatal error, it is better than crashing (for example
in ClamAV we simply turn off the JIT and fallback to our own interpreter).

I wrap all my toplevel functions that call into LLVM with (there are only 3):
HANDLER_TRY(handler) {
...
return 0;
} HANDLER_END(handler);
return ...some_errorcode...

class ScopedExceptionHandler {
    public:
        jmp_buf &getEnv() { return env;}
        void Set() {
            /* set the exception handler's return location to here for the
             * current thread */
            ExceptionReturn.set((const jmp_buf*)&env);
        }
        ~ScopedExceptionHandler() {
            /* leaving scope, remove exception handler for current thread */
            ExceptionReturn.erase();
        }
    private:
        jmp_buf env;
};

static sys::ThreadLocal<const jmp_buf> ExceptionReturn;

static void NORETURN jit_exception_handler(void)
{
    jmp_buf* buf = const_cast<jmp_buf*>(ExceptionReturn.get());
    if (buf) {
        // For errors raised during bytecode generation and execution.
        longjmp(*buf, 1);
    } else {
        // Oops, got no error recovery pointer set up,
        // this is probably an error raised during shutdown.
        cli_errmsg("[Bytecode JIT]: exception handler called, but no recovery point set up");
        // should never happen, we remove the error handler when we don't use
        // LLVM anymore, and when we use it, we do set an error recovery point.
        llvm_unreachable("Bytecode JIT]: no exception handler recovery installed, but exception hit!");
    }
}

void llvm_error_handler(void *user_data, const std::string &reason)
{
    // Output it to stderr, it might exceed the 1k/4k limit of cli_errmsg
    cli_errmsg("[Bytecode JIT]: [LLVM error] %s\n", reason.c_str());
    jit_exception_handler();
}

};
#define HANDLER_TRY(handler) \
    if (setjmp(handler.getEnv()) == 0) {\
        handler.Set();

#define HANDLER_END(handler) \
    } else cli_warnmsg("[Bytecode JIT]: recovered from error\n");





More information about the llvm-dev mailing list