We have some downstream functions for which we know that they return null if and only if their argument is null.
This fact allows the following optimization to be done. Let we have a null check of the function return value and branch on that check:
Then we could replace the comparison %foo.res == null with %ptr == null, and this would allow us to sink the @foo call into the %if.not.null block.
Proposal
We add an attribute "preserves-arg-nullness"="ARGUMENT_ID" to mark such functions. We could then teach GVN for example to propagate %arg nullness if proven that @foo(%arg) is null and vice versa.
Please express your concerns about this. Also if know of such functions/intrinsics present in LLVM, please share them here so we could mark them with this attribute.
What’s the performance impact of this? Can you measure it or is it in the noise?
We already have tons of attributes, so new attributes must be well justified.
Plus, sinking a function call into a branch requires a lot more than the attribute you’re proposing. What’s the plan for that sinking part?
In our downstream compiler, we have a commonly used function that exposes this property. This function does some mapping of the address argument. It doesn’t necessarily return the original pointer, so ‘returned’ is not applicable. But it preserves nullness of the argument, i.e. it returns null if and only if the argument is null.
We want to take advantage of this fact. We can do this either by handling this downstream or generalizing this property to an upstream attribute. The question is: will such an attribute be useful for other LLVM users? Are there intrinsics, builtin functions, or just commonly used idioms that expose the same property? This seems like a rather generic pattern. If we can find other motivating examples, we can work through upstreaming this attribute.
Sinking in the original example is something we can do for our downstream functions, but this is orthogonal to the semantics of the proposed attribute.
This function does some mapping of the address argument. It doesn’t necessarily return the original pointer, so ‘returned’ is not applicable. But it preserves nullness of the argument, i.e. it returns null if and only if the argument is null.
There is an API in ValueTracking: getArgumentAliasingToReturnedPointer. The argument aliases to the returned value, but you cannot use it to replace one value with another. It also has a MustPreserveNullness parameter. The API is used within isKnownNonZero, for example.
Currently, it only supports intrinsic, but if this works for the use case in question, it would be a simple downstream change.