The branch instruction is available in various permutations, but in all circumstances it is essentially a goto statement. The target of a branch instruction is a label. Most branch instructions are conditional and based on a comparative expression or a Boolean condition. For an unconditional goto, use the br instruction. Loop and other transfer-of-control statements in C# are implemented as 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 should be on the evaluation stack.
Compare instructions perform a comparison on 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 11-7 lists the compare instructions. In the table, assume that t2 is top value on the evaluation stack, and t1 is the second value. t2 is the last item placed on the evaluation stack and the next to be removed.
Instruction |
Comparison |
---|---|
ceq |
t1 equal to t2 |
cgt |
t1 greater than t2 |
clt |
t1 less than t2 |
cgt.un and clt.un |
Unsigned or unordered version of the comparison operations |
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 {1}" br end greater: ldstr "{0} is greater than {1}" end: ldc.i4.3 box int32 ldc.i4.m1 box int32 call void [mscorlib] System.Console::WriteLine( string, object, object) ret } } }
As a convenience, branch and compare 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 is needed.
Table 11-8 lists 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 in iterations of the loop:
.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 } } }
The sample code uses the short form of the branch instruction. Be careful when branching in the short form. 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 further than 1 byte away. The distance from the branch site to the label must be describable in a single byte. If not, the application might branch to the wrong location.
There are a variety of ways to call methods. So far, only the call and callvirt methods have been shown in 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 methods have the same general syntax:
callsuffix returntype [assembly] signature
The returntype is placed on the evaluation stack; assembly is the location of the method. If in the current assembly, the assembly element can be ignored. The complete signature of the method is represented by signature.
Table 11-9 lists the call instructions in MSIL.
Instruction |
Description |
---|---|
call |
Intended for calling nonvirtual methods. However, the instruction can be applied to virtual methods. The invocation is still interpreted as a nonvirtual call. |
callvirt |
Calls a virtual method. For nonvirtual methods, a nonvirtual call is conducted. |
calli |
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. Use the ldftn instruction to place a function pointer on the stack for a particular method. This sample code includes 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 } } } |
jmp |
Jumps from the current method to the target method and transfers the arguments. The caller and callee must have matching signatures. 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 instruction after the jmp instruction is orphaned. Therefore, the message "After jump" is not displayed. MethodB returns directly to Main, not MethodA. |
|
tail |
The tail command is a prefix instruction and similar to the jmp command. However, the arguments must be loaded explicitly on the evaluation stack and the method signatures can be different. Otherwise, the functions are operationally equivalent. This is the syntax of the tail instruction:
|