Introducción al lenguaje ensamblador IL de .NET [Parte 2]

[ <-- Introducción al lenguaje ensamblador IL de .NET [Parte 1] ]

El Stack de evaluación

El stack de evaluación puede ser considerado como un stack normal como otro cualquiera, sin embargo este stack es únicamente usado para guardar información justo antes de ejecutar una instrucción. Sabemos que la información guardada en memoria cuando podemos tener acceso a realizar alguna operación sobre ella. Lo mismo pasa si movemos esa información a los registros en un lenguaje ensamblador antes de invocar alguna instrucción o interrupción. De la misma manera que tenemos que mover la información (en el caso del primer tutorial fue una cadena de texto) al stack antes de procesarla (para mostrar esa cadena en pantalla). Al principio de nuestro método ‘main’ (véase figura 1.1 en la anterior entrada), nos damos cuenta que tuvimos que almacenar información a lo largo de nuestro método. Por ello usamos la directiva .maxstack, si hubiéramos indicado la directiva con un número tres (.maxstack 3), entonces el JIT hubiera creado un stack con 3 celdas para su uso posterior en el método. Ojo, esto no significa que únicamente podemos cargar 3 valores en la vida de nuestro método, simplemente significa que podemos albergar 3 valores a la vez como máximo. Una vez que el método finaliza, el stack y sus valores se eliminan. Así es como funciona el recolector de Basura en .NET. Además, no hay limitación en cuanto al tipo de dato que podemos usar para almacenar en el stack. Podremos mover cualquier tipo de dato (cadenas, enteros, objetos, etc) al stack siempre que queramos.

Tomemos otro ejemplo que nos aclarará el concepto del stack de evaluación:

//Sumar.il
//Suma dos números

.assembly extern mscorlib {}

.assembly Sumar
{
    .ver 1:0:1:0
}
.module Sumar.exe

.method static void main() cil managed
{
    .maxstack 2
    .entrypoint

    ldstr "La suma de 50 y 30 es = "
    call void [mscorlib]System.Console::Write (string)

    ldc.i4.s 50
    ldc.i4 30
    add
    call void [mscorlib]System.Console::Write (int32)
    ret
}
Figura 1.3 – Sumar.il, sumando dos números predefinidos

El trozo de código encima de nuestro método main es el mismo que en el ejemplo anterior, el cual ya explicamos. Únicamente ha cambiado el nombre del módulo. En este código lo importante a discutir es el .maxstack 2, el cual indica al JIT a preparar suficiente espacio en memoria para que podamos guardar dos valores. Entonces cargamos la cadena en el stack y la imprimimos. Lo próximo que hacemos es cargar dos enteros en memoria a la vez (usando las instrucciones ldc.i4.s y ldc.i4), usar la instrucción de suma y finalmente mostrar por pantalla el resultado entero que hemos obtenido. La instrucción de suma usada (add) tomará del stack de evaluación dos valores (siempre que sean encontrados), los sumará y guardará el resultado al comienzo del stack (es decir, un ‘push’). Justo después de la instrucción de suma, encontramos otra instrucción llamada Write que escribirá algo por pantalla. Este método requiere que haya un valor guardado en el stack. En este caso requerirá que sea del tipo entero. Si lo encuentra, entonces escribirá su valor por pantalla o en caso contrario levantará un error.

No os liéis con ldc.i4.s y ldc.i4, ambos representan un tipo de dato entero (integer), pero el primero es un único byte y el segundo son cuatro bytes.

Espero que hayáis entendido la forma de usar el stack de evaluación y como funciona. Ahora veremos los tipos de datos del lenguaje ILAsm como una parte fundamental del mismo.

Tipos de Datos ILAsm

 

Al igual que cuando aprendes cualquier otro lenguaje, hay que hablar sobre sus tipos de datos soportados. Este caso es igual. Tómate un tiempo para observar la tabla de abajo (figura 1.4) para aprender cuales son los tipos de datos más comunes en este lenguaje, pero antes, me gustaría añadir una cosa. No hay consistencia en la definición de los tipos de datos en los diferentes lenguajes de .NET. Por ejemplo para indicar un tipo de dato entero (32 bit) en VB.NET usamos ‘Integer‘ pero en C# y en VC++ no es así, aunque luego ambos casos se representen usando System.Int32. Además, necesitamos tener en cuenta si es un tipo básico con la especificación del Lenguaje común (CLS, Common Language Specification) o no. Como por ejemplo el tipo de dato UInt32 no está reconocido por VB.NET por ello no es básico CLS.

Bien, empecemos por aprender la tabla que nos proporciona los nuevos nombres para los tipos de datos de ILAsm:

Nombre en IL Tipo Base en .NET Significado (Descripción) Básico CLS
Void Sin valor, solo usado como tipo de retorno No
Bool System.Boolean Booleano No
Char System.Char Carácter (16-bit unicode) No
int8 System.SByte Entero 1-byte (con signo) No
int16 System.Int16 Entero 2-byte (con signo) No
int32 System.Int32 Entero 4-byte (con signo) Si
int64 System.64 Entero 8-byte (con signo) Si
native int System.IntPtr Entero (con signo) Si
unsigned int8 System.Byte Entero 1-byte (sin signo) Si
unsigned int16 System.UInt16 Entero 2-byte (sin signo) Si
unsigned int32 System.UInt32 Entero 4-byte (sin signo) No
unsigned int64 System.UInt64 Entero 8-byte (sin signo) Si
native unsigned int System.UIntPtr Entero (sin signo) Si
Float32 System.Single Coma flotante de 4-byte No
Float64 System.Double Coma flotante de 8-byte No
object System.Object Tipo objeto Si
& Puntero administrado Si
* System.IntPtr Puntero no administrado Si
typedef System.Typed Reference Tipo especial que contiene datos y explícitamente indica su tipo Si
Array System.Array Array Si
string System.String Cadena de texto Si
Figura 1.4 – Tipos de Datos en ILAsm

También tenemos abreviaciones para los tipos de datos en ILAsm como por ejemplo .i4, .i4.s, .u4, etc como usamos en el ejemplo anterior. Los tipos de datos mencionados en la tabla de arriba muestran los tipos de datos son reconocidos en ILAsm y además menciona cuales son tipos básicos CLS y cuales no. Entonces, teniendo en mente esa información, podremos llamar a cualquier función como esta:

call int32 FuncionEjemplo (string, int32, float64) 

Lo cual significa, que la función ‘FuncionEjemplo‘ devuelve un valor del tipo int32 (System.Int32) y toma tres valores como parámetros que son del tipo string (System.String), int32 (System.Int32) y float64 (System.Double) respectivamente. Toma nota que estos son datos básicos en el CLR y en ILAsm. Cuando estés interesado en tratar tipos de datos que no son básicos (definidos por el usuario) entonces el código podrá lucir como esto:

//Código en C#
ColorTranslator.ToHtml(Color);

//Código en ILAsm
call instance string [System.Drawing]System.Drawing.ColorTranslator::ToHtml(valuetype [System.Drawing]System.Drawing.Color)

Toma nota que hemos definido explícitamente los tipos de dato en los parámetros y incluyendo además el espacio de nombre (namespace) donde el tipo de dato reside y una palabra clave que indicará al JIT que estamos referenciando a un tipo de dato no básico (valuetype).

Esto lo dejaré más claro en la próxima entrega del tutorial, donde trataremos más intensamente con los tipos de datos. También veremos una de las bases de cualquier lenguaje: Condiciones y bucles.

 

[ Introducción al lenguaje ensamblador IL de .NET [Parte 3] –> ]

Advertisement

2 Responses to Introducción al lenguaje ensamblador IL de .NET [Parte 2]

  1. Pingback: Introducción al lenguaje ensamblador IL de .NET [Parte 1] « El Blog de JuDelCo

  2. Pingback: Introducción al lenguaje ensamblador IL de .NET [Parte 3] « El Blog de JuDelCo

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Seguir

Get every new post delivered to your Inbox.

Únete a otros 228 seguidores