Skip to content

Commit 484541a

Browse files
committed
Teach backend to emit iinc instructions
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
1 parent 461b30f commit 484541a

File tree

3 files changed

+89
-3
lines changed

3 files changed

+89
-3
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,23 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
8888
case Assign(lhs, rhs) =>
8989
val s = lhs.symbol
9090
val Local(tk, _, idx, _) = locals.getOrMakeLocal(s)
91-
genLoad(rhs, tk)
92-
lineNumber(tree)
93-
bc.store(idx, tk)
91+
92+
rhs match {
93+
case Apply(Select(larg: Ident, nme.ADD), Literal(x) :: Nil)
94+
if larg.symbol == s && tk.isIntSizedType && x.isShortRange =>
95+
lineNumber(tree)
96+
bc.iinc(idx, x.intValue)
97+
98+
case Apply(Select(larg: Ident, nme.SUB), Literal(x) :: Nil)
99+
if larg.symbol == s && tk.isIntSizedType && Constant(-x.intValue).isShortRange =>
100+
lineNumber(tree)
101+
bc.iinc(idx, -x.intValue)
102+
103+
case _ =>
104+
genLoad(rhs, tk)
105+
lineNumber(tree)
106+
bc.store(idx, tk)
107+
}
94108

95109
case _ =>
96110
genLoad(tree, UNIT)

compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ trait BCodeIdiomatic {
433433

434434
final def load( idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ILOAD, idx, tk) } // can-multi-thread
435435
final def store(idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ISTORE, idx, tk) } // can-multi-thread
436+
final def iinc( idx: Int, increment: Int): Unit = jmethod.visitIincInsn(idx, increment) // can-multi-thread
436437

437438
final def aload( tk: BType): Unit = { emitTypeBased(JCodeMethodN.aloadOpcodes, tk) } // can-multi-thread
438439
final def astore(tk: BType): Unit = { emitTypeBased(JCodeMethodN.astoreOpcodes, tk) } // can-multi-thread
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package dotty.tools.backend.jvm
2+
3+
import org.junit.Test
4+
import org.junit.Assert._
5+
6+
import scala.tools.asm.Opcodes._
7+
8+
class IincTest extends DottyBytecodeTest {
9+
import ASMConverters._
10+
11+
@Test def increment = test(
12+
"""{
13+
| var i = x
14+
| i += 1
15+
| i += 54
16+
| i += 127
17+
| i -= 1
18+
| i -= 54
19+
| i -= 128
20+
| i
21+
|}""".stripMargin,
22+
List(1, 54, 127, -1, -54, -128)
23+
)
24+
25+
@Test def wideIncrement = test(
26+
"""{
27+
| var i = x
28+
| i += 128
29+
| i += 8765
30+
| i += 32767
31+
| i -= 129
32+
| i -= 8765
33+
| i -= 32768
34+
| i
35+
|}""".stripMargin,
36+
List(128, 8765, 32767, -129, -8765, -32768)
37+
)
38+
39+
@Test def tooBigForIinc = test(
40+
"""{
41+
| var i = x
42+
| i += 32768
43+
| i += 56789
44+
| i += 2147483647
45+
| i -= 32769
46+
| i -= 56789
47+
| i -= 2147483647
48+
| i
49+
|}""".stripMargin,
50+
Nil
51+
)
52+
53+
private def test(code: String, expectedIincs: List[Int])= {
54+
val source =
55+
s"""class Increment {
56+
| def test(x: Int): Int = $code
57+
|}
58+
""".stripMargin
59+
60+
checkBCode(source) { dir =>
61+
val clsIn = dir.lookupName("Increment.class", directory = false).input
62+
val clsNode = loadClassNode(clsIn)
63+
val meth = getMethod(clsNode, "test")
64+
65+
val foundIincs = instructionsFromMethod(meth).collect { case iinc: Incr => iinc.incr }
66+
67+
assertEquals(expectedIincs, foundIincs)
68+
}
69+
}
70+
71+
}

0 commit comments

Comments
 (0)