diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 749320eafda..df397c41160 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -314,9 +314,10 @@ private val jvmFilePhases = staticDefaultFunctionPhase then syntheticAccessorPhase then + jvmArgumentNullabilityAssertions then toArrayPhase then - jvmOptimizationLoweringPhase then + jvmBuiltinOptimizationLoweringPhase then additionalClassAnnotationPhase then typeOperatorLowering then replaceKFunctionInvokeWithFunctionInvokePhase then 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/JvmBuiltinOptimizationLowering.kt similarity index 72% rename from compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt rename to compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt index d547596abb7..66373d75973 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/JvmBuiltinOptimizationLowering.kt @@ -6,17 +6,15 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass import org.jetbrains.kotlin.codegen.intrinsics.Not -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.builders.irGetField -import org.jetbrains.kotlin.ir.builders.irSetField -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl @@ -26,15 +24,16 @@ import org.jetbrains.kotlin.ir.types.isNullableAny import org.jetbrains.kotlin.ir.types.isPrimitiveType import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -internal val jvmOptimizationLoweringPhase = makeIrFilePhase( - ::JvmOptimizationLowering, +internal val jvmBuiltinOptimizationLoweringPhase = makeIrFilePhase( + ::JvmBuiltinOptimizationLowering, name = "JvmBuiltinOptimizationLowering", - description = "Optimize code for JVM code generation" + description = "Optimize builtin calls for JVM code generation" ) -class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass { +class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass { companion object { fun isNegation(expression: IrExpression, context: JvmBackendContext): Boolean = @@ -72,38 +71,9 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass } override fun lower(irFile: IrFile) { - val transformer = object : IrElementTransformer { - - // Thread the current class through the transformations in order to replace - // final default accessor calls with direct backing field access when - // possible. - override fun visitClass(declaration: IrClass, data: IrClass?): IrStatement { - declaration.transformChildren(this, declaration) - return declaration - } - - // For some functions, we clear the current class field since the code could end up - // in another class then the one it is nested under in the IR. - // TODO: Loosen this up for local functions for lambdas passed as an inline lambda - // argument to an inline function. In that case the code does end up in the current class. - override fun visitFunction(declaration: IrFunction, currentClass: IrClass?): IrStatement { - val codeMightBeGeneratedInDifferentClass = declaration.isSuspend || - declaration.isInline || - declaration.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA - declaration.transformChildren(this, currentClass.takeUnless { codeMightBeGeneratedInDifferentClass }) - return declaration - } - - override fun visitCall(expression: IrCall, currentClass: IrClass?): IrExpression { - expression.transformChildren(this, currentClass) - - if (expression.symbol.owner.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { - if (currentClass == null) return expression - val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return expression - val property = simpleFunction.correspondingPropertySymbol?.owner ?: return expression - if (property.isLateinit) return expression - return optimizePropertyAccess(expression, simpleFunction, property, currentClass) - } + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) if (isNegation(expression, context) && isNegation(expression.dispatchReceiver!!, context)) { return (expression.dispatchReceiver as IrCall).dispatchReceiver!! @@ -145,41 +115,9 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass return expression } - private fun optimizePropertyAccess( - expression: IrCall, - accessor: IrSimpleFunction, - property: IrProperty, - currentClass: IrClass - ): IrExpression { - if (accessor.parentAsClass == currentClass && - property.backingField?.parentAsClass == currentClass && - accessor.modality == Modality.FINAL && - !accessor.isExternal - ) { - val backingField = property.backingField!! - val receiver = expression.dispatchReceiver - return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) { - if (backingField.isStatic && receiver != null) { - // If the field is static, evaluate the receiver for potential side effects. - +receiver.coerceToUnit(context.irBuiltIns) - } - if (accessor.valueParameters.size > 0) { - +irSetField( - receiver.takeUnless { backingField.isStatic }, - backingField, - expression.getValueArgument(expression.valueArgumentsCount - 1)!! - ) - } else { - +irGetField(receiver.takeUnless { backingField.isStatic }, backingField) - } - } - } - return expression - } - - override fun visitWhen(expression: IrWhen, currentClass: IrClass?): IrExpression { + override fun visitWhen(expression: IrWhen): IrExpression { val isCompilerGenerated = expression.origin == null - expression.transformChildren(this, currentClass) + expression.transformChildrenVoid(this) // Remove all branches with constant false condition. expression.branches.removeIf { it.condition.isFalseConst() && isCompilerGenerated @@ -321,19 +259,19 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass } } - override fun visitBlockBody(body: IrBlockBody, currentClass: IrClass?): IrBody { - body.transformChildren(this, currentClass) + override fun visitBlockBody(body: IrBlockBody): IrBody { + body.transformChildrenVoid(this) removeUnnecessaryTemporaryVariables(body.statements) return body } - override fun visitContainerExpression(expression: IrContainerExpression, currentClass: IrClass?): IrExpression { - expression.transformChildren(this, currentClass) + override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { + expression.transformChildrenVoid(this) removeUnnecessaryTemporaryVariables(expression.statements) return expression } - override fun visitGetValue(expression: IrGetValue, currentClass: IrClass?): IrExpression { + override fun visitGetValue(expression: IrGetValue): IrExpression { // Replace IrGetValue of an immutable temporary variable with a constant // initializer with the constant initializer. val variable = expression.symbol.owner @@ -342,7 +280,6 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass else expression } - } - irFile.transformChildren(transformer, null) + }) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt index c1417840ef3..e39973e86c0 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.types.makeNotNull -import org.jetbrains.kotlin.ir.util.coerceToUnit import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -130,7 +129,16 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT if (receiver != null && needBlock) { // Evaluate `dispatchReceiver` for the sake of its side effects, then return `setOrGetExpr`. return context.createIrBuilder(setOrGetExpr.symbol, setOrGetExpr.startOffset, setOrGetExpr.endOffset).irBlock(setOrGetExpr) { - +receiver.coerceToUnit(context.irBuiltIns) + // `coerceToUnit()` is private in InsertImplicitCasts, have to reproduce it here + val receiverVoid = IrTypeOperatorCallImpl( + receiver.startOffset, receiver.endOffset, + context.irBuiltIns.unitType, + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, + context.irBuiltIns.unitType, + receiver + ) + + +receiverVoid setOrGetExpr.receiver = null +setOrGetExpr } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index 16160c94f09..2436d156a16 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -37,7 +37,7 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.util.TypeTranslator -import org.jetbrains.kotlin.ir.util.coerceToUnit +import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.psi2ir.containsNull @@ -147,7 +147,7 @@ internal class InsertImplicitCasts( body.transformPostfix { statements.forEachIndexed { i, irStatement -> if (irStatement is IrExpression) { - body.statements[i] = irStatement.coerceToUnit(irBuiltIns) + body.statements[i] = irStatement.coerceToUnit() } } } @@ -163,7 +163,7 @@ internal class InsertImplicitCasts( if (i == lastIndex) irStatement.cast(type) else - irStatement.coerceToUnit(irBuiltIns) + irStatement.coerceToUnit() } } } @@ -171,7 +171,7 @@ internal class InsertImplicitCasts( override fun visitReturn(expression: IrReturn): IrExpression = expression.transformPostfix { value = if (expression.returnTargetSymbol is IrConstructorSymbol) { - value.coerceToUnit(irBuiltIns) + value.coerceToUnit() } else { val returnTargetDescriptor = expression.returnTarget val isLambdaReturnValue = returnTargetDescriptor is AnonymousFunctionDescriptor @@ -233,7 +233,7 @@ internal class InsertImplicitCasts( override fun visitLoop(loop: IrLoop): IrExpression = loop.transformPostfix { condition = condition.cast(builtIns.booleanType) - body = body?.coerceToUnit(irBuiltIns) + body = body?.coerceToUnit() } override fun visitThrow(expression: IrThrow): IrExpression = @@ -249,7 +249,7 @@ internal class InsertImplicitCasts( aCatch.result = aCatch.result.cast(type) } - finallyExpression = finallyExpression?.coerceToUnit(irBuiltIns) + finallyExpression = finallyExpression?.coerceToUnit() } override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression = @@ -318,7 +318,7 @@ internal class InsertImplicitCasts( return when { expectedType.isUnit() -> - coerceToUnit(irBuiltIns) + coerceToUnit() valueType.isDynamic() && !expectedType.isDynamic() -> if (expectedType.isNullableAny()) @@ -383,6 +383,14 @@ internal class InsertImplicitCasts( ) } + private fun IrExpression.coerceToUnit(): IrExpression { + val valueType = getKotlinType(this) + return coerceToUnitIfNeeded(valueType, irBuiltIns) + } + + private fun getKotlinType(irExpression: IrExpression) = + irExpression.type.originalKotlinType!! + private fun KotlinType.isBuiltInIntegerType(): Boolean = KotlinBuiltIns.isByte(this) || KotlinBuiltIns.isShort(this) || diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 54799e11fef..5d7db2bc5d9 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -155,14 +154,6 @@ fun IrExpression.isTrueConst() = this is IrConst<*> && this.kind == IrConstKind. fun IrExpression.isFalseConst() = this is IrConst<*> && this.kind == IrConstKind.Boolean && this.value == false -fun IrExpression.coerceToUnit(builtins: IrBuiltIns): IrExpression { - val valueType = getKotlinType(this) - return coerceToUnitIfNeeded(valueType, builtins) -} - -private fun getKotlinType(irExpression: IrExpression) = - irExpression.type.toKotlinType() - fun IrExpression.coerceToUnitIfNeeded(valueType: KotlinType, irBuiltIns: IrBuiltIns): IrExpression { return if (KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, irBuiltIns.unitType.toKotlinType())) this diff --git a/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt b/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt index 4a3016e4743..68228903fc5 100644 --- a/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt +++ b/compiler/testData/codegen/bytecodeText/accessorForOverridenVal.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR package b abstract class B { diff --git a/compiler/testData/codegen/bytecodeText/constants/noInlineNonConst.kt b/compiler/testData/codegen/bytecodeText/constants/noInlineNonConst.kt index b505c039a64..22d136b4577 100644 --- a/compiler/testData/codegen/bytecodeText/constants/noInlineNonConst.kt +++ b/compiler/testData/codegen/bytecodeText/constants/noInlineNonConst.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR val x = 1 class A { diff --git a/compiler/testData/codegen/bytecodeText/kt3845.kt b/compiler/testData/codegen/bytecodeText/kt3845.kt index be2bdbaf62d..6c5ecb66962 100644 --- a/compiler/testData/codegen/bytecodeText/kt3845.kt +++ b/compiler/testData/codegen/bytecodeText/kt3845.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR class Example { var a1 = 0