The base pointer has an in bounds address of an allocated object, which means that it points into an allocated object, or to its end.
The phrase “an allocated object” here makes it sound like this could be any allocated object. Is that correct? What I would have expected is that it has to be the allocated object that the base pointer is derived from.
More concretely, consider this code – using C syntax for readability, but this is meant to represent LLV IR:
int a = 0, b = 0;
uintptr_t a_addr = (uintptr_t)&a;
uintptr_t b_addr = (uintptr_t)&b;
// Non-inbounds pointer arithmetic that produces a pointer
// derived from `a` with the address of `b`.
int *a2 = getelementptr(a, (b_addr - a_addr)/sizeof(int));
// Inbounds pointer arithmetic on `a2`.
int *a3 = getelementptr_inbounds(a2, 1);
Is a3 poison or not? Under LangRef as written, I would expect “no”, since a2 and a2 + 1 are inbounds if some allocated object, namely b. But it’s the wrong allocated object! a2 is derived from a and so that’s the only allocated object it can ever access, and so it’s the only allocated object that should ever matter. So probably a3 should be poison, and the LangRef should be updated?
Cc @nikic – do you know if any optimization cares either way?
I would think that using a2 / a3 for memory access would violate the LLVM IR pointer aliasing rules saying “Any memory access must be done through a pointer value associated with an address range of the memory access”, and in the example the a2/a3 pointer values (according to the rules) are associated with the storage of a while they point to the storage of b ?
Yeah that would definitely be UB, but that’s not the question I asked. I asked specifically about the value of a3.
If we want to talk about memory accesses, let me extend the example:
int a = 0, b = 0;
uintptr_t a_addr = (uintptr_t)&a;
uintptr_t b_addr = (uintptr_t)&b;
intptr_t diff = (b_addr - a_addr)/sizeof(int);
// Non-inbounds pointer arithmetic that produces a pointer
// derived from `a` with the address of `b`.
int *a2 = getelementptr(a, diff);
// Inbounds pointer arithmetic on `a2`.
int *a3 = getelementptr_inbounds(a2, 1);
// Move back to a.
int *a4 = getelementptr(a3, -diff-1);
// Now load.
int _val = *a4;
Is that load allowed? a4 is derived from a and points at the right thing (a+diff+1-diff-1 == a), so if no poison gets in the way then the answer is yes. But of course if a3 is poison then a4 is poison and loading from it is UB.