Why doesn't `x < zero(x)` for an unsigned integer lead to dead-code elimination?

julia> isnegative(x) = x < zero(x)
isnegative (generic function with 1 method)

julia> @code_typed isnegative(UInt(2))
CodeInfo(
1 ─ %1 = Base.ult_int(x, 0x0000000000000000)::Bool
└──      return %1
) => Bool

I would have thought that this would be evaluated to false for an unsigned integer, although it’s a bit of a special case

1 Like

LLVM is definitely smart enough to figure this one out but inference isn’t yet.

3 Likes

If I understand your comment correctly,

julia> @code_llvm isnegative(UInt(1))
;  @ REPL[1]:1 within `isnegative`
define i8 @julia_isnegative_267(i64 zeroext %0) #0 {
top:
  ret i8 0
}

shows that the function returns 0, so this does evaluate to false in real code? The issue is with @code_typed, then?

roughly yes. To be specific, it’s not an issue with @code_typed, but with the typed code. Julia goes through several stages of optimization, and @code_typed is correctly showing that this optimization is not being done by julia (but it is getting done by LLVM). It would be better if Julia was smart enough to prove this because deleting code early in the compilation pipeline tends to speed up compilation (because the rest of compilation then has less code to work with).

5 Likes

Also might allow type inference to use it, if you have a if isnegative(x) branch where it returns different types on both sides of the branch.

5 Likes

It does constant-fold(?). So change/clarify the title?

If I understand @Oscar_Smith correctly, that happens, just late in the compilation pipeline. I.e. we can rely on it, and that it would most likely inline, even without:

julia> @inline isnegative(x) = x < zero(x)

What you mentioned would only speed up compilation, not runtime use of code after that.

The ret i8 0 that @code_llvm doesn’t seem bad enough, but @code_native shows more verbose with e.g. unneeded movq %rsp, %rbp. You would get that in your code if NOT inlined, but otherwise not.

My intended use case was the one suggested above, where isnegative is used in an if-else with different types returned in each branch. I was hoping that this may be evaluated in a type-stable manner. What wording would you recommend for the title?

I believe you’re looking for dead code elimination. Constant folding isn’t valid here because x isn’t known.