[K/N] Run inline property accessors later to inline all cases including devirtualized

This commit is contained in:
Elena Lepilkina
2021-09-13 17:34:17 +03:00
committed by Space
parent b32ab48596
commit bdb61ec5d7
5 changed files with 75 additions and 33 deletions
@@ -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.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.backend.common.ir.isPure 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.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid 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 { private fun isSimpleSetter(callee: IrSimpleFunction, backingField: IrField): Boolean {
val body = callee.body?.let { it as IrBlockBody } ?: return false val body = callee.body?.let { it as IrBlockBody } ?: return false
val statementsSizeCheck = when (body.statements.size) {
val stmt = body.statements.singleOrNull() ?: return false 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 val setFieldStmt = stmt as? IrSetField ?: return false
if (setFieldStmt.symbol !== backingField.symbol) return false if (setFieldStmt.symbol !== backingField.symbol) return false
@@ -394,14 +394,15 @@ internal val bitcodePhase = NamedCompilerPhase(
name = "Bitcode", name = "Bitcode",
description = "LLVM Bitcode generation", description = "LLVM Bitcode generation",
lower = contextLLVMSetupPhase then lower = contextLLVMSetupPhase then
propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields
// from dependencies can be changed during lowerings.
returnsInsertionPhase then returnsInsertionPhase then
buildDFGPhase then buildDFGPhase then
devirtualizationAnalysisPhase then devirtualizationAnalysisPhase then
dcePhase then dcePhase then
removeRedundantCallsToFileInitializersPhase then removeRedundantCallsToFileInitializersPhase then
devirtualizationPhase 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 redundantCoercionsCleaningPhase then
createLLVMDeclarationsPhase then createLLVMDeclarationsPhase then
ghaPhase 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 // Inline accessors only in optimized builds due to separate compilation and possibility to get broken
// debug information. // debug information.
disableUnless(propertyAccessorInlinePhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(propertyAccessorInlinePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(inlineClassPropertyAccessorsPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(removeRedundantCallsToFileInitializersPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(removeRedundantCallsToFileInitializersPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.phaser.namedUnitPhase
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis 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.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.RedundantCoercionsCleaner
import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering
import org.jetbrains.kotlin.backend.konan.optimizations.* 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) } } 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( internal val devirtualizationAnalysisPhase = makeKonanModuleOpPhase(
name = "DevirtualizationAnalysis", name = "DevirtualizationAnalysis",
description = "Devirtualization analysis", 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<DECLARATION_ORIGIN_BRIDGE_METHOD>()?.bridgeTarget })
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() { context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitFile(declaration: IrFile): IrFile { override fun visitFile(declaration: IrFile): IrFile {
declaration.declarations.removeAll { declaration.declarations.removeAll {
@@ -311,30 +311,6 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
return declaration 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 { private fun IrBuilderWithScope.irIsNull(expression: IrExpression): IrExpression {
val binary = expression.type.computeBinaryType() val binary = expression.type.computeBinaryType()
return when (binary) { return when (binary) {
@@ -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
}
}