Introduction
These instructions move values between the evaluation stack and the places a method keeps them: its arguments, its local variables, memory reached through an address, and the
metadata (constants and string literals). Throughout, before → after shows the top of the stack, with the rightmost item on top; int32,
int64, native int, F (floating point), O (object reference) and & (managed pointer) are the
kinds of value the stack holds. Values smaller than 32 bits are widened to int32 when loaded (sign-extended for
the signed forms, zero-extended for the unsigned ones) and truncated when stored.
Exceptions and verifiability, for the whole page. Loading or storing through an address (ldind, stind, ldobj,
stobj, cpobj, cpblk, initblk, initobj) throws NullReferenceException if the address is null,
and can fail with a platform access violation for a bad one. Code that does this through a managed pointer (&) is verifiable; through a
native int or an unmanaged pointer it is not. Instructions that only move values between the stack, arguments and locals throw nothing.
Arguments
Arguments are numbered from 0. In an instance method argument 0 is the this reference and the first declared parameter is 1. The short forms
ldarg.0 to ldarg.3 take no operand; .s takes a one-byte number (0 to 255); the two-byte opcodes take a two-byte number.
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
02 | ldarg.0 | … → …, value | Pushes argument 0 (the this reference in an instance method). | None. |
03 | ldarg.1 | … → …, value | Pushes argument 1. | None. |
04 | ldarg.2 | … → …, value | Pushes argument 2. | None. |
05 | ldarg.3 | … → …, value | Pushes argument 3. | None. |
0E u1 | ldarg.s num | … → …, value | Pushes the argument with the given number (0 to 255). | None. The number must name an argument of the method. |
FE 09 u2 | ldarg num | … → …, value | Pushes the argument with the given number (0 to 65,535). | None. |
0F u1 | ldarga.s num | … → …, address | Pushes the address of the argument as a managed pointer, so a callee can read or change it (a ref argument). | None. Verifiable. |
FE 0A u2 | ldarga num | … → …, address | The same with a two-byte number. | None. Verifiable. |
10 u1 | starg.s num | …, value → … | Pops a value into the argument, narrowing it to the argument’s type. | None. |
FE 0B u2 | starg num | …, value → … | The same with a two-byte number. | None. |
Local variables
Locals are numbered from 0 in the order of the local variable signature. Unless the body’s InitLocals flag is set, their starting values are undefined.
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
06 | ldloc.0 | … → …, value | Pushes local variable 0. | None. |
07 | ldloc.1 | … → …, value | Pushes local variable 1. | None. |
08 | ldloc.2 | … → …, value | Pushes local variable 2. | None. |
09 | ldloc.3 | … → …, value | Pushes local variable 3. | None. |
11 u1 | ldloc.s num | … → …, value | Pushes the local with the given number (0 to 255). | None. Reading a local that was never assigned is unverifiable unless InitLocals is set. |
FE 0C u2 | ldloc num | … → …, value | The same with a two-byte number. | None. |
12 u1 | ldloca.s num | … → …, address | Pushes the address of the local as a managed pointer. | None. Verifiable. |
FE 0D u2 | ldloca num | … → …, address | The same with a two-byte number. | None. Verifiable. |
0A | stloc.0 | …, value → … | Pops a value into local variable 0. | None. |
0B | stloc.1 | …, value → … | Pops a value into local variable 1. | None. |
0C | stloc.2 | …, value → … | Pops a value into local variable 2. | None. |
0D | stloc.3 | …, value → … | Pops a value into local variable 3. | None. |
13 u1 | stloc.s num | …, value → … | Pops a value into the local with the given number, narrowing it to the local’s type. | None. |
FE 0E u2 | stloc num | …, value → … | The same with a two-byte number. | None. |
Constants and string literals
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
14 | ldnull | … → …, null | Pushes the null object reference. | None. |
15 | ldc.i4.m1 | … → …, int32 | Pushes the int32 −1. | None. |
16 | ldc.i4.0 | … → …, int32 | Pushes 0 (also the false and null-like zero). | None. |
17 | ldc.i4.1 | … → …, int32 | Pushes 1. | None. |
18 | ldc.i4.2 | … → …, int32 | Pushes 2. | None. |
19 | ldc.i4.3 | … → …, int32 | Pushes 3. | None. |
1A | ldc.i4.4 | … → …, int32 | Pushes 4. | None. |
1B | ldc.i4.5 | … → …, int32 | Pushes 5. | None. |
1C | ldc.i4.6 | … → …, int32 | Pushes 6. | None. |
1D | ldc.i4.7 | … → …, int32 | Pushes 7. | None. |
1E | ldc.i4.8 | … → …, int32 | Pushes 8. | None. |
1F i1 | ldc.i4.s num | … → …, int32 | Pushes the signed one-byte operand, widened to int32 (−128 to 127). | None. |
20 i4 | ldc.i4 num | … → …, int32 | Pushes the four-byte operand. | None. |
21 i8 | ldc.i8 num | … → …, int64 | Pushes the eight-byte operand as an int64. | None. |
22 r4 | ldc.r4 num | … → …, F | Pushes the single-precision operand; on the stack it is a floating-point value of the machine’s internal precision. | None. |
23 r8 | ldc.r8 num | … → …, F | Pushes the double-precision operand. | None. |
72 token | ldstr string | … → …, string | Pushes a reference to the string literal the token names (a #US heap entry, UTF-16). The same literal gives the same object each time. | None. |
Loads and stores through an address
The address is a managed pointer (&), an unmanaged pointer or a native int. A load pops the address and pushes the value; a store pops the value
and then the address. The unaligned. and volatile. prefixes may come before any of them.
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
46 | ldind.i1 | …, addr → …, int32 | Loads a signed byte and sign-extends it. | NullReferenceException on a null address. |
47 | ldind.u1 | …, addr → …, int32 | Loads an unsigned byte and zero-extends it. | As above. |
48 | ldind.i2 | …, addr → …, int32 | Loads a signed 16-bit value and sign-extends it. | As above. |
49 | ldind.u2 | …, addr → …, int32 | Loads an unsigned 16-bit value and zero-extends it. | As above. |
4A | ldind.i4 | …, addr → …, int32 | Loads a 32-bit integer. | As above. |
4B | ldind.u4 | …, addr → …, int32 | Loads an unsigned 32-bit integer (it stays an int32 on the stack, with the same bits). | As above. |
4C | ldind.i8 | …, addr → …, int64 | Loads a 64-bit integer. (There is no ldind.u8: the bits are the same.) | As above. |
4D | ldind.i | …, addr → …, native int | Loads a native-sized integer. | As above. |
4E | ldind.r4 | …, addr → …, F | Loads a single-precision float. | As above. |
4F | ldind.r8 | …, addr → …, F | Loads a double-precision float. | As above. |
50 | ldind.ref | …, addr → …, O | Loads an object reference. | As above. |
51 | stind.ref | …, addr, O → … | Stores an object reference. | NullReferenceException on a null address. |
52 | stind.i1 | …, addr, int32 → … | Stores the low byte of the value. | As above. |
53 | stind.i2 | …, addr, int32 → … | Stores the low 16 bits. | As above. |
54 | stind.i4 | …, addr, int32 → … | Stores a 32-bit integer. | As above. |
55 | stind.i8 | …, addr, int64 → … | Stores a 64-bit integer. | As above. |
56 | stind.r4 | …, addr, F → … | Narrows the float to single precision and stores it. | As above. |
57 | stind.r8 | …, addr, F → … | Stores a double-precision float. | As above. |
DF | stind.i | …, addr, native int → … | Stores a native-sized integer. | As above. |
Value types, blocks, duplicate and discard
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
71 token | ldobj type | …, addr → …, value | Loads the value of the given type (usually a value type, so it is copied onto the stack) from the address. | NullReferenceException on a null address. Verifiable through a managed pointer. |
81 token | stobj type | …, addr, value → … | Stores the value of the given type at the address. | NullReferenceException on a null address. |
70 token | cpobj type | …, dest, src → … | Copies the value of the given type from the source address to the destination address. | NullReferenceException on a null address. |
FE 15 token | initobj type | …, addr → … | Sets the value type at the address to all zero bits: every field is 0, false or null. (C# writes default(T) this way.) | NullReferenceException on a null address. |
FE 17 | cpblk | …, dest, src, size → … | Copies size bytes (an unsigned int32) from the source to the destination address. The blocks must not overlap; the result is undefined if they do. | NullReferenceException on a null address. Not verifiable. |
FE 18 | initblk | …, addr, value, size → … | Sets size bytes starting at the address to the byte value. | NullReferenceException on a null address. Not verifiable. |
25 | dup | …, value → …, value, value | Duplicates the top of the stack. For a value type the copy is a copy. | None. |
26 | pop | …, value → … | Discards the top of the stack. (A call whose result is not used ends with it.) | None. |
Not described
Known, not yet covered
- Alignment and atomicity rules: what
unaligned.andvolatile.mean for each width is on the prefixes page only in outline, not for each processor.
Unknown
- How a runtime treats
cpblkwith overlapping blocks (the specification calls the result undefined).
Links
Reference
- ECMA-335 – partition III, section 3: the base instructions
- System.Reflection.Emit.OpCodes – the runtime library’s table of opcodes, the one the explorer’s table was checked against (.NET)