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
This commit is contained in:
Alexander Udalov
2022-01-24 14:26:28 +01:00
parent 6e4131de8e
commit 6379fe4c4c
18 changed files with 290 additions and 70 deletions
@@ -478,8 +478,8 @@ Also sets `-jvm-target` value equal to the selected JDK version"""
@Argument( @Argument(
value = "-Xtype-enhancement-improvements-strict-mode", 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," + 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. " + "including freshly supported reading of the type use annotations from class files.\n" +
"See KT-45671 for more details" "See KT-45671 for more details"
) )
var typeEnhancementImprovementsInStrictMode: Boolean by FreezableVar(false) 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) 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<AnalysisFlag<*>, Any> { override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
val result = super.configureAnalysisFlags(collector, languageVersion) val result = super.configureAnalysisFlags(collector, languageVersion)
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics
@@ -288,6 +288,8 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
put(JVMConfigurationKeys.VALIDATE_IR, arguments.validateIr) put(JVMConfigurationKeys.VALIDATE_IR, arguments.validateIr)
put(JVMConfigurationKeys.VALIDATE_BYTECODE, arguments.validateBytecode) put(JVMConfigurationKeys.VALIDATE_BYTECODE, arguments.validateBytecode)
put(JVMConfigurationKeys.LINK_VIA_SIGNATURES, arguments.linkViaSignatures)
val assertionsMode = val assertionsMode =
JVMAssertionsMode.fromStringOrNull(arguments.assertionsMode) JVMAssertionsMode.fromStringOrNull(arguments.assertionsMode)
if (assertionsMode == null) { if (assertionsMode == null) {
@@ -153,4 +153,7 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> VALIDATE_BYTECODE = public static final CompilerConfigurationKey<Boolean> VALIDATE_BYTECODE =
CompilerConfigurationKey.create("Validate generated JVM bytecode"); CompilerConfigurationKey.create("Validate generated JVM bytecode");
public static final CompilerConfigurationKey<Boolean> LINK_VIA_SIGNATURES =
CompilerConfigurationKey.create("Link JVM IR symbols via signatures, instead of by descriptors");
} }
@@ -18,10 +18,11 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.path import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.file 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.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName
import java.util.* import java.util.*
class KtDiagnosticReporterWithImplicitIrBasedContext( class KtDiagnosticReporterWithImplicitIrBasedContext(
@@ -118,7 +119,7 @@ internal class IrBasedSuppressCache : AbstractKotlinSuppressCache<IrElement>() {
private fun collectSuppressAnnotationKeys(element: IrElement): Boolean = private fun collectSuppressAnnotationKeys(element: IrElement): Boolean =
(element as? IrAnnotationContainer)?.annotations?.filter { (element as? IrAnnotationContainer)?.annotations?.filter {
it.type.classifierOrNull?.signature == SUPPRESS it.type.classOrNull?.owner?.hasEqualFqName(SUPPRESS) == true
}?.flatMap { }?.flatMap {
buildList { buildList {
fun addIfStringConst(irConst: IrConst<*>) { fun addIfStringConst(irConst: IrConst<*>) {
@@ -153,4 +154,4 @@ internal class IrBasedSuppressCache : AbstractKotlinSuppressCache<IrElement>() {
override fun getSuppressingStrings(annotated: IrElement): Set<String> = annotationKeys[annotated].orEmpty() override fun getSuppressingStrings(annotated: IrElement): Set<String> = annotationKeys[annotated].orEmpty()
} }
private val SUPPRESS = IdSignature.CommonSignature("kotlin", "Suppress", null, 0) private val SUPPRESS = FqName("kotlin.Suppress")
@@ -16,13 +16,14 @@
package org.jetbrains.kotlin.backend.common package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classOrNull 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.types.isUnit
import org.jetbrains.kotlin.ir.util.usesDefaultArguments import org.jetbrains.kotlin.ir.util.usesDefaultArguments
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -98,7 +99,7 @@ fun collectTailRecursionCalls(irFunction: IrFunction, followFunctionReference: (
} }
private fun IrExpression.isUnitRead(): Boolean = 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) { override fun visitWhen(expression: IrWhen, data: ElementKind) {
expression.branches.forEach { expression.branches.forEach {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile 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.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.codegen.CodegenFactory import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -73,11 +74,18 @@ open class JvmIrCodegenFactory(
) : CodegenFactory.CodegenInput ) : CodegenFactory.CodegenInput
override fun convertToIr(input: CodegenFactory.IrConversionInput): JvmIrBackendInput { 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) = val (mangler, symbolTable) =
if (externalSymbolTable != null) externalMangler!! to externalSymbolTable if (externalSymbolTable != null) externalMangler!! to externalSymbolTable
else { else {
val mangler = JvmDescriptorMangler(MainFunctionDetector(input.bindingContext, input.languageVersionSettings)) 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 mangler to symbolTable
} }
val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors)) val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors))
@@ -114,7 +122,8 @@ open class JvmIrCodegenFactory(
symbolTable, symbolTable,
frontEndContext, frontEndContext,
stubGenerator, stubGenerator,
mangler mangler,
enableIdSignatures,
) )
val pluginContext by lazy { val pluginContext by lazy {
@@ -121,7 +121,9 @@ private class FileClassLowering(val context: JvmBackendContext) : FileLoweringPa
} }
annotations = 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 else irFile.annotations
metadata = irFile.metadata 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 = fun IrFile.getFileClassInfo(): JvmFileClassInfo =
when (val fileEntry = this.fileEntry) { when (val fileEntry = this.fileEntry) {
is PsiIrFileEntry -> is PsiIrFileEntry ->
@@ -24,12 +24,7 @@ import org.jetbrains.kotlin.ir.builders.irSetField
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.*
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.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
@@ -43,11 +38,16 @@ internal val jvmOptimizationLoweringPhase = makeIrFilePhase(
) )
class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass { class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass {
private companion object {
companion object { private fun isNegation(expression: IrExpression): Boolean =
fun isNegation(expression: IrExpression, context: JvmBackendContext): Boolean = expression is IrCall && expression.symbol.owner.let { not ->
expression is IrCall && not.name == OperatorNameConventions.NOT &&
(expression.symbol as? IrPublicSymbolBase<*>)?.signature == context.irBuiltIns.booleanNotSymbol.signature not.extensionReceiverParameter == null &&
not.valueParameters.isEmpty() &&
not.dispatchReceiverParameter.let { receiver ->
receiver != null && receiver.type.isBoolean()
}
}
} }
private val IrFunction.isObjectEquals private val IrFunction.isObjectEquals
@@ -98,7 +98,7 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
return optimizePropertyAccess(expression, data) return optimizePropertyAccess(expression, data)
} }
if (isNegation(expression, context) && isNegation(expression.dispatchReceiver!!, context)) { if (isNegation(expression) && isNegation(expression.dispatchReceiver!!)) {
return (expression.dispatchReceiver as IrCall).dispatchReceiver!! return (expression.dispatchReceiver as IrCall).dispatchReceiver!!
} }
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
@@ -144,9 +145,10 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
copyAttributes(reference) copyAttributes(reference)
val function = reference.symbol.owner val function = reference.symbol.owner
val isRestricted = reference.symbol.owner.extensionReceiverParameter?.type?.classOrNull?.owner?.annotations?.any { val extensionReceiver = function.extensionReceiverParameter?.type?.classOrNull
it.type.classOrNull?.signature == IdSignature.CommonSignature("kotlin.coroutines", "RestrictsSuspension", null, 0) val isRestricted = extensionReceiver != null && extensionReceiver.owner.annotations.any {
} == true it.type.classOrNull?.isClassWithFqName(FqNameUnsafe("kotlin.coroutines.RestrictsSuspension")) == true
}
val suspendLambda = val suspendLambda =
if (isRestricted) context.ir.symbols.restrictedSuspendLambdaClass.owner if (isRestricted) context.ir.symbols.restrictedSuspendLambdaClass.owner
else context.ir.symbols.suspendLambdaClass.owner else context.ir.symbols.suspendLambdaClass.owner
@@ -481,22 +481,26 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
).substitute(type as IrType) ).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 if (this !is IrClassSymbol) return null
val signature = signature?.asPublic() val signature = signature?.asPublic()
if (signature == null || signature.packageFqName != "kotlin") return null return if (signature != null) {
if (signature.packageFqName == StandardNames.BUILT_INS_PACKAGE_NAME.asString())
return PrimitiveType.getByShortName(signature.declarationFqName) signature.declarationFqName
} else null
} else {
override fun TypeConstructorMarker.getPrimitiveArrayType(): PrimitiveType? { val parent = owner.parent
if (this !is IrClassSymbol) return null if (parent is IrPackageFragment && parent.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
owner.name.asString()
val signature = signature?.asPublic() else null
if (signature == null || signature.packageFqName != "kotlin") return null }
return PrimitiveType.getByShortArrayName(signature.declarationFqName)
} }
override fun TypeConstructorMarker.isUnderKotlinPackage(): Boolean { override fun TypeConstructorMarker.isUnderKotlinPackage(): Boolean {
@@ -9,12 +9,14 @@ import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.ir.declarations.IrClass 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.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.hasEqualFqName import org.jetbrains.kotlin.ir.util.hasEqualFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")
object IdSignatureValues { object IdSignatureValues {
@@ -55,9 +57,14 @@ fun getPublicSignature(packageFqName: FqName, name: String) =
private fun IrType.isClassType(signature: IdSignature.CommonSignature, hasQuestionMark: Boolean? = null): Boolean { private fun IrType.isClassType(signature: IdSignature.CommonSignature, hasQuestionMark: Boolean? = null): Boolean {
if (this !is IrSimpleType) return false if (this !is IrSimpleType) return false
if (hasQuestionMark != null && this.hasQuestionMark != hasQuestionMark) 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 = fun IrClassifierSymbol.isClassWithFqName(fqName: FqNameUnsafe): Boolean =
this is IrClassSymbol && classFqNameEquals(this, fqName) this is IrClassSymbol && classFqNameEquals(this, fqName)
@@ -71,6 +78,17 @@ private val idSignatureToPrimitiveType: Map<IdSignature.CommonSignature, Primiti
getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, it.typeName.asString()) getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, it.typeName.asString())
} }
private val shortNameToPrimitiveType: Map<Name, PrimitiveType> =
PrimitiveType.values().associateBy(PrimitiveType::typeName)
private val idSignatureToUnsignedType: Map<IdSignature.CommonSignature, UnsignedType> =
UnsignedType.values().associateBy {
getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, it.typeName.asString())
}
private val shortNameToUnsignedType: Map<Name, UnsignedType> =
UnsignedType.values().associateBy(UnsignedType::typeName)
val primitiveArrayTypesSignatures: Map<PrimitiveType, IdSignature.CommonSignature> = val primitiveArrayTypesSignatures: Map<PrimitiveType, IdSignature.CommonSignature> =
PrimitiveType.values().associateWith { PrimitiveType.values().associateWith {
getPublicSignature(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, "${it.typeName.asString()}Array") 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.isNullablePrimitiveType(): Boolean = isPrimitiveType(true)
fun IrType.getPrimitiveType(): PrimitiveType? = fun IrType.getPrimitiveType(): PrimitiveType? =
if (this is IrSimpleType && classifier is IrClassSymbol) getPrimitiveOrUnsignedType(idSignatureToPrimitiveType, shortNameToPrimitiveType)
idSignatureToPrimitiveType[classifier.signature]
else null
fun IrType.isUnsignedType(hasQuestionMark: Boolean = false): Boolean = fun IrType.isUnsignedType(hasQuestionMark: Boolean = false): Boolean =
this is IrSimpleType && hasQuestionMark == this.hasQuestionMark && getUnsignedType() != null this is IrSimpleType && hasQuestionMark == this.hasQuestionMark && getUnsignedType() != null
fun IrType.getUnsignedType(): UnsignedType? = fun IrType.getUnsignedType(): UnsignedType? =
if (this is IrSimpleType && classifier is IrClassSymbol) getPrimitiveOrUnsignedType(idSignatureToUnsignedType, shortNameToUnsignedType)
when (classifier.signature) {
IdSignatureValues.uByte -> UnsignedType.UBYTE fun <T : Enum<T>> IrType.getPrimitiveOrUnsignedType(byIdSignature: Map<IdSignature.CommonSignature, T>, byShortName: Map<Name, T>): T? {
IdSignatureValues.uShort -> UnsignedType.USHORT if (this !is IrSimpleType) return null
IdSignatureValues.uInt -> UnsignedType.UINT val symbol = classifier as? IrClassSymbol ?: return null
IdSignatureValues.uLong -> UnsignedType.ULONG if (symbol.signature != null) return byIdSignature[symbol.signature]
else -> null
} val klass = symbol.owner
else null 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 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.isFloatArray(): Boolean = isNotNullClassType(primitiveArrayTypesSignatures[PrimitiveType.FLOAT]!!)
fun IrType.isDoubleArray(): Boolean = isNotNullClassType(primitiveArrayTypesSignatures[PrimitiveType.DOUBLE]!!) 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 { fun IrType.isClassType(fqName: FqNameUnsafe, hasQuestionMark: Boolean): Boolean {
if (this !is IrSimpleType) return false if (this !is IrSimpleType) return false
if (this.hasQuestionMark != hasQuestionMark) return false if (this.hasQuestionMark != hasQuestionMark) return false
@@ -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.IrScriptImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazySymbolTable 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.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
@@ -472,7 +473,17 @@ open class SymbolTable(
} }
override fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol = 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( fun referenceClass(
sig: IdSignature, sig: IdSignature,
@@ -154,14 +154,14 @@ abstract class KotlinIrLinker(
} }
} }
override fun getDeclaration(symbol: IrSymbol): IrDeclaration? { override fun getDeclaration(symbol: IrSymbol): IrDeclaration? =
if (!symbol.isPublicApi) { deserializeOrResolveDeclaration(symbol, false)
if (symbol.hasDescriptor) {
val descriptor = symbol.descriptor protected fun deserializeOrResolveDeclaration(symbol: IrSymbol, allowSymbolsWithoutSignaturesFromOtherModule: Boolean): IrDeclaration? {
if (!platformSpecificSymbol(symbol)) { if (!allowSymbolsWithoutSignaturesFromOtherModule) {
if (descriptor.module !== currentModule) return null if (!symbol.isPublicApi && symbol.hasDescriptor && !platformSpecificSymbol(symbol) &&
} symbol.descriptor.module !== currentModule
} ) return null
} }
if (!symbol.isBound) { if (!symbol.isBound) {
@@ -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")
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext 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.IrField
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
@@ -38,7 +39,8 @@ class JvmIrLinker(
symbolTable: SymbolTable, symbolTable: SymbolTable,
override val translationPluginContext: TranslationPluginContext?, override val translationPluginContext: TranslationPluginContext?,
private val stubGenerator: DeclarationStubGenerator, private val stubGenerator: DeclarationStubGenerator,
private val manglerDesc: JvmDescriptorMangler private val manglerDesc: JvmDescriptorMangler,
private val enableIdSignatures: Boolean,
) : KotlinIrLinker(currentModule, messageLogger, typeSystem.irBuiltIns, symbolTable, emptyList()) { ) : KotlinIrLinker(currentModule, messageLogger, typeSystem.irBuiltIns, symbolTable, emptyList()) {
// TODO: provide friend modules // 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>): IrModuleDeserializer = override fun createCurrentModuleDeserializer(moduleFragment: IrModuleFragment, dependencies: Collection<IrModuleDeserializer>): IrModuleDeserializer =
JvmCurrentModuleDeserializer(moduleFragment, dependencies) JvmCurrentModuleDeserializer(moduleFragment, dependencies)
+5 -1
View File
@@ -81,6 +81,8 @@ where advanced options include:
-Xlambdas=indy Generate lambdas using `invokedynamic` with `LambdaMetafactory.metafactory`. Requires `-jvm-target 1.8` or greater. -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()`. Lambda objects created using `LambdaMetafactory.metafactory` will have different `toString()`.
-Xlambdas=class Generate lambdas as explicit classes -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-call-assertions Don't generate not-null assertions for arguments of platform types
-Xno-kotlin-nothing-value-exception -Xno-kotlin-nothing-value-exception
Do not use KotlinNothingValueException available since 1.4 Do not use KotlinNothingValueException available since 1.4
@@ -133,7 +135,9 @@ where advanced options include:
-Xsuppress-missing-builtins-error -Xsuppress-missing-builtins-error
Suppress the "cannot access built-in declaration" error (useful with -no-stdlib) Suppress the "cannot access built-in declaration" error (useful with -no-stdlib)
-Xtype-enhancement-improvements-strict-mode -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-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-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 -Xuse-javac Use javac for Java source and class files analysis
@@ -23,6 +23,7 @@ class IrInlineBodiesHandler(testServices: TestServices) : AbstractIrHandler(test
override fun processModule(module: TestModule, info: IrBackendInput) { override fun processModule(module: TestModule, info: IrBackendInput) {
val irModule = info.irModuleFragment val irModule = info.irModuleFragment
irModule.acceptChildrenVoid(InlineFunctionsCollector()) irModule.acceptChildrenVoid(InlineFunctionsCollector())
assertions.assertTrue(declaredInlineFunctionSignatures.isNotEmpty())
irModule.acceptChildrenVoid(InlineCallBodiesCheck()) irModule.acceptChildrenVoid(InlineCallBodiesCheck())
assertions.assertTrue((info as IrBackendInput.JvmIrBackendInput).backendInput.symbolTable.allUnbound.isEmpty()) assertions.assertTrue((info as IrBackendInput.JvmIrBackendInput).backendInput.symbolTable.allUnbound.isEmpty())
} }
@@ -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<T>
enum class E { ENTRY }
fun function(s: String): Array<Int> {
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<IdSignature>()
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.<get-property>" in allSignatures)
assertTrue(message, "test/property.<set-property>" in allSignatures)
} else {
assertEmpty(allSignatures)
}
}
}
}