It has a list of the guarantees provided to the compiler by ptr::add at doc.rust-lang.org/std/primitive..
It maps to an LLVM inbounds GEP rather than GEP. There's documentation on this at llvm.org/docs/LangRef.h. Guarantees result will be within the bounds of the object without wrapping.
Conversation
It can't cheat by wrapping all the around and back into the same object. It's a bit silly that the safe one is called wrapping_add because allowing wrapping is only one of the things it allows.
Note that ptr::wrapping_add is safe and ptr::add is unsafe due to the UB.
1
1
It's UB to deference a pointer outside the bounds of the object it was derived from regardless of which one you use. The purpose of ptr::add is that it's also undefined to create the pointer outside bounds of the object which enables optimizations even if load isn't right there.
1
Replying to
i need you to realize you linked me at the exact same part of rust docs that i, myself, linked. i was not asking why one is safe and the other is not, i was asking what optimization differences there are
2
llvm.org/docs/GetElemen is informative in a subtle way: because an inbounds gep cannot overflow, the compiler can make assumptions about ordering of pointers, among other things
1
Replying to
Main guarantees is that it's in-bound. ptr::wrapping_add method is poorly named. The main difference is that it permits the result to not be within the bounds of the object. Disallowing wrapping as part of inbounds is a bonus thing.
1
So, among many other things, it's guaranteed to not be a null pointer and null checks can be eliminated. A negative offset will go backwards within the object, a positive offset will go forwards within the object (up to one byte past the end) and a zero offset will stay the same.
2
Replying to
"a `negative offset` will go backwards within the object" is not necessarily true, and the entire reason i'm looking closely at these functions
2
if you have a 2^31+1 byte alloc in a 2^32-byte address space, 0x80000001 may be a valid offset that does not go backwards
2
1
Replying to
I'm listing out some of the properties guaranteed by inbounds GEP for optimizations. It's a much stricter variant of the usual C rules for signed integer arithmetic. It's the strict interpretation of the C standard's definition of pointer arithmetic used by Clang everywhere.
1
For example, in C:
char *foo(char *x) {
return x + 10;
}
Compile this with `clang foo.c -S -emit-llvm -o - -O2`.
The function `foo` is a guarantee that `x` is not NULL and is at least 10 bytes large. The result is at most 1 byte past the end of `x`. It's a promise.
If you use -fwrapv, Clang will generate it without inbounds, so -fwrapv does more than just relaxing the signed integer arithmetic rules.
Rust gives a choice for each case, with ptr::add being the usual approach. It's normal C-style pointer arithmetic. It's unusual to need more.
1
Rust defines the maximize size of an object as isize::MAX elsewhere, i.e. it would be undefined behavior to allocate an 2^31+1 byte object.
It's hard to avoid it being undefined in C due to ptrdiff_t overflow being undefined, but it doesn't assign responsibility to avoid it.

