The branch instruction is available in various permutations, but in all circumstances it is essentially a goto. The target of a branch instruction is a label. Most branch instructions are conditional and based on a Boolean condition. For an unconditional goto, use the br instruction. Loop and other transfer-of-control statements in C# are implemented with some combination of branch instructions.
A conditional branch can be made with the brtrue and brfalse statements. The brtrue instruction branches on a true condition, whereas the brfalse branches on a false condition. These instructions consume a Boolean value, which must be placed on the evaluation stack prior to the statements.
Comparison instructions perform a comparison of the top two values of the evaluation stack. The two values are replaced on the evaluation stack with the result of the comparison, which is either true or false. The comparison should be between related types.
Table 14-4 lists the comparison instructions. In the table, assume that t2 is the top value on the evaluation stack, t1 is the next value (t2 is the last item placed on the evaluation stack and the next to be removed).
This sample code shows unconditional and conditional branching. It also contains an example of a comparison instruction:
.assembly extern mscorlib {} .assembly application {} .namespace Donis.CSharpBook { .class Starter { .method static public void Main() il managed { .entrypoint ldc.i4.3 ldc.i4.1 cgt brtrue greater ldstr "{0} is less than or equal to {1}" br end greater: ldstr "{0} is greater than {1}" end: ldc.i4.3 box int32 ldc.i4.1 box int32 call void [mscorlib] System.Console::WriteLine( string, object, object) ret } } }
As a convenience, branch and comparison instructions are combinable. The combined instruction compares the top two values of the evaluation stack and branches on the result. These are called comparative branching instructions. Instead of requiring two instructions to perform the test, only one instruction is needed.
Table 14-5 lists comparative branching instructions.
Table 14-5. Comparative branching instructions
Instruction | Description |
---|---|
beq | Branch on equal |
bne | Branch on not equal |
bge | Branch on greater than or equal |
bgt | Branch on greater than |
ble | Branch on less than or equal |
blt | Branch on less than |
bgt.un, blt.un, and bne.un | The unsigned version of these instructions |
Here is an example of a for loop in MSIL code. The loop increments the count from zero to five. The current count is displayed to the console in iterations of the loop. The sample code uses the short form of the branch instruction. As with all short-form instructions, the operand is limited to a single byte. The short form of branch instructions cannot jump to a label that is more than a one-byte offset from the beginning of the next instruction. If the branch offset is greater than one byte, ILASM generates a compiler error:
.assembly extern mscorlib {} .assembly application {} .namespace Donis.CSharpBook { .class Starter { .method static public void Main() il managed { .entrypoint .locals (int32 count) ldc.i4.0 stloc.0 br.s loop for: ldloc count ldc.i4.1 add dup stloc count call void [mscorlib] System.Console::WriteLine(int32) loop: ldloc count ldc.i4.5 clt brtrue.s for ret } } }
There are many ways to call methods. So far in this chapter, only the call and callvirt instructions have been shown in the sample code. Some instructions or actions, such as newobj, call a method implicitly. The newobj instruction calls a constructor, whereas the static constructor is called implicitly on the first access to the class or object.
The call instructions have the same general syntax:
callsuffix returntype [assembly] signature
The value of the returntype is placed on the evaluation stack by the method; assembly is the location of the method. If the method is in the current assembly, the assembly element can be omitted. The complete signature of the method is represented by signature.
This section lists the call instructions in MSIL.
The call instruction is intended for calling nonvirtual methods. The call instruction can be used with a virtual method. In this circumstance, the virtual method is called as a normal method, which is based on the class in which the method is declared rather than on the derived instance.
The callvirt instruction calls a virtual method. For nonvirtual methods, a nonvirtual call is conducted.
The calli instruction calls a function indirectly through a function pointer. Place each function parameter and then the function pointer on the evaluation stack. The calli syntax is slightly different from other call instructions. It does not include the target assembly or method name in the syntax, but it does include the return type and the signature other than the name. Use the ldftn instruction to place a function pointer for a particular method on the stack.
This sample code shows both the calli and ldftn instructions:
.assembly extern mscorlib {} .assembly application {} .namespace Donis.CSharpBook { .class Starter { .method static public void Main() il managed { .entrypoint ldstr "Donis" ldftn void Donis.CSharpBook.Starter::Name(string) calli void(string) ret } .method static public void Name(string) il managed { ldstr "Hello, {0}!" ldarg.0 call void [mscorlib] System.Console::WriteLine(string, object) ret } } }
The jmp instruction jumps from the current method to the target method and transfers the arguments. The caller and callee must have matching signatures, and the evaluation stack for the current method must be empty. The code after the jmp site in the calling function is abandoned.
Here is sample code of the jmp instruction:
.assembly extern mscorlib {} .assembly application {} .namespace Donis.CSharpBook { .class Starter { .method static public void Main() il managed { .entrypoint ldstr "Aloha!" call void Donis.CSharpBook.Starter::MethodA(string) ret } .method static public void MethodA(string) il managed { ldstr "Before jump" call void [mscorlib] System.Console::WriteLine(string) ldstr "In MethodA: {0}" ldarg.0 call void [mscorlib] System.Console::WriteLine(string, object) jmp void Donis.CSharpBook.Starter::MethodB(string) ldstr "After jump" call void [mscorlib] System.Console::WriteLine(string) ret } .method static public void MethodB(string) il managed { ldstr "In MethodB: {0}" ldarg.0 call void [mscorlib] System.Console::WriteLine(string, object) ret } } }
This program jumps from MethodA to MethodB. In MethodA, the instructions after the jmp instruction are orphaned. Therefore, the message "After jump" is not displayed. MethodB returns directly to Main, not MethodA.
The tail instruction is a prefix instruction and similar to the jmp instruction. However, the arguments must be loaded explicitly on the evaluation stack and the method signatures can be different. Otherwise, the functions are operationally equivalent.
Here is the syntax of the tail instruction:
tail callsuffix returntype [assembly] signature
18.189.195.34