Skip to content

Commit

Permalink
Teach backend to emit iinc instructions
Browse files Browse the repository at this point in the history
The backend is now able to turn `x += 42` into an `iinc 42`
instruction. The optimization only applies to `+=` and `-=`, provided
the the net increment fits inside a signed 16-bit value (the ASM library
handles choosing `iinc` or `wide iinc` as is appropriate).

Ported from scala/scala#9713
  • Loading branch information
harpocrates committed Jul 30, 2021
1 parent 461b30f commit 29de10a
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 13 deletions.
20 changes: 17 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
71 changes: 71 additions & 0 deletions compiler/test/dotty/tools/backend/jvm/IincTest.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
Expand Down

0 comments on commit 29de10a

Please sign in to comment.