JVM_IR: Use direct field access instead of calling certain accessors.
Final default properties accessors that access a backing field on the same class can be replaced by direct field use. Perform the optimization late in the pipeline to allow lowerings to expose more opportunities for optimizations.
This commit is contained in:
@@ -314,10 +314,9 @@ private val jvmFilePhases =
|
||||
staticDefaultFunctionPhase then
|
||||
syntheticAccessorPhase then
|
||||
|
||||
|
||||
jvmArgumentNullabilityAssertions then
|
||||
toArrayPhase then
|
||||
jvmBuiltinOptimizationLoweringPhase then
|
||||
jvmOptimizationLoweringPhase then
|
||||
additionalClassAnnotationPhase then
|
||||
typeOperatorLowering then
|
||||
replaceKFunctionInvokeWithFunctionInvokePhase then
|
||||
|
||||
+84
-21
@@ -6,15 +6,17 @@
|
||||
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.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.builders.irGetField
|
||||
import org.jetbrains.kotlin.ir.builders.irSetField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
@@ -24,16 +26,15 @@ 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.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
|
||||
internal val jvmBuiltinOptimizationLoweringPhase = makeIrFilePhase(
|
||||
::JvmBuiltinOptimizationLowering,
|
||||
internal val jvmOptimizationLoweringPhase = makeIrFilePhase(
|
||||
::JvmOptimizationLowering,
|
||||
name = "JvmBuiltinOptimizationLowering",
|
||||
description = "Optimize builtin calls for JVM code generation"
|
||||
description = "Optimize code for JVM code generation"
|
||||
)
|
||||
|
||||
class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass {
|
||||
class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass {
|
||||
|
||||
companion object {
|
||||
fun isNegation(expression: IrExpression, context: JvmBackendContext): Boolean =
|
||||
@@ -71,9 +72,38 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
|
||||
}
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val transformer = object : IrElementTransformer<IrClass?> {
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
if (isNegation(expression, context) && isNegation(expression.dispatchReceiver!!, context)) {
|
||||
return (expression.dispatchReceiver as IrCall).dispatchReceiver!!
|
||||
@@ -115,9 +145,41 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen): IrExpression {
|
||||
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 {
|
||||
val isCompilerGenerated = expression.origin == null
|
||||
expression.transformChildrenVoid(this)
|
||||
expression.transformChildren(this, currentClass)
|
||||
// Remove all branches with constant false condition.
|
||||
expression.branches.removeIf {
|
||||
it.condition.isFalseConst() && isCompilerGenerated
|
||||
@@ -259,19 +321,19 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
body.transformChildrenVoid(this)
|
||||
override fun visitBlockBody(body: IrBlockBody, currentClass: IrClass?): IrBody {
|
||||
body.transformChildren(this, currentClass)
|
||||
removeUnnecessaryTemporaryVariables(body.statements)
|
||||
return body
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, currentClass: IrClass?): IrExpression {
|
||||
expression.transformChildren(this, currentClass)
|
||||
removeUnnecessaryTemporaryVariables(expression.statements)
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
override fun visitGetValue(expression: IrGetValue, currentClass: IrClass?): IrExpression {
|
||||
// Replace IrGetValue of an immutable temporary variable with a constant
|
||||
// initializer with the constant initializer.
|
||||
val variable = expression.symbol.owner
|
||||
@@ -280,6 +342,7 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower
|
||||
else
|
||||
expression
|
||||
}
|
||||
})
|
||||
}
|
||||
irFile.transformChildren(transformer, null)
|
||||
}
|
||||
}
|
||||
+2
-10
@@ -21,6 +21,7 @@ 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
|
||||
@@ -129,16 +130,7 @@ 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) {
|
||||
// `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
|
||||
+receiver.coerceToUnit(context.irBuiltIns)
|
||||
setOrGetExpr.receiver = null
|
||||
+setOrGetExpr
|
||||
}
|
||||
|
||||
+7
-15
@@ -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.coerceToUnitIfNeeded
|
||||
import org.jetbrains.kotlin.ir.util.coerceToUnit
|
||||
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()
|
||||
body.statements[i] = irStatement.coerceToUnit(irBuiltIns)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ internal class InsertImplicitCasts(
|
||||
if (i == lastIndex)
|
||||
irStatement.cast(type)
|
||||
else
|
||||
irStatement.coerceToUnit()
|
||||
irStatement.coerceToUnit(irBuiltIns)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ internal class InsertImplicitCasts(
|
||||
override fun visitReturn(expression: IrReturn): IrExpression =
|
||||
expression.transformPostfix {
|
||||
value = if (expression.returnTargetSymbol is IrConstructorSymbol) {
|
||||
value.coerceToUnit()
|
||||
value.coerceToUnit(irBuiltIns)
|
||||
} 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()
|
||||
body = body?.coerceToUnit(irBuiltIns)
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow): IrExpression =
|
||||
@@ -249,7 +249,7 @@ internal class InsertImplicitCasts(
|
||||
aCatch.result = aCatch.result.cast(type)
|
||||
}
|
||||
|
||||
finallyExpression = finallyExpression?.coerceToUnit()
|
||||
finallyExpression = finallyExpression?.coerceToUnit(irBuiltIns)
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression =
|
||||
@@ -318,7 +318,7 @@ internal class InsertImplicitCasts(
|
||||
|
||||
return when {
|
||||
expectedType.isUnit() ->
|
||||
coerceToUnit()
|
||||
coerceToUnit(irBuiltIns)
|
||||
|
||||
valueType.isDynamic() && !expectedType.isDynamic() ->
|
||||
if (expectedType.isNullableAny())
|
||||
@@ -383,14 +383,6 @@ 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) ||
|
||||
|
||||
@@ -18,6 +18,7 @@ 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
|
||||
@@ -154,6 +155,14 @@ 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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
package b
|
||||
|
||||
abstract class B {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
val x = 1
|
||||
|
||||
class A {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
class Example
|
||||
{
|
||||
var a1 = 0
|
||||
|
||||
Reference in New Issue
Block a user