JVM IR: support value classes only when feature is enabled

Remove NeedsToVisit because it doesn't really help with performance, and
it significantly increases code complexity.
This commit is contained in:
Alexander Udalov
2023-08-24 16:45:16 +02:00
committed by Space Team
parent 2dee47a8c1
commit 12bbdebed2
13 changed files with 41 additions and 262 deletions
@@ -176,6 +176,8 @@ fun unboxedTypeOfInlineClass(boxedType: Type, state: GenerationState): Type? {
}
fun getMultiFieldValueClassUnboxInfo(boxedType: Type, state: GenerationState): MultiFieldValueClassUnboxInfo? {
if (!state.config.supportMultiFieldValueClasses) return null
val descriptor =
state.jvmBackendClassResolver.resolveToClassDescriptors(boxedType).singleOrNull()?.takeIf { it.isMultiFieldValueClass() }
?: return null
@@ -297,7 +297,7 @@ private fun AbstractInsnNode.isInlineClassUnboxing(state: GenerationState) =
}
private fun AbstractInsnNode.isMultiFieldValueClassUnboxing(state: GenerationState) =
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
state.config.supportMultiFieldValueClasses && isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
isMultiFieldValueClassUnboxingMethodDescriptor(state)
}
@@ -100,4 +100,6 @@ class JvmBackendConfig(configuration: CompilerConfiguration) {
val noNewJavaAnnotationTargets: Boolean = configuration.getBoolean(JVMConfigurationKeys.NO_NEW_JAVA_ANNOTATION_TARGETS)
val oldInnerClassesLogic: Boolean = configuration.getBoolean(JVMConfigurationKeys.OLD_INNER_CLASSES_LOGIC)
val supportMultiFieldValueClasses: Boolean = languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses)
}
@@ -176,7 +176,9 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
}
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || checkWithUseSiteTargets()) {
checkMultiFieldValueClassAnnotationRestrictions(declaration, annotation, context, reporter)
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ValueClasses)) {
checkMultiFieldValueClassAnnotationRestrictions(declaration, annotation, context, reporter)
}
return
}
@@ -30,7 +30,7 @@ open class ScopeWithIr(val scope: Scope, val irElement: IrElement)
abstract class IrElementTransformerVoidWithContext : IrElementTransformerVoid() {
protected open val scopeStack = mutableListOf<ScopeWithIr>()
private val scopeStack = mutableListOf<ScopeWithIr>()
protected open fun createScope(declaration: IrSymbolOwner): ScopeWithIr =
ScopeWithIr(Scope(declaration.symbol), declaration)
@@ -380,7 +380,8 @@ private val jvmFilePhases = listOf(
forLoopsPhase,
collectionStubMethodLowering,
singleAbstractMethodPhase,
jvmValueClassPhase,
jvmMultiFieldValueClassPhase,
jvmInlineClassPhase,
tailrecPhase,
// makePatchParentsPhase(),
@@ -113,7 +113,7 @@ internal val bridgePhase = makeIrFilePhase(
::BridgeLowering,
name = "Bridge",
description = "Generate bridges",
prerequisite = setOf(jvmValueClassPhase, inheritedDefaultMethodsOnClassesPhase)
prerequisite = setOf(jvmInlineClassPhase, inheritedDefaultMethodsOnClassesPhase)
)
internal class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
@@ -29,6 +31,17 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
val jvmInlineClassPhase = makeIrFilePhase(
::JvmInlineClassLowering,
name = "InlineClasses",
description = "Lower inline classes",
// forLoopsPhase may produce UInt and ULong which are inline classes.
// Standard library replacements are done on the not mangled names for UInt and ULong classes.
// Collection stubs may require mangling by value class rules.
// SAM wrappers may require mangling for fun interfaces with value class parameters
prerequisite = setOf(forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase),
)
/**
* Adds new constructors, box, and unbox functions to inline classes as well as replacement
* functions and bridges to avoid clashes between overloaded function. Changes call with
@@ -37,10 +50,7 @@ import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
* We do not unfold inline class types here. Instead, the type mapper will lower inline class
* types to the types of their underlying field.
*/
internal class JvmInlineClassLowering(
context: JvmBackendContext,
scopeStack: MutableList<ScopeWithIr>,
) : JvmValueClassAbstractLowering(context, scopeStack) {
internal class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
override val replacements: MemoizedValueClassAbstractReplacements
get() = context.inlineClassReplacements
@@ -5,9 +5,10 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.inline
import org.jetbrains.kotlin.backend.common.lower.irCatch
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
@@ -17,6 +18,7 @@ import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.lower.BlockOrBody.Block
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
@@ -39,10 +41,18 @@ import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
internal class JvmMultiFieldValueClassLowering(
context: JvmBackendContext,
scopeStack: MutableList<ScopeWithIr>,
) : JvmValueClassAbstractLowering(context, scopeStack) {
val jvmMultiFieldValueClassPhase = makeIrFilePhase(
{ c: JvmBackendContext ->
if (c.config.supportMultiFieldValueClasses)
JvmMultiFieldValueClassLowering(c)
else
FileLoweringPass.Empty
},
name = "MultiFieldValueClasses",
description = "Lower multi-field value classes",
)
internal class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
private sealed class MfvcNodeInstanceAccessor {
abstract val instance: MfvcNodeInstance
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.name.Name
internal abstract class JvmValueClassAbstractLowering(
val context: JvmBackendContext,
override val scopeStack: MutableList<ScopeWithIr>,
) : FileLoweringPass, IrElementTransformerVoidWithContext() {
abstract val replacements: MemoizedValueClassAbstractReplacements
@@ -1,225 +0,0 @@
/*
* Copyright 2010-2022 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.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassLoweringDispatcherSharedData.Access.Body
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassLoweringDispatcherSharedData.Access.Header
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
val jvmValueClassPhase = makeIrFilePhase(
::JvmValueClassLoweringDispatcher,
name = "Value Classes",
description = "Lower value classes",
// forLoopsPhase may produce UInt and ULong which are inline classes.
// Standard library replacements are done on the not mangled names for UInt and ULong classes.
// Collection stubs may require mangling by value class rules.
// SAM wrappers may require mangling for fun interfaces with value class parameters
prerequisite = setOf(forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase),
)
internal class JvmValueClassLoweringDispatcher(private val context: JvmBackendContext) : IrElementTransformerVoidWithContext(),
FileLoweringPass {
override val scopeStack: MutableList<ScopeWithIr> = mutableListOf()
private val inlineClassLowering: JvmInlineClassLowering = JvmInlineClassLowering(context, scopeStack)
private val multiFieldValueClassLowering: JvmMultiFieldValueClassLowering = JvmMultiFieldValueClassLowering(context, scopeStack)
override fun lower(irFile: IrFile) = withinScope(irFile) {
irFile.transformChildrenVoid()
}
override fun visitClassNew(declaration: IrClass): IrClass = if (declaration.requiresHandling()) {
declaration
.let(multiFieldValueClassLowering::visitClassNew)
.let(inlineClassLowering::visitClassNew)
} else {
declaration
}
private fun IrElement.requiresHandling(): Boolean {
val visitor = NeedsToVisit(context)
accept(visitor, null)
return visitor.result
}
}
private class NeedsToVisit(private val context: JvmBackendContext) : IrElementVisitorVoid {
var result = false
private val replacements = context.valueClassLoweringDispatcherSharedData
private val visitedParameters = mutableSetOf<IrSymbol>()
override fun visitElement(element: IrElement) {
if (!result) element.acceptChildrenVoid(this)
}
private fun IrElement.acceptAndGetResult(): Boolean {
acceptVoid(this@NeedsToVisit)
return result
}
private val IrClass.needsHandling: Boolean
get() = isValue || typeParameters.any { it.acceptAndGetResult() }
private val IrType.needsHandling: Boolean
get() = classifierOrNull?.isBound == true && erasedUpperBound.needsHandling || this is IrSimpleType && arguments.any { it.typeOrNull?.needsHandling == true }
override fun visitClass(declaration: IrClass) {
visitClassHeader(declaration)
if (result) return
visitClassBody(declaration)
}
private fun visitClassBody(declaration: IrClass) {
if (result) return
result = replacements.classResults.getOrPut(Body to declaration) {
declaration.declarations.any { it.acceptAndGetResult() }
}
}
private fun visitClassHeader(declaration: IrClass) {
if (result) return
result = replacements.classResults.getOrPut(Header to declaration) {
declaration.needsHandling ||
declaration.typeParameters.any { it.acceptAndGetResult() } ||
declaration.superTypes.mapNotNull { (it.classifierOrNull as? IrClassSymbol)?.owner }.any { visitClassHeader(it); result }
}
}
override fun visitFunction(declaration: IrFunction) = visitFunction(declaration, withBody = true)
private fun visitFunctionHeader(declaration: IrFunction) {
if (result) return
result = replacements.functionResults.getOrPut(Header to declaration) {
declaration.parent.let { it is IrClass && it.needsHandling } ||
declaration.typeParameters.any { it.acceptAndGetResult() } ||
declaration.dispatchReceiverParameter?.acceptAndGetResult() == true ||
declaration.extensionReceiverParameter?.acceptAndGetResult() == true ||
declaration.valueParameters.any { it.acceptAndGetResult() } ||
declaration.returnType.needsHandling ||
(declaration as? IrSimpleFunction)?.overriddenSymbols?.any { visitFunction(it.owner, withBody = false); result } == true
}
}
private fun visitFunction(declaration: IrFunction, withBody: Boolean) {
visitFunctionHeader(declaration)
if (result) return
if(withBody) visitFunctionBody(declaration)
}
private fun visitFunctionBody(declaration: IrFunction) {
if (result) return
result = replacements.functionResults.getOrPut(Body to declaration) {
declaration.body?.acceptAndGetResult() == true
}
}
override fun visitValueParameter(declaration: IrValueParameter) {
if (result || !visitedParameters.add(declaration.symbol)) return
result = declaration.type.needsHandling
if (result) return
super.visitValueParameter(declaration)
}
override fun visitTypeParameter(declaration: IrTypeParameter) {
if (result || !visitedParameters.add(declaration.symbol)) return
result = declaration.superTypes.any { it.needsHandling }
if (result) return
super.visitTypeParameter(declaration)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
if (result) return
visitFunction(expression.symbol.owner, withBody = false)
if (result) return
super.visitFunctionReference(expression)
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
if (result) return
visitFunction(expression.symbol.owner, withBody = false)
if (result) return
super.visitFunctionAccess(expression)
}
override fun visitField(declaration: IrField) = visitField(declaration, withBody = true)
private fun visitFieldHeader(declaration: IrField) {
if (result) return
result = replacements.fieldResults.getOrPut(Header to declaration) {
declaration.type.needsHandling
}
}
private fun visitFieldBody(declaration: IrField) {
if (result) return
result = replacements.fieldResults.getOrPut(Body to declaration) {
super.visitField(declaration)
result
}
}
private fun visitField(declaration: IrField, withBody: Boolean) {
visitFieldHeader(declaration)
if (withBody) visitFieldBody(declaration)
}
override fun visitFieldAccess(expression: IrFieldAccessExpression) {
if (result) return
visitField(expression.symbol.owner, withBody = false)
if (result) return
super.visitFieldAccess(expression)
}
override fun visitVariable(declaration: IrVariable) {
if (result) return
result = declaration.type.needsHandling
if (result) return
super.visitVariable(declaration)
}
override fun visitStringConcatenation(expression: IrStringConcatenation) {
if (result) return
result = expression.arguments.any { it.type.needsHandling }
if (result) return
super.visitStringConcatenation(expression)
}
override fun visitCall(expression: IrCall) {
if (result) return
if (expression.symbol == context.irBuiltIns.eqeqSymbol) {
for (it in 0 until expression.valueArgumentsCount) {
result = expression.getValueArgument(it)?.type?.needsHandling ?: false
if (result) return
}
}
super.visitCall(expression)
}
override fun visitExpression(expression: IrExpression) {
if (result) return
result = expression.type.needsHandling
if (result) return
super.visitExpression(expression)
}
}
@@ -185,8 +185,6 @@ class JvmBackendContext(
val multiFieldValueClassReplacements = MemoizedMultiFieldValueClassReplacements(irFactory, this)
val valueClassLoweringDispatcherSharedData = MemoizedValueClassLoweringDispatcherSharedData()
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
val inlineMethodGenerationLock = Any()
@@ -1,20 +0,0 @@
/*
* Copyright 2010-2023 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
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
class MemoizedValueClassLoweringDispatcherSharedData {
enum class Access { Header, Body }
val functionResults: ConcurrentMap<Pair<Access, IrFunction>, Boolean> = ConcurrentHashMap()
val classResults: ConcurrentMap<Pair<Access, IrClass>, Boolean> = ConcurrentHashMap()
val fieldResults: ConcurrentMap<Pair<Access, IrField>, Boolean> = ConcurrentHashMap()
}