diff options
Diffstat (limited to 'test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala | 64 |
1 files changed, 50 insertions, 14 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala index 902af7b7fa..0021a1784d 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala @@ -18,18 +18,14 @@ import scala.tools.testing.ClearAfterClass object UnreachableCodeTest extends ClearAfterClass.Clearable { // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks, // see comment in BCodeBodyBuilder - var methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method") - var dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code") - var noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none") - - // jvm-1.5 disables computing stack map frames, and it emits dead code as-is. note that this flag triggers a deprecation warning - var noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none -deprecation") + var methodOptCompiler = newCompiler(extraArgs = "-Yopt:l:method") + var dceCompiler = newCompiler(extraArgs = "-Yopt:unreachable-code") + var noOptCompiler = newCompiler(extraArgs = "-Yopt:l:none") def clear(): Unit = { methodOptCompiler = null dceCompiler = null noOptCompiler = null - noOptNoFramesCompiler = null } } @@ -40,11 +36,10 @@ class UnreachableCodeTest extends ClearAfterClass { val methodOptCompiler = UnreachableCodeTest.methodOptCompiler val dceCompiler = UnreachableCodeTest.dceCompiler val noOptCompiler = UnreachableCodeTest.noOptCompiler - val noOptNoFramesCompiler = UnreachableCodeTest.noOptNoFramesCompiler def assertEliminateDead(code: (Instruction, Boolean)*): Unit = { val method = genMethod()(code.map(_._1): _*) - LocalOptImpls.removeUnreachableCodeImpl(method, "C") + dceCompiler.genBCode.bTypes.localOpt.removeUnreachableCodeImpl(method, "C") val nonEliminated = instructionsFromMethod(method) val expectedLive = code.filter(_._2).map(_._1).toList assertSameCode(nonEliminated, expectedLive) @@ -152,11 +147,6 @@ class UnreachableCodeTest extends ClearAfterClass { // Finally, instructions in the dead basic blocks are replaced by ATHROW, as explained in // a comment in BCodeBodyBuilder. assertSameCode(noDce.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ATHROW), Op(ATHROW))) - - // when NOT computing stack map frames, ASM's ClassWriter does not replace dead code by NOP/ATHROW - val warn = "target:jvm-1.5 is deprecated" - val noDceNoFrames = singleMethodInstructions(noOptNoFramesCompiler)(code, allowMessage = _.msg contains warn) - assertSameCode(noDceNoFrames.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ICONST_2), Op(IRETURN))) } @Test @@ -225,4 +215,50 @@ class UnreachableCodeTest extends ClearAfterClass { assertTrue(List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(3)), List("java/lang/Object", Label(4))), Label(3), Label(4)) === List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(1)), List("java/lang/Object", Label(3))), Label(1), Label(3))) } + + @Test + def loadNullNothingBytecode(): Unit = { + val code = + """class C { + | def nl: Null = null + | def nt: Nothing = throw new Error("") + | def cons(a: Any) = () + | + | def t1 = cons(null) + | def t2 = cons(nl) + | def t3 = cons(throw new Error("")) + | def t4 = cons(nt) + |} + """.stripMargin + val List(c) = compileClasses(noOptCompiler)(code) + + assertSameSummary(getSingleMethod(c, "nl"), List(ACONST_NULL, ARETURN)) + + assertSameSummary(getSingleMethod(c, "nt"), List( + NEW, DUP, LDC, "<init>", ATHROW)) + + assertSameSummary(getSingleMethod(c, "t1"), List( + ALOAD, ACONST_NULL, "cons", RETURN)) + + // GenBCode introduces POP; ACONST_NULL after loading an expression of type scala.runtime.Null$, + // see comment in BCodeBodyBuilder.adapt + assertSameSummary(getSingleMethod(c, "t2"), List( + ALOAD, ALOAD, "nl", POP, ACONST_NULL, "cons", RETURN)) + + // the bytecode generated by GenBCode is ... ATHROW; INVOKEVIRTUAL C.cons; RETURN + // the ASM classfile writer creates a new basic block (creates a label) right after the ATHROW + // and replaces all instructions by NOP*; ATHROW, see comment in BCodeBodyBuilder.adapt + // NOTE: DCE is enabled by default and gets rid of the redundant code (tested below) + assertSameSummary(getSingleMethod(c, "t3"), List( + ALOAD, NEW, DUP, LDC, "<init>", ATHROW, NOP, NOP, NOP, ATHROW)) + + // GenBCode introduces an ATHROW after the invocation of C.nt, see BCodeBodyBuilder.adapt + // NOTE: DCE is enabled by default and gets rid of the redundant code (tested below) + assertSameSummary(getSingleMethod(c, "t4"), List( + ALOAD, ALOAD, "nt", ATHROW, NOP, NOP, NOP, ATHROW)) + + val List(cDCE) = compileClasses(dceCompiler)(code) + assertSameSummary(getSingleMethod(cDCE, "t3"), List(ALOAD, NEW, DUP, LDC, "<init>", ATHROW)) + assertSameSummary(getSingleMethod(cDCE, "t4"), List(ALOAD, ALOAD, "nt", ATHROW)) + } } |