Note that the use case here is for llvm IR that the Rust compiler generates. It seems very hard in general to know if two mutable references point to items in the same allocation.
Note that the use case here is for llvm IR that the Rust compiler generates. It seems very hard in general to know if two mutable references point to items in the same allocation.
> Does the standard not allow you to pass any pointers which could be aliased based on arbitrary pointer arithmetic
With the right hackish arithmetic, all pointers could be aliased with pointer arithmetic in C. Using `restrict` says your code won't do that so the compiler can optimize more.
> It seems very hard in general to know if two mutable references point to items in the same allocation.
Yeah, and in the limit it's impossible for C. It's not the compiler's job to figure it out, it's the code's job to uphold the promise.
It's different with Rust because you've got mutable/exclusive or immutable/shared promises from the borrow checker. And Rust doesn't let you dereference raw pointers outside of an unsafe block.
It seems to me that mislabeling your pointers `restrict` in C is akin to doing reckless operations inside of `unsafe` blocks in Rust. In both cases, you told the compiler "trust me, I know what I'm doing".
This is not true. The behaviour is undefined in C if pointer arithmetic results in crossing the beginning or end of an object. If we have int a, b;, then the behaviour is undefined if we do &b - &a even if we never use the result to try to reconstruct &b from &a.
> Using `restrict` says your code won't do that so the compiler can optimize more.
`restrict` covers objects modified through one pointer and accessed through another. It does not cover pointers that point to the same object in general; if only one is used to access the object, or if both are used only for reading, all is fine. (It's actually a little bit more complicated than I'm presenting here, but not in a way that's relevant right now.)
Yes, it's technically UB. But which operation could any C compiler disallow in practice: converting from pointer to integer, arithmetic on integers, or converting from integer to pointer?
int a[2], b[2];
int offset(void) { return b - a; }
int check(void) { return &a[1] + offset() == &b[1]; }
The function check() is optimised by GCC to return zero at -O1 optimisation level or higher, because it reasons that no matter how offset() is implemented, either the addition is undefined, or the comparison results in false.(Note that GCC does this even in a few cases where it is unclear whether the optimisation is valid. The example I provided is slightly more complicated than I would have liked to avoid that issue; the optimisation is definitely valid in this example.)
However, your code doesn't actually address my comment about conversions and arithmetic. Where does this code code fail?
int a[2], b[2];
uintptr_t x = (uintptr_t)a;
uintptr_t y = (uintptr_t)b;
uintptr_t offset = y - x;
int* p = (int*)(x + offset);
printf("b == p: %d\n", b==p);
I didn't try _every_ compiler on Godbolt, but I didn't see it misbehave anywhere I did try.edit: changed to uintptr_t
Yeah, but I did say: "which operation could any C compiler disallow in practice: converting from pointer to integer, arithmetic on integers, or converting from integer to pointer?"
If C/C++ compilers keep breaking pointer arithmetic in the game of exploiting undefined behavior for optimizations, people are going to start doing pointer arithmetic with integers when they need it. And they do need it sometimes, for debuggers, profilers, memory checkers, JITs, garbage collectors, shared memory, and so on.
If I had used `uintptr_t` instead of `ssize_t`, I think it's even compliant as far as wraparound goes.
edit: Note I changed the code to use uintptr_t
If I could, I would disallow integer to pointer---there are (in my opinion) better ways to address a hardware register than to do
unsigned char *uart = (unsigned char *)0xdeadbeef; /* [1] */
But I don't think it will go away any time soon in C (Over 30+ years later, and we're still a few years out from 1's compliment and signed magnitude integers being deprecated).[1] Here's one way using NASM:
global uart
absolute 0xdeadbeef
uart resb 1
Then in C code: extern unsigned char uart;Yeah, and I know that's why the C standard has so much slop in it. They want to keep the window of conformance open for past or future architectures.
> If I could, I would disallow integer to pointer [...]
It's not just about 0xDeadBeef hardware addresses, and I don't think anyone is talking about breaking assembly language.
However, C has unions and Rust has enums. You want those to share the same piece of memory in the fewest bytes you can get away with. Some interpreters, written in C, even use nan-tagging to store the important 48 bits of a pointer in the 52 bits of payload with double precision floats.
You don't have to like it, and you can discourage your children or coworkers from doing it, but there are legitimate reasons for all of this. The examples look ugly because they're short and stupid.
And nobody has even mentioned that `A[i] == (A + i) == (i + A) == i[A]` is going to continue to be valid in C. https://stackoverflow.com/a/381549
Any sort of pointer arithmetics in the presence of restrict pointers seems like a giant footgun.
As far as I can read the standard, it does not forbid aliasing `restrict` pointer as long as these pointers are not used to access anything. Here y2 aliases with x, but at no point is anything accessed through y2 (whether reading or writing). So it I feel like it respects the letter of the law.