Do you mean unsigned branches? JB/JBE/JA/JAE instead of JL/JLE/JG/JGE? Are there actually code patterns where it's preferable to just JE/JNE? AFAIK, computing a pointer outside of the underlying array's boundaries (except for computing the one-past-the-last-element pointer) is UB so e.g.
for (SomeStruct * curr = p, * end = &p[N]; curr < end; curr += 2) {
// processing the pair of curr[0] and curr[1], with care
// in case when curr[1] doesn't exist
}
is an invalid optimization of
for (SomeStruct * curr = p, * end = &p[N]; curr != end; ) {
// processing the pair of curr[0] and curr[1], with care
// in case when curr[1] doesn't exist
if (++curr != end) { ++curr; }
}
> Do you mean unsigned branches? JB/JBE/JA/JAE instead of JL/JLE/JG/JGE?
Yeah, that's what I meant. Thanks for speaking more clearly.
> Are there actually code patterns where it's preferable to just JE/JNE?
That's a good point, and sidesteps the issue of pointer signedness.
I think sometimes JE / JNE isn't enough. For example, if you want to process a buffer in reverse order using pointer arithmetic:
/* p starts off one past the end of the buffer */
char *p = buffer + bufsize;
while (--p >= buffer) {
/* ... */
}
I'm not sure if this would technically be undefined behavior, though, as the C standard only explicitly permits computing a pointer one past the end of the array, and other out-of-bounds computations are undefined, IIRC.
In practice, I don't think any compiler would miscompile this.