cpointersgccpointer-arithmetic

Invalid optimization for pointer comparison in GCC? (introduced in GCC 7.1)


I have a puzzling problem regarding optimization and pointer equality that seems to happen only in GCC from version 7.1 and higher until the latest GCC version.

The Problem can be shown with one simple example:

#include <stdio.h>
#include <stddef.h>

int main() {
    int test1[3] = {0};
    int test2[3] = {0};
    ptrdiff_t diff = test2 - test1;
    int *test3 = test1 + diff;

    printf("%p == %p = %d\n", test1 + diff, test2, test1 + diff == test2);
    printf("%p == %p = %d\n", test1 + diff + 2, test2 + 2, test1 + diff + 2 == test2 + 2);
    printf("%p == %p = %d\n", test3 + 2, test2 + 2, test3 + 2 == test2 + 2);

    printf("\n\n");

    printf("%p == %p = %d\n", &(test1 + diff)[0], &test2[0], &(test1 + diff)[0] == &test2[0]);
    printf("%p == %p = %d\n", &(test1 + diff)[2], &test2[2], &(test1 + diff)[2] == &test2[2]);
    printf("%p == %p = %d\n", &test3[2], &test2[2], &test3[2] == &test2[2]);
    
    return 0;
}

The code performs pointer arithmetic between the two arrays. The goal is to find the Element in array2 at the same offset in array1. diff is the offset of the first elements of the arrays in memory. The problem only occurs with optimization set to -O1 or higher.

The expected output would be for all checks to be = 1 however this program will print

0x7fff4aa00e78 == 0x7fff4aa00e78 = 1
0x7fff4aa00e80 == 0x7fff4aa00e80 = 0
0x7fff4aa00e80 == 0x7fff4aa00e80 = 1


0x7fff4aa00e78 == 0x7fff4aa00e78 = 1
0x7fff4aa00e80 == 0x7fff4aa00e80 = 0
0x7fff4aa00e80 == 0x7fff4aa00e80 = 1

If I perform the check of the pointer to the first element directly it shows as equal but if i try to compare the nth element in the array the comparison shows as false, even though the address is clearly the same.

More interesting even, if i store the result of the arithmetic operation in a new pointer and compare the array elements from there, it shows up as equal again.

I have searched far and wide and have not found a suitable explanation for this problem other than gcc performing an invalid optimization here. The closest i could find was described in this Blogpost http://kristerw.blogspot.com/2016/12/pointer-comparison-invalid-optimization.html. However the problem in the Blog targets the "one element after" specifically which is not the case in my problem.

I could find no other compiler that exhibits this exact problem (tested with several MSVC and clang versions). Compiling with GCC 6.5 shows the correct output.


Solution

  • Pointer arithmetic in C has always only been allowed to get carried out inside an array. Where single variables like structs etc (scalars) can get treated like an array of just one single item.

    Doing pointer arithmetic outside the bounds of an array is undefined behavior - anything can happen. And as we can see from your example, this appears to be the perfect example of "anything happens". What is undefined behavior and how does it work?

    Specifically, C23 6.5.7 (additive operators) says:

    If the pointer operand and the result do not point to elements of the same array object or one past the last element of the array object, the behavior is undefined.