Throughout this text, I've often employed the ILDASM utility to illustrate the Microsoft intermediate language (MSIL) that the C# compiler generates. I used ILDASM to give you a more complete understanding of the lower-level workings of the C# compiler. Although I briefly explain what each MSIL instruction does in the context of the chapter in which the instruction is used, I thought it would be nice to have a central listing of all the instructions. In the following table, I list each MSIL instruction, its opcode, the parameters that are passed to the instruction, and the resulting stack transition.
Here are a few notes that should help you when reading the table:
-
Because pushing means to place something on the stack, I use the phrase pushing a value rather than the more verbose pushing the value onto the stack.
-
Because popping is widely understood to mean taking something off the stack, I use the phrase popping the value instead of the more verbose popping the value off the stack.
-
When I use the word indirect, I'm referring to retrieving a value through its address. Therefore, the phrase pushes (indirect) value should be understood to mean pushes the value pointed at by an address onto the stack.
-
The term native int represents a 32-bit integer on all 32-bit versions of Microsoft Windows.
-
The letter F represents the native floating point.
-
The state transition column is intended to illustrate a before-and-after picture of the stack. For example, the stack transition: "…, value1, value2-> …, result" indicates that before the instruction is executed, the stack must contain two values (value1,value2), and that after the instruction is executed, these values will be replaced with one value (result). Note that the ellipsis character simply indicates unknown values on the stack that have nothing to do with this instruction.
Instruction (Opcode) | Description | Stack Transition | |
---|---|---|---|
add (0x58) | Adds two values, pushing result | ...,val1,val2->...,result | |
add.ovf (0xD6) | Adds integer values, pushing result; throws exception on overflow | ...,val1,val2->...,result | |
add.ovf.un (0xD7) | Adds unsigned integer values, pushing result; throws exception on overflow | ...,val1,val2->...,result | |
and (0x5F) | Bitwise AND of two integral values, pushing an integral value | ...val1,val2->...,result | |
arglist (0xFE 0x00) | Pushes argument list handle for current method | ...->...,argListHandle | |
beq int32 (0x3B) | Branches to specified offset if two top stack values are equal | ...,value,value->... | |
beq.s int8 (0x2E) | Branches to specified offset if two top stack values are equal | ...,value,value->... | |
bge int32 (0x3C) | Branches to specified offset if value1 is greater than or equal to value2 | ...,value1,value2->... | |
bge.s int8 (0x2F) | Branches to specified offset if value1 is greater than or equal to value2 | ...,value1,value2->... | |
bge.un int32 (0x41) | Branches to specified offset if value1 is greater than or equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
bge.un.s int8 (0x34) | Branches to specified offset if value1 is greater than or equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
bgt int32 (0x3D) | Branches to specified offset if value1 is greater than value2 | ...,value1,value2->... | |
bgt.s int8 (0x30) | Branches to specified offset if value1 is greater than value2 | ...,value1,value2->... | |
bgt.un int32 (0x42) | Branches to specified offset if value1 is greater than value2 (unsigned or unordered) | ...,value1,value2->... | |
bgt.un.s int8 (0x35) | Branches to specified offset if value1 is greater than value2 (unsigned or unordered) | ...,value1,value2->... | |
ble int32 (0x3E) | Branches to specified offset if value1 is less than or equal to value2 | ...,value1,value2->... | |
ble.s int8 (0x31) | Branches to specified offset if value1 is less than or equal to value2 | ...,value1,value2->... | |
ble.un int32 (0x43) | Branches to specified offset if value1 is less than or equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
ble.un.s int8 (0x36) | Branches to specified offset if value1 is less than or equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
blt.un int32 (0x44) | Branches to specified offset if value1 is less than value2 (unsigned or unordered) | ...,value1,value2->... | |
blt.un.s int8 (0x37) | Branches to specified offset if value1 is less than value2 (unsigned or unordered) | ...,value1,value2->... | |
bne.un.s int8 (0x33) | Branches to specified offset if value1 isn't equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
blt int32 (0x3F) | Branches to specified offset if value1 is less than value2 | ...,value1,value2->... | |
blt.s int8 (0x32) | Branches to specified offset if value1 is less than value2 | ...,value1,value2->... | |
bne.un int32 (0x40) | Branches to specified offset if value1 isn't equal to value2 (unsigned or unordered) | ...,value1,value2->... | |
box type (0x8C) | Converts value type to object reference | ...,valType->...,obj | |
br int32 (0x38) | Unconditional branch to specified offset | ...->... | |
br.s int8 (0x2B) | Branches to specified offset | ...->... | |
Break (0x01) | Informs the debugger that a breakpoint has been reached | ...->... | |
brfalse int32 (0x39) | Branches to specified offset if value on stack is false | ...,value->... | |
brfalse.s int8 (0x2C) | Branches to specified offset if value on stack is false | ...,value->... | |
brtrue int32 (0x3A) | Branches to specified offset if value on stack is true | ...,value->... | |
brtrue.s int8 (0x2D) | Branches to specified offset if value on stack is true | ...,value->... | |
call method (0x28) | Calls a method | ...->... | |
calli signature (0x29) | Calls method indicated by address on stack; stack also contains 1...n arguments |
...,arg1,arg2,argN, method->retVal (retVal is not always pushed onto the stack as a result of the calli signature instruction.) |
|
callvirt method (0x6F) | Calls virtual method of obj |
...,obj,arg1... argn->...,retVal (Not always pushed onto the stack for the callvirt method instruction.) |
|
castclass type (0x74) | Casts obj to class | ...,obj->...,obj2 | |
ceq (0xFE 0x01) | Compares equality of two values on stack; pushes 1 if equal; otherwise, pushes 0 | ...,val1,val2->...,result | |
cgt (0xFE 0x02) | Compares to see whether val1 is greater than val2; pushes 1 if true and 0 if false | ...,val1,val2->...,result | |
cgt.un (0xFE 0x03) | Compares to see whether val1 is greater than val2 (unsigned or unordered); pushes 1 if true and 0 if false | ...,val1,val2->...,result | |
ckfinite (0xC3) | Checks for a finite real number; exception thrown if not a number (NaN) or (+/-)infinity; otherwise, value is left on stack | ...,value->...,value | |
clt (0xFE 0x04) | Compares to see whether val1 is less than val2; pushes 1 if true and 0 if false | ...,val1,val2->...,result | |
clt.un (0xFE 0x05) | Compares to see whether val1 is less than val2 (unsigned or unordered); pushes 1 if true and 0 if false | ...,val1,val2->...,result | |
conv.i (0xD3) | Converts value to native int, pushing resulting native int | ...,value->...,result | |
conv.i1 (0x67) | Converts value to int8, pushing int32 | ...,value->...,result | |
conv.i2 (0x68) | Converts value to int16, pushing int32 | ...,value->...,result | |
conv.i4 (0x69) | Converts value to int32, pushing int32 | ...,value->...,result | |
conv.i8 (0x6A) | Converts value to int64, pushing int64 | ...,value->...,result | |
conv.ovf.i (0xD4) | Converts value to native int, pushing resulting native int; throws exception on overflow | ...,value->...,result | |
conv.ovf.i.un (0x8A) | Converts unsigned value to native int, pushing resulting native int; throws exception on overflow | ...,value->...,result | |
conv.ovf.i1 (0xB3) | Converts value to int8, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i1.un (0x82) | Converts value to uint8, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i2 (0xB5) | Converts value to int16, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i2.un (0x83) | Converts value to uint16, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i4 (0xB7) | Converts value to int32, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i4.un (0x84) | Converts value to uint32, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.i8 (0xB9) | Converts value to int64, pushing resulting int64; throws exception on overflow | ...,value->...,result | |
conv.ovf.i8.un (0x85) | Converts value to uint64, pushing resulting int64; throws exception on overflow | ...,value->...,result | |
conv.ovf.u (0xD5) | Converts value to native unsigned int, pushing resulting native unsigned int; throws exception on overflow | ...,value->...,result | |
conv.ovf.u.un (0x8B) | Converts unsigned value to native unsigned int, pushing resulting native int; throws exception on overflow | ...,value->...,result | |
conv.ovf.u2 (0xB6) | Converts value to uint16, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.u2.un (0x87) | Converts unsigned value to uint16, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.u4 (0xB8) | Converts value to uint32, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.u4.un (0x88) | Converts unsigned value to uint32, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.u8 (0xBA) | Converts value to uint16, pushing resulting int64; throws exception on overflow | ...,value->...,result | |
conv.ovf.u8.un (0x89) | Converts unsigned value to uint64, pushing resulting int64; throws exception on overflow | ...,value->...,result | |
conv.ovf.u1 (0xB4) | Converts value to uint8, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.ovf.u1.un (0x86) | Converts unsigned value to uint8, pushing resulting int32; throws exception on overflow | ...,value->...,result | |
conv.r.un (0x76) | Converts unsigned integer to floating point, pushing F | ...,value->...,result | |
conv.r4 (0x6B) | Converts value to float32, pushing F | ...,value->...,result | |
conv.r8 (0x6C) | Converts value to float64, pushing F | ...,value->...,result | |
conv.u (0xE0) | Converts value to native unsigned int, pushing native int | ...,value->...,result | |
conv.u1 (0xD2) | Converts value to uint8, pushing resulting int32 | ...,value->...,result | |
conv.u2 (0xD1) | Converts value to uint16, pushing resulting int32 | ...,value->...,result | |
conv.u4 (0x6D) | Converts value to uint32, pushing int32 | ...,value->...,result | |
conv.u8 (0x6E) | Converts value to uint32, pushing int32 | ...,value->...,result | |
cpblk (0xFE 0x17) | Copies size bytes from srcAddr to destAddr in memory | ...,destAddr,srcAddr, size->... | |
cpobj type (0x70) | Copies a value type | ...,destAddr, srcAddr->... | |
div (0x5B) | Divides value1 by value2, pushing result | ...,val1,val2->...,result | |
div.un (0x5C) | Divides (unsigned) integer values, pushing result | ...,val1,val2->...,result | |
dup (0x25) | Duplicates the value at top of stack | ...,value-> ...,value,value | |
endfilter (0xFE 0x11) | Returns from the filter clause of an SEH exception | ...,value->... | |
endfinally (0xDC) | Returns from a finally clause | ...->... | |
jmp method (0x27) | Transfers control to the specified method | ...->... | |
initblk (0xFE 0x18) | Initializes a block of size memory starting at addr with value | ...,addr,value,size->... | |
initobj classToken (0xFE 0x15) | Initializes a value type | ...,valueObjAddr->... | |
isinst type (0x75) |
Tests whether an object is an instance of a type or interface, pushing resulting cast if successful or null on failure. (A null is defined by the CLI as zero (having a bit pattern of all bits zero).) |
...,obj->...,result | |
ldarg uint32 (0xFE 0x09) | Pushes argument at specified index | ...->...,value | |
ldarg.0 (0x02) | Pushes the first argument of a method | ...->...,value | |
ldarg.1 (0x03) | Pushes the second argument of a method | ...->...,value | |
ldarg.2 (0x04) | Pushes the third argument of a method | ...->...,value | |
ldarg.3 (0x05) | Pushes the fourth argument of a method | ...->...,value | |
ldarg.s uint8 (0x0E) | Pushes specified argument | ...->...,value | |
ldarga uint32 (0xFE 0x0A) | Pushes address of argument at specified index | ...->...,address | |
ldarga.s uint8 (0x0F) | Pushes address of specified argument | ...->...,value | |
ldc.i4.m1 (0x15) | Pushes the literal value, -1 | ...->...,-1 | |
ldc.i4 int32 (0x20) | Pushes specified 32-bit value | ...-> ...,value | |
ldc.i4.0 (0x16) | Pushes the literal value, 0 | ...->...,0 | |
ldc.i4.1 (0x17) | Pushes the literal value, 1 | ...->...,1 | |
ldc.i4.2 (0x18) | Pushes the literal value, 2 | ...->...,2 | |
ldc.i4.3 (0x19) | Pushes the literal value, 3 | ...>...,3 | |
ldc.i4.4 (0x1A) | Pushes the literal value, 4 | ...->...,4 | |
ldc.i4.5 (0x1B) | Pushes the literal value, 5 | ...->...,5 | |
ldc.i4.6 (0x1C) | Pushes the literal value, 6 | ...->...,6 | |
ldc.i4.7 (0x1D) | Pushes the literal value, 7 | ...->...,7 | |
ldc.i4.8 (0x1E) | Pushes the literal value, 8 | ...->...,8 | |
ldc.i4.s int8 (0x1F) | Pushes specified 8-bit value as 32-bit | ...->...,value | |
ldc.i8 int64 (0x21) | Pushes specified 64-bit value | ...->...,value | |
ldc.r4 float32 (0x22) | Pushes specified 32-bit floating point | ...->...,value | |
ldc.r8 float64 (0x23) | Pushes specified 64-bit floating point | ...->...,value | |
ldelem.i (0x97) | Pushes array element (native int) as native int | ...array,index->..., value | |
ldelem.i1 (0x90) | Pushes array element (int8) as int32 | ...array,index->..., value | |
ldelem.i2 (0x92) | Pushes array element (int16) as int32 | ...array,index->..., value | |
ldelem.i4 (0x94) | Pushes array element (int32) as int32 | ...array,index->..., value | |
ldelem.i8 (0x96) | Pushes array element (int64) as int64 | ...array,index->..., value | |
ldelem.r4 (0x98) | Pushes array element (float32) as F | ...array,index->..., value | |
ldelem.r8 (0x99) | Pushes array element (float64) as F | ...array,index->..., value | |
ldelem.ref (0x9A) | Pushes array element (object) as object | ...array,index->..., value | |
ldelem.u1 (0x91) | Pushes array element (uint8) as int32 | ...array,index-> ...,value | |
ldelem.u2 (0x93) | Pushes array element (uint16) as int32 | ...array,index-> ...,value | |
ldelem.u4 (0x95) | Pushes array element (uint32) as int32 | ...array,index-> ...,value | |
ldelema type (0x8F) | Pushes the address of an array element | ...,array,index-> ...,addr | |
ldfld field (0x7B) | Pushes field of an object | ...,obj->...,value | |
ldflda field (0x7C) | Pushes field address of an object | ...,obj->...,addr | |
ldftn method (0xFE 0x06) | Pushes the method pointer referenced by method | ...->...,ftn | |
ldind.i (0x4D) | Pushes (indirect) value of type native int as native int | ...,addr->...,value | |
ldind.i1 (0x46) | Pushes (indirect) value of type int8 as int32 | ...,addr->...,value | |
ldind.i2 (0x48) | Pushes (indirect) value of type int16 as int32 | ...,addr->...,value | |
ldind.i4 (0x4A) | Pushes (indirect) value of type int32 as int32 | ...,addr->...,value | |
ldind.i8 (0x4C) | Pushes (indirect) value of type int64 as int64 | ...,addr->...,value | |
ldind.u1 (0x47) | Pushes (indirect) value of type uint8 as int32 | ...,addr->...,value | |
ldind.u2 (0x49) | Pushes (indirect) value of type uint16 as int32 | ...,addr->...,value | |
ldind.u4 (0x4B) | Pushes (indirect) value of type uint32 as int32 | ...,addr->...,value | |
ldind.r4 (0x4E) | Pushes (indirect) value of type float32 as F | ...,addr->...,value | |
ldind.r8 (0x4F) | Pushes (indirect) value of type float64 as F
|