@@ -8,16 +8,18 @@ import org.junit.Test
88import scala .tools .asm .Opcodes ._
99import org .junit .Assert ._
1010
11+ import scala .tools .asm .tree .ClassNode
1112import scala .tools .nsc .backend .jvm .AsmUtils ._
1213import scala .tools .testing .AssertUtil ._
1314
1415import CodeGenTools ._
1516import scala .tools .partest .ASMConverters
1617import ASMConverters ._
1718import scala .tools .testing .ClearAfterClass
19+ import scala .collection .convert .decorateAsScala ._
1820
1921object MethodLevelOptsTest extends ClearAfterClass .Clearable {
20- var methodOptCompiler = newCompiler(extraArgs = " -Ybackend:GenBCode - Yopt:l:method" )
22+ var methodOptCompiler = newCompiler(extraArgs = " -Yopt:l:method" )
2123 def clear (): Unit = { methodOptCompiler = null }
2224}
2325
@@ -29,6 +31,8 @@ class MethodLevelOptsTest extends ClearAfterClass {
2931
3032 def wrapInDefault (code : Instruction * ) = List (Label (0 ), LineNumber (1 , Label (0 ))) ::: code.toList ::: List (Label (1 ))
3133
34+ def locals (c : ClassNode , m : String ) = findAsmMethod(c, m).localVariables.asScala.toList.map(l => (l.name, l.index)).sortBy(_._2)
35+
3236 @ Test
3337 def eliminateEmptyTry (): Unit = {
3438 val code = " def f = { try {} catch { case _: Throwable => 0; () }; 1 }"
@@ -559,4 +563,182 @@ class MethodLevelOptsTest extends ClearAfterClass {
559563 getSingleMethod(c, " t" ).instructions.dropNonOp,
560564 List (VarOp (ALOAD , 1 ), Jump (IFNULL , Label (6 )), Op (ICONST_1 ), Op (IRETURN ), Label (6 ), Op (ICONST_0 ), Op (IRETURN )))
561565 }
566+
567+ @ Test
568+ def t5313 (): Unit = {
569+ val code =
570+ """ class C {
571+ | def randomBoolean = scala.util.Random.nextInt % 2 == 0
572+ |
573+ | // 3 stores to kept1 (slot 1), 1 store to result (slot 2)
574+ | def t1 = {
575+ | var kept1 = new Object
576+ | val result = new java.lang.ref.WeakReference(kept1)
577+ | kept1 = null // we can't eliminate this assignment because result can observe
578+ | // when the object has no more references. See SI-5313
579+ | kept1 = new Object // could eliminate this one with a more elaborate analysis (we know it contains null)
580+ | // however, such is not implemented: if a var is live, then stores are kept.
581+ | result
582+ | }
583+ |
584+ | // only two variables are live: kept2 and kept3. they end up on slots 1 and 2.
585+ | // kept2 has 2 stores, kept3 has 1 store.
586+ | def t2 = {
587+ | var erased2 = null // we can eliminate this store because it's never used
588+ | val erased3 = erased2 // and this
589+ | var erased4 = erased2 // and this
590+ | val erased5 = erased4 // and this
591+ | var kept2: Object = new Object // ultimately can't be eliminated
592+ | while(randomBoolean) {
593+ | val kept3 = kept2
594+ | kept2 = null // this can't, because it clobbers kept2, which is used
595+ | erased4 = null // safe to eliminate
596+ | println(kept3)
597+ | }
598+ | 0
599+ | }
600+ |
601+ | def t3 = {
602+ | var kept4 = new Object // have to keep, it's used
603+ | try
604+ | println(kept4)
605+ | catch {
606+ | case _ : Throwable => kept4 = null // have to keep, it clobbers kept4 which is used
607+ | }
608+ | 0
609+ | }
610+ |
611+ | def t4 = {
612+ | var kept5 = new Object
613+ | print(kept5)
614+ | kept5 = null // can't eliminate it's a clobber and it's used
615+ | print(kept5)
616+ | kept5 = null // eliminated by nullness analysis (store null to a local that is known to be null)
617+ | 0
618+ | }
619+ |
620+ | def t5 = {
621+ | while(randomBoolean) {
622+ | var kept6: AnyRef = null // not used, but have to keep because it clobbers the next used store
623+ | // on the back edge of the loop
624+ | kept6 = new Object // used
625+ | println(kept6)
626+ | }
627+ | 0
628+ | }
629+ |}
630+ """ .stripMargin
631+
632+ val List (c) = compileClasses(methodOptCompiler)(code)
633+ def stores (m : String ) = getSingleMethod(c, m).instructions.filter(_.opcode == ASTORE )
634+
635+ assertEquals(locals(c, " t1" ), List ((" this" ,0 ), (" kept1" ,1 ), (" result" ,2 )))
636+ assert(stores(" t1" ) == List (VarOp (ASTORE , 1 ), VarOp (ASTORE , 2 ), VarOp (ASTORE , 1 ), VarOp (ASTORE , 1 )),
637+ textify(findAsmMethod(c, " t1" )))
638+
639+ assertEquals(locals(c, " t2" ), List ((" this" ,0 ), (" kept2" ,1 ), (" kept3" ,2 )))
640+ assert(stores(" t2" ) == List (VarOp (ASTORE , 1 ), VarOp (ASTORE , 2 ), VarOp (ASTORE , 1 )),
641+ textify(findAsmMethod(c, " t2" )))
642+
643+ assertEquals(locals(c, " t3" ), List ((" this" ,0 ), (" kept4" ,1 )))
644+ assert(stores(" t3" ) == List (VarOp (ASTORE , 1 ), VarOp (ASTORE , 1 )),
645+ textify(findAsmMethod(c, " t3" )))
646+
647+ assertEquals(locals(c, " t4" ), List ((" this" ,0 ), (" kept5" ,1 )))
648+ assert(stores(" t4" ) == List (VarOp (ASTORE , 1 ), VarOp (ASTORE , 1 )),
649+ textify(findAsmMethod(c, " t4" )))
650+
651+ assertEquals(locals(c, " t5" ), List ((" this" ,0 ), (" kept6" ,1 )))
652+ assert(stores(" t5" ) == List (VarOp (ASTORE , 1 ), VarOp (ASTORE , 1 )),
653+ textify(findAsmMethod(c, " t5" )))
654+ }
655+
656+ @ Test
657+ def testCpp (): Unit = {
658+ // copied from an old test (run/test-cpp.scala)
659+ val code =
660+ """ class C {
661+ | import scala.util.Random._
662+ |
663+ | def t1(x: Int) = {
664+ | val y = x
665+ | println(y)
666+ | }
667+ |
668+ | def t2 = {
669+ | val x = 2
670+ | val y = x
671+ | println(y)
672+ | }
673+ |
674+ | def t3 = {
675+ | val x = this
676+ | val y = x
677+ | println(y)
678+ | }
679+ |
680+ | def f = nextInt
681+ |
682+ | def t4 = {
683+ | val x = f
684+ | val y = x
685+ | println(y)
686+ | }
687+ |
688+ | def t5 = {
689+ | var x = nextInt
690+ | var y = x
691+ | println(y)
692+ |
693+ | y = nextInt
694+ | x = y
695+ | println(x)
696+ | }
697+ |}
698+ """ .stripMargin
699+
700+ val List (c) = compileClasses(methodOptCompiler)(code)
701+ assertEquals(locals(c, " t1" ), List ((" this" , 0 ), (" x" , 1 )))
702+
703+ assertEquals(locals(c, " t2" ), List ((" this" , 0 ), (" x" , 1 )))
704+ // we don't have constant propagation (yet).
705+ // the local var can't be optimized as a store;laod sequence, there's a GETSTATIC between the two
706+ assertEquals(
707+ textify(findAsmMethod(c, " t2" )),
708+ getSingleMethod(c, " t2" ).instructions.dropNonOp.map(_.opcode),
709+ List (
710+ ICONST_2 , ISTORE ,
711+ GETSTATIC , // Predef.MODULE$
712+ ILOAD , INVOKESTATIC , // boxToInteger
713+ INVOKEVIRTUAL , // println
714+ RETURN ))
715+
716+ assertEquals(locals(c, " t3" ), List ((" this" , 0 )))
717+ assertEquals(locals(c, " t4" ), List ((" this" , 0 ), (" x" , 1 )))
718+ assertEquals(locals(c, " t5" ), List ((" this" , 0 ), (" x" , 1 )))
719+ }
720+
721+ @ Test
722+ def t7006 (): Unit = {
723+ val code =
724+ """ class C {
725+ | def t: Unit = {
726+ | try {
727+ | val x = 3
728+ | } finally {
729+ | print("hello")
730+ | }
731+ | while(true) { }
732+ | }
733+ |}
734+ """ .stripMargin
735+ val List (c) = compileClasses(methodOptCompiler)(code)
736+ val t = getSingleMethod(c, " t" )
737+ assertEquals(t.handlers, Nil )
738+ assertEquals(locals(c, " t" ), List ((" this" , 0 )))
739+ assertEquals(t.instructions.summary,
740+ List (
741+ GETSTATIC , LDC , " print" ,
742+ - 1 , GOTO ))
743+ }
562744}
0 commit comments