From ad7bf0853ddf3d9efe87cefef3c4f7e32f9639c8 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Tue, 29 Oct 2019 11:55:59 +0700 Subject: [PATCH] [Linker] Extend KotlinIrLinker infrastructure to support libraries that doesn't contain IR. Add IrProviderForInteropStubs for ir-less interop KLIBs. --- .../kotlin/backend/konan/ToplevelPhases.kt | 4 +- .../kotlin/backend/konan/descriptors/utils.kt | 12 +++ .../konan/ir/IrProviderForInteropStubs.kt | 83 +++++++++++++++++++ .../serialization/KonanIrModuleSerializer.kt | 36 +++++--- .../konan/serialization/KonanIrlinker.kt | 10 ++- backend.native/tests/build.gradle | 23 +++++ .../tests/link/ir_providers/hello.kt | 1 + .../tests/link/ir_providers/library/empty.kt | 0 .../ir_providers/library/manifest.properties | 1 + .../kotlin/konan/library/KonanLibrary.kt | 2 + .../konan/library/SearchPathResolver.kt | 2 +- 11 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrProviderForInteropStubs.kt create mode 100644 backend.native/tests/link/ir_providers/hello.kt create mode 100644 backend.native/tests/link/ir_providers/library/empty.kt create mode 100644 backend.native/tests/link/ir_providers/library/manifest.properties diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index c9c1cf9597b..72728c0dffb 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary +import org.jetbrains.kotlin.backend.konan.ir.IrProviderForInteropStubs import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier @@ -179,9 +180,10 @@ internal val psiToIrPhase = konanUnitPhase( val functionIrClassFactory = BuiltInFictitiousFunctionIrClassFactory( symbolTable, generatorContext.irBuiltIns, reflectionTypes) + val irProviderForInteropStubs = IrProviderForInteropStubs() val symbols = KonanSymbols(this, symbolTable, symbolTable.lazyWrapper, functionIrClassFactory) val module = translator.generateModuleFragment(generatorContext, environment.getSourceFiles(), - deserializer, listOf(functionIrClassFactory)) + deserializer, listOf(irProviderForInteropStubs, functionIrClassFactory)) if (this.stdlibModule in modulesWithoutDCE) { functionIrClassFactory.buildAllClasses() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt index 4a93896fd67..cc3da6c6f05 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt @@ -6,6 +6,11 @@ package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.klibModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.kotlinLibrary +import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER +import org.jetbrains.kotlin.library.BaseKotlinLibrary import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.deserialization.descriptors.* @@ -47,3 +52,10 @@ fun DeclarationDescriptor.findTopLevelDescriptor(): DeclarationDescriptor { val ModuleDescriptor.isForwardDeclarationModule get() = name == Name.special("") + +fun BaseKotlinLibrary.isInteropLibrary() = + manifestProperties["ir_provider"] == KLIB_INTEROP_IR_PROVIDER_IDENTIFIER + +fun ModuleDescriptor.isFromInteropLibrary() = + if (klibModuleOrigin !is DeserializedKlibModuleOrigin) false + else kotlinLibrary.isInteropLibrary() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrProviderForInteropStubs.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrProviderForInteropStubs.kt new file mode 100644 index 00000000000..ef04f608237 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrProviderForInteropStubs.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir + +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyProperty +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyTypeAlias +import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.resolve.descriptorUtil.module + +/** + * Generates external IR declarations for descriptors from interop libraries. + */ +class IrProviderForInteropStubs : LazyIrProvider { + + override lateinit var declarationStubGenerator: DeclarationStubGenerator + + override fun getDeclaration(symbol: IrSymbol): IrLazyDeclarationBase? = + if (symbol.descriptor.module.isFromInteropLibrary()) { + provideIrDeclaration(symbol) + } else { + null + } + + private fun provideIrDeclaration(symbol: IrSymbol): IrLazyDeclarationBase = when (symbol) { + is IrSimpleFunctionSymbol -> provideIrFunction(symbol) + is IrPropertySymbol -> provideIrProperty(symbol) + is IrTypeAliasSymbol -> provideIrTypeAlias(symbol) + else -> error("Unsupported interop declaration: symbol=$symbol, descriptor=${symbol.descriptor}") + } + + private fun provideIrFunction(symbol: IrSimpleFunctionSymbol): IrLazyFunction = + declarationStubGenerator.symbolTable.declareSimpleFunction( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + symbol.descriptor, this::createFunctionDeclaration + ) as IrLazyFunction + + private fun createFunctionDeclaration(symbol: IrSimpleFunctionSymbol) = + IrLazyFunction( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + symbol, declarationStubGenerator, declarationStubGenerator.typeTranslator + ) + + private fun provideIrProperty(symbol: IrPropertySymbol): IrLazyProperty = + declarationStubGenerator.symbolTable.declareProperty( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + symbol.descriptor, propertyFactory = this::createPropertyDeclaration + ) as IrLazyProperty + + private fun createPropertyDeclaration(symbol: IrPropertySymbol) = + IrLazyProperty( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + symbol, declarationStubGenerator, declarationStubGenerator.typeTranslator, null + ) + + private fun provideIrTypeAlias(symbol: IrTypeAliasSymbol): IrLazyTypeAlias = + declarationStubGenerator.symbolTable.declareTypeAlias( + symbol.descriptor, this::createTypeAlias + ) as IrLazyTypeAlias + + private fun createTypeAlias(symbol: IrTypeAliasSymbol): IrLazyTypeAlias = + IrLazyTypeAlias( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + symbol, symbol.descriptor.name, + symbol.descriptor.visibility, symbol.descriptor.isActual, + declarationStubGenerator, declarationStubGenerator.typeTranslator + ) +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt index f7e4c81c2f2..a0589307785 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt @@ -1,18 +1,32 @@ package org.jetbrains.kotlin.backend.konan.serialization import org.jetbrains.kotlin.backend.common.LoggingContext -import org.jetbrains.kotlin.backend.common.serialization.IrModuleSerializer -import org.jetbrains.kotlin.backend.konan.RuntimeNames -import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler -import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable -import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsGlobalDeclarationTable -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrFileSerializer -import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.backend.common.serialization.* +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary +import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns -import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.resolve.descriptorUtil.module + +private class KonanDeclarationTable( + descriptorTable: DescriptorTable, + globalDeclarationTable: GlobalDeclarationTable, + startIndex: Long +) : DeclarationTable(descriptorTable, globalDeclarationTable, startIndex), + DescriptorUniqIdAware by DeserializedDescriptorUniqIdAware { + + /** + * It is incorrect to compute UniqId for declarations from metadata-based libraries. + * Instead we should get precomputed value from metadata. + */ + override fun tryComputeBackendSpecificUniqId(declaration: IrDeclaration): UniqId? { + return if (declaration.descriptor.module.isFromInteropLibrary()) { + UniqId(declaration.descriptor.getUniqId() ?: error("No uniq id found for ${declaration.descriptor}")) + } else { + null + } + } +} class KonanIrModuleSerializer( logger: LoggingContext, @@ -24,6 +38,6 @@ class KonanIrModuleSerializer( private val globalDeclarationTable = KonanGlobalDeclarationTable(irBuiltIns) override fun createSerializerForFile(file: IrFile): KonanIrFileSerializer = - KonanIrFileSerializer(logger, DeclarationTable(descriptorTable, globalDeclarationTable, 0)) + KonanIrFileSerializer(logger, KonanDeclarationTable(descriptorTable, globalDeclarationTable, 0)) } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index d37001a1c94..6be72f67ea2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.konan.serialization import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.common.serialization.* +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.resolve.descriptorUtil.module class KonanIrLinker( currentModule: ModuleDescriptor, @@ -58,7 +60,7 @@ class KonanIrLinker( moduleDescriptor.konanLibrary!!.file(fileIndex) override fun readFileCount(moduleDescriptor: ModuleDescriptor) = - moduleDescriptor.run { if (isForwardDeclarationModule) 0 else konanLibrary!!.fileCount() } + moduleDescriptor.run { if (isForwardDeclarationModule || !shouldBeDeserialized()) 0 else konanLibrary!!.fileCount() } private val ModuleDescriptor.userName get() = konanLibrary!!.libraryFile.absolutePath @@ -68,6 +70,12 @@ class KonanIrLinker( return globalDeserializationState } + /** + * If declaration is from interop library then IR for it is generated by IrProviderForInteropStubs. + */ + override fun DeclarationDescriptor.shouldBeDeserialized(): Boolean = + !module.isFromInteropLibrary() + val modules: Map get() = mutableMapOf().apply { deserializersForModules.filter { !it.key.isForwardDeclarationModule }.forEach { this.put(it.key.konanLibrary!!.libraryName, it.value.module) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 92a8d5c231e..c58ad145a57 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3701,6 +3701,29 @@ task library_mismatch(type: KonanDriverTest) { goldValue = isWindows() ? messages.replaceAll('\\/', '\\\\') : messages } +task library_ir_provider_mismatch(type: KonanDriverTest) { + def dir = buildDir.absolutePath + def lib = "$projectDir/link/ir_providers/library/empty.kt" + def invalidManifest = "$projectDir/link/ir_providers/library/manifest.properties" + + def currentTarget = project.target.name + + doBefore { + def konancScript = isWindows() ? "konanc.bat" : "konanc" + def konanc = "$dist/bin/$konancScript" + "$konanc $lib -p library -o $dir/supported_ir_provider/empty -target $currentTarget".execute().waitFor() + "$konanc $lib -p library -o $dir/unsupported_ir_provider/empty -manifest $invalidManifest -target $currentTarget".execute().waitFor() + + } + source = "link/ir_providers/hello.kt" + flags = ['-target', currentTarget, '-l', 'empty', '-r', "$dir/unsupported_ir_provider", '-r', "$dir/supported_ir_provider"] + compilerMessages = true + def messages = + "warning: skipping $dir/unsupported_ir_provider/empty.klib. The library requires unknown IR provider UNSUPPORTED.\n" + + "hello\n" + goldValue = isWindows() ? messages.replaceAll('\\/', '\\\\') : messages +} + def target = ext.platformManager.targetByName(project.testTarget ?: 'host') def targetName = target.name diff --git a/backend.native/tests/link/ir_providers/hello.kt b/backend.native/tests/link/ir_providers/hello.kt new file mode 100644 index 00000000000..dbd2d6996f7 --- /dev/null +++ b/backend.native/tests/link/ir_providers/hello.kt @@ -0,0 +1 @@ +fun main() = println("hello") \ No newline at end of file diff --git a/backend.native/tests/link/ir_providers/library/empty.kt b/backend.native/tests/link/ir_providers/library/empty.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/backend.native/tests/link/ir_providers/library/manifest.properties b/backend.native/tests/link/ir_providers/library/manifest.properties new file mode 100644 index 00000000000..c2751ab1308 --- /dev/null +++ b/backend.native/tests/link/ir_providers/library/manifest.properties @@ -0,0 +1 @@ +ir_provider=UNSUPPORTED \ No newline at end of file diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt index 55f97e1f3a6..276ea7760e3 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt @@ -10,6 +10,8 @@ const val KLIB_PROPERTY_INTEROP = "interop" const val KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS = "exportForwardDeclarations" const val KLIB_PROPERTY_INCLUDED_HEADERS = "includedHeaders" +const val KLIB_INTEROP_IR_PROVIDER_IDENTIFIER = "kotlin.native.cinterop" + interface TargetedLibrary { val targetList: List val includedPaths: List diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt index 42fe58cf42e..85308e19184 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt @@ -81,7 +81,7 @@ internal class KonanLibraryProperResolver( localKonanDir, skipCurrentDir, logger, - emptyList() + listOf(KLIB_INTEROP_IR_PROVIDER_IDENTIFIER) ), SearchPathResolverWithTarget { override fun libraryBuilder(file: File, isDefault: Boolean) = createKonanLibrary(file, target, isDefault)