Nonnull assumption on literal pointers not propagated to icmp when in different address spaces

My goal is to get LLVM to eliminate the branch in the following example:

define i64 @test() #0 {
top:
  %ptr = addrspacecast {}* inttoptr (i64 139739127959648 to {}*) to {} addrspace(10)*
  call void @llvm.assume(i1 true) ["nonnull"({} addrspace(10)* %ptr)]
  %cond = icmp ne {} addrspace(10)* %ptr, null
  br i1 %cond, label %one, label %two

one:
  ret i64 1

two:
  ret i64 2
}

(See Compiler Explorer)

Eventually, I’d like to do this with a nonintegral address space, but it seems like it doesn’t even work for integral ones. Is there some workaround to get this to work or is this expected when inlining pointers like this? Thanks for your help!

Per my comment on slack, I don’t think the address space part matters, but rather the fact that it looks like because it becomes a constexpr, the assumption isn’t handled.

The following code, has the desired behavior

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

declare void @llvm.assume(i1)

define i64 @test({}* %a) #0 {
top:
  %ptr = addrspacecast {}* %a to {} addrspace(10)*
  %cond = icmp ne {} addrspace(10)* %ptr, null
  call void @llvm.assume(i1 %cond)
  br i1 %cond, label %one, label %two

one:
  ret i64 1

two:
  ret i64 2
}

LLVM doesn’t understand null/nonnull for non-0 address spaces. I have long wished to add better support for non-0 null pointers, and track the sentinel pointer value in the datalayout per address space to enable this.

Thank you very much for both your replies!

How big of a change would you expect that to be? In Julia’s case, the null pointer actually does respond to a literal zero, but I have been told that we still need to use non-integral pointers for various other reasons, so this would be quite useful. I’m still new to LLVM, but if you think it would be relatively straightforward to add, I might look into it.

I think it would be a moderate amount of work to implement. The hard part is really defining the semantics (and probably renaming things referring to null)

Is it really the address space part though?

For example the following code using addrspace 10, but still eliminating the constant expression works. While I concur better support for noninteger addressspaces is useful, it feels to me that the problem here is the constant expression, not the address space

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

declare void @llvm.assume(i1)

define i64 @test({} addrspace(10)* %a) #0 {
top:
  %cond = icmp ne {} addrspace(10)* %a, null
  call void @llvm.assume(i1 %cond)
  br i1 %cond, label %one, label %two

one:
  ret i64 1

two:
  ret i64 2
}

I think it’s both. The assume is an escape hatch we wanted to use, and the constant expression of nixed that. But if we had better null semantics for pointers in address-spaces we would not have needed to reach after the escape hatch.

1 Like

The nonnull annotation won’t do anything for you, but if the assume is just using the comparison to “null”, that should work as-is since it should be interpreted as a literal 0 without special meaning