diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/optimizations/PropertyAccessorInlineLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/optimizations/PropertyAccessorInlineLowering.kt index 8c522da87fb..3640f1a4cee 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/optimizations/PropertyAccessorInlineLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/optimizations/PropertyAccessorInlineLowering.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.backend.common.ir.isPure +import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -127,8 +128,14 @@ class PropertyAccessorInlineLowering(private val context: CommonBackendContext) private fun isSimpleSetter(callee: IrSimpleFunction, backingField: IrField): Boolean { val body = callee.body?.let { it as IrBlockBody } ?: return false - - val stmt = body.statements.singleOrNull() ?: return false + val statementsSizeCheck = when (body.statements.size) { + 1 -> true + // In K/N backend this lowering should be called after devirtualization. At this point IrReturns are already added. + 2 -> (body.statements[1] as? IrReturn)?.value?.type?.isUnit() == true + else -> false + } + if (!statementsSizeCheck) return false + val stmt = body.statements[0] val setFieldStmt = stmt as? IrSetField ?: return false if (setFieldStmt.symbol !== backingField.symbol) return false diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 90df9aec410..413c22f83d9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -394,14 +394,15 @@ internal val bitcodePhase = NamedCompilerPhase( name = "Bitcode", description = "LLVM Bitcode generation", lower = contextLLVMSetupPhase then - propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields - // from dependencies can be changed during lowerings. returnsInsertionPhase then buildDFGPhase then devirtualizationAnalysisPhase then dcePhase then removeRedundantCallsToFileInitializersPhase then devirtualizationPhase then + propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields + // from dependencies can be changed during lowerings. + inlineClassPropertyAccessorsPhase then redundantCoercionsCleaningPhase then createLLVMDeclarationsPhase then ghaPhase then @@ -493,6 +494,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { // Inline accessors only in optimized builds due to separate compilation and possibility to get broken // debug information. disableUnless(propertyAccessorInlinePhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) + disableUnless(inlineClassPropertyAccessorsPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(removeRedundantCallsToFileInitializersPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 6fd4d4e0d41..5d07650ed9a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.phaser.namedUnitPhase import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD +import org.jetbrains.kotlin.backend.konan.lower.InlineClassPropertyAccessorsLowering import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering import org.jetbrains.kotlin.backend.konan.optimizations.* @@ -121,6 +122,12 @@ internal val returnsInsertionPhase = makeKonanModuleOpPhase( op = { context, irModule -> irModule.files.forEach { ReturnsInsertionLowering(context).lower(it) } } ) +internal val inlineClassPropertyAccessorsPhase = makeKonanModuleOpPhase( + name = "InlineClassPropertyAccessorsLowering", + description = "Inline class property accessors", + op = { context, irModule -> irModule.files.forEach { InlineClassPropertyAccessorsLowering(context).lower(it) } } +) + internal val devirtualizationAnalysisPhase = makeKonanModuleOpPhase( name = "DevirtualizationAnalysis", description = "Devirtualization analysis", @@ -201,11 +208,6 @@ internal val dcePhase = makeKonanModuleOpPhase( } }) - // TODO: Bridge function normally calls it's target, but it could be optimized out by Autoboxing and InlinePropertyAccessor - // lowerings. But Devirtualization doesn't handle this correctly, and can replace bridge call with call of it's target. - // So it's safer not to remove target if bridge is preserved, even if target itself is not called directly - referencedFunctions.addAll(referencedFunctions.mapNotNull { it.origin.safeAs()?.bridgeTarget }) - context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() { override fun visitFile(declaration: IrFile): IrFile { declaration.declarations.removeAll { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 5dce6db9442..0cb19197b8a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -311,30 +311,6 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT return declaration } - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid(this) - // Make replacement only in optimized builds due to separate compilation and possibility to get broken - // debug information. - if (!context.shouldOptimize()) - return expression - - val property = expression.symbol.owner.correspondingPropertySymbol?.owner ?: return expression - - property.parent.let { - if (it is IrClass && it.isInline && property.backingField != null) { - expression.dispatchReceiver?.let { receiver -> - return builder.at(expression) - .irCall(symbols.reinterpret, expression.type, listOf(receiver.type, expression.type)) - .apply { - extensionReceiver = receiver - } - } - } - } - - return expression - } - private fun IrBuilderWithScope.irIsNull(expression: IrExpression): IrExpression { val binary = expression.type.computeBinaryType() return when (binary) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InlineClassPropertyAccessorsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InlineClassPropertyAccessorsLowering.kt new file mode 100644 index 00000000000..fb0cbf2c284 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InlineClassPropertyAccessorsLowering.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + + +import org.jetbrains.kotlin.backend.common.FileLoweringPass + +import org.jetbrains.kotlin.backend.common.lower.* +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.visitors.* + + +/** + * Boxes and unboxes values of value types when necessary. + */ +internal class InlineClassPropertyAccessorsLowering(val context: Context) : FileLoweringPass { + + private val transformer = InlineClassAccessorsTransformer(context) + + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(transformer) + } + +} + +private class InlineClassAccessorsTransformer(private val context: Context) : IrBuildingTransformer(context) { + + private val symbols = context.ir.symbols + + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + + val property = expression.symbol.owner.correspondingPropertySymbol?.owner ?: return expression + + property.parent.let { + if (it is IrClass && it.isInline && property.backingField != null) { + expression.dispatchReceiver?.let { receiver -> + return builder.at(expression) + .irCall(symbols.reinterpret, expression.type, listOf(receiver.type, expression.type)) + .apply { + extensionReceiver = receiver + } + } + } + } + + return expression + } +}