[JVM_IR] Use iinc for incrementing Int variables.

Fix line number generation for assignments where the right-hand
side of the assignment is not on the same line.

Fix line number generation for intrinsics functions where the
function is not on the same line as the last argument.

Be careful to not break stepping behavior with the iinc
optimizations.
This commit is contained in:
Mads Ager
2020-03-26 15:03:49 +01:00
committed by Dmitry Petrov
parent 58685be4e2
commit 5570a5fe74
13 changed files with 205 additions and 19 deletions
@@ -149,13 +149,15 @@ class ExpressionCodegen(
private fun markNewLabel() = Label().apply { mv.visitLabel(this) } private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
private fun getLineNumberForOffset(offset: Int): Int = fileEntry?.getLineNumber(offset)?.plus(1) ?: -1
private fun IrElement.markLineNumber(startOffset: Boolean) { private fun IrElement.markLineNumber(startOffset: Boolean) {
val offset = if (startOffset) this.startOffset else endOffset val offset = if (startOffset) this.startOffset else endOffset
if (offset < 0) { if (offset < 0) {
return return
} }
if (fileEntry != null) { if (fileEntry != null) {
val lineNumber = fileEntry.getLineNumber(offset) + 1 val lineNumber = getLineNumberForOffset(offset)
assert(lineNumber > 0) assert(lineNumber > 0)
if (lastLineNumber != lineNumber) { if (lastLineNumber != lineNumber) {
lastLineNumber = lineNumber lastLineNumber = lineNumber
@@ -164,6 +166,8 @@ class ExpressionCodegen(
} }
} }
fun markLineNumber(element: IrElement) = element.markLineNumber(true)
// TODO remove // TODO remove
fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue { fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue {
if (expression.attributeOwnerId === context.fakeContinuation) { if (expression.attributeOwnerId === context.fakeContinuation) {
@@ -591,9 +595,73 @@ class ExpressionCodegen(
throw AssertionError("Non-mapped local declaration: $dump\n in ${irFunction.dump()}") throw AssertionError("Non-mapped local declaration: $dump\n in ${irFunction.dump()}")
} }
private fun handlePlusMinus(expression: IrSetVariable, value: IrExpression?, isMinus: Boolean): Boolean {
if (value is IrConst<*> && value.kind == IrConstKind.Int) {
val delta = (value as IrConst<Int>).value
val upperBound = Byte.MAX_VALUE.toInt() + (if (isMinus) 1 else 0)
val lowerBound = Byte.MIN_VALUE.toInt() + (if (isMinus) 1 else 0)
if (delta in lowerBound..upperBound) {
expression.markLineNumber(startOffset = true)
mv.iinc(findLocalIndex(expression.symbol), if (isMinus) -delta else delta)
return true
}
}
return false
}
private fun hasSameLineNumber(element0: IrElement, element1: IrElement): Boolean =
getLineNumberForOffset(element0.startOffset) == getLineNumberForOffset(element1.startOffset)
// Use iinc for all for the set var int special cases where we can.
// Be careful to make sure that debugging behavior does not change and
// only perform the optimization if that can be done without losing
// line number information.
private fun handleIntVariableSpecialCases(expression: IrSetVariable): Boolean {
if (expression.symbol.owner.type.isInt()) {
when (expression.origin) {
IrStatementOrigin.PREFIX_INCR, IrStatementOrigin.PREFIX_DECR -> {
expression.markLineNumber(startOffset = true)
mv.iinc(findLocalIndex(expression.symbol), if (expression.origin == IrStatementOrigin.PREFIX_INCR) 1 else -1)
return true
}
IrStatementOrigin.PLUSEQ, IrStatementOrigin.MINUSEQ -> {
val argument = (expression.value as IrCall).getValueArgument(0)!!
if (!hasSameLineNumber(argument, expression)) {
return false
}
return handlePlusMinus(expression, argument, expression.origin is IrStatementOrigin.MINUSEQ)
}
IrStatementOrigin.EQ -> {
val value = expression.value
if (!hasSameLineNumber(value, expression)) {
return false
}
if (value is IrCall) {
val receiver = value.dispatchReceiver ?: return false
val symbol = expression.symbol
if (!hasSameLineNumber(receiver, expression)) {
return false
}
if (value.origin == IrStatementOrigin.PLUS || value.origin == IrStatementOrigin.MINUS) {
val argument = value.getValueArgument(0)!!
if (receiver is IrGetValue && receiver.symbol == symbol && hasSameLineNumber(argument, expression)) {
return handlePlusMinus(expression, argument, value.origin == IrStatementOrigin.MINUS)
}
}
}
}
}
}
return false
}
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue { override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue {
expression.markLineNumber(startOffset = true) if (!handleIntVariableSpecialCases(expression)) {
setVariable(expression.symbol, expression.value, data) expression.value.markLineNumber(startOffset = true)
expression.value.accept(this, data).materializeAt(expression.symbol.owner.type)
expression.markLineNumber(startOffset = true)
mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType)
}
return unitValue return unitValue
} }
@@ -7,11 +7,9 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.* import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.commons.Method
@@ -26,7 +24,7 @@ abstract class IntrinsicMethod {
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? = open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
with(codegen) { with(codegen) {
val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner) val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner)
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data) val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data, expression)
stackValue.put(mv) stackValue.put(mv)
return MaterialValue(this, stackValue.type, expression.type) return MaterialValue(this, stackValue.type, expression.type)
} }
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
@@ -46,10 +45,16 @@ object IrIllegalArgumentException : IntrinsicMethod() {
v.athrow() v.athrow()
} }
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { override fun invoke(
v: InstructionAdapter,
codegen: ExpressionCodegen,
data: BlockInfo,
expression: IrFunctionAccessExpression
): StackValue {
codegen.markLineNumber(expression)
v.anew(exceptionTypeDescriptor) v.anew(exceptionTypeDescriptor)
v.dup() v.dup()
return super.invoke(v, codegen, data) return super.invoke(v, codegen, data, expression)
} }
} }
} }
@@ -65,8 +65,14 @@ open class IrIntrinsicFunction(
return returnType return returnType
} }
open fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { open fun invoke(
v: InstructionAdapter,
codegen: ExpressionCodegen,
data: BlockInfo,
expression: IrFunctionAccessExpression
): StackValue {
loadArguments(codegen, data) loadArguments(codegen, data)
codegen.markLineNumber(expression)
return StackValue.onStack(genInvokeInstructionWithResult(v)) return StackValue.onStack(genInvokeInstructionWithResult(v))
} }
@@ -50,10 +50,16 @@ object RangeTo : IntrinsicMethod() {
v.invokespecial(signature.returnType.internalName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, argType, argType), false) v.invokespecial(signature.returnType.internalName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, argType, argType), false)
} }
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { override fun invoke(
v: InstructionAdapter,
codegen: ExpressionCodegen,
data: BlockInfo,
expression: IrFunctionAccessExpression
): StackValue {
codegen.markLineNumber(expression)
v.anew(returnType) v.anew(returnType)
v.dup() v.dup()
return super.invoke(v, codegen, data) return super.invoke(v, codegen, data, expression)
} }
} }
} }
@@ -40,10 +40,6 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
context.state.intrinsics.getIntrinsic(expression.symbol.descriptor) is Not context.state.intrinsics.getIntrinsic(expression.symbol.descriptor) is Not
} }
private fun hasNoSideEffectsForNullCompare(expression: IrExpression): Boolean {
return expression.type.isPrimitiveType() && (expression is IrConst<*> || expression is IrGetValue)
}
private val IrFunction.isObjectEquals private val IrFunction.isObjectEquals
get() = name.asString() == "equals" && get() = name.asString() == "equals" &&
valueParameters.count() == 1 && valueParameters.count() == 1 &&
@@ -0,0 +1,41 @@
fun main(args: Array<String>) {
var i = 10
// -- 4 of these fit in iinc instructions
i += 1
i += 127
i += 128
i += -1
i += -128
i += -129
// -- 4 of these fit in iinc instructions
i -= 1
i -= 128
i -= 129
i -= -1
i -= -127
i -= -128
// -- 4 of these fit in iinc instructions
i = i + 1
i = i + 127
i = i + 128
i = i + -1
i = i + -128
i = i + -129
// -- 4 of these fit in iinc instructions
i = i - 1
i = i - 128
i = i - 129
i = i - -1
i = i - -127
i = i - -128
}
// JVM_IR_TEMPLATES
// 16 IINC
// JVM_TEMPLATES
// 0 IINC
@@ -1,6 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// KT-36641 TODO Generate IINC instruction for prefix increment in JVM_IR
fun main(args: Array<String>) { fun main(args: Array<String>) {
var i = 10 var i = 10
++i ++i
+47
View File
@@ -0,0 +1,47 @@
//FILE: test.kt
fun box() {
var i = 0
++i
i += 1
i +=
1
i -= 1
i -=
1
i = i + 1
i =
i + 1
i =
i +
1
}
// IGNORE_BACKEND: JVM
// The current backend has strange stepping behavior for assignments.
// It generates the line number for the assignment first, and then
// the evaluation of the right hand side with line numbers.
// That leads to the line number with the assignment typically
// not being hit at all as it has no instructions. Also, stepping
// through the evaluation of the right hand side and then hitting
// the line number for the actual assignment makes more sense as
// that is the actual evaluation order.
// LINENUMBERS
// test.kt:3
// test.kt:4
// test.kt:5
// test.kt:6
// test.kt:7
// test.kt:6
// test.kt:8
// test.kt:9
// test.kt:10
// test.kt:9
// test.kt:11
// test.kt:13
// test.kt:12
// test.kt:15
// test.kt:16
// test.kt:15
// test.kt:14
// test.kt:17
@@ -124,6 +124,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/flagsInMultiFileInherit.kt"); runTest("compiler/testData/codegen/bytecodeText/flagsInMultiFileInherit.kt");
} }
@TestMetadata("iincGeneration.kt")
public void testIincGeneration() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/iincGeneration.kt");
}
@TestMetadata("inheritedPropertyAnnotations.kt") @TestMetadata("inheritedPropertyAnnotations.kt")
public void testInheritedPropertyAnnotations() throws Exception { public void testInheritedPropertyAnnotations() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/inheritedPropertyAnnotations.kt"); runTest("compiler/testData/codegen/bytecodeText/inheritedPropertyAnnotations.kt");
@@ -67,6 +67,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest {
runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt"); runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt");
} }
@Test
@TestMetadata("iincStepping.kt")
public void testIincStepping() throws Exception {
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test @Test
@TestMetadata("inlineCallableReference.kt") @TestMetadata("inlineCallableReference.kt")
public void testInlineCallableReference() throws Exception { public void testInlineCallableReference() throws Exception {
@@ -67,6 +67,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt"); runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt");
} }
@Test
@TestMetadata("iincStepping.kt")
public void testIincStepping() throws Exception {
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test @Test
@TestMetadata("inlineCallableReference.kt") @TestMetadata("inlineCallableReference.kt")
public void testInlineCallableReference() throws Exception { public void testInlineCallableReference() throws Exception {
@@ -124,6 +124,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/flagsInMultiFileInherit.kt"); runTest("compiler/testData/codegen/bytecodeText/flagsInMultiFileInherit.kt");
} }
@TestMetadata("iincGeneration.kt")
public void testIincGeneration() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/iincGeneration.kt");
}
@TestMetadata("inheritedPropertyAnnotations.kt") @TestMetadata("inheritedPropertyAnnotations.kt")
public void testInheritedPropertyAnnotations() throws Exception { public void testInheritedPropertyAnnotations() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/inheritedPropertyAnnotations.kt"); runTest("compiler/testData/codegen/bytecodeText/inheritedPropertyAnnotations.kt");