6

This is an excerpt from the asm user guide:

In order to save space, a compiled method does not contain one frame per instruction: in fact it contains only the frames for the instructions that correspond to jump targets or exception handlers, or that follow unconditional jump instructions. Indeed the other frames can be easily and quickly inferred from these ones.

I can understand why the jvm needs stack map frames at jump targets and for exception handlers, but isn't the need to have a stack map frame after a goto a needless requirement, since there must be a jump instruction somewhere in the method bytecode which points to the instruction just after the goto instruction, and that case will be handled by the first requirement. It has to be that way or otherwise the instruction after goto will be unreachable, and hence discardable.

Example:

A method and it's bytecode are given below:

public class t {
    public static void main(String[] s) {
        int i = 10;
        while ( i > 0 ) {
            i = i + 1;
        }
        int j = 10;
    }
}



  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: bipush        10
         2: istore_1
         3: iload_1
         4: ifle          14
         7: iload_1
         8: iconst_1
         9: iadd
        10: istore_1
        11: goto          3
        14: bipush        10
        16: istore_2
        17: return
      LineNumberTable:
        line 9: 0
        line 10: 3
        line 11: 7
        line 13: 14
        line 14: 17
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 3
          locals = [ int ]
        frame_type = 10 /* same */
}

Instruction at index 11 is a goto instruction and the instruction at index 4 has 14, the instruction just after 11 as it's jump target.

What's the rational behind requiring a stack map frame following an unconditional jump?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
saga
  • 1,933
  • 2
  • 17
  • 44

1 Answers1

6

The goal of stack maps is to allow verification to be performed in a single linear pass through the code.

If the code following a goto has no jumps pointing to it, then the code is dead and theoretically does not need to be verified. However, the verifier does not know this. Since it is performing a single pass through the code, it is impossible to know ahead of time whether the code is dead or not. Therefore, it has to verify everything, and this means that it needs a stack frame to start with.

Note that this is different from the legacy non-stack map verifier. Prior to the introduction of stack maps, the verifier would simply iterate through all the code it found, (effectively) following jumps, and iterate until convergence. This means that under the old verifier, dead code is never touched at all.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • 1
    The general case does not even involve dead code but rather two instructions that jump to the same location. The old verifier had to resolve the common super types of frames for both locations what was a rather costly operation. – Rafael Winterhalter Jun 10 '18 at 20:20
  • 1
    @RafaelWinterhalter not only did it have to merge the frames, if the result differed from what had been used in the first pass, it had to do another pass using the merged frame as starting point and if the subsequent code contained one or more backward branches to that code location, it may cause another merge operation with a new result frame, as this answer says, “until convergence”. Think of nested loops containing try/catch blocks… – Holger Jun 29 '18 at 12:47