El misterioso conv.u

Este será casi con seguridad mi último post del 2006, pero eso no lo puedo asegurar ya que cuando me pica la curiosidad, siento esa irresistible necesidad de saciarla. Y eso es lo que me ha ocurrido esta mañana. Como había comentado antes no había usado punteros en c# y este hecho me ha picado, de modo que he tratado ver como son manejados por el CLR.


Bien, todo era normal hasta que he visto este código IL en donde no entendía el misterioso conv.u,  el código que estaba usando es el siguiente:


  public static unsafe float TestPointer2()
        {
            float f = -128.625f;
            int* i = (int*) &f;
 
            float* fp = (float*)i;
            return *fp;
        }
 

Mi sesión de depuración:

!clrstack -a
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0xb7c (2940)
ESP EIP
0012f3f4 00e600ed TestCastPointer.Test.TestPointer2()
LOCALS:
0x0012f400 = 0x00000000
<CLR reg> = 0x00000000
<CLR reg> = 0x00000000
0x0012f3f4 = 0x00000000

0012f444 00e6009a TestCastPointer.Program.Main(System.String[])
PARAMETERS:
args = 0x01321b5c

0012f69c 79e88f63 [GCFrame: 0012f69c]

!ip2md 00e600ed
MethodDesc: 00973078
Method Name: TestCastPointer.Test.TestPointer2()
Class: 009713d0
MethodTable: 00973090
mdToken: 06000003
Module: 00972c14
IsJitted: yes
m_CodeOrIL: 00e600c0

!dumpil 00973078
ilAddr = 0040208c
IL_0000: nop
IL_0001: ldc.r4 -128.625000 //Carga (push) el valor float (4bytes) en el stack
IL_0006: stloc.0 //Saca del stack y carga en la variable 0
IL_0007: ldloca.s VAR OR ARG 0 //Carga la dirección de la variable local existente
//en la dirección (0) en el stack de evaluación.

IL_0009: conv.u //Aquí este misterioso conv.u que se supone convierte
//el valor del stack en un unsigned int

IL_000a: stloc.1 //Saca el valor (pop) del stack y lo carga en var 1

Estado del stack
0x0012f3f4 = 0xc300a000 (-128,625f)
CLR reg = 0x0012f3f4 (var 1)
IL_000b: ldloc.1                    //Carga la var 1 en el stack de evaluación
IL_000c: stloc.2 //Saca el valor (pop) del stack y lo carga en var 2
IL_000d: ldloc.2 //Carga la var 2 en el stack de evaluación
IL_000e: ldind.r4 //Carga en la pila un float indirectamente
IL_000f: stloc.3
IL_0010: br.s IL_0012
IL_0012: ldloc.3
IL_0013: ret

Obviamente, hay muchas cosas que todavía no entiendo del IL y menos el ensablador que es generado por el JIT, pero de eso hablaré el año que viene. LA cuestión es la siguiente, es ese conv.u. He puesto como se queda el estado de la pila del CLR tras ejecutar el IL_000a. La reflexión que me he hecho es la siguiente; si lo que hemos colocado en el stack es una dirección de memoria, en este caso la de la variable f, para que quiero convertirla a un int, ¿no es la dirección de memoria un int32? 

Tras darle algunas vueltas he visto el código que genera el JIT, ¿donde esta el conv.u?

    25:             float f = -128.625f;
0000002e mov dword ptr [ebp-3Ch],0C300A000h //carga el valor en [ebp-3Ch] (f)
26: int* i = (int*) &f;
00000035 lea eax,[ebp-3Ch] //carga en el acumulador la dirección de f
00000038 mov edi,eax //mueve el acumulador al índice de destino (edi)
27:
28: float* fp = (float*)i;
0000003a mov esi,edi //mueve el índice de destino al índice fuente (esi)
29: return *fp;
0000003c fld dword ptr [esi] //carga el contenido del índice fuente como float
0000003e fstp dword ptr [ebp-48h] //almacena el contenido del puntero base – 48h
00000041 nop
00000042 jmp 00000044
30: }
00000044 fld dword ptr [ebp-48h] //prepara el stack para la devolución cargando el float

Entonces ha sido cuando leyendo el Ecma-335 (1.1.4.2 Managed Pointers) me he encontrado con esto:

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.

¿tendrá que ver con esto?, yo creo que me quedo aquí.

Un comentario en “El misterioso conv.u”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *