https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97976
Peter Bisroev <peter at int19h dot net> changed: What |Removed |Added ---------------------------------------------------------------------------- Resolution|INVALID |--- Status|RESOLVED |UNCONFIRMED --- Comment #3 from Peter Bisroev <peter at int19h dot net> --- Hi Andrew, I was thinking about this a bit more and decided to try the loop in reverse in a more simplified test case. I know this test case demonstrates a corner case that no one will probably implement. However I still think it merits some further investigation just in case this affects some other parts of the optimizer. You can see this code below: //////////////////// int containsBackwards(const uint8_t* p, uint8_t target) { for (; p; --p) { if (*p == target) { return 1; } } return 0; } const uint8_t* findBackwards(const uint8_t* p, uint8_t target) { for (; p; --p) { if (*p == target) { break; } } return p; } //////////////////// Function containsBackwards(), while searching backwards, should return 1 if target byte is found and 0 if it was not and p points to address 0. Function findBackwards() is similar but returns the address of the first byte that matched the target or pointer to address 0 if a match was not found. Unless I am mistaken, the sample code above is not hitting any undefined behavior such as dereferencing a NULL pointer and there is a well defined loop terminating condition. This is the code that is generated with gcc trunk and gcc 9.1: //////////////////// containsBackwards(unsigned char const*, unsigned char): xor eax, eax test rdi, rdi setne al ret findBackwards(unsigned char const*, unsigned char): test rdi, rdi jne .L5 jmp .L6 .L8: sub rdi, 1 .L5: cmp BYTE PTR [rdi], sil jne .L8 mov rax, rdi ret .L6: xor eax, eax ret //////////////////// I would have expected both functions to be compiled to nearly the same code, but the looping is missing in containsBackwards() function. And unless I am mistaken gcc 8.3 generates the output that we would expect to see here. You can see this example in Compiler Explorer here: https://godbolt.org/z/hWE4xs What is also interesting, if we replace uint8_t by uint32_t in containsBackwards() function it will work with gcc 9.3 but with gcc 10.1 it will behave in exactly the same way as above returning the result based on the validity of the p pointer. Additionally, thinking about my first test case. I know it was technically in the undefined territory, but just for my personal understanding, is the compiler allowed to assume that pi can never become null like you have suggested? In theory it can overflow and become 0 and the loop will terminate but unfortunately I am not 100% certain of what the standard's view on this is. In addition, can the compiler assume that callback(*pi) will never return true? What are your thoughts? Thank you! --peter