2016-01-16 - In IL: Variables and Types
In IL
- Part 1 - Introduction
- Part 2 - Variables and Types
- Part 3 - Variables in Visual Basic .NET
- Part 4 - Instructions and the Stack
- Part 5 - Volume of a Cylinder (Operations)
- Part 6 - Branching Instructions
- Part 7 - Largest of Two Numbers (if-else)
- Part 8 - Largest of Three Numbers (If-ElseIf-Else)
- Part 9 - Switch instruction
- Part 10 - Grade Analyser (switch)
- Part 11 - Prize Calculator (switch-2)
- Part 12 - VB Grade Analyser (Select)
- Part 13 - Loop Instructions
- Part 14 - Print the Alphabet (while)
- Part 15 - Print the Alphabet (do while, for)
- Part 16 - Print the Alphabet (Do Until)
- Part 17 - Print the Alphabet (break, continue)
- Part 18 - Array Instructions
- Part 19 - Summing Arrays
- Part 20 - Other Instructions
- Part 21 - Assemblies
- Part 22 - Class Definitions
- Part 23 - C# Classes and Structs
- Part 24 - VB Classes, Modules and Structures
- Part 25 - Field Definitions
- Part 26 - Field Declarations
Variables are important to any program as they determine where information gets stored. Types are equally important as they determine the format of that information. So to start this investigation off we are going to look at how and where information is stored in an IL program. The following C# program declares a bunch of variables and then writes the string representation of those variables to the screen.
If you compile this program and then look at the generated IL code using ildasm or ilspy you will see something like the following (Note that you may need to compile it in debug mode to stop the compiler from optimizing out some variables).
Now there is a lot there but we are just interested in is how variables are declared so we are going to ignore most of it for now. Line 63 to 79 contains what we are interested in.
.locals is an IL directive that is used to declare the variables of a method. The init keyword indicates that these variables should be initialized to their default values. The declaration of local variables is very similar to how arrays are declared in many languages. It starts with a number in square brackets that denotes the variable’s index. Next there’s a type identifier which denotes the type of the variable. Finally there is the optional name of the variable. IL instructions reference variables either by their index or their name which gets translated to the index when the program is assembled.
If you look at the rest of the method you can see these indexes being used. For example on line 122 we see “ldloc.0” followed by “call void [mscorlib]System.Console::WriteLine(bool)”. In the C# code we call Console.WriteLine on line 27 and pass it a bool argument when we go to print the b variable. Looking in the locals array we see “[0] bool b” on line 63 which we know is the declaration of the b variable with index 0 and type bool. So “ldloc.0” is likely doing something with the b variable so that it can be printed by a call to Console.WriteLine.
Since the .locals directive contains the name of the variable it is fairly easy to map C# types to IL types. bool is bool, char is char, float32 is float, int64 is long etc. These types are built-in types that are inherently understood by the virtual machine. Some are more complicated though like the C# decimal type declared as “valuetype [mscorlib]System.Decimal” and ArrayList declared as “class [mscorlib]System.Collections.ArrayList”. These are user defined types described in assemblies that can be accessed by the virtual machine to look up their definition. Square brackets surround the name of the assembly in which the type is defined. Following the square brackets is the fully-qualified name (Includes but the name of the type and the namespace in which it is defined) of the type.
The types that start with “valuetype” are user defined value types. A value type variable directly contains the data of that variable. The types that start with “class” are user defined reference types. Reference types contain references to a location where the data of that variable is stored. A value type variable can be converted to a reference type variable through a process known as boxing. This involves copying the variable’s data to another location and then creating a reference type variable that references that location. Every value type has a corresponding reference type. The reverse is not generally the case.
The built-in types can also be classified as either value or reference types. The following table describes some of the built in types and specifies what kind of type they are.
Built-in Type | Description | Kind |
---|---|---|
bool | True/false value | Value type |
char | Unicode 16-bit char. | Value type |
float32 | IEC 60559:1989 32-bit float | Value type |
float64 | IEC 60559:1989 64-bit float | Value type |
int8 | Signed 8-bit integer | Value type |
int16 | Signed 16-bit integer | Value type |
int32 | Signed 32-bit integer | Value type |
int64 | Signed 64-bit integer | Value type |
uint8 | Unsigned 8-bit integer | Value type |
uint16 | Unsigned 16-bit integer | Value type |
uint32 | Unsigned 32-bit integer | Value type |
uint64 | Unsigned 64-bit integer | Value type |
object | Base of all types | Reference Type |
string | Unicode string | Reference Type |
Next time we will see how IL variables work with a Visual Basic .NET program.
Comments: