Arithmetic Instructions

Arithmetic Instructions

MSIL supports the standard arithmetic instructions. The operands of the arithmetic operation are retrieved from the evaluation stack and replaced with the result. For example, the add instruction has two operands, which are removed for the evaluation stack and replaced with the summation.

Table 11-11 lists the common arithmetic instructions. The final column is the number of operands consumed from the evaluation stack during the instruction.

Table 11-11: Arithmetic Instructions

Instruction

Description

Operands

add

Addition

2

sub

Subtraction

2

mul

Multiplication

2

div

Division

2

rem

Remainder

2

neg

Negate

1

Conversion Operations

It is often necessary to convert between types. The conv instruction casts between primitive types. The instruction has a single operand, which is the target of the cast. Conversions can cause overflows when the memory size of the source is larger than the target. For example, this could happen when casting from a 64-bit integer to a 32-bit integer. The high-order bits are trimmed when an overflow occurs, which changes the normal results. It's important to note that the overflow is ignored, and subtle bugs are possible. The conv.ovf instruction prevents an overflow from going undetected. When an overflow occurs, the modified instruction raises an overflow exception.

The following code contains an ostentatious overflow condition. The conversion raises the overflow exception. This code is not protected from exceptions, and the application is terminated.

.assembly extern mscorlib {}
.assembly application {}

.namespace Donis.CSharpBook {

    .class Starter {

        .method static public void Main() il managed {
            .entrypoint
            ldc.i8 4000000000
            conv.ovf.i4
            pop
            ret
        }
    }
}

Exception Handling

Exception handling is implemented in the CLR. Each managed language exposes exception handling in a language-specific manner. In C#, there are the try, catch, throw, and finally keywords. The language compiler, such as csc, compiles code for exception handling into language-agnostic MSIL code. For the details of exception handling in C#, read Chapter 9, "Exception Handling."

There are two strategies for implementing exception handling in MSIL code: as exception clauses or as scoped exceptions.

Exception clauses are implemented after the core MSIL code of a method in the exceptionhandling section. An exception clause consists of a protected block, an exception identification, and an exception handler. Execution must fall into a protected block naturally. Also, you cannot jump into a protected block. To exit the protected block or handler, use the leave instruction. Of course, when an exception is raised, execution is transferred away from the protected block. Do not attempt to exit a protected block or exception handler with a branch statement.

Exception clauses have a handler. There are four types of handlers, and each is assigned a unique code. Table 11-12 lists the four handlers.

Table 11-12: Exception Clauses

Code

Description

0

This clause defines an exception handler.

1

This clause defines an exception filter. The filter determines when the handler should be run. The exception filter ends at an endfilter instruction.

2

This clause defines a finally handler. The finally handler is called whether an exception is raised or not. Cleanup code that must be executed regardless should reside in the finally handler. The finally handler ends at an endfinally instruction.

4

This clause defines a fault handler. This is similar to a finally handler, except that this clause is called only when an exception is raised. The fault handler ends at the endfault instruction.

This is the syntax of the try clause:

  • .try label1 to label2 exceptiontype handler label3 to label4

label1 and label2 define the protected code, whereas label3 and label4 set the scope of the handler. The exception type specifies the type of handler as catch, filter, finally, or fault handler.

In the previous example, an overflow exception is thrown and not caught. The following code catches the overflow exception:

.assembly extern mscorlib {}
.assembly application {}

.namespace Donis.CSharpBook {

    .class Starter {

        .method static public void Main() il managed {
            .entrypoint

start:      ldc.i8 4000000000
            conv.ovf.i4
            pop
            leave done
stop:       callvirt instance string [mscorlib]
 System.Exception::get_Message()
            call void [mscorlib] System.Console::WriteLine(string)
            leave done
done:       ret
            .try start to stop catch [mscorlib] System.Exception
 handler stop to done
        }
    }
}

Scoped exceptions are similar to exception handling in C#, in which there is a try and catch block. Scoped exceptions do not employ exception clauses at the end of the method.

This is the conversion exception code written for scoped exceptions:

.assembly extern mscorlib {}
.assembly application {}

.namespace Donis.CSharpBook {

    .class Starter {

        .method static public void Main() il managed {
            .entrypoint
            .try {
                ldc.i8 4000000000
                conv.ovf.i4
                pop
                leave done
            }
            catch [mscorlib] System.Exception {
                callvirt instance string
                [mscorlib] System.Exception::get_Message()
                call void
 [mscorlib] System.Console::WriteLine(string)
                leave done
            }
done:        ret
        }
    }
}

Miscellaneous Operations

This chapter includes several tables that explain categories of MSIL instructions. However, some MSIL instructions appearing in the sample code of this chapter have not been included in any table. Other instructions have not appeared in the body of code or in a table but are valuable nonetheless. Table 11-13 details some of these miscellaneous MSIL instructions.

Table 11-13: Miscellaneous MSIL Instructions

Instruction

Description

and

Bitwise and.

break

Insert a debugger breakpoint.

castclass

Cast an instance to a different type.

dup

Duplicate the top slot of the evaluation stack.

dop

Nonoperational instruction (a blank instruction).

or

Bitwise or.

pop

Remove the top element of the evaluation stack.

rethrow

Propagate an exception.

throw

Throw an application exception.