[LLVMdev] RFC: implicit null checks in llvm

Sanjoy Das sanjoy at playingwithpointers.com
Tue Apr 28 21:27:44 PDT 2015


> I'd like to make sure this is headed in a direction that we'll be able to use/extend it for LLILC/CoreCLR (C#).

Great!

> And I'm trying to understand how I might achieve that.  Allowing a target configuration to direct the null check folding to also fold away the call seems most straightforward; is that the right idea?  Or would it need to be something more like a separate pass that can be run for such targets immediately after this pass, which does the extra folding?

I'd say it depends on the semantics of your runtime.  If this is
strictly an optimization then I'd have a preference for the latter.
If this is a correctness issue then I think the former makes more
sense, since otherwise the GenerateImplicitNullChecks (or
whatever we call it) pass won't be meaning preserving.

> Related, regarding these restrictions:
>
>       > The landingpad for the unwind destination can only be a cleanup landingpad, and the result of the landingpad instruction itself is always undef.
>
> will they be enforced by the verifier?  Could we perhaps make that conditional on the personality routine?

Yes, we can make these dependent on the personality routine.

> 2) Is the signature
>
>         T  <at> llvm.load_with_trap(T*)        [modulo name mangling]
>         void  <at> llvm.store_with_trap(T, T*) [modulo name mangling]
>
> meant to imply that the pointer must be in address space zero, or that any address space is acceptable?

We use statepoints also, so non-zero address space pointers will have to work.

> 3) I didn't see a follow-up to this point:
>
>       > If you really cared about the best codegen this would be done in machine IR after scheduling and target LoadStore opts.
>
> Can you elaborate on whether/why this is/isn't the plan?  I'd hate for the use of implicit checks to come at the cost of worse load/store codegen, especially since we'll have null checks on most heap loads/stores.

So there are two related issues here:

 1. it may just be too difficult to do this correctly once we're out
    of LLVM IR.  For instance, we may need to call on alias analysis:

      if (x != null) {
        x.f = 42;
        y.f = 100;
      }

    can be transformed, by LLVM's normal optimization pipeline, to

      if (x != null) {
        y.f = 100;
        x.f = 42;
      }

    if it can prove that x does not alias y.

    Now we cannot use the store to x.f to throw an NPE
    (NullPointerException) because then we'd have thrown an NPE after
    the store to y.f while the original program had the NPE throw
    before the store to y.f.  To implicit-ify this null check we'd
    have to swap the order of the two stores and essentially reverse
    the above transform.  To do this safely, we'd need alias analysis
    to prove that x does not alias y.

    Re-discovering if pointer X is pointer Y with an offset of 24
    bytes may also be difficult.  This is important since in may cases
    the load doing the null check will be off a pointer derived from
    the pointer we want to check for nullness.

 2. we may be able to get away with representing the trapping load in
    a way that looks like a normal load to the backend.  I'm not sure
    if we can do this safely (the backend cannot assume that the load
    won't trap) but in principle this should allow most of the
    load/store optimizations to kick in.

-- Sanjoy




More information about the llvm-dev mailing list