During the transition to opaque pointers (see https://llvm.org/docs/OpaquePointers.html for context) a somewhat recurring issue is the choice of source element types for getelementptr instructions. Consider these two GEPs:
getelementptr [[i32 x 10], x 10], ptr %p, i64 0, i64 1, i64 0
getelementptr i8, ptr %p, i64 40
If I counted correctly, these two GEPs perform the same address calculation. With typed pointers, they differed in their source and result pointer types. With opaque pointers, these GEPs are now strictly equivalent.
Various transforms in LLVM need to generate a GEP with a certain offset from a base pointer. In order to minimize bitcasts, they will usually try to use the source pointer element type as the GEP source element type and try to find a sequence of GEP indices that encodes the desired offset. If they can’t do that, they fall back to bitcasts to i8*, doing an i8 GEP and bitcasting to the result pointer type (or give up altogether).
The most sophisticated and complete implementation of this exists in SROA, which will determine the optimal GEP to generate in some three hundred lines of code: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Scalar/SROA.cpp#L1420-L1714
Now, with opaque pointers, doing this is entirely pointless. We can always simply generate a “getelementptr i8, ptr %Base, i64 Offset” without introducing bitcasts. With opaque pointers, the basic i8 GEP representation is already the optimal one.
In addition to that, generating something other than an i8 GEP requires us to pick an appropriate source element type. With typed pointers, that was simply the pointer element type. With opaque pointers, doing this requires some kind of heuristic guess. It would be possible to recurse through operations (selects, phis, etc) until we hit a value with an associated type, such as a GEP, alloca or global. However, doing this is not reliable, because there might not be such a value (e.g. if it is based on a function argument). There is no guarantee that this would arrive at the same GEP type as typed pointers did, and I think doing this goes against the spirit of opaque pointers.
My proposal here is that in opaque pointers mode, LLVM should consider i8 GEPs canonical for GEPs with constant offsets. We should not attempt to “guess” a good GEP type to use, and we should not try to generate complex GEP structures if a simple one will do. I don’t think there’s really any disadvantages to this, apart from the fact that it makes the discrepancy between typed and opaque pointer mode larger.
Context for this mail is https://reviews.llvm.org/D109259, where Arthur requested this to be discussed on llvm-dev.