Search code examples
kotlinjvmbytecodekotlinc

Kotlin Compiler: `nop`s in bytecode


I'm checking kotlinc bytecode of capturing lambdas. And trying to understand the reason why resulting bytecode has nop instructions.

kotlinc -jvm-target 1.6 .

private inline fun lambdaCapturing(f: () -> Int): Int = f()

fun main(args: Array<String>) {
    lambdaCapturing { 42 }
}

As a result I'm getting

public final class x.y.z.LambdaCaptKt {
  private static final int lambdaCapturing(kotlin.jvm.functions.Function0<java.lang.Integer>);
    Code:
       0: ldc           #8                  // int 0
       2: istore_1
       3: aload_0
       4: invokeinterface #14,  1           // InterfaceMethod kotlin/jvm/functions/Function0.invoke:()Ljava/lang/Object;
       9: checkcast     #16                 // class java/lang/Number
      12: invokevirtual #20                 // Method java/lang/Number.intValue:()I
      15: ireturn

  public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #29                 // String args
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore_1
       8: iconst_0
       9: istore_2
      10: nop
      11: nop
      12: nop
      13: return
}

with several nop instructions in main function.

If I will compile the same code snippet with -Xno-optimize, main function will look like

public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #29                 // String args
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: nop
       7: iconst_0
       8: istore_1
       9: nop
      10: iconst_0
      11: istore_2
      12: bipush        10
      14: nop
      15: goto          18
      18: invokestatic  #41                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      21: checkcast     #16                 // class java/lang/Number
      24: invokevirtual #20                 // Method java/lang/Number.intValue:()I
      27: nop
      28: goto          31
      31: pop
      32: return

There are nops as well.

  1. What is the reason to have nops in non-optimised code? (debug info/...)
  2. Is there any reason to have nops in optimized code?

Solution

  • The reason for nops in the bytecode that the Kotlin compiler emits is the possibility for the debugger to put a breakpoint at the closing brace, i.e. after the last statement, of a function or an if-clause and other clauses, and to make it possible to step to those locations. Doing that requires an instruction present in the bytecode that is also marked with the line number.

    Some nops are then optimized away if they are redundant, such as when there's already a valid instruction following the last statement instructions.