ECMA 335 dicates that there’s a need for the conv.u
IL opcode for the conversion from managed pointers to unmanaged pointers.
Managed pointers that do not point to managed memory can be converted
(using conv.u or conv.ovf.u) into unmanaged pointers, but this is not
verifiable.
One can see this in the implementation of Unsafe.AsPointer<T>
:
public static void* AsPointer<T>(ref T value)
{
// ldarg.0
// conv.u
// ret
}
This is IL + 64bit disassembly code I got during debugging with a fixed
block in C# (which besides pinning also emits conv.u
)
fixed (int* unmanagedPointerFromManaged = &managedPointer) {
// ldloc.1 -> managedPointer
// stloc.s 4 -> this is pinned temp int&
// ldloc.s 4
// conv.u -> ???
// stloc.3 -> this is the unmanagedPointer
//mov rcx,qword ptr [rbp+50h] // managedPointer
//mov qword ptr [rbp+30h],rcx // save the address maybe temp
//mov rcx,qword ptr [rbp+30h] // load the address of the temp
//mov qword ptr [rbp+20h],rcx // maybe this is the conv.u..
//mov rcx,qword ptr [rbp+20h] // conv.u ?
//mov qword ptr [rbp+38h],rcx // this is unmanaged ptr
//nop
//rcx,qword ptr [rbp+38h]
//dword ptr [rcx],6
*unmanagedPointerFromManaged = 6;
}
In this specific example the conv.u
seems to compile into these two (a bit of unnecessary?) instructions.
mov qword ptr [rbp+20h],rcx
mov rcx,qword ptr [rbp+20h]
which just repeat the same two prior instructions, doing no “conversion” work.
Aren’t managed pointers (&s, byrefs) always a native int anyways?
Why do we need that conversion and what does the conversion really do?