__builtin_assume with complex condition not working

I’m trying to use __builtin_assume to model the return value of a function to get a system version which I know will be in a particular range. Compiler Explorer is a simple example, where adding the assumption eliminates all the unreachable switch cases, which is exactly what I want.

Unfortunately, the actual function can also return -1 to indicate an error, and when I try to add that to my assume the whole thing falls apart: Compiler Explorer. I was hoping for the cases for 21 and 28 to still be eliminated, since my __builtin_assume should rule them out, but that’s not happening. Is there any way to make this work?

Can you change it to unsigned i, or do you need to make the compiler smarter: Compiler Explorer ?

Huh, that might be workable, thanks! How come unsigned makes the assumption work?

EDIT: Unfortunately, the real API is a bit more complicated: it returns a string and you have to convert into an int yourself. The unsigned trick doesn’t seem to work with that: Compiler Explorer.

It worked before because we did not have an || in the condition for the unsigned case, just i > 28.
Now the || is back. Compiler needs to be improved. Maybe open an issue?

1 Like

Assumptions not working with complex conditionals · Issue #65504 · llvm/llvm-project · GitHub. I think I’ll be able to use the unsigned trick to work around this though; thanks for the suggestion.

Turns out just having the cast in the assume is enough: Compiler Explorer

1 Like

It also turns out that inlining needs to take place for the __builtin_assume to have any effect … I was hoping we’d have some IPA that would kick in even without inlining, but that doesn’t seem to be the case. It’d be nice if we had a way to express assumptions directly on function return values that could be applied to even prototypes, but I couldn’t find anything of the sort.

The example you gave works fine w/o inlining (add __attribute__((noinline))):
It is IPSCCP that does the propagation (look for IR Dump After IPSCCPPass on [module]):

Attributor should be able to do it too.

The closes we have (which is not a good match) is:
which does not do what you want but “could”.

You’re right. Inlining is required for something more representative of my actual code though, where there’s a layer of caching added to only fetch the system property once: Compiler Explorer. Adding -DALWAYS_INLINE to force the inlining gets the range-based case elimination back (Compiler Explorer), and the higher inlining threshold of -Os works as well (Compiler Explorer).

IPSCCP isn’t changing the function at all in this case: Compiler Explorer. CorrelatedValuePropagationPass seems to be doing the job instead, with both -DALWAYS_INLINE (Compiler Explorer) and -Os (Compiler Explorer).