https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97225
Bug ID: 97225 Summary: Failure to optimize out conditional addition of zero Product: gcc Version: 10.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: osandov at osandov dot com Target Milestone: --- For the following code: #include <stddef.h> struct vector { int *data; size_t size; }; int *vector_end(struct vector *vec) { return vec->data + vec->size; } GCC 10.2.0 on x86-64 generates the following code (same on -O2, -O3, and -Os): vector_end: movq 8(%rdi), %rdx movq (%rdi), %rax leaq (%rax,%rdx,4), %rax ret However, vector_end() needs to handle empty vectors represented as { NULL, 0 }. Pointer arithmetic on a null pointer is undefined behavior (even NULL + 0, as far as I can tell from the C standard), so the correct code is: int *vector_end(struct vector *vec) { if (vec->size == 0) return vec->data; return vec->data + vec->size; } I'd expect this to generate the same code, but GCC 10.2.0 generates a conditional move with -O2 and -O3: vector_end: movq 8(%rdi), %rdx movq (%rdi), %rax testq %rdx, %rdx leaq (%rax,%rdx,4), %rcx cmovne %rcx, %rax ret And a branch with -Os: vector_end: movq 8(%rdi), %rdx movq (%rdi), %rax testq %rdx, %rdx je .L1 leaq (%rax,%rdx,4), %rax .L1: ret Clang 10.0.1, on the other hand, generates the same code with and without the size check (oddly enough, it also falls back to a conditional move if the size member is an int or unsigned int instead of size_t/unsigned long): vector_end: # @vector_end movq 8(%rdi), %rax shlq $2, %rax addq (%rdi), %rax retq Can GCC avoid the conditional move/branch here?