Introduction
A branch transfers control to another instruction in the same method. Its operand is a signed offset, counted from the end of the branch instruction itself to the
first byte of the target: a target one instruction after a 2-byte br.s has offset 0, and an offset of −2 would jump back to the br.s itself. Each branch has a
short form (an opcode ending in .s with a one-byte offset, −128 to 127) and a long form (a four-byte offset); the two do exactly the same
and a compiler picks the short one when the target is near enough. Tools write the target as a label, IL_0012, which is the offset of the target instruction from the start of
the code in hex.
The conditional branches pop their operands, so the stack at the target is the stack after the pop. Wherever control can arrive from, the stack at an instruction must hold the same number and kinds of values. A branch must not jump into or out of a try block or a handler; the instruction that leaves one is leave. A target that is not the start of an instruction is invalid.
Exceptions and verifiability, for the whole page. None of these instructions throws. A branch whose target breaks the rules above, or whose operand types do not match,
is not verifiable. ilasm also accepts brnull and brzero as other names of brfalse, and brinst for brtrue.
Unconditional and single-operand branches
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
2B i1 | br.s target | … → … | Jumps to the target. | None. |
38 i4 | br target | … → … | Jumps to the target. | None. |
2C i1 | brfalse.s target | …, value → … | Pops a value and jumps if it is false: 0, or the null reference. | None. |
39 i4 | brfalse target | …, value → … | As above, long form. | None. |
2D i1 | brtrue.s target | …, value → … | Pops a value and jumps if it is true: not 0, or a non-null reference. | None. |
3A i4 | brtrue target | …, value → … | As above, long form. | None. |
Comparing branches
Each pops two values, a then b (b on top), compares them and jumps if the condition holds: the same as a comparison followed by brtrue. The operands are two
integers of the same kind or two floats (beq and bne.un also take two object references). The .un forms compare integers as unsigned; for floats
the plain forms are false and the .un forms are true when either operand is a NaN, so bne.un is the branch for “not equal” (a NaN is not equal to anything)
and ble.un jumps on “not greater than”.
| Opcode | Instruction | Stack | Jumps if | Exceptions · verifiability |
|---|---|---|---|---|
2E i1 | beq.s target | …, a, b → … | a = b | None. |
3B i4 | beq target | …, a, b → … | a = b | None. |
2F i1 | bge.s target | …, a, b → … | a ≥ b (signed or float) | None. |
3C i4 | bge target | …, a, b → … | a ≥ b (signed or float) | None. |
30 i1 | bgt.s target | …, a, b → … | a > b (signed or float) | None. |
3D i4 | bgt target | …, a, b → … | a > b (signed or float) | None. |
31 i1 | ble.s target | …, a, b → … | a ≤ b (signed or float) | None. |
3E i4 | ble target | …, a, b → … | a ≤ b (signed or float) | None. |
32 i1 | blt.s target | …, a, b → … | a < b (signed or float) | None. |
3F i4 | blt target | …, a, b → … | a < b (signed or float) | None. |
33 i1 | bne.un.s target | …, a, b → … | a ≠ b, or unordered | None. |
40 i4 | bne.un target | …, a, b → … | a ≠ b, or unordered | None. |
34 i1 | bge.un.s target | …, a, b → … | a ≥ b unsigned, or unordered | None. |
41 i4 | bge.un target | …, a, b → … | a ≥ b unsigned, or unordered | None. |
35 i1 | bgt.un.s target | …, a, b → … | a > b unsigned, or unordered | None. |
42 i4 | bgt.un target | …, a, b → … | a > b unsigned, or unordered | None. |
36 i1 | ble.un.s target | …, a, b → … | a ≤ b unsigned, or unordered | None. |
43 i4 | ble.un target | …, a, b → … | a ≤ b unsigned, or unordered | None. |
37 i1 | blt.un.s target | …, a, b → … | a < b unsigned, or unordered | None. |
44 i4 | blt.un target | …, a, b → … | a < b unsigned, or unordered | None. |
Switch
| Opcode | Instruction | Stack | What it does | Exceptions · verifiability |
|---|---|---|---|---|
45 u4, i4 × n | switch (t0, t1, …) | …, value → … | Pops an unsigned int32. If it is less than the count n, jumps to the target with that number (0-based); otherwise falls through to the next instruction. The operand is the count n and then n signed 32-bit offsets, all counted from the end of the whole instruction (4 + 4n + 1 bytes), not from their own position. | None. An empty table (n = 0) is valid and always falls through. |
Example: 45 03 00 00 00 0C 00 00 00 0D 00 00 00 0E 00 00 00 is a switch with three targets that start 12, 13 and 14 bytes after the end of the instruction.
Not described
Known, not yet covered
- Stack consistency at targets: the rule is stated here, but the explorer does not simulate the stack, so it does not report a mismatch.
Unknown
- How the runtime treats a
switchoperand with a huge count that runs past the end of the code (the explorer reports it as cut off).
Links
Reference
- ECMA-335 – partition III, section 3: br, brfalse, brtrue, beq and the other comparing branches, switch
- System.Reflection.Emit.OpCodes – the runtime library’s table of opcodes (.NET)