On Mar 31, 2014, at 6:58 PM, Peter Collingbourne wrote:
> I wanted to propose an IR extension that would allow us to support
> exception handling for non-call operations that may trap. I wanted to
> with loads and stores through a null pointer, and later we might extend
> div/rem/mod zero. This feature is obviously useful for implementing
> such as Java and Go which deterministically translate such operations
> exceptions which may be caught by the user.
> There are a couple of somewhat orthogonal features that this would
> 1) Deterministic handling for loads and stores through a null pointer.
> 2) Ability to unwind a load/store to a specific basic block, like invoke.
> At the moment, we do not exactly have 1), as the optimizer considers
> non-volatile loads/stores through a null pointer to have undefined
> behavior. Volatile loads/stores are closer, but they come with their own
> set of baggage that can inhibit optimization. (For example, if we can
> that a load would always succeed, 'volatile' prevents us from reordering
> the load or deleting it if it is dead.) So I propose to add an attribute
> 'load' and 'store', which we can call, say, 'nullcheck', with the
> additional semantics:
> - If the memory address is between zero and a target-defined value (i.e.
> size of the zero page) the instruction is guaranteed to trap in a
> target-defined manner.
> - The optimizer may only delete or reorder nullcheck instructions if the
> program cannot observe such a transformation by installing a signal
> for the trap. Therefore, the optimizer would be able to drop the
> if it can prove that the address will always be non-null.
> To support 2), I propose a couple of new instructions. I haven't come up
> great names for these instructions, but:
> - 'iload' is to 'load' as 'invoke' is to 'call'. That is, the instruction
> a terminator and has normal and unwind destinations. e.g.
> %v = iload i8* %ptr to label %try.cont unwind label %lpad
> - Similarly, 'istore' is to 'store' as 'invoke' is to 'call'.
> istore i8 %v, i8* %ptr to label %try.cont unwind label %lpad
> These instructions always have 'nullcheck' semantics, plus:
> - If the instruction traps and the program has installed a signal handler
> for the trap which unwinds, the unwind is guaranteed to land at the
> landing pad.
> I've been working on an implementation of 'iload' and 'istore' which are
> in the attached patches, if you are interested. (They aren't ready to go
> in yet.) I have asm parsing/printing for both, and code generation for
> 'iload'. Would be interested in getting feedback on code generation as
> is my first serious foray into the backend -- I haven't tried running the
> generated code yet and the DAG builder is a mashup of the DAG builders
> 'invoke' and 'load', but I've eyeballed the asm it generates (e.g. llc
> iload-exception.s for the attached iload-exception.ll) and it looks
Hi Peter. All due respect, I don’t think it’s right to introduce new
load/store instructions with equivalent semantics to
‘invoke’ is different. It is needed because there is no way for the
caller to explicitly check for an exception.
We do introduce intrinsics that encapsulate things like overflow checks.
This is done to eliminate control flow edges that tend to inhibit LLVM’s
optimizer and instruction selection. But you’re not removing the control
flow, so this technique does not apply. Null checks should actually be
exposed in IR so general optimizations can remove redundant checks.
Ideally this would just be a machine code pass that can hoist a load/store
above a branch and nuke the compare. However, I can see how it’s easier
to relate the compare operand to the address arithmetic at IR level.
To do this at IR level, you could introduce a pre-CodeGen pass that
converts cmp+br+load/store into a checked load intrinsic. Since this
intrinsic only exists for instruction selection, the optimizer doesn’t
need to know about it.
The intrinsic would need to be lowered to an MI pseudo-instruction that
feels like a load/store to the backend, but is a terminator. During code
emission you could grab the address of that pseudo load/store and its
resulting branch target to inform the runtime.
I can envision having a front end generate an intrinsic for explicit null
checks so that an optimization pass can do something special with these,
like reorder null checks and let the runtime recover. But I would not
include the load/store semantics in the check. And this is entirely
independent from the codegen issue of whether to use trapping loads and
> LLVM Developers mailing list
> [email protected] http://llvm.cs.uiuc.edu