No, p+1 is OK because you are allowed to make a pointer one past the end in C (so that common pointer loops work as expected) but you cannot dereference them.
So now I think it's the first optimization the one that is wrong. If you replace a variable with another, don't you need to keep information of the original variable?
I mean, if a==b and you do *a=1 you can replace it with *b=1, but then you need to keep the information that 'a' was written to, so other optimizations (the third one) don't think it wasn't. Or am I missing something else here?
Edit: sorry for the code block, otherwise the asterisks are removed.
It constructs a pointer to the "one past the end" element, but that is fine, and the original program never dereferences that pointer. Again: there is no UB in the original program.
https://c.godbolt.org/z/qe3f4K
`*(&i+1)` dereferences the pointer "one past the end" which is UB.