Labels and Flow Control Instructions

Labels and Flow Control Instructions

Flow control instructions include branching instructions, switch instructions, exiting and ending instructions used with structured exception handling (SEH) blocks, and a return instruction.

Unconditional Branching Instructions

Unconditional branching instructions take no arguments from the stack and have a signed integer parameter. The parameter specifies the offset in bytes from the current position within the IL stream. The ILAsm notation does allow you to specify the offset explicitly (for example, br -234), but this practice is not recommended for an obvious reason: it’s difficult to calculate the offset correctly when you’re writing in a programming language.

It is much safer and less troublesome to use labels instead, letting the ILAsm compiler calculate the correct offsets. Labels, which you’ve already encountered many times, are simple names followed by a colon:

 Loop:  br  Loop 

The ILAsm compiler does not automatically choose between long-parameter and short-parameter forms. Thus, if you specify a short-parameter instruction and put the target label farther than the short parameter permits, the calculated offset is truncated to 1 byte, and the ILAsm compiler issues an error message.

Unconditional branching instructions take nothing from the evaluation stack and put nothing on it.

  • br <int32> (0x38)  Branch <int32> bytes from the current position.

  • br.s <int8> (0x2B)  The short-parameter form of br.

Conditional Branching Instructions

Conditional branching instructions differ from the unconditional instructions in one aspect only: they take one <value> from the evaluation stack, check to see whether the condition specified by the opcode is true, and, if it is, branch according to the instruction parameter.

  • brfalse (brnull, brzero) <int32> (0x39)  Branch if <value> is 0.

  • brfalse.s <int8> (0x2C)  The short-parameter form of brfalse. Note that the synonyms brnull.s and brzero.s do not exist.

  • brtrue (brinst) <int32> (0x3A)  Branch if <value> is nonzero.

  • brtrue.s <int8> (0x2D)  The short-parameter form of brtrue. No synonyms exist.

Comparative Branching Instructions

Comparative branching instructions take two values (<value1>, <value2>) from the evaluation stack and compare them according to the condition specified by the opcode. Not all combinations of types of <value1> and <value2> are valid; Table 10-1 lists the valid combinations.

Table 10-1  Valid Type Combinations in Comparison Instructions

Type of <valuei>

Can Be Compared with Type

int32

int32, native int.

int64

int64.

native int

int32, native int, & (equality or nonequality comparisons only).

Float

Float. Without exception, all floating-point comparisons are formulated as <condition> or unordered. Unordered is true when at least one of the operands is NaN (not a number). A bizarre concept, if you ask me, but of course no one does.

& (managed pointer)

native int (equality or nonequality comparisons only), &. Unless the compared values are pointers to the same array or value type or pointers to pinned variables, comparing two managed pointers should be limited to equality or nonequality comparisons, because the garbage collection subsystem (GC) might move the managed pointers in an unpredictable way at unpredictable moments.

ObjectRef

ObjectRef (equality or nonequality comparisons only). “Greater than” unsigned comparison is also admissible and is used to compare an object reference to null, because objects are subject to garbage collection, and their references can be changed by the GC at will. I strongly recommend avoiding the comparison to null. It is much safer and more logical to use brtrue, and it also saves loading a null reference on the stack.

  • beq <int32> (0x3B)  Branch if <value1> is equal to <value2>.

  • beq.s <int8> (0x2E)  The short-parameter form of beq.

  • bne.un <int32> (0x40)  Branch if the two values are not equal. Integer values are interpreted as unsigned.

  • bne.un.s <int8> (0x33)  The short-parameter form of bne.un.

  • bge <int32> (0x3C)  Branch if greater or equal.

  • bge.s <int8> (0x2F)  The short-parameter form of bge.

  • bge.un <int32> (0x41)  Branch if greater or equal. Integer values are interpreted as unsigned.

  • bge.un.s <int8> (0x34)  The short-parameter form of bge.un.

  • bgt <int32> (0x3D)  Branch if greater.

  • bgt.s <int8> (0x30)  The short-parameter form of bgt.

  • bgt.un <int32> (0x42)  Branch if greater. Integer values are interpreted as unsigned.

  • bgt.un.s <int8> (0x35)  The short-parameter form of bgt.un.

  • ble <int32>(0x3E)  Branch if less or equal.

  • ble.s <int8> (0x31)  The short-parameter form of ble.

  • ble.un <int32> (0x43)  Branch if less or equal. Integer values are interpreted as unsigned.

  • ble.un.s <int8> (0x36)  The short-parameter form of ble.un.

  • blt <int32> (0x3F)  Branch if less.

  • blt.s <int8> (0x32)  The short-parameter form of blt.

  • blt.un <int32> (0x44)  Branch if less. Integer values are interpreted as unsigned.

  • blt.un.s <int8> (0x37)  The short-parameter form of blt.un.

The switch Instruction

The switch instruction implements a jump table. This instruction is unique in the sense that it has not one, not two, but N+1 parameters following it, where N is the number of cases in the switch. The first parameter is a 4-byte unsigned integer specifying the number of cases, and the following N parameters are 4-byte signed integers specifying offsets to the targets (cases). The ILAsm notation is as follows:

switch(Label1, Label2, ,LabelN)  // Default case Label1:  Label2:   LabelN: 

As in the case of branching instructions, ILAsm syntax allows you to replace the labels in a switch( ) instruction with explicit offsets, but I definitely do not recommend this.

The instruction takes one value from the stack and converts it to an unsigned integer. It then switches to the target according to the value of this unsigned integer. A 0 value corresponds to the first target offset on the list. If the value is greater than or equal to the number of targets, the switch instruction is ignored, and control is passed to the instruction immediately following the switch. In this sense, the default case in IL is always the first case of the switch.

  • switch <unsigned int32> <int32> <int32> (0x45)  Branch to one of the <unsigned int32> offsets.

The break Instruction

This break instruction is not equivalent to the break statement in C, which is used as an exit from the switch cases. The break instruction in IL inserts a breakpoint into the IL stream and is used for debugging only. This instruction does not have parameters and does not touch the evaluation stack.

  • break (0x01)  Debugging breakpoint.

SEH Block Exiting Instructions

The blocks of code involved in structured exception handling cannot be entered or exited by simple branching because of the strict stack state requirements imposed on them. The leave instruction, or its short-parameter form, is used to exit a guarded block (a try block) or an exception handler block. You cannot use this instruction, however, to exit a filter, finally, or fault block. (For more details about these blocks, see Chapter 11, “Structured Exception Handling.”)

The instruction has one integer parameter specifying the offset of the target and works the same way as an unconditional branching instruction except that it empties the evaluation stack before the branching. The ILAsm notation for this instruction is similar to the notation for unconditional branching instructions: leave <label> or leave <int32>, the latter one highly unrecommended.

  • leave <int32> (0xDD)  Clear the stack and branch <int32> bytes from the current point.

  • leave.s <int8> (0xDE)  The short-parameter form of leave.

SEH Block Ending Instructions

IL has two specific instructions to mark the end of filter, finally, and fault blocks. Unlike leave, these instructions mark the lexical end of a block rather than an algorithmic end, or point of exit. These instructions have no parameters.

  • endfilter (0xFE 0x11)  The lexical end of a filter block. The instruction takes one 4-byte integer value from the evaluation stack and signals the execution engine whether the associated exception handler should be engaged (a value of 1) or whether the exception identification should be continued (a value other than 1), because this filter doesn’t know what to do with this particular exception.

  • endfinally (endfault) (0xDC) The lexical end of a finally or fault block. This instruction clears the evaluation stack.

The ret Instruction

The return instruction—ret—returns from a called method to the call site. It has no parameters. If the called method should return a value of a certain type, exactly one value of the required type must be on the evaluation stack at the moment of return. The ret instruction causes this value to be removed from the evaluation stack of the called method and put on the evaluation stack of the calling method. If the called method returns void, its evaluation stack must be empty at the moment of return.

  • ret (0x2A)  Return from a method.



Inside Microsoft. NET IL Assembler
Inside Microsoft .NET IL Assembler
ISBN: 0735615470
EAN: 2147483647
Year: 2005
Pages: 147
Authors: SERGE LIDIN

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net