From 6379fe4c4caf013423375aad70ee6e1d68de9202 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 24 Jan 2022 14:26:28 +0100 Subject: [PATCH] JVM IR: link via descriptors instead of signatures by default Doing so speeds up psi2ir ~2 times, and thus improves total compiler performance by about 6-8%. Unless JVM IR is in the mode where linking via signatures is the only way (-Xserialize-ir, -Xklib), signatures are actually not needed at all, SymbolTable can use the frontend representation (descriptors for FE1.0, and hopefully FIR elements for K2) as hash table keys. The only catch is that since other backends still need to work with signatures, all the common IR utilities, such as irTypePredicates.kt, need to work correctly for IR elements both with signatures and without. Also, introduce a fallback compiler flag -Xlink-via-signatures, in case something goes wrong, to be able to troubleshoot and workaround any issues. #KT-48233 --- .../arguments/K2JVMCompilerArguments.kt | 11 +- .../jetbrains/kotlin/cli/jvm/jvmArguments.kt | 2 + .../kotlin/config/JVMConfigurationKeys.java | 3 + ...osticReporterWithImplicitIrBasedContext.kt | 9 +- .../common/TailRecursionCallsCollector.kt | 5 +- .../kotlin/backend/jvm/JvmIrCodegenFactory.kt | 13 ++- .../backend/jvm/lower/FileClassLowering.kt | 9 +- .../jvm/lower/JvmOptimizationLowering.kt | 24 ++-- .../jvm/lower/SuspendLambdaLowering.kt | 8 +- .../kotlin/ir/types/IrTypeSystemContext.kt | 30 ++--- .../kotlin/ir/types/irTypePredicates.kt | 47 +++++--- .../jetbrains/kotlin/ir/util/SymbolTable.kt | 13 ++- .../common/serialization/KotlinIrLinker.kt | 16 +-- .../DisabledIdSignatureDescriptor.kt | 50 ++++++++ .../backend/jvm/serialization/JvmIrLinker.kt | 6 +- compiler/testData/cli/jvm/extraHelp.out | 6 +- .../backend/handlers/IrInlineBodiesHandler.kt | 1 + .../jvm/compiler/JvmIrLinkageModeTest.kt | 107 ++++++++++++++++++ 18 files changed, 290 insertions(+), 70 deletions(-) create mode 100644 compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/DisabledIdSignatureDescriptor.kt create mode 100644 compiler/tests/org/jetbrains/kotlin/jvm/compiler/JvmIrLinkageModeTest.kt diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index f10914ea84e..84368502365 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -478,8 +478,8 @@ Also sets `-jvm-target` value equal to the selected JDK version""" @Argument( value = "-Xtype-enhancement-improvements-strict-mode", - description = "Enable strict mode for some improvements in the type enhancement for loaded Java types based on nullability annotations," + - "including freshly supported reading of the type use annotations from class files. " + + description = "Enable strict mode for some improvements in the type enhancement for loaded Java types based on nullability annotations,\n" + + "including freshly supported reading of the type use annotations from class files.\n" + "See KT-45671 for more details" ) var typeEnhancementImprovementsInStrictMode: Boolean by FreezableVar(false) @@ -509,6 +509,13 @@ Also sets `-jvm-target` value equal to the selected JDK version""" ) var enhanceTypeParameterTypesToDefNotNull: Boolean by FreezableVar(false) + @Argument( + value = "-Xlink-via-signatures", + description = "Link JVM IR symbols via signatures, instead of descriptors. \n" + + "This mode is slower, but can be useful in troubleshooting problems with the JVM IR backend" + ) + var linkViaSignatures: Boolean by FreezableVar(false) + override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap, Any> { val result = super.configureAnalysisFlags(collector, languageVersion) result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index e0d0ed7e0ec..60861c8bd8e 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -288,6 +288,8 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr put(JVMConfigurationKeys.VALIDATE_IR, arguments.validateIr) put(JVMConfigurationKeys.VALIDATE_BYTECODE, arguments.validateBytecode) + put(JVMConfigurationKeys.LINK_VIA_SIGNATURES, arguments.linkViaSignatures) + val assertionsMode = JVMAssertionsMode.fromStringOrNull(arguments.assertionsMode) if (assertionsMode == null) { diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 8a1708a53c9..e6a5dd6e627 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -153,4 +153,7 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey VALIDATE_BYTECODE = CompilerConfigurationKey.create("Validate generated JVM bytecode"); + + public static final CompilerConfigurationKey LINK_VIA_SIGNATURES = + CompilerConfigurationKey.create("Link JVM IR symbols via signatures, instead of by descriptors"); } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/KtDiagnosticReporterWithImplicitIrBasedContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/KtDiagnosticReporterWithImplicitIrBasedContext.kt index 789bdeb6c87..9b942afea3a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/KtDiagnosticReporterWithImplicitIrBasedContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/KtDiagnosticReporterWithImplicitIrBasedContext.kt @@ -18,10 +18,11 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.path import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.ir.util.hasEqualFqName import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.name.FqName import java.util.* class KtDiagnosticReporterWithImplicitIrBasedContext( @@ -118,7 +119,7 @@ internal class IrBasedSuppressCache : AbstractKotlinSuppressCache() { private fun collectSuppressAnnotationKeys(element: IrElement): Boolean = (element as? IrAnnotationContainer)?.annotations?.filter { - it.type.classifierOrNull?.signature == SUPPRESS + it.type.classOrNull?.owner?.hasEqualFqName(SUPPRESS) == true }?.flatMap { buildList { fun addIfStringConst(irConst: IrConst<*>) { @@ -153,4 +154,4 @@ internal class IrBasedSuppressCache : AbstractKotlinSuppressCache() { override fun getSuppressingStrings(annotated: IrElement): Set = annotationKeys[annotated].orEmpty() } -private val SUPPRESS = IdSignature.CommonSignature("kotlin", "Suppress", null, 0) +private val SUPPRESS = FqName("kotlin.Suppress") diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/TailRecursionCallsCollector.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/TailRecursionCallsCollector.kt index 55cd7a004bb..12480482677 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/TailRecursionCallsCollector.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/TailRecursionCallsCollector.kt @@ -16,13 +16,14 @@ package org.jetbrains.kotlin.backend.common +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.IdSignatureValues +import org.jetbrains.kotlin.ir.types.isClassWithFqName import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.usesDefaultArguments import org.jetbrains.kotlin.ir.visitors.IrElementVisitor @@ -98,7 +99,7 @@ fun collectTailRecursionCalls(irFunction: IrFunction, followFunctionReference: ( } private fun IrExpression.isUnitRead(): Boolean = - this is IrGetObjectValue && symbol.signature == IdSignatureValues.unit + this is IrGetObjectValue && symbol.isClassWithFqName(StandardNames.FqNames.unit) override fun visitWhen(expression: IrWhen, data: ElementKind) { expression.branches.forEach { diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt index 16c73e0f0c6..e283f0f26d4 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.phaser.* import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.ir.getKtFile +import org.jetbrains.kotlin.backend.jvm.serialization.DisabledIdSignatureDescriptor import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.codegen.CodegenFactory import org.jetbrains.kotlin.codegen.state.GenerationState @@ -73,11 +74,18 @@ open class JvmIrCodegenFactory( ) : CodegenFactory.CodegenInput override fun convertToIr(input: CodegenFactory.IrConversionInput): JvmIrBackendInput { + val enableIdSignatures = + input.configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES) || + input.configuration[JVMConfigurationKeys.SERIALIZE_IR, JvmSerializeIrMode.NONE] != JvmSerializeIrMode.NONE || + input.configuration[JVMConfigurationKeys.KLIB_PATHS, emptyList()].isNotEmpty() val (mangler, symbolTable) = if (externalSymbolTable != null) externalMangler!! to externalSymbolTable else { val mangler = JvmDescriptorMangler(MainFunctionDetector(input.bindingContext, input.languageVersionSettings)) - val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl) + val signaturer = + if (enableIdSignatures) JvmIdSignatureDescriptor(mangler) + else DisabledIdSignatureDescriptor + val symbolTable = SymbolTable(signaturer, IrFactoryImpl) mangler to symbolTable } val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors)) @@ -114,7 +122,8 @@ open class JvmIrCodegenFactory( symbolTable, frontEndContext, stubGenerator, - mangler + mangler, + enableIdSignatures, ) val pluginContext by lazy { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FileClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FileClassLowering.kt index 7bca9cabbcd..2afbdfbd750 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FileClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FileClassLowering.kt @@ -121,7 +121,9 @@ private class FileClassLowering(val context: JvmBackendContext) : FileLoweringPa } annotations = - if (isMultifilePart) irFile.annotations.filterNot { it.symbol.owner.parentAsClass.symbol.signature == JVM_NAME } + if (isMultifilePart) irFile.annotations.filterNot { + it.symbol.owner.parentAsClass.hasEqualFqName(JvmFileClassUtil.JVM_NAME) + } else irFile.annotations metadata = irFile.metadata @@ -142,13 +144,8 @@ private class FileClassLowering(val context: JvmBackendContext) : FileLoweringPa } } } - - private companion object { - private val JVM_NAME = IdSignature.CommonSignature("kotlin.jvm", "JvmName", null, 0) - } } - fun IrFile.getFileClassInfo(): JvmFileClassInfo = when (val fileEntry = this.fileEntry) { is PsiIrFileEntry -> diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt index 00e8ad292d7..1f7986e7ec3 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt @@ -24,12 +24,7 @@ import org.jetbrains.kotlin.ir.builders.irSetField import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.copyWithOffsets -import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* @@ -43,11 +38,16 @@ internal val jvmOptimizationLoweringPhase = makeIrFilePhase( ) class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass { - - companion object { - fun isNegation(expression: IrExpression, context: JvmBackendContext): Boolean = - expression is IrCall && - (expression.symbol as? IrPublicSymbolBase<*>)?.signature == context.irBuiltIns.booleanNotSymbol.signature + private companion object { + private fun isNegation(expression: IrExpression): Boolean = + expression is IrCall && expression.symbol.owner.let { not -> + not.name == OperatorNameConventions.NOT && + not.extensionReceiverParameter == null && + not.valueParameters.isEmpty() && + not.dispatchReceiverParameter.let { receiver -> + receiver != null && receiver.type.isBoolean() + } + } } private val IrFunction.isObjectEquals @@ -98,7 +98,7 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass return optimizePropertyAccess(expression, data) } - if (isNegation(expression, context) && isNegation(expression.dispatchReceiver!!, context)) { + if (isNegation(expression) && isNegation(expression.dispatchReceiver!!)) { return (expression.dispatchReceiver as IrCall).dispatchReceiver!! } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt index 960504629a1..1fdcc59ab5c 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.jvm.AsmTypes @@ -144,9 +145,10 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin copyAttributes(reference) val function = reference.symbol.owner - val isRestricted = reference.symbol.owner.extensionReceiverParameter?.type?.classOrNull?.owner?.annotations?.any { - it.type.classOrNull?.signature == IdSignature.CommonSignature("kotlin.coroutines", "RestrictsSuspension", null, 0) - } == true + val extensionReceiver = function.extensionReceiverParameter?.type?.classOrNull + val isRestricted = extensionReceiver != null && extensionReceiver.owner.annotations.any { + it.type.classOrNull?.isClassWithFqName(FqNameUnsafe("kotlin.coroutines.RestrictsSuspension")) == true + } val suspendLambda = if (isRestricted) context.ir.symbols.restrictedSuspendLambdaClass.owner else context.ir.symbols.suspendLambdaClass.owner diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt index 8e461d8690c..02e3446b3a7 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt @@ -481,22 +481,26 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon ).substitute(type as IrType) } - override fun TypeConstructorMarker.getPrimitiveType(): PrimitiveType? { + override fun TypeConstructorMarker.getPrimitiveType(): PrimitiveType? = + getNameForClassUnderKotlinPackage()?.let(PrimitiveType::getByShortName) + + override fun TypeConstructorMarker.getPrimitiveArrayType(): PrimitiveType? = + getNameForClassUnderKotlinPackage()?.let(PrimitiveType::getByShortArrayName) + + private fun TypeConstructorMarker.getNameForClassUnderKotlinPackage(): String? { if (this !is IrClassSymbol) return null val signature = signature?.asPublic() - if (signature == null || signature.packageFqName != "kotlin") return null - - return PrimitiveType.getByShortName(signature.declarationFqName) - } - - override fun TypeConstructorMarker.getPrimitiveArrayType(): PrimitiveType? { - if (this !is IrClassSymbol) return null - - val signature = signature?.asPublic() - if (signature == null || signature.packageFqName != "kotlin") return null - - return PrimitiveType.getByShortArrayName(signature.declarationFqName) + return if (signature != null) { + if (signature.packageFqName == StandardNames.BUILT_INS_PACKAGE_NAME.asString()) + signature.declarationFqName + else null + } else { + val parent = owner.parent + if (parent is IrPackageFragment && parent.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME) + owner.name.asString() + else null + } } override fun TypeConstructorMarker.isUnderKotlinPackage(): Boolean { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt index d8433d1c8bc..9e51fdd9d1e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt @@ -9,12 +9,14 @@ import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrPackageFragment import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.hasEqualFqName import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqNameUnsafe +import org.jetbrains.kotlin.name.Name @Suppress("ObjectPropertyName") object IdSignatureValues { @@ -55,9 +57,14 @@ fun getPublicSignature(packageFqName: FqName, name: String) = private fun IrType.isClassType(signature: IdSignature.CommonSignature, hasQuestionMark: Boolean? = null): Boolean { if (this !is IrSimpleType) return false if (hasQuestionMark != null && this.hasQuestionMark != hasQuestionMark) return false - return signature == classifier.signature + return signature == classifier.signature || + classifier.owner.let { it is IrClass && it.hasFqNameEqualToSignature(signature) } } +private fun IrClass.hasFqNameEqualToSignature(signature: IdSignature.CommonSignature): Boolean = + name.asString() == signature.shortName && + hasEqualFqName(FqName("${signature.packageFqName}.${signature.declarationFqName}")) + fun IrClassifierSymbol.isClassWithFqName(fqName: FqNameUnsafe): Boolean = this is IrClassSymbol && classFqNameEquals(this, fqName) @@ -71,6 +78,17 @@ private val idSignatureToPrimitiveType: Map = + PrimitiveType.values().associateBy(PrimitiveType::typeName) + +private val idSignatureToUnsignedType: Map = + UnsignedType.values().associateBy { + getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, it.typeName.asString()) + } + +private val shortNameToUnsignedType: Map = + UnsignedType.values().associateBy(UnsignedType::typeName) + val primitiveArrayTypesSignatures: Map = PrimitiveType.values().associateWith { getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, "${it.typeName.asString()}Array") @@ -97,23 +115,24 @@ fun IrType.isPrimitiveType(hasQuestionMark: Boolean = false): Boolean = fun IrType.isNullablePrimitiveType(): Boolean = isPrimitiveType(true) fun IrType.getPrimitiveType(): PrimitiveType? = - if (this is IrSimpleType && classifier is IrClassSymbol) - idSignatureToPrimitiveType[classifier.signature] - else null + getPrimitiveOrUnsignedType(idSignatureToPrimitiveType, shortNameToPrimitiveType) fun IrType.isUnsignedType(hasQuestionMark: Boolean = false): Boolean = this is IrSimpleType && hasQuestionMark == this.hasQuestionMark && getUnsignedType() != null fun IrType.getUnsignedType(): UnsignedType? = - if (this is IrSimpleType && classifier is IrClassSymbol) - when (classifier.signature) { - IdSignatureValues.uByte -> UnsignedType.UBYTE - IdSignatureValues.uShort -> UnsignedType.USHORT - IdSignatureValues.uInt -> UnsignedType.UINT - IdSignatureValues.uLong -> UnsignedType.ULONG - else -> null - } - else null + getPrimitiveOrUnsignedType(idSignatureToUnsignedType, shortNameToUnsignedType) + +fun > IrType.getPrimitiveOrUnsignedType(byIdSignature: Map, byShortName: Map): T? { + if (this !is IrSimpleType) return null + val symbol = classifier as? IrClassSymbol ?: return null + if (symbol.signature != null) return byIdSignature[symbol.signature] + + val klass = symbol.owner + val parent = klass.parent + if (parent !is IrPackageFragment || parent.fqName != StandardNames.BUILT_INS_PACKAGE_FQ_NAME) return null + return byShortName[klass.name] +} fun IrType.isMarkedNullable() = (this as? IrSimpleType)?.hasQuestionMark ?: false @@ -151,8 +170,6 @@ fun IrType.isLongArray(): Boolean = isNotNullClassType(primitiveArrayTypesSignat fun IrType.isFloatArray(): Boolean = isNotNullClassType(primitiveArrayTypesSignatures[PrimitiveType.FLOAT]!!) fun IrType.isDoubleArray(): Boolean = isNotNullClassType(primitiveArrayTypesSignatures[PrimitiveType.DOUBLE]!!) -// TODO: remove this method using FqNames. -// Need to refactor declarationBuilders.kt: visibilty is known, need to add info about package in IrFactory.buildClass (similar to name). fun IrType.isClassType(fqName: FqNameUnsafe, hasQuestionMark: Boolean): Boolean { if (this !is IrSimpleType) return false if (this.hasQuestionMark != hasQuestionMark) return false diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt index 08e74e55bd7..cc929482724 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrScriptImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.lazy.IrLazySymbolTable +import org.jetbrains.kotlin.ir.descriptors.IrBasedClassDescriptor import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpressionBody import org.jetbrains.kotlin.ir.symbols.* @@ -472,7 +473,17 @@ open class SymbolTable( } override fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol = - classSymbolTable.referenced(descriptor) { signature -> createClassSymbol(descriptor, signature) } + @Suppress("Reformat") + // This is needed for cases like kt46069.kt, where psi2ir creates descriptor-less IR elements for adapted function references. + // In JVM IR, symbols are linked via descriptors by default, so for an adapted function reference, an IrBasedClassDescriptor + // is created for any classifier used in the function parameter/return types. Any attempt to translate such type to IrType goes + // to this method, which puts the descriptor into unboundClasses, which causes an assertion failure later because we won't bind + // such symbol anywhere. + // TODO: maybe there's a better solution. + if (descriptor is IrBasedClassDescriptor) + descriptor.owner.symbol + else + classSymbolTable.referenced(descriptor) { signature -> createClassSymbol(descriptor, signature) } fun referenceClass( sig: IdSignature, diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt index c32cf2feaff..d05b5be3a89 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt @@ -154,14 +154,14 @@ abstract class KotlinIrLinker( } } - override fun getDeclaration(symbol: IrSymbol): IrDeclaration? { - if (!symbol.isPublicApi) { - if (symbol.hasDescriptor) { - val descriptor = symbol.descriptor - if (!platformSpecificSymbol(symbol)) { - if (descriptor.module !== currentModule) return null - } - } + override fun getDeclaration(symbol: IrSymbol): IrDeclaration? = + deserializeOrResolveDeclaration(symbol, false) + + protected fun deserializeOrResolveDeclaration(symbol: IrSymbol, allowSymbolsWithoutSignaturesFromOtherModule: Boolean): IrDeclaration? { + if (!allowSymbolsWithoutSignaturesFromOtherModule) { + if (!symbol.isPublicApi && symbol.hasDescriptor && !platformSpecificSymbol(symbol) && + symbol.descriptor.module !== currentModule + ) return null } if (!symbol.isBound) { diff --git a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/DisabledIdSignatureDescriptor.kt b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/DisabledIdSignatureDescriptor.kt new file mode 100644 index 00000000000..30db5f93074 --- /dev/null +++ b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/DisabledIdSignatureDescriptor.kt @@ -0,0 +1,50 @@ +/* + * 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.serialization + +import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType +import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.util.KotlinMangler + +object DisabledDescriptorMangler : KotlinMangler.DescriptorMangler { + override val String.hashMangle: Long + get() = error("Should not be called") + + override fun DeclarationDescriptor.isExported(compatibleMode: Boolean): Boolean = + error("Should not be called") + + override fun DeclarationDescriptor.mangleString(compatibleMode: Boolean): String = + error("Should not be called") + + override fun DeclarationDescriptor.signatureString(compatibleMode: Boolean): String = + error("Should not be called") + + override fun DeclarationDescriptor.fqnString(compatibleMode: Boolean): String = + error("Should not be called") + + override fun ClassDescriptor.mangleEnumEntryString(compatibleMode: Boolean): String = + error("Should not be called") + + override fun PropertyDescriptor.mangleFieldString(compatibleMode: Boolean): String = + error("Should not be called") +} + +object DisabledIdSignatureDescriptor : IdSignatureDescriptor(DisabledDescriptorMangler) { + override fun composeSignature(descriptor: DeclarationDescriptor): IdSignature? = null + + override fun composeEnumEntrySignature(descriptor: ClassDescriptor): IdSignature? = null + + override fun composeFieldSignature(descriptor: PropertyDescriptor): IdSignature? = null + + override fun composeAnonInitSignature(descriptor: ClassDescriptor): IdSignature? = null + + override fun createSignatureBuilder(type: SpecialDeclarationType): DescriptorBasedSignatureBuilder = + error("Should not be called") +} diff --git a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/ir/backend/jvm/serialization/JvmIrLinker.kt b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/ir/backend/jvm/serialization/JvmIrLinker.kt index 1e5206117d1..fb173b7f32d 100644 --- a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/ir/backend/jvm/serialization/JvmIrLinker.kt +++ b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/ir/backend/jvm/serialization/JvmIrLinker.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.builders.TranslationPluginContext +import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl @@ -38,7 +39,8 @@ class JvmIrLinker( symbolTable: SymbolTable, override val translationPluginContext: TranslationPluginContext?, private val stubGenerator: DeclarationStubGenerator, - private val manglerDesc: JvmDescriptorMangler + private val manglerDesc: JvmDescriptorMangler, + private val enableIdSignatures: Boolean, ) : KotlinIrLinker(currentModule, messageLogger, typeSystem.irBuiltIns, symbolTable, emptyList()) { // TODO: provide friend modules @@ -91,6 +93,8 @@ class JvmIrLinker( } } + override fun getDeclaration(symbol: IrSymbol): IrDeclaration? = + deserializeOrResolveDeclaration(symbol, !enableIdSignatures) override fun createCurrentModuleDeserializer(moduleFragment: IrModuleFragment, dependencies: Collection): IrModuleDeserializer = JvmCurrentModuleDeserializer(moduleFragment, dependencies) diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index b1d602b13ff..7ef113e9e23 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -81,6 +81,8 @@ where advanced options include: -Xlambdas=indy Generate lambdas using `invokedynamic` with `LambdaMetafactory.metafactory`. Requires `-jvm-target 1.8` or greater. Lambda objects created using `LambdaMetafactory.metafactory` will have different `toString()`. -Xlambdas=class Generate lambdas as explicit classes + -Xlink-via-signatures Link JVM IR symbols via signatures, instead of descriptors. + This mode is slower, but can be useful in troubleshooting problems with the JVM IR backend -Xno-call-assertions Don't generate not-null assertions for arguments of platform types -Xno-kotlin-nothing-value-exception Do not use KotlinNothingValueException available since 1.4 @@ -133,7 +135,9 @@ where advanced options include: -Xsuppress-missing-builtins-error Suppress the "cannot access built-in declaration" error (useful with -no-stdlib) -Xtype-enhancement-improvements-strict-mode - Enable strict mode for some improvements in the type enhancement for loaded Java types based on nullability annotations,including freshly supported reading of the type use annotations from class files. See KT-45671 for more details + Enable strict mode for some improvements in the type enhancement for loaded Java types based on nullability annotations, + including freshly supported reading of the type use annotations from class files. + See KT-45671 for more details -Xuse-fast-jar-file-system Use fast implementation on Jar FS. This may speed up compilation time, but currently it's an experimental mode -Xuse-ir Use the IR backend. This option has no effect unless the language version less than 1.5 is used -Xuse-javac Use javac for Java source and class files analysis diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInlineBodiesHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInlineBodiesHandler.kt index fdccd135afc..1fddd11c42a 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInlineBodiesHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInlineBodiesHandler.kt @@ -23,6 +23,7 @@ class IrInlineBodiesHandler(testServices: TestServices) : AbstractIrHandler(test override fun processModule(module: TestModule, info: IrBackendInput) { val irModule = info.irModuleFragment irModule.acceptChildrenVoid(InlineFunctionsCollector()) + assertions.assertTrue(declaredInlineFunctionSignatures.isNotEmpty()) irModule.acceptChildrenVoid(InlineCallBodiesCheck()) assertions.assertTrue((info as IrBackendInput.JvmIrBackendInput).backendInput.symbolTable.allUnbound.isEmpty()) } diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/JvmIrLinkageModeTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/JvmIrLinkageModeTest.kt new file mode 100644 index 00000000000..6ab696b1c1e --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/JvmIrLinkageModeTest.kt @@ -0,0 +1,107 @@ +/* + * 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.jvm.compiler + +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.codegen.CodegenTestCase +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.utils.addIfNotNull + +class JvmIrLinkageModeTest : CodegenTestCase() { + override val backend: TargetBackend + get() = TargetBackend.JVM_IR + + private var enableLinkageViaSignatures: Boolean? = null + + private var source = """ + package test + + class Class + interface Interface + sealed class Sealed + enum class E { ENTRY } + + fun function(s: String): Array { + fun Boolean.local() {} + return arrayOf(s.length) + } + typealias S = String + var property: S? = "OK" + """.trimIndent() + + fun testLinkageViaDescriptors() { + enableLinkageViaSignatures = false + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY) + loadText(source) + generateAndCreateClassLoader(true) + } + + fun testLinkageViaSignatures() { + enableLinkageViaSignatures = true + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY) + loadText(source) + generateAndCreateClassLoader(true) + } + + override fun updateConfiguration(configuration: CompilerConfiguration) { + super.updateConfiguration(configuration) + if (enableLinkageViaSignatures!!) { + configuration.put(JVMConfigurationKeys.LINK_VIA_SIGNATURES, true) + } + } + + override fun setupEnvironment(environment: KotlinCoreEnvironment) { + val idSignatureShouldBePresent = environment.configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES) + IrGenerationExtension.registerExtension(environment.project, LinkageTestIrExtension(idSignatureShouldBePresent)) + super.setupEnvironment(environment) + } + + private class LinkageTestIrExtension(val idSignatureShouldBePresent: Boolean) : IrGenerationExtension { + override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { + val file = moduleFragment.files.single() + val signatures = mutableListOf() + file.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitDeclaration(declaration: IrDeclarationBase) { + super.visitDeclaration(declaration) + signatures.addIfNotNull(declaration.symbol.signature) + } + }) + val allSignatures = signatures.map { it.render().substringBefore("|") }.toSet() + if (idSignatureShouldBePresent) { + val message = allSignatures.sorted().joinToString("\n") + assertTrue(message, "test/Class" in allSignatures) + assertTrue(message, "test/Interface" in allSignatures) + assertTrue(message, "test/Sealed" in allSignatures) + assertTrue(message, "test/E" in allSignatures) + assertTrue(message, "test/function" in allSignatures) + assertTrue(message, "test/S" in allSignatures) + assertTrue(message, "test/property" in allSignatures) + assertTrue(message, "test/property." in allSignatures) + assertTrue(message, "test/property." in allSignatures) + } else { + assertEmpty(allSignatures) + } + } + } +}