From 39656209f0e85a3643b9e5901c9feb5464a4ca63 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Tue, 29 Nov 2022 16:26:03 +0200 Subject: [PATCH] [K/N] Enable klib IR phases in dynamic driver Support SpecialBackendChecksPhase and CopyDefaultValuesToActualPhase --- .../backend/konan/KonanLoweringPhases.kt | 2 +- .../konan/driver/DynamicCompilerDriver.kt | 4 +- .../konan/driver/phases/BackendPhases.kt | 32 ++++++++++++++ .../lower/SpecialBackendChecksTraversal.kt | 42 ++++++++++++------- 4 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index eddb4d54107..09f31907207 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -83,7 +83,7 @@ internal fun makeKonanModuleOpPhase( ) internal val specialBackendChecksPhase = makeKonanModuleLoweringPhase( - ::SpecialBackendChecksTraversal, + { SpecialBackendChecksTraversal(it, it.interopBuiltIns, it.ir.symbols, it.irBuiltIns) }, name = "SpecialBackendChecks", description = "Special backend checks" ) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt index 855660caf28..cf755d632e3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt @@ -45,7 +45,7 @@ internal class DynamicCompilerDriver : CompilerDriver() { private fun produceKlib(engine: PhaseEngine, config: KonanConfig, environment: KotlinCoreEnvironment) { val frontendOutput = engine.useContext(FrontendContextImpl(config)) { it.runFrontend(environment) } - if (frontendOutput is FrontendPhaseOutput.ShouldNotGenerateCode) { + if (frontendOutput == FrontendPhaseOutput.ShouldNotGenerateCode) { return } require(frontendOutput is FrontendPhaseOutput.Full) @@ -55,8 +55,10 @@ internal class DynamicCompilerDriver : CompilerDriver() { val psiToIrContext = PsiToIrContextImpl(config, frontendOutput.moduleDescriptor, frontendOutput.bindingContext) val psiToIrOutput = engine.useContext(psiToIrContext) { psiToIrEngine -> val output = psiToIrEngine.runPsiToIr(frontendOutput, isProducingLibrary = true) + psiToIrEngine.runSpecialBackendChecks(output) output } + engine.runPhase(CopyDefaultValuesToActualPhase, psiToIrOutput.irModule) psiToIrOutput } val serializerOutput = engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt new file mode 100644 index 00000000000..6bbf29dacee --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt @@ -0,0 +1,32 @@ +/* + * 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.konan.driver.phases + +import org.jetbrains.kotlin.backend.common.lower +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine +import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier +import org.jetbrains.kotlin.backend.konan.lower.SpecialBackendChecksTraversal +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment + +internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase( + "SpecialBackendChecks", + "Special backend checks", +) { context, input -> + SpecialBackendChecksTraversal(context, context.interopBuiltIns, input.symbols, input.irModule.irBuiltins).lower(input.irModule) +} + + +internal val CopyDefaultValuesToActualPhase = createSimpleNamedCompilerPhase( + name = "CopyDefaultValuesToActual", + description = "Copy default values from expect to actual declarations", +) { _, input -> + ExpectToActualDefaultValueCopier(input).process() +} + +internal fun PhaseEngine.runSpecialBackendChecks(psiToIrOutput: PsiToIrOutput) { + runPhase(SpecialBackendChecksPhase, psiToIrOutput) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt index 7efabd8603a..c5bd06c29e8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt @@ -15,11 +15,13 @@ import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.cgen.* import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* @@ -35,14 +37,26 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.fileUtils.descendantRelativeTo import java.io.File -internal class SpecialBackendChecksTraversal(val context: Context) : FileLoweringPass { - override fun lower(irFile: IrFile) = irFile.acceptChildrenVoid(BackendChecker(context, irFile)) +/** + * Kotlin/Native-specific language checks. Most importantly, it checks C/Objective-C interop restrictions. + * TODO: Should be moved to compiler frontend after K2. + */ +internal class SpecialBackendChecksTraversal( + private val context: PhaseContext, + private val interop: InteropBuiltIns, + private val symbols: KonanSymbols, + private val irBuiltIns: IrBuiltIns, +) : FileLoweringPass { + override fun lower(irFile: IrFile) = irFile.acceptChildrenVoid(BackendChecker(context, interop, symbols, irBuiltIns, irFile)) } -private class BackendChecker(val context: Context, val irFile: IrFile) : IrElementVisitorVoid { - val interop = context.interopBuiltIns - val symbols = context.ir.symbols - val irBuiltIns = context.irBuiltIns +private class BackendChecker( + private val context: PhaseContext, + val interop: InteropBuiltIns, + val symbols: KonanSymbols, + val irBuiltIns: IrBuiltIns, + private val irFile: IrFile, +) : IrElementVisitorVoid { val target = context.config.target fun reportError(location: IrElement, message: String): Nothing = @@ -95,7 +109,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme } private fun IrConstructor.isOverrideInit() = - this.annotations.hasAnnotation(context.interopBuiltIns.objCOverrideInit.fqNameSafe) + this.annotations.hasAnnotation(interop.objCOverrideInit.fqNameSafe) private fun checkCanGenerateOverrideInit(irClass: IrClass, constructor: IrConstructor) { val superClass = irClass.getSuperClassNotAny()!! @@ -104,7 +118,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme }.toList() val superConstructor = superConstructors.singleOrNull() ?: run { - val annotation = context.interopBuiltIns.objCOverrideInit.name + val annotation = interop.objCOverrideInit.name if (superConstructors.isEmpty()) reportError(constructor, """ @@ -122,7 +136,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme // Remove fake overrides of this init method, also check for explicit overriding: irClass.declarations.forEach { if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols && it.isReal) { - val annotation = context.interopBuiltIns.objCOverrideInit.name + val annotation = interop.objCOverrideInit.name reportError(constructor, "constructor with @$annotation overrides initializer that is already overridden explicitly" ) @@ -138,7 +152,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme } private fun checkCanGenerateActionImp(function: IrSimpleFunction) { - val action = "@${context.interopBuiltIns.objCAction.name}" + val action = "@${interop.objCAction.name}" function.extensionReceiverParameter?.let { reportError(it, "$action method must not have extension receiver") @@ -162,7 +176,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme private fun checkCanGenerateOutletSetterImp(property: IrProperty) { val descriptor = property.descriptor - val outlet = "@${context.interopBuiltIns.objCOutlet.name}" + val outlet = "@${interop.objCOutlet.name}" if (!descriptor.isVar) reportError(property, "$outlet property must be var") @@ -243,7 +257,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme if (!hasObjCClassSupertype) reportError(irClass, "Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)") - val methodsOfAny = context.ir.symbols.any.owner.declarations.filterIsInstance().toSet() + val methodsOfAny = symbols.any.owner.declarations.filterIsInstance().toSet() irClass.declarations.filterIsInstance().filter { it.isReal }.forEach { method -> val overriddenMethodOfAny = method.allOverriddenFunctions.firstOrNull { @@ -318,7 +332,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme } if (callee.returnType.isNativePointed(symbols) && - !callee.hasCCallAnnotation("CppClassConstructor")) + !callee.hasCCallAnnotation("CppClassConstructor")) reportError(expression, "Native interop types constructors must not be called directly") } @@ -341,7 +355,7 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme else ", but captures at:\n ${captures.joinToString("\n ") { val location = it.getCompilerMessageLocation(irFile)!! val relativePath = File(location.path).descendantRelativeTo(cwd).path - val capturedValueName = if (it is IrGetValue) ": ${it.symbol.owner.name.asString()}" else "" + val capturedValueName = if (it is IrGetValue) ": ${it.symbol.owner.name.asString()}" else "" "$relativePath:${location.line}:${location.column}$capturedValueName" }}"