rorygraves / scalac_perf

The Scala programming language
http://www.scala-lang.org/
16 stars 3 forks source link

"exhaustive" pattern match can't elide allocation of scrutinee #85

Open hrhino opened 5 years ago

hrhino commented 5 years ago
scala> def test(a: Option[Int], b: Int) = (a, b) match { case (Some(x), y) => x + y; case (None, y) => y }
[[syntax trees at end of                    patmat]] // <console>
def test(a: Option[Int], b: Int): Int = {
  case <synthetic> val x1: (Option[Int], Int) = scala.Tuple2.apply[Option[Int], Int](a, b);
  case8() {
    if (x1.ne(null)) {
      <synthetic> val p2: Option[Int] = x1._1;
      val y: Int = x1._2;
      if (p2.isInstanceOf[Some[Int]]) {
        <synthetic> val x3: Some[Int] = (p2.asInstanceOf[Some[Int]]: Some[Int]);
        {
          val x: Int = x3.value;
          matchEnd7(x.+(y))
        }
      }
      else
        case9()
    }
    else
      case9()
  };
  case9() {
    if (x1.ne(null)) {
      <synthetic> val p4: Option[Int] = x1._1;
      val y: Int = x1._2;
      if (scala.None.==(p4))
        matchEnd7(y)
      else
        case10()
    }
    else
      case10()
  };
  case10() {
    matchEnd7(throw new MatchError(x1))
  };
  matchEnd7(x: Int) {
    x
  }
}
  public int test(scala.Option<java.lang.Object>, int);
    descriptor: (Lscala/Option;I)I
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=6, args_size=3
         0: new           #24                 // class scala/Tuple2
         3: dup
         4: iload_2
         5: invokestatic  #30                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
         8: astore        5
        10: aload_1
        11: aload         5
        13: invokespecial #33                 // Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
        16: astore        4
        18: aload_1
        19: instanceof    #35                 // class scala/Some
        22: ifeq          41
        25: aload_1
        26: checkcast     #35                 // class scala/Some
        29: invokevirtual #39                 // Method scala/Some.value:()Ljava/lang/Object;
        32: invokestatic  #43                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
        35: iload_2
        36: iadd
        37: istore_3
        38: goto          66
        41: getstatic     #48                 // Field scala/None$.MODULE$:Lscala/None$;
        44: aload_1
        45: invokevirtual #52                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
        48: ifeq          56
        51: iload_2
        52: istore_3
        53: goto          66
        56: new           #54                 // class scala/MatchError
        59: dup
        60: aload         4
        62: invokespecial #57                 // Method scala/MatchError."<init>":(Ljava/lang/Object;)V
        65: athrow
        66: iload_3
        67: ireturn

note that the tuple (local 4) is only used at instruction 60, to produce a MatchError which can only be hit when a is null. we should float the tuple construction into the failure case.

toth @fommil for pointing this out (a year ago)

hrhino commented 5 years ago

also, we shouldn't need to null-check the scrutinee, seeing as we just made it. this turns into a question of nullness-tracking, though.