diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 334c47f3511d..164e602bbe1b 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -88,9 +88,23 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case Assign(lhs, rhs) => val s = lhs.symbol val Local(tk, _, idx, _) = locals.getOrMakeLocal(s) - genLoad(rhs, tk) - lineNumber(tree) - bc.store(idx, tk) + + rhs match { + case Apply(Select(larg: Ident, nme.ADD), Literal(x) :: Nil) + if larg.symbol == s && tk.isIntSizedType && x.isShortRange => + lineNumber(tree) + bc.iinc(idx, x.intValue) + + case Apply(Select(larg: Ident, nme.SUB), Literal(x) :: Nil) + if larg.symbol == s && tk.isIntSizedType && Constant(-x.intValue).isShortRange => + lineNumber(tree) + bc.iinc(idx, -x.intValue) + + case _ => + genLoad(rhs, tk) + lineNumber(tree) + bc.store(idx, tk) + } case _ => genLoad(tree, UNIT) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala index ea257ee959ea..fffefae8a165 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala @@ -433,6 +433,7 @@ trait BCodeIdiomatic { final def load( idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ILOAD, idx, tk) } // can-multi-thread final def store(idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ISTORE, idx, tk) } // can-multi-thread + final def iinc( idx: Int, increment: Int): Unit = jmethod.visitIincInsn(idx, increment) // can-multi-thread final def aload( tk: BType): Unit = { emitTypeBased(JCodeMethodN.aloadOpcodes, tk) } // can-multi-thread final def astore(tk: BType): Unit = { emitTypeBased(JCodeMethodN.astoreOpcodes, tk) } // can-multi-thread diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index d984b612ced2..22fcacbb6a9c 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -759,17 +759,14 @@ class TestBCode extends DottyBytecodeTest { FrameEntry(1, List(1), List()), VarOp(Opcodes.ILOAD, 1), Op(Opcodes.ICONST_5), - Jump(Opcodes.IF_ICMPGT, Label(16)), + Jump(Opcodes.IF_ICMPGT, Label(13)), Field(Opcodes.GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), VarOp(Opcodes.ILOAD, 1), Invoke(Opcodes.INVOKESTATIC, "scala/runtime/BoxesRunTime", "boxToInteger", "(I)Ljava/lang/Integer;", false), Invoke(Opcodes.INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), - VarOp(Opcodes.ILOAD, 1), - Op(Opcodes.ICONST_1), - Op(Opcodes.IADD), - VarOp(Opcodes.ISTORE, 1), + Incr(Opcodes.IINC, 1, 1), Jump(Opcodes.GOTO, Label(2)), - Label(16), + Label(13), FrameEntry(3, List(), List()), Op(Opcodes.RETURN)) diff --git a/compiler/test/dotty/tools/backend/jvm/IincTest.scala b/compiler/test/dotty/tools/backend/jvm/IincTest.scala new file mode 100644 index 000000000000..ad19a4b489ae --- /dev/null +++ b/compiler/test/dotty/tools/backend/jvm/IincTest.scala @@ -0,0 +1,71 @@ +package dotty.tools.backend.jvm + +import org.junit.Test +import org.junit.Assert._ + +import scala.tools.asm.Opcodes._ + +class IincTest extends DottyBytecodeTest { + import ASMConverters._ + + @Test def increment = test( + """{ + | var i = x + | i += 1 + | i += 54 + | i += 127 + | i -= 1 + | i -= 54 + | i -= 128 + | i + |}""".stripMargin, + List(1, 54, 127, -1, -54, -128) + ) + + @Test def wideIncrement = test( + """{ + | var i = x + | i += 128 + | i += 8765 + | i += 32767 + | i -= 129 + | i -= 8765 + | i -= 32768 + | i + |}""".stripMargin, + List(128, 8765, 32767, -129, -8765, -32768) + ) + + @Test def tooBigForIinc = test( + """{ + | var i = x + | i += 32768 + | i += 56789 + | i += 2147483647 + | i -= 32769 + | i -= 56789 + | i -= 2147483647 + | i + |}""".stripMargin, + Nil + ) + + private def test(code: String, expectedIincs: List[Int])= { + val source = + s"""class Increment { + | def test(x: Int): Int = $code + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Increment.class", directory = false).input + val clsNode = loadClassNode(clsIn) + val meth = getMethod(clsNode, "test") + + val foundIincs = instructionsFromMethod(meth).collect { case iinc: Incr => iinc.incr } + + assertEquals(expectedIincs, foundIincs) + } + } + +} diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 3e9f191ab279..741928203d77 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -397,10 +397,7 @@ class InlineBytecodeTests extends DottyBytecodeTest { List( IntOp(BIPUSH, 10), VarOp(ISTORE, 1), - VarOp(ILOAD, 1), - Op(ICONST_1), - Op(IADD), - VarOp(ISTORE, 1), + Incr(IINC, 1, 1), VarOp(ILOAD, 1), Op(IRETURN), )