[RFC] Attribute indicating that function preserves argument nullness

Background

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:

; Returns null if and only if %arg is null
declare i8* @foo(i8* %arg)
...
bb1:
  %foo.res = call i8* @foo(i8* %ptr)
  %null.check = icmp eq i8* %foo.res, null
  br i1 %null.check, label %if.null, label %if.not.null

if.null:
  %arg.user = call void @bar(i8* %ptr)
  %foo.user = call void @bar(i8* %foo.res)

if.not.null:
  %arg.user2 = call void @bar(i8* %ptr)
  %foo.user2 = call void @bar(i8* %foo.res)

In block %if.null, where the function return value is proven to be null, we can not only replace all its uses with null, but also its argument uses:

if.null:
  %arg.user = call void @bar(i8* null)
  %foo.user = call void @bar(i8* null)

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?

Thank you!

Put returned on the call site for your desired effect:

if.null:
  %arg.user = call void @bar(i8* returned %ptr)
  %foo.user = call void @bar(i8* returned %foo.res)

Also, attributes are not designed as you indicate.

Let me clarify Dmitry’s question.

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.

Ping

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.