[PATCH] Clarify wording in the LangRef around !invariant.load

Philip Reames listmail at philipreames.com
Mon Nov 24 12:29:27 PST 2014


On 11/20/2014 10:09 PM, Andrew Trick wrote:
>> On Nov 20, 2014, at 5:33 PM, Sanjoy Das <sanjoy at playingwithpointers.com> wrote:
>>
>> Something I think needs clarification is whether !invariant.load is a
>> dynamic property or a static property.  In other words, is the pointer
>> being loaded from specified to be constant only once an
>> !invariant.load has executed, or is it constant by the virtue of there
>> *existing* an invariant.load?
>>
>> If the pointer being loaded from is specified being constant only once
>> an !invariant.load has executed, it means !invariant.loads cannot be
>> hoisted above control dependencies in general:
>>
>> transforming
>>
>> ```
>> int *x = < known dereferenceable >
>> if (t) {
>>   m = load(x) !invariant.load
>> }
>> *x = 42
>> ```
>>
>> to
>>
>> ```
>> int *x = < known dereferenceable >
>> m = load(x) !invariant.load
>> if (t) {
>> }
>> *x = 42
>> ```
>>
>> makes a well-defined program undefined when t is false.  Hoisting invariant loads through control dependencies can introduce UB.
> As a practical matter, hoisting invariant load can happen, and the hoisted load will retain its invariant property. The invariance is definitely dynamic, but applies as soon as the load is dereferenceable.
>
>>
>> If the pointer being loaded from is specified being constant if there
>> *exists* a use of the pointer in an !invariant.load, then:
>>
>> transforming
>>
>> ```
>> int *x = < known dereferenceable >
>> *x = 42
>> ```
>>
>> to
>>
>> ```
>> int *x = < known dereferenceable >
>> if (false) {
>>   m = load(x) !invariant.load
>> }
>> *x = 42
>> ```
>>
>> introduces UB.  This is counter-intuitive -- introducing a load that
>> will *never be executed* makes the program undefined.
>>
>>
>>
>> Personally, I think the `load` is incorrect place to annotate
>> invariance -- invariance should be just like `dereferenceable`, an
>> attribute on a *pointer* + offset range.
> !invariant.load metadata is not a semantically rich or clean way to express invariance. To be more expressive and sound, we should have an attribute on pointer+offset range, just like dereferencable. And to make that attribute more useful, we should define an intrinsic that attaches attributes to values:
>
> Something like:
>
>    llvm.assume_attribute(%p) invariant
>
> This intrinsic would be control dependent, and only reads from its argument (I think).
>
> ---
>
> However, !invariant.load is already in use, would probably be difficult to replace, and is generally a convenient shortcut in some cases. Without !invariant.load, the frontend would need to determine all of the program points at which we "cast" an address to an invariant.
>
> The simplest way to approach this is to define the fully general intrinsic that you want, and define !invariant.load in terms of it.
>
> I claim that this sequence involving !invariant.load metadata:
> ---
> int *p = <known dereferenceable>
> ...
> <any arbitrary operations or control flow>
> ...
> m = load(p) !invariant.load
> ---
> Is equivalent to this sequence, given the definition of assume_attribute above:
> ---
> int *p = <known dereferenceable>
> llvm_assume_attribute(p) invariant
> ...
> <any arbitrary operations or control flow>
> ...
> m = load(x)
> ---
> The semantic hole arises because we could have two pointer definitions:
> ---
> int *p1 = <known dereferenceable>
> ...
> int *p2 = <known dereferenceable>
> ...
> <any arbitrary operations or control flow>
> ...
> if (p1 == p2)
>    m = load(p2) !invariant.load
>>
> Substituting p1 for p2 in the load now changes the semantics.
>
> So, the frontend needs to ensure this can't happen before using !invariant.load. Currently, this is likely done by hiding object initialization inside an opaque runtime call. I think initialization could be exposed at IR level as long as there is no way to compare the pointers of the raw uninitialized object and the invariant object. This can be done with an opaque call (or some special cast) that appears to potentially reallocate the object. Comparing pointers across that call would be illegal.
Andy phrased this really well.  I agree with everything he said.  :)
>
> -Andy
>
>> http://reviews.llvm.org/D6346
>>
>>




More information about the llvm-commits mailing list