Skip to content

Commit 794c95a

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). Fixes scala/bug#7452
1 parent 4ede583 commit 794c95a

File tree

5 files changed

+90
-3
lines changed

5 files changed

+90
-3
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,23 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
7979
case Assign(lhs, rhs) =>
8080
val s = lhs.symbol
8181
val Local(tk, _, idx, _) = locals.getOrMakeLocal(s)
82-
genLoad(rhs, tk)
83-
lineNumber(tree)
84-
bc.store(idx, tk)
82+
83+
rhs match {
84+
case Apply(fun @ Select(larg: Ident, nme.ADD), Literal(x) :: Nil)
85+
if larg.symbol == s && tk.isIntSizedType && x.isShortRange =>
86+
lineNumber(tree)
87+
bc.iinc(idx, x.intValue)
88+
89+
case Apply(fun @ Select(larg: Ident, nme.SUB), Literal(x) :: Nil)
90+
if larg.symbol == s && tk.isIntSizedType && Constant(-x.intValue).isShortRange =>
91+
lineNumber(tree)
92+
bc.iinc(idx, -x.intValue)
93+
94+
case _ =>
95+
genLoad(rhs, tk)
96+
lineNumber(tree)
97+
bc.store(idx, tk)
98+
}
8599

86100
case _ =>
87101
genLoad(tree, UNIT)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ abstract class BCodeIdiomatic {
392392

393393
final def load( idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ILOAD, idx, tk) } // can-multi-thread
394394
final def store(idx: Int, tk: BType): Unit = { emitVarInsn(Opcodes.ISTORE, idx, tk) } // can-multi-thread
395+
final def iinc( idx: Int, increment: Int): Unit = jmethod.visitIincInsn(idx, increment) // can-multi-thread
395396

396397
final def aload( tk: BType): Unit = { emitTypeBased(JCodeMethodN.aloadOpcodes, tk) } // can-multi-thread
397398
final def astore(tk: BType): Unit = { emitTypeBased(JCodeMethodN.astoreOpcodes, tk) } // can-multi-thread

test/files/jvm/iinc.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
def increment
2+
iinc 1
3+
iinc 54
4+
iinc 127
5+
iinc -1
6+
iinc -54
7+
iinc -128
8+
end increment
9+
def wideIncrement
10+
iinc 128
11+
iinc 8765
12+
iinc 32767
13+
iinc -129
14+
iinc -8765
15+
iinc -32768
16+
end wideIncrement
17+
def tooBigForIinc
18+
end tooBigForIinc

test/files/jvm/iinc/Increment_1.scala

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
class Increment {
2+
3+
// `iinc`
4+
def increment(x: Int): Int = {
5+
var i = x
6+
i += 1
7+
i += 54
8+
i += 127
9+
i -= 1
10+
i -= 54
11+
i -= 128
12+
i
13+
}
14+
15+
// `wide iinc`
16+
def wideIncrement(x: Int): Int = {
17+
var i = x
18+
i += 128
19+
i += 8765
20+
i += 32767
21+
i -= 129
22+
i -= 8765
23+
i -= 32768
24+
i
25+
}
26+
27+
def tooBigForIinc(x: Int): Int = {
28+
var i = x
29+
i += 32768
30+
i += 56789
31+
i += 2147483647
32+
i -= 32769
33+
i -= 56789
34+
i -= 2147483647
35+
i
36+
}
37+
}

test/files/jvm/iinc/test.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import scala.tools.partest.BytecodeTest
2+
3+
import scala.tools.asm.tree.IincInsnNode
4+
5+
object Test extends BytecodeTest {
6+
def show: Unit = {
7+
val classNode = loadClassNode("Increment")
8+
for (name <- List("increment", "wideIncrement", "tooBigForIinc")) {
9+
println(s"def $name")
10+
getMethod(classNode, name).instructions.toArray().collect {
11+
case insn: IincInsnNode => println(s" iinc ${insn.incr}")
12+
}
13+
println(s"end $name")
14+
}
15+
}
16+
}
17+

0 commit comments

Comments
 (0)