Currently, we can’t reliably run LLVM optimizations on any function with a try/catch since it mis-optimizes it. This is captured in the following issue:
Codegen bugs are always really nasty, since they can be so unpredictable. Today I realized we may be able to change our codegen representation to avoid this case with minimal effort! If codegen always out-lined the body of try/catch code, it should no longer be able to generate bad code. I think this may even let it perform better optimizations than currently. The key here is that the return path from setjmp needs to avoid referencing any state.
In pseudo-C syntax, this would mean codegen would take the Julia function:
function f()
setup
try
try-body
catch
catch-body
end
rest-of-function
end
And emit something of the form:
jl_value_t *f(args, …) { /* the actual function */
alloca /* local state */
<setup> /* user code */
switch (f_trycatch(&alloca)) {
case 0: /* normal fall-through */
break;
case 1: /* user code */
<catch-body>
break;
default:
abort(); /* corrupted codegen */
}
<rest-of-function> /* user code */
}
int f_trycatch(struct* alloca) {
/* return value describes control flow continuation path */
/* all other local state (including a gc-frame)
/* is packaged into the alloca struct that is passed as a pointer argument */
if (int control-flow = setjmp(&alloca->jmpbuf)) /* Expr(:enter) */
return control-flow;
<try-body> /* user code */
return 0; /* Expr(:leave) */
}
Since there’s already a couple function calls on this code path, I don’t think the addition of the extra local jmp statement will impact performance. While the removal of volatile load / store may actually permit improved codegen optimizations (so, net benefit).
I think this should work, but having a second person review this concept is always good, so I’m posting here for review and other suggestions.