diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index 17b9451c346..bbc8f679818 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -3941,6 +3941,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { public void testJavaPrimitiveType() throws Exception { runTest("compiler/testData/codegen/bytecodeText/intrinsics/javaPrimitiveType.kt"); } + + @Test + @TestMetadata("postfixIncrDecr.kt") + public void testPostfixIncrDecr() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt"); + } } @Nested diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index 561da8f3183..5ccdf883668 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -811,7 +811,6 @@ class JvmSymbols( addFunction("equals", irBuiltIns.booleanType, isStatic = true).apply { addValueParameter("a", arrayType) addValueParameter("b", arrayType) - } } @@ -856,6 +855,19 @@ class JvmSymbols( val remainderUnsignedLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("remainderUnsigned") val toUnsignedStringLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("toUnsignedString") + val intPostfixIncr = createPostfixIncrDecrFun("", irBuiltIns.intType) + val intPostfixDecr = createPostfixIncrDecrFun("", irBuiltIns.intType) + + private fun createPostfixIncrDecrFun(intrinsicName: String, type: IrType): IrSimpleFunctionSymbol = + irFactory.buildFun { + name = Name.special(intrinsicName) + origin = IrDeclarationOrigin.IR_BUILTINS_STUB + }.apply { + parent = kotlinJvmInternalPackage + addValueParameter("value", type) + returnType = type + }.symbol + private fun createJavaPrimitiveClass(fqName: FqName, type: IrType): IrClassSymbol = createClass(fqName) { klass -> klass.addFunction("compareUnsigned", irBuiltIns.intType, isStatic = true).apply { addValueParameter("x", type) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index ff499739a33..989c2e90292 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -87,7 +87,9 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) { symbols.throwTypeCastException.toKey()!! to ThrowException(Type.getObjectType("kotlin/TypeCastException")), symbols.throwUnsupportedOperationException.toKey()!! to ThrowException(Type.getObjectType("java/lang/UnsupportedOperationException")), symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException, - symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic + symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic, + symbols.intPostfixIncr.toKey()!! to PostfixIinc(1), + symbols.intPostfixDecr.toKey()!! to PostfixIinc(-1) ) + numberConversionMethods() + unaryFunForPrimitives("plus", UnaryPlus) + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt new file mode 100644 index 00000000000..4d4f1cfc517 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.intrinsics + +import org.jetbrains.kotlin.backend.jvm.codegen.* +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.org.objectweb.asm.Type + +class PostfixIinc(private val delta: Int) : IntrinsicMethod() { + override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { + val argument = expression.getValueArgument(0) as? IrGetValue + ?: error("IrGetValue expected: ${expression.dump()}") + val varIndex = codegen.frameMap.getIndex(argument.symbol) + if (varIndex == -1) + error("Unmapped variable: ${argument.render()}") + argument.accept(codegen, data).materialize() + codegen.mv.iinc(varIndex, delta) + return MaterialValue(codegen, Type.INT_TYPE, argument.type) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt index bb8fe1087dd..8c15a12892d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +import org.jetbrains.kotlin.util.OperatorNameConventions internal val jvmOptimizationLoweringPhase = makeIrFilePhase( ::JvmOptimizationLowering, @@ -405,11 +406,47 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass } } + if (expression.origin == IrStatementOrigin.POSTFIX_DECR || expression.origin == IrStatementOrigin.POSTFIX_INCR) { + expression.rewritePostfixIncrDecr()?.let { return it } + } + expression.transformChildren(this, data) removeUnnecessaryTemporaryVariables(expression.statements) return expression } + private fun IrContainerExpression.rewritePostfixIncrDecr(): IrCall? { + return when (origin) { + IrStatementOrigin.POSTFIX_INCR, IrStatementOrigin.POSTFIX_DECR -> { + val tmpVar = this.statements[0] as? IrVariable ?: return null + val getIncrVar = tmpVar.initializer as? IrGetValue ?: return null + if (!getIncrVar.type.isInt()) return null + + val setVar = this.statements[1] as? IrSetValue ?: return null + if (setVar.symbol != getIncrVar.symbol) return null + val setVarValue = setVar.value as? IrCall ?: return null + val calleeName = setVarValue.symbol.owner.name + if (calleeName != OperatorNameConventions.INC && calleeName != OperatorNameConventions.DEC) return null + val calleeArg = setVarValue.dispatchReceiver as? IrGetValue ?: return null + if (calleeArg.symbol != tmpVar.symbol) return null + + val getTmpVar = this.statements[2] as? IrGetValue ?: return null + if (getTmpVar.symbol != tmpVar.symbol) return null + + val intrinsicSymbol = + if (calleeName == OperatorNameConventions.INC) + context.ir.symbols.intPostfixIncr + else + context.ir.symbols.intPostfixDecr + + return IrCallImpl.fromSymbolOwner(this.startOffset, this.endOffset, intrinsicSymbol) + .apply { putValueArgument(0, getIncrVar) } + } + else -> + null + } + } + override fun visitGetValue(expression: IrGetValue, data: IrClass?): IrExpression { // Replace IrGetValue of an immutable temporary variable with a constant // initializer with the constant initializer. diff --git a/compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt b/compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt new file mode 100644 index 00000000000..18c888f5636 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt @@ -0,0 +1,34 @@ +// IGNORE_BACKEND_FIR: JVM_IR + +fun use(i: Int) {} + +fun testPostfixIncr0() { + var k = 0 + k++ + use(k) +} + +fun testPostfixIncr1() { + var k = 0 + val t = k++ + use(k) + use(t) +} + +fun testPostfixDecr0() { + var k = 0 + k-- + use(k) +} + +fun testPostfixDecr1() { + var k = 0 + val t = k-- + use(k) + use(t) +} + +// 6 ISTORE +// 8 ILOAD +// 0 ICONST_1 +// 4 IINC \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java index fc25fb0b113..367f7ae0400 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java @@ -3797,6 +3797,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { public void testJavaPrimitiveType() throws Exception { runTest("compiler/testData/codegen/bytecodeText/intrinsics/javaPrimitiveType.kt"); } + + @Test + @TestMetadata("postfixIncrDecr.kt") + public void testPostfixIncrDecr() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt"); + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index 3e550e343fc..20e45d114a4 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -3941,6 +3941,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { public void testJavaPrimitiveType() throws Exception { runTest("compiler/testData/codegen/bytecodeText/intrinsics/javaPrimitiveType.kt"); } + + @Test + @TestMetadata("postfixIncrDecr.kt") + public void testPostfixIncrDecr() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/intrinsics/postfixIncrDecr.kt"); + } } @Nested