Skip to content

Control flow

ethological edited this page Mar 17, 2026 · 2 revisions

Control flow

Unconditionals

Before I even start with conditionals or something, I'm going to quickly gloss over the oneunconditional instruction in the JVM.
It is called goto, it also has a derivative with a wide (extended width address) called goto_w.

The goto instruction carrying the opcode 167 (a7h) has 1 operand that is masked from 2 bytes into a branch address.
The instruction is unconditional which essentially means jump ALWAYS and does not need elements on the stack since there's no condition to test
The branch address (being a mask of 2 bytes provided in big-endian order) is relative NOT absolute, meaning it doesn't act like an index into an array where you just say which instruction you want to jump to from 0, the address starts at the current instruction (the goto) and goes to lower addresses, imagine it like +2 meaning to jump over 2 bytes from the current instruction, this is done so addresses can be larger and in general the instruction set be more efficient.

The goto_w is the same exact thing just that it provides 4 bytes instead of 2 that are masked the same way (in big-endian order) into a 32-bit address.
Bytecode manipulation libraries like ASM abstract this away and choose the opcode based on how far you're trying to jump, there's actually a few more wide instructions in the JVM.

If conditionals

ifeq | ifne

The ifeq instruction is used when comparing states (boolean). It check if the top of the stack (last element) is 0, if it is then it jumps to the specified label, if not flow continues on. Let's say you found this block of bytecode:

ldc 1
ifeq B

This would not jump due to the fact that the last stack element (1) is not 0 so the condition isn't satisfied.

The ifne instruction is simply the opposite meaning it would jump if it saw that the last stack element wasn't 0.

if_icmpeq | if_icmpne

The if_icmpeq simply checks two integer values for equality using of course the stack. Here's an example:

ldc 5
ldc 6
if_icmpeq B

In this case it wouldn't jump as 5 != 6.

The if_icmpne instruction is again simply the opposite meaning with the same bytecode it would jump this time because it checks for inequality opposed to the if_icmpeq instruction.

Lookupswitch

The lookupswitch instruction can be confusing so here's an explanation on how it works.

Let's take this lookupswitch using the JASM syntax:

lookupswitch
   .case 1 A
   .case 2 B
   .case 3 C
   .default D

Now a lookupswitch is very easy to understand if we imagine what a switch in Java would look like:

switch (expression) {
   case 1:
      // do something in case expression is equal to be 1
      break;
   case 2:
      // do something in case expression is equal to be 2
      break;
   default:
      break;
}

Now as we can see the switch statement depends on what the expression equals to, for example if the expression is 1 + 1 it would obviously equal to 2 meaning our case 2: code will be hit.

This works about the same in bytecode. The lookupswitch looks up the last stack element and depending on what it is, it jumps to a specific label, in our previous code we can see that if the last stack element happens to be 1 then we jump to the A label if it's 2 then we jump to the B label and if it's none then our default is triggered which jumps to D an example where default would be triggered is this:

bipush 2
lookupswitch
   .case 1 A
   .default B

And that's it now you know how different control flow instructions work in the JVM.

Clone this wiki locally