diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 6dc018a7456..164cbf9a11c 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -780,80 +780,17 @@ class ExpressionCodegen( throw AssertionError("Non-mapped local declaration: ${irSymbol.owner.dump()}\n in ${irFunction.dump()}") } - private fun handlePlusMinus(expression: IrSetValue, value: IrExpression?, isMinus: Boolean): Boolean { - if (value is IrConst<*> && value.kind == IrConstKind.Int) { - @Suppress("UNCHECKED_CAST") - val delta = (value as IrConst).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: IrSetValue): 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 visitSetValue(expression: IrSetValue, data: BlockInfo): PromisedValue { - if (!handleIntVariableSpecialCases(expression)) { - expression.value.markLineNumber(startOffset = true) - expression.value.accept(this, data).materializeAt(expression.symbol.owner.type) - // We set the value of parameters only for default values. The inliner accepts only - // a very specific bytecode pattern for default arguments and does not tolerate a - // line number on the store. Therefore, if we are storing to a parameter, we do not - // output a line number for the store. - if (expression.symbol !is IrValueParameterSymbol) { - expression.markLineNumber(startOffset = true) - } - mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType) + expression.value.markLineNumber(startOffset = true) + expression.value.accept(this, data).materializeAt(expression.symbol.owner.type) + // We set the value of parameters only for default values. The inliner accepts only + // a very specific bytecode pattern for default arguments and does not tolerate a + // line number on the store. Therefore, if we are storing to a parameter, we do not + // output a line number for the store. + if (expression.symbol !is IrValueParameterSymbol) { + expression.markLineNumber(startOffset = true) } + mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType) return unitValue } @@ -1414,22 +1351,26 @@ class ExpressionCodegen( wrapIntoKClass: Boolean, data: BlockInfo ): PromisedValue { - if (classReference is IrGetClass) { - // TODO transform one sort of access into the other? - JavaClassProperty.invokeWith(classReference.argument.accept(this, data)) - } else if (classReference is IrClassReference) { - val classType = classReference.classType - val classifier = classType.classifierOrNull - if (classifier is IrTypeParameterSymbol) { - val success = putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS) - assert(success) { - "Non-reified type parameter under ::class should be rejected by type checker: ${classType.render()}" - } + when (classReference) { + is IrGetClass -> { + // TODO transform one sort of access into the other? + JavaClassProperty.invokeWith(classReference.argument.accept(this, data)) } + is IrClassReference -> { + val classType = classReference.classType + val classifier = classType.classifierOrNull + if (classifier is IrTypeParameterSymbol) { + val success = putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS) + assert(success) { + "Non-reified type parameter under ::class should be rejected by type checker: ${classType.render()}" + } + } - generateClassInstance(mv, classType, typeMapper) - } else { - throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}") + generateClassInstance(mv, classType, typeMapper) + } + else -> { + throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}") + } } if (wrapIntoKClass) { diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntIncr.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntIncr.kt new file mode 100644 index 00000000000..17e2dd082b0 --- /dev/null +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntIncr.kt @@ -0,0 +1,37 @@ +/* + * 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.* +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.org.objectweb.asm.Type + +class IntIncr(private val isPrefix: Boolean) : IntrinsicMethod() { + override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue { + val irGetValue = expression.getValueArgument(0) as? IrGetValue + ?: error("IrGetValue expected as valueArgument #0: ${expression.dump()}") + val irDelta = expression.getValueArgument(1) as? IrConst<*> + ?: error("IrConst expected as valueArgument #1: ${expression.dump()}") + if (irDelta.kind != IrConstKind.Int) + error("Int const expected: ${irDelta.dump()}") + val delta = IrConstKind.Int.valueOf(irDelta) + if (delta > Byte.MAX_VALUE || delta < Byte.MIN_VALUE) + error("Int const should be in (Byte.MIN_VALUE .. Byte.MAX_VALUE): ${irDelta.dump()}") + val varIndex = codegen.frameMap.getIndex(irGetValue.symbol) + if (varIndex == -1) + error("Unmapped variable: ${irGetValue.render()}") + if (isPrefix) { + codegen.mv.iinc(varIndex, delta) + irGetValue.accept(codegen, data).materialize() + } else { + irGetValue.accept(codegen, data).materialize() + codegen.mv.iinc(varIndex, delta) + } + return MaterialValue(codegen, Type.INT_TYPE, irGetValue.type) + } +} diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index 6e8afe0b6c4..2a2bbb633da 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -90,8 +90,8 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) { symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException, symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic, symbols.jvmDebuggerInvokeSpecialIntrinsic.toKey()!! to JvmDebuggerInvokeSpecial, - symbols.intPostfixIncr.toKey()!! to PostfixIinc(1), - symbols.intPostfixDecr.toKey()!! to PostfixIinc(-1) + symbols.intPostfixIncrDecr.toKey()!! to IntIncr(isPrefix = false), + symbols.intPrefixIncrDecr.toKey()!! to IntIncr(isPrefix = true) ) + numberConversionMethods() + unaryFunForPrimitives("plus", UnaryPlus) + diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt deleted file mode 100644 index 4d4f1cfc517..00000000000 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/PostfixIinc.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt index 7279f317f89..7cf3fa1eeed 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin import org.jetbrains.kotlin.backend.jvm.ir.IrInlineScopeResolver import org.jetbrains.kotlin.backend.jvm.ir.findInlineCallSites import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrFileEntry import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.irGetField import org.jetbrains.kotlin.ir.builders.irSetField @@ -25,6 +27,7 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.* @@ -72,10 +75,14 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass } override fun lower(irFile: IrFile) { - irFile.transformChildren(Transformer(irFile.findInlineCallSites(context)), null) + irFile.transformChildren(Transformer(irFile.fileEntry, irFile.findInlineCallSites(context)), null) } - private inner class Transformer(private val inlineScopeResolver: IrInlineScopeResolver) : IrElementTransformer { + private inner class Transformer( + private val fileEntry: IrFileEntry, + private val inlineScopeResolver: IrInlineScopeResolver + ) : IrElementTransformer { + private val dontTouchTemporaryVals = HashSet() override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclaration?): IrStatement = @@ -368,39 +375,36 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass } } } - + private fun IrStatement.isLoopVariable() = this is IrVariable && origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE 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 + if (origin != IrStatementOrigin.POSTFIX_INCR && origin != IrStatementOrigin.POSTFIX_DECR) 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 tmpVar = this.statements[0] as? IrVariable ?: return null + val getIncrVar = tmpVar.initializer as? IrGetValue ?: return null + if (!getIncrVar.type.isInt()) return null - val getTmpVar = this.statements[2] as? IrGetValue ?: return null - if (getTmpVar.symbol != tmpVar.symbol) 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 intrinsicSymbol = - if (calleeName == OperatorNameConventions.INC) - context.ir.symbols.intPostfixIncr - else - context.ir.symbols.intPostfixDecr + val getTmpVar = this.statements[2] as? IrGetValue ?: return null + if (getTmpVar.symbol != tmpVar.symbol) return null - return IrCallImpl.fromSymbolOwner(this.startOffset, this.endOffset, intrinsicSymbol) - .apply { putValueArgument(0, getIncrVar) } - } - else -> - null + val delta = if (calleeName == OperatorNameConventions.INC) + IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, 1) + else + IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, -1) + + return IrCallImpl.fromSymbolOwner(this.startOffset, this.endOffset, context.ir.symbols.intPostfixIncrDecr).apply { + putValueArgument(0, getIncrVar) + putValueArgument(1, delta) } } @@ -417,6 +421,81 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass expression } } + + override fun visitSetValue(expression: IrSetValue, data: IrDeclaration?): IrExpression { + expression.transformChildren(this, data) + return rewritePrefixIncrDecr(expression) ?: expression + } + + private fun rewritePrefixIncrDecr(expression: IrSetValue): IrExpression? { + if (!expression.symbol.owner.type.isInt()) + return null + when (expression.origin) { + IrStatementOrigin.PREFIX_INCR, IrStatementOrigin.PREFIX_DECR -> { + return prefixIncr(expression, if (expression.origin == IrStatementOrigin.PREFIX_INCR) 1 else -1) + } + IrStatementOrigin.PLUSEQ, IrStatementOrigin.MINUSEQ -> { + val argument = (expression.value as IrCall).getValueArgument(0)!! + if (!hasSameLineNumber(argument, expression)) { + return null + } + return rewriteCompoundAssignmentAsPrefixIncrDecr(expression, argument, expression.origin is IrStatementOrigin.MINUSEQ) + } + IrStatementOrigin.EQ -> { + val value = expression.value + if (!hasSameLineNumber(value, expression)) { + return null + } + if (value is IrCall) { + val receiver = value.dispatchReceiver + ?: return null + val symbol = expression.symbol + if (!hasSameLineNumber(receiver, expression)) { + return null + } + 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 rewriteCompoundAssignmentAsPrefixIncrDecr( + expression, argument, value.origin == IrStatementOrigin.MINUS + ) + } + } + } + } + } + return null + } + + private fun rewriteCompoundAssignmentAsPrefixIncrDecr( + expression: IrSetValue, + value: IrExpression?, + isMinus: Boolean + ): IrExpression? { + if (value is IrConst<*> && value.kind == IrConstKind.Int) { + val delta = IrConstKind.Int.valueOf(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) { + return prefixIncr(expression, if (isMinus) -delta else delta) + } + } + return null + } + + private fun prefixIncr(expression: IrSetValue, delta: Int): IrExpression { + val startOffset = expression.startOffset + val endOffset = expression.endOffset + return IrCallImpl.fromSymbolOwner(startOffset, endOffset, context.ir.symbols.intPrefixIncrDecr).apply { + putValueArgument(0, IrGetValueImpl(startOffset, endOffset, expression.symbol)) + putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, delta)) + } + } + + private fun hasSameLineNumber(e1: IrElement, e2: IrElement) = + getLineNumberForOffset(e1.startOffset) == getLineNumberForOffset(e2.startOffset) + + private fun getLineNumberForOffset(offset: Int): Int = + fileEntry.getLineNumber(offset) + 1 } } - 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 0125b79ce06..96ae75c6de5 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 @@ -922,17 +922,18 @@ class JvmSymbols( val remainderUnsignedLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("remainderUnsigned") val toUnsignedStringLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("toUnsignedString") - val intPostfixIncr = createPostfixIncrDecrFun("", irBuiltIns.intType) - val intPostfixDecr = createPostfixIncrDecrFun("", irBuiltIns.intType) + val intPostfixIncrDecr = createIncrDecrFun("") + val intPrefixIncrDecr = createIncrDecrFun("") - private fun createPostfixIncrDecrFun(intrinsicName: String, type: IrType): IrSimpleFunctionSymbol = + private fun createIncrDecrFun(intrinsicName: String): IrSimpleFunctionSymbol = irFactory.buildFun { name = Name.special(intrinsicName) origin = IrDeclarationOrigin.IR_BUILTINS_STUB }.apply { parent = kotlinJvmInternalPackage - addValueParameter("value", type) - returnType = type + addValueParameter("value", irBuiltIns.intType) + addValueParameter("delta", irBuiltIns.intType) + returnType = irBuiltIns.intType }.symbol private fun createJavaPrimitiveClass(fqName: FqName, type: IrType): IrClassSymbol = createClass(fqName) { klass ->