From c69a52b8d18c254146435ea717c2126fd2823540 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 27 Aug 2019 10:40:23 +1000 Subject: [PATCH] Avoid ObjectRef overhead for effectively final case vars Given ```scala class C { lazy val s: reflect.internal.SymbolTable = ??? import s._ def test(t: Tree) = { t match { case ap @ Apply(sel @ Select(_, _), _) if (ap, sel).hashCode > 0 => 1 case ap @ Apply(sel @ Select(_, _), _) if (ap, sel).hashCode == 0 => () => ap } } } ``` ``` $ scalac -Xprint:jvm sandbox/test.scala 2>&1 | tee /tmp/old && qscalac -Xprint:jvm sandbox/test.scala 2>&1 | tee /tmp/new && diff -U1000 /tmp/{old,new} ``` ```diff --- /tmp/old 2019-08-27 10:47:15.000000000 +1000 +++ /tmp/new 2019-08-27 10:47:18.000000000 +1000 @@ -1,80 +1,80 @@ [[syntax trees at end of jvm]] // test.scala package { class C extends Object { final lazy private[this] var s: scala.reflect.internal.SymbolTable = _; @volatile private[this] var bitmap$0: Boolean = _; private def s$lzycompute(): scala.reflect.internal.SymbolTable = { C.this.synchronized(if (C.this.bitmap$0.unary_!()) { C.this.s = (scala.Predef.???(): scala.reflect.internal.SymbolTable); C.this.bitmap$0 = true }); C.this.s }; - lazy def s(): scala.reflect.internal.SymbolTable = (if (C.this.bitmap$0.unary_!()) + lazy def s(): scala.reflect.internal.SymbolTable = if (C.this.bitmap$0.unary_!()) C.this.s$lzycompute() else - C.this.s: scala.reflect.internal.SymbolTable); + C.this.s; def test(t: reflect.internal.Trees$Tree): Object = { var rc13: Boolean = false; - var x2: runtime.ObjectRef = scala.runtime.ObjectRef.create((null: reflect.internal.Trees$Apply)); + var x2: reflect.internal.Trees$Apply = (null: reflect.internal.Trees$Apply); { case val x1: reflect.internal.Trees$Tree = t; case15(){ if (x1.$isInstanceOf[reflect.internal.Trees$Apply]()) { rc13 = true; - x2.elem = (x1.$asInstanceOf[reflect.internal.Trees$Apply](): reflect.internal.Trees$Apply); + x2 = (x1.$asInstanceOf[reflect.internal.Trees$Apply](): reflect.internal.Trees$Apply); { - val sel: reflect.internal.Trees$Tree = x2.elem.$asInstanceOf[reflect.internal.Trees$Apply]().fun(); + val sel: reflect.internal.Trees$Tree = x2.fun(); if (sel.$isInstanceOf[reflect.internal.Trees$Select]()) { val x4: reflect.internal.Trees$Select = (sel.$asInstanceOf[reflect.internal.Trees$Select](): reflect.internal.Trees$Select); - if (new Tuple2(x2.elem.$asInstanceOf[reflect.internal.Trees$Apply](), x4).hashCode().>(0)) + if (new Tuple2(x2, x4).hashCode().>(0)) matchEnd14(scala.Int.box(1)) else case16() } else case16() } } else case16() }; case16(){ if (rc13) { - val sel: reflect.internal.Trees$Tree = x2.elem.$asInstanceOf[reflect.internal.Trees$Apply]().fun(); + val sel: reflect.internal.Trees$Tree = x2.fun(); if (sel.$isInstanceOf[reflect.internal.Trees$Select]()) { val x9: reflect.internal.Trees$Select = (sel.$asInstanceOf[reflect.internal.Trees$Select](): reflect.internal.Trees$Select); - if (new Tuple2(x2.elem.$asInstanceOf[reflect.internal.Trees$Apply](), x9).hashCode().==(0)) + if (new Tuple2(x2, x9).hashCode().==(0)) matchEnd14({ $anonfun(x2) }) else case17() } else case17() } else case17() }; case17(){ matchEnd14(throw new MatchError(x1)) }; matchEnd14(x: Object){ x } } }; - final def $anonfun$test$1(x2$1: runtime.ObjectRef): reflect.internal.Trees$Apply = x2$1.elem.$asInstanceOf[reflect.internal.Trees$Apply](); + final def $anonfun$test$1(x2$1: reflect.internal.Trees$Apply): reflect.internal.Trees$Apply = x2$1; def (): C = { C.super.(); () } } } ``` --- src/compiler/scala/tools/nsc/transform/LambdaLift.scala | 3 ++- .../scala/tools/nsc/transform/patmat/MatchOptimization.scala | 4 ++-- test/files/run/sd187.check | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index c6a25bdd14e..6420b40d08b 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -178,7 +178,8 @@ abstract class LambdaLift extends InfoTransform { renamable += sym changedFreeVars = true debuglog(s"$sym is free in $enclosure") - if (sym.isVariable) sym setFlag CAPTURED + if (sym.isVariable && !sym.hasStableFlag) // write-once synthetic case vars are marked STABLE + sym setFlag CAPTURED } !enclosure.isClass } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index ba25b21820f..2fc39ea7c27 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -14,7 +14,7 @@ package scala.tools.nsc.transform.patmat import scala.language.postfixOps -import scala.tools.nsc.symtab.Flags.MUTABLE +import scala.tools.nsc.symtab.Flags.{MUTABLE, STABLE} import scala.collection.mutable import scala.reflect.internal.util.Position @@ -149,7 +149,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) lazy val storedCond = freshSym(selectorPos, BooleanTpe, "rc") setFlag MUTABLE lazy val treesToHoist: List[Tree] = { - nextBinder setFlag MUTABLE + nextBinder setFlag MUTABLE | STABLE // mark stable to tell lambalift not to capture these as the value will be assigned prior to capture and never reassigned. nextBinder.setPos(selectorPos) List(storedCond, nextBinder) map (b => ValDef(b, codegen.mkZero(b.info))) } diff --git a/test/files/run/sd187.check b/test/files/run/sd187.check index f88fbc29233..2257aa0f0cc 100644 --- a/test/files/run/sd187.check +++ b/test/files/run/sd187.check @@ -7,7 +7,7 @@ }; [107]def commonSubPattern([124]x: [127]): [107]AnyVal = [205]{ [205] var rc6: [205]Boolean = [205]false; - [205] var x3: [205]String = [205][205][205]null.asInstanceOf[[205]String]; + [205] var x3: [205]String = [205][205][205]null.asInstanceOf[[205]String]; [205]{ [205]case val x1: [205]Any = [205]x; [205]case8(){