This section contains only informative text. |
This Annex shows two complete examples written using ilasm. ANNOTATION To illustrate how CIL maps to high-level languages, these examples are annotated with near-equivalent programs in high-level languages. The program in section B.1 was written in Microsoft C#, and the program in section B.2 was written in Visual Basic. |
B.1 Mutually Recursive Program (with tail. Calls) The following is an example of a mutually recursive program that uses tail. calls. The methods below determine whether a number is even or odd. .assembly extern mscorlib { } .assembly test.exe { } .class EvenOdd { .method private static bool IsEven(int32 N) cil managed { .maxstack 2 ldarg.0 // N ldc.i4.0 bne.un NonZero ldc.i4.1 ret NonZero: ldarg.0 ldc.i4.1 sub tail. call bool EvenOdd::IsOdd(int32) ret } // end of method "EvenOdd::IsEven" .method private static bool IsOdd(int32 N) cil managed { .maxstack 2 // Demonstrates use of argument names and labels // Notice that the assembler does not convert these // automatically to their short versions ldarg N ldc.i4.0 bne.un NonZero ldc.i4.0 ret NonZero: ldarg N ldc.i4.1 sub tail. call bool EvenOdd::IsEven(int32) ret } // end of method "EvenOdd::IsOdd" .method public static void Test(int32 N) cil managed { .maxstack 1 ldarg N call void [mscorlib]System.Console::Write(int32) ldstr " is " call void [mscorlib]System.Console::Write(string) ldarg N call bool EvenOdd::IsEven(int32) brfalse LoadOdd ldstr "even" Print: call void [mscorlib]System.Console::WriteLine(string) ret LoadOdd: ldstr "odd" br Print } // end of method "EvenOdd::Test" } // end of class "EvenOdd" //Global method .method public static void main() cil managed { .entrypoint .maxstack 1 ldc.i4.5 call void EvenOdd::Test(int32) ldc.i4.2 call void EvenOdd::Test(int32) ldc.i4 100 call void EvenOdd::Test(int32) ldc.i4 1000001 call void EvenOdd::Test(int32) ret } // end of global method "main" ANNOTATION The following code simulates in C# the operations of the above program, although C# does not generate tail. for the calls, so the program fails on the final line with an out-of-stack exception. class EvenOdd { private static bool IsEven(int N) { if (N==0) return true; else return IsOdd(N-1); /* Should be tail. call */ } private static bool IsOdd(int N) { if (N==0) return false; else return IsEven(N-1); /* Should be tail. call */ } public static void Test(int N) { System.Console.Write(N); System.Console.Write(" is "); System.Console.WriteLine(EvenOdd.IsEven(N) ? "even" : "odd"); } static void Main(string[] args) { EvenOdd.Test(5); EvenOdd.Test(2); EvenOdd.Test(100); EvenOdd.Test(1000001); } } |
B.2 Using Value Types The following program shows how rational numbers can be implemented using value types. .assembly extern mscorlib { } .assembly rational.exe { } .class private sealed Rational extends [mscorlib]System.ValueType implements [mscorlib]System.IComparable { .field public int32 Numerator .field public int32 Denominator .method virtual public int32 CompareTo(object o) // Implements IComparable::CompareTo(Object) { ldarg.0 // "this" as a managed pointer ldfld int32 value class Rational::Numerator ldarg.1 // "o" as an object unbox value class Rational ldfld int32 value class Rational::Numerator beq.s TryDenom ldc.i4.0 ret TryDenom: ldarg.0 // "this" as a managed pointer ldfld int32 value class Rational::Denominator ldarg.1 // "o" as an object unbox value class Rational ldfld int32 class Rational::Denominator ceq ret } .method virtual public string ToString() // Implements Object::ToString { .locals init (class [mscorlib]System.Text.StringBuilder SB, string S, object N, object D) newobj void [mscorlib]System.Text.StringBuilder::.ctor() stloc.s SB ldstr "The value is: {0}/{1}" stloc.s S ldarg.0 // managed pointer to self dup ldfld int32 value class Rational::Numerator box [mscorlib]System.Int32 stloc.s N ldfld int32 value class Rational::Denominator box [mscorlib]System.Int32 stloc.s D ldloc.s SB ldloc.s S ldloc.s N ldloc.s D call instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendFormat(string, object, object) callvirt instance string [mscorlib]System.Object::ToString() ret } .method public value class Rational Mul(value class Rational) { .locals init (value class Rational Result) ldloca.s Result dup ldarg.0 // "this" ldfld int32 value class Rational::Numerator ldarga.s 1 // arg ldfld int32 value class Rational::Numerator mul stfld int32 value class Rational::Numerator ldarg.0 // "this" ldfld int32 value class Rational::Denominator ldarga.s 1 // arg ldfld int32 value class Rational::Denominator mul stfld int32 value class Rational::Denominator ldloc.s Result ret } } .method static void main() { .entrypoint .locals init (value class Rational Half, value class Rational Third, value class Rational Temporary, object H, object T) // Initialize Half, Third, H, and T ldloca.s Half dup ldc.i4.1 stfld int32 value class Rational::Numerator ldc.i4.2 stfld int32 value class Rational::Denominator ldloca.s Third dup ldc.i4.1 stfld int32 value class Rational::Numerator ldc.i4.3 stfld int32 value class Rational::Denominator ldloc.s Half box value class Rational stloc.s H ldloc.s Third box value class Rational stloc.s T // WriteLine(H.IComparable::CompareTo(H)) // Call CompareTo via interface using boxed instance ldloc H dup callvirt int32 [mscorlib]System.IComparable::CompareTo(object) call void [mscorlib]System.Console::WriteLine(bool) // WriteLine(Half.CompareTo(T)) // Call CompareTo via value type directly ldloca.s Half ldloc T call instance int32 value class Rational::CompareTo(object) call void [mscorlib]System.Console::WriteLine(bool) // WriteLine(Half.ToString()) // Call virtual method via value type directly ldloca.s Half call instance string class Rational::ToString() call void [mscorlib]System.Console::WriteLine(string) // WriteLine(T.ToString) // Call virtual method inherited from Object, via boxed instance ldloc T callvirt string [mscorlib]System.Object::ToString() call void [mscorlib]System.Console::WriteLine(string) // WriteLine((Half.Mul(T)).ToString()) // Mul is called on two value types, returning a value type // ToString is then called directly on that value type // Note that we are required to introduce a temporary variable // since the call to ToString requires // a managed pointer (address) ldloca.s Half ldloc.s Third call instance value class Rational Rational::Mul(value class Rational) stloc.s Temporary ldloca.s Temporary call instance string Rational::ToString() call void [mscorlib]System.Console::WriteLine(string) ret } ANNOTATION An interesting part of this example is its use of boxing and unboxing. In the method CompareTo, the third and fourth instructions are ldarg and unbox. That's because ldarg.1 is the argument o, which is a boxed object. To be able to use the ldfld instruction, you need the value type contained in the boxed object, so you need to call unbox to get a reference to the value type. If what you were passing were not of type Rational, unbox would give you an exception; otherwise it would give you the unboxed value and you could fetch the field out of it. Similarly, after the label TryDenom, there's another unbox, to get the denominator. Next, in the method ToString, there are two boxed int32s. We have loaded the numerator and denominator fields, which are 32-bit integers, Then we want to store the numerator in the variable N and the denominator in the variable D, which are of type object, so we have to box them. The next box operations are in main(). We want to take the Rational value Half and store it in H, which is of type object, so it must be boxed. Similarly, the Rational value Third must be boxed to store it in the object type T. In a high-level language, boxing and unboxing are handled by the compiler, as shown in the following equivalent Visual Basic code: Option Explicit On Option Strict On Module Module1 Private Structure Rational Implements System.IComparable Public Numerator As Integer Public Denominator As Integer Public Function CompareTo(ByVal o As Object) As Integer _ Implements System.IComparable.CompareTo 'Return 0 if I'm different from o, 1 if we're the same 'Assumes that rationals are stored in a canonical format If Me.Numerator <> CType(o, Rational).Numerator Then Return 0 If Me.Denominator <> CType(o, Rational).Denominator Then Return 0 Return 1 End Function Public Overrides Function ToString() As String Dim SB As New System.Text.StringBuilder, S As String, N As Object, D As Object S = "The value is: {0}/{1}" N = Me.Numerator D = Me.Denominator SB.AppendFormat(S, N, D) Return SB.ToString() End Function Public Function Mul(ByVal R As Rational) As Rational 'Multiplies two rationals, but does NOT put into canonical form Dim Result As Rational Result.Numerator = Me.Numerator * R.Numerator Result.Denominator = Me.Denominator * R.Denominator Return Result End Function End Structure Sub Main() Dim Half As Rational, Third As Rational, Temporary As Rational Dim H As Object, T As Object Half.Numerator = 1 Half.Denominator = 2 Third.Numerator = 1 Third.Denominator = 3 H = Half T = Third System.Console.WriteLine(CBool(CType(H, Rational) .CompareTo(H))) System.Console.WriteLine(CBool(Half.CompareTo(T))) System.Console.WriteLine(Half.ToString()) System.Console.WriteLine(T.ToString()) Temporary = Half.Mul(Third) System.Console.WriteLine(Temporary.ToString()) End Sub End Module |
|