Ninja Ferret

The random thoughts of a software developer

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:

blog comments powered by Disqus