diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2014-10-07 21:17:14 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2014-11-04 13:50:28 +0100 |
commit | 8c6327dd3893363719dabff8d1a74286a5f9a1da (patch) | |
tree | 9b01ddb97ca56fb40a214da56227e1a2067c378f /test | |
parent | 0eed07e729f4acb13c1cb700a7d1a2fbc382a1f7 (diff) | |
download | scala-8c6327dd3893363719dabff8d1a74286a5f9a1da.tar.gz scala-8c6327dd3893363719dabff8d1a74286a5f9a1da.tar.bz2 scala-8c6327dd3893363719dabff8d1a74286a5f9a1da.zip |
GenBCode: Simplify branching instructions
This commit implements simplifications to branching instructions, for
example `CondJump l; GOTO l` is replaced by `POP; GOTO l`.
The individual optimizations are explained in doc comments.
A later commit will add compiler flags to allow enabling the new
optimizations.
Diffstat (limited to 'test')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala new file mode 100644 index 0000000000..a51bce7939 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala @@ -0,0 +1,221 @@ +package scala.tools.nsc +package backend.jvm +package opt + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes._ +import org.junit.Assert._ + +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +@RunWith(classOf[JUnit4]) +class SimplifyJumpsTest { + @Test + def simpleGotoReturn(): Unit = { + val ops = List( + Jump(GOTO, Label(2)), // replaced by RETURN + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(1), // multiple labels OK + Label(2), + Label(3), + Op(RETURN) + ) + val method = genMethod()(ops: _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), Op(RETURN) :: ops.tail) + } + + @Test + def simpleGotoThrow(): Unit = { + val rest = List( + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(1), + Label(2), + Label(3), + Op(ATHROW) + ) + val method = genMethod()( + Op(ACONST_NULL) :: + Jump(GOTO, Label(2)) :: // replaced by ATHROW + rest: _* + ) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), Op(ACONST_NULL) :: Op(ATHROW) :: rest) + } + + @Test + def gotoThrowInTry(): Unit = { + val handler = List(ExceptionHandler(Label(1), Label(2), Label(4), Some("java/lang/Throwable"))) + val initialInstrs = List( + Label(1), + Op(ACONST_NULL), + Jump(GOTO, Label(3)), // not by ATHROW (would move the ATHROW into a try block) + Label(2), + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(3), + Op(ATHROW), + Label(4), + Op(POP), + Op(RETURN) + ) + val method = genMethod(handlers = handler)(initialInstrs: _*) + assertFalse(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), initialInstrs) + + val optMethod = genMethod()(initialInstrs: _*) // no handler + assertTrue(LocalOpt.simplifyJumps(optMethod)) + assertSameCode(instructionsFromMethod(optMethod).take(3), List(Label(1), Op(ACONST_NULL), Op(ATHROW))) + } + + @Test + def simplifyBranchOverGoto(): Unit = { + val begin = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(2)) + ) + val rest = List( + Jump(GOTO, Label(3)), + Label(11), // other labels here are allowed + Label(2), + VarOp(ILOAD, 1), + Op(RETURN), + Label(3), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(begin ::: rest: _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode( + instructionsFromMethod(method), + List(VarOp(ILOAD, 1), Jump(IFLT, Label(3))) ::: rest.tail ) + + // no label allowed between begin and rest. if there's another label, then there could be a + // branch that label. eliminating the GOTO would change the behavior. + val nonOptMethod = genMethod()(begin ::: Label(22) :: rest: _*) + assertFalse(LocalOpt.simplifyJumps(nonOptMethod)) + } + + @Test + def ensureGotoRemoved(): Unit = { + def code(jumps: Instruction*) = List( + VarOp(ILOAD, 1)) ::: jumps.toList ::: List( + Label(2), + + Op(RETURN), + Label(3), + Op(RETURN) + ) + + // ensures that the goto is safely removed. ASM supports removing while iterating, but not the + // next element of the current. Here, the current is the IFGE, the next is the GOTO. + val method = genMethod()(code(Jump(IFGE, Label(2)), Jump(GOTO, Label(3))): _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), code(Jump(IFLT, Label(3)))) + } + + @Test + def removeJumpToSuccessor(): Unit = { + val ops = List( + Jump(GOTO, Label(1)), + Label(11), + Label(1), + Label(2), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(ops: _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops.tail) + } + + @Test + def collapseJumpChains(): Unit = { + def ops(target1: Int, target2: Int, target3: Int) = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(target1)), // initially 1, then 3 + VarOp(ILOAD, 1), + Op(IRETURN), + + Label(2), + Jump(GOTO, Label(target3)), + + Label(1), + Jump(GOTO, Label(target2)), // initially 2, then 3 + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor optimization (once target2 is replaced by 3) + Op(RETURN), + + Label(3), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(ops(1, 2, 3): _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(3, 3, 3)) + } + + @Test + def collapseJumpChainLoop(): Unit = { + def ops(target: Int) = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(target)), + + Label(4), + Jump(GOTO, Label(3)), + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor (label 3) + Op(IRETURN), + + Label(3), + Jump(GOTO, Label(4)), + + Label(2), + Jump(GOTO, Label(3)) + ) + + val method = genMethod()(ops(2): _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(3)) + } + + @Test + def simplifyThenElseSameTarget(): Unit = { + def ops(jumpOp: Instruction) = List( + VarOp(ILOAD, 1), + jumpOp, + Label(2), + Jump(GOTO, Label(1)), + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor (label 1) + Op(IRETURN), + + Label(1), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + + val method = genMethod()(ops(Jump(IFGE, Label(1))): _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(Op(POP))) + } + + @Test + def thenElseSameTargetLoop(): Unit = { + def ops(br: List[Instruction]) = List( + VarOp(ILOAD, 1), + VarOp(ILOAD, 2)) ::: br ::: List( + Label(1), + Jump(GOTO, Label(1)) + ) + val method = genMethod()(ops(List(Jump(IF_ICMPGE, Label(1)))): _*) + assertTrue(LocalOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(List(Op(POP), Op(POP)))) + } +} |