Description
Clang does not seem to be able to figure out these two functions are the same:
void f(const int *data, size_t size) {
if (size < 5) __builtin_trap();
if (size == 5) __builtin_trap();
}
void g(const int *data, size_t size) {
if (size < 5 || size == 5) __builtin_trap();
}
Given f
, it emits two branches. Given g
, it figures out this comparison is simply size <= 5
:
This came up as I was putting together a fix for #78829, equivalently step 1 of #78771. Making the change described there generally optimized better, except that *(span.begin() + 5)
gets slightly worse.
This is because there are two not-quite-redundant preconditions on this operation, whether begin() + 5
is allowed, and whether operator*
is allowed. begin() + 5
's precondition is size() >= 5
. In particular, if size() == 5
, it is still okay to utter begin() + 5
because you just get to end()
. However, *iter
then needs to check if we're at end()
, at which point size() == 5
is rejected. Without being able to combine the two failure conditions into size() <= 5
, we get two branches instead of one.
Here's a godbolt link with a more extensive set of examples.
https://godbolt.org/z/K6Yz4M8ae
(CC @danakj. I suspect this missed optimization impacts Chromium's existing checked iterator too.)