During the recent discussion on clarifying non-integral pointer types, I noticed that ptrtoint
is extremely underspecified. The current LangRef wording just says
The ‘
ptrtoint
’ instruction convertsvalue
to integer typety2
by interpreting the pointer value as an integer and either truncating or zero extending that value to the size of the integer type. Ifvalue
is smaller thanty2
then a zero extension is done. Ifvalue
is larger thanty2
then a truncation is done. If they are the same size, then nothing is done (no-op cast ) other than a type change.
This is probably fine for most architectures where a pointer representation is just the address (although it might be a bit more interesting if you are using the high bits for extra metadata). However, with non-integral pointers we really need to clarify what this conversion does.
Which of the following behaviours should ptrtoint use if we have something like a fat pointer struct of four i64
s { base_addr; max_addr; offset; flags}
:
- Return the bitwise representation of the fat pointer as if we had stored it to memory as a
ptr addrspace(N)
and loaded back asi256
- Return the underlying address of the pointer, so in this case
base_addr+offset
. - Something else?
One problem with option 1 that I see when looking at the latest C standard draft
Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space
And the icmp eq ptr
semantics since right now say:
If the operands are pointer typed, the pointer values are compared as if they were integers.
This suggests to me that icmp eq ptr %a, %b
is equivalent to icmp eq i64 ptrtoint(%a), ptrtoint(%b)
and if we want icmp
to behave as defined by the C standard we must compare the underlying address here instead of the full bitwise comparison (which would always return false for fat pointers to adjacent objects). As far as I can tell this C requirement also does not hold for MTE tagged pointers where adjacent objects use different MTE tags and thus compare different despite having the same address.
My mental model of ptrtoint
so far has been option 2, i.e. discard all metadata and return the underlying address. I will upload a PR to update the LangRef to clarify ptrtoint
(and icmp eq/ne ptr
) once we have consensus on the desired semantics.
The main challenge I see with using the underlying address is that we don’t currently have a datalayout property for the address range of pointers and would need to add this for fat pointers. In the CHERI world we have been able to get by with using the index width (as that always happens to match the address range), but I believe AMDGPU fat pointers have a 48-bit address range with a 32-bit index. The simplest solution to this problem would be adding another datalayout component after the index width (I originally proposed this in ⚙ D135158 [DataLayout] Introduce DataLayout::getPointerIntegralSize(AS) but that review stalled).