MSIL - 3. If Statements...
For my third post in the MSIL series I want to focus on a simple if statement, how do I represent the code
if (x != 0) using IL?
The source code for this can be found in my
subversion repository.
Implementing division
I want to take a look at the
Divide() method of the Calculator class:
public int Divide(int numerator, int denominator)
{
if (denominator != 0)
{
throw new ArgumentException("denominator cannot be zero",
"denominator");
}
return numerator/denominator;
}
This gives me the opportunity to show the implementation of conditional statements and the throwing of exceptions. What would this implementation generate in IL terms for handling the equality?
.method public hidebysig newslot virtual final
instance int32 Divide(int32 numerator,
int32 denominator) cil managed
{
// Code size 37 (0x25)
.maxstack 3
.locals init ([0] int32 CS$1$0000,
[1] bool CS$4$0001)
IL_0000: nop
IL_0001: ldarg.2
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000a: brtrue.s IL_001d
IL_000c: nop
IL_000d: ldstr "denominator cannot be zero"
IL_0012: ldstr "denominator"
IL_0017: newobj instance void [mscorlib]System.ArgumentException::.ctor(string,
string)
IL_001c: throw
IL_001d: ldarg.1
IL_001e: ldarg.2
IL_001f: div
IL_0020: stloc.0
IL_0021: br.s IL_0023
IL_0023: ldloc.0
IL_0024: ret
} // end of method CalculatorImplementation::Divide
The
if statement is executed between
IL_0001 and
IL000a. The first thing is to push onto the stack the demoninator (
ldarg.2) and the constant integer zero (
ldc.i4.0) then compare them using the equals comparison operator (
ceq), which pushes either a
1 onto the stack if it is true and
0 if it is false. As I am doing a
not equals comparison we then compare the result of the first comparison with
0 to find out if the two items do not match, the result of this equality is placed back onto the stack.
What do we know now?
brtrue.s IL_001d tells the compiler that if the calculation above (i.e. is the denominator not equal to zero) is true then jump past the exception throwing statements to the calculation part at position
IL001d that follows the standard pattern of the Add and Subtract methods described in my first post.
The resulting IL looks like:
private static void DefineDivideMethod(TypeBuilder typeBuilder, Type interfaceType)
{
var interfaceSubtractMethod = interfaceType.GetMethod("Divide");
var methodBuilder = typeBuilder.DefineMethod("Divide",
MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Final |
MethodAttributes.NewSlot |
MethodAttributes.HideBySig);
methodBuilder.SetReturnType(interfaceSubtractMethod.ReturnType);
var parameters = interfaceSubtractMethod
.GetParameters()
.Select(parameter => parameter.ParameterType)
.ToArray();
methodBuilder.SetParameters(parameters);
methodBuilder.InitLocals = true;
var il = methodBuilder.GetILGenerator();
var returnLabel = il.DefineLabel();
var trueLabel = il.DefineLabel();
il.DeclareLocal(typeof (int));
il.DeclareLocal(typeof (bool));
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Brtrue_S, trueLabel);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldstr, "denominator cannot be zero");
il.Emit(OpCodes.Ldstr, "demoninator");
il.Emit(OpCodes.Newobj, typeof (ArgumentException).GetConstructor(new[] {typeof (string), typeof (string)}));
il.Emit(OpCodes.Throw);
il.MarkLabel(trueLabel);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Div);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Br_S, returnLabel);
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(methodBuilder, interfaceSubtractMethod);
}
So there it is, a simple if statement. The IL is starting to look increasingly complicated and it is becoming very clear that trying to dynamically generate code for anything but simple methods is increasingly difficult. What I will go onto in the very next post is how to do a simple loop to complete the fundamentals of programming.
Tags: