[K/N] Untie PsiToIr phase from Context

This commit is contained in:
Sergey Bogolepov
2022-11-29 16:08:39 +02:00
committed by Space Team
parent 182662f7ed
commit b828672a2e
11 changed files with 181 additions and 51 deletions
@@ -5,10 +5,14 @@
package org.jetbrains.kotlin.backend.konan
import llvm.*
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.llvm.*
import llvm.LLVMArrayType
import llvm.LLVMConstInt
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.konan.llvm.ConstValue
import org.jetbrains.kotlin.backend.konan.llvm.StaticData
import org.jetbrains.kotlin.backend.konan.llvm.constValue
import org.jetbrains.kotlin.backend.konan.llvm.toLLVMType
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
@@ -23,10 +27,11 @@ import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
internal fun KonanSymbols.getTypeConversion(actualType: IrType, expectedType: IrType): IrSimpleFunctionSymbol? =
// TODO: Find a better home for this function than Context.
internal fun Context.getTypeConversion(actualType: IrType, expectedType: IrType): IrSimpleFunctionSymbol? =
getTypeConversionImpl(actualType.getInlinedClassNative(), expectedType.getInlinedClassNative())
private fun KonanSymbols.getTypeConversionImpl(
private fun Context.getTypeConversionImpl(
actualInlinedClass: IrClass?,
expectedInlinedClass: IrClass?
): IrSimpleFunctionSymbol? {
@@ -34,8 +39,8 @@ private fun KonanSymbols.getTypeConversionImpl(
return when {
actualInlinedClass == null && expectedInlinedClass == null -> null
actualInlinedClass != null && expectedInlinedClass == null -> context.getBoxFunction(actualInlinedClass)
actualInlinedClass == null && expectedInlinedClass != null -> context.getUnboxFunction(expectedInlinedClass)
actualInlinedClass != null && expectedInlinedClass == null -> getBoxFunction(actualInlinedClass)
actualInlinedClass == null && expectedInlinedClass != null -> getUnboxFunction(expectedInlinedClass)
else -> error("actual type is ${actualInlinedClass?.fqNameForIrSerialization}, expected ${expectedInlinedClass?.fqNameForIrSerialization}")
}?.symbol
}
@@ -12,11 +12,12 @@ import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.konan.descriptors.BridgeDirections
import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysisResult
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrContext
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrOutput
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.backend.konan.llvm.CodegenClassMetadata
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportCodeSpec
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportedInterface
import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis
@@ -66,13 +67,27 @@ internal class Context(
config: KonanConfig,
val environment: KotlinCoreEnvironment,
val frontendServices: FrontendServices,
var bindingContext: BindingContext,
override var bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
) : KonanBackendContext(config), ConfigChecks {
) : KonanBackendContext(config), PsiToIrContext {
fun populateAfterPsiToIr(
psiToIrOutput: PsiToIrOutput
) {
irModules = psiToIrOutput.irModules
irModule = psiToIrOutput.irModule
expectDescriptorToSymbol = psiToIrOutput.expectDescriptorToSymbol
ir = KonanIr(this, psiToIrOutput.irModule)
ir.symbols = psiToIrOutput.symbols
if (psiToIrOutput.irLinker is KonanIrLinker) {
irLinker = psiToIrOutput.irLinker
}
}
/**
* Valid from [createSymbolTablePhase] until [destroySymbolTablePhase].
*/
var symbolTable: SymbolTable? = null
override var symbolTable: SymbolTable? = null
lateinit var cAdapterGenerator: CAdapterGenerator
@@ -102,7 +117,7 @@ internal class Context(
val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) }
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) }
val reflectionTypes: KonanReflectionTypes by lazy(PUBLICATION) {
override val reflectionTypes: KonanReflectionTypes by lazy(PUBLICATION) {
KonanReflectionTypes(moduleDescriptor)
}
@@ -162,7 +177,7 @@ internal class Context(
override val typeSystem: IrTypeSystemContext
get() = IrTypeSystemContextImpl(irBuiltIns)
val interopBuiltIns by lazy {
override val interopBuiltIns by lazy {
InteropBuiltIns(this.builtIns)
}
@@ -202,7 +217,7 @@ internal class Context(
var referencedFunctions: Set<IrFunction>? = null
internal val stdlibModule
override val stdlibModule
get() = this.builtIns.any.module
val declaredLocalArrays: MutableMap<String, LLVMTypeRef> = HashMap()
@@ -222,6 +237,8 @@ internal class Context(
}
}
}
override fun dispose() {}
}
internal class ContextLogger(val context: LoggingContext) {
@@ -9,12 +9,16 @@ import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrContext
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrInput
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrOutput
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
@@ -31,8 +35,6 @@ import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CleanableBindingContext
import org.jetbrains.kotlin.utils.DFS
object KonanStubGeneratorExtensions : StubGeneratorExtensions() {
@@ -41,11 +43,12 @@ object KonanStubGeneratorExtensions : StubGeneratorExtensions() {
}
}
internal fun Context.psiToIr(
symbolTable: SymbolTable,
isProducingLibrary: Boolean,
internal fun PsiToIrContext.psiToIr(
input: PsiToIrInput,
useLinkerWhenProducingLibrary: Boolean
) {
): PsiToIrOutput {
val symbolTable = symbolTable!!
val (moduleDescriptor, environment, isProducingLibrary) = input
// Translate AST to high level IR.
val expectActualLinker = config.configuration[CommonConfigurationKeys.EXPECT_ACTUAL_LINKER] ?: false
val messageLogger = config.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None
@@ -102,7 +105,7 @@ internal fun Context.psiToIr(
}
}
} else {
val exportedDependencies = (getExportedDependencies() + libraryToCacheModule?.let { listOf(it) }.orEmpty()).distinct()
val exportedDependencies = (moduleDescriptor.getExportedDependencies(config) + libraryToCacheModule?.let { listOf(it) }.orEmpty()).distinct()
val irProviderForCEnumsAndCStructs =
IrProviderForCEnumAndCStructStubs(generatorContext, interopBuiltIns, symbols)
@@ -200,7 +203,7 @@ internal fun Context.psiToIr(
}
}
expectDescriptorToSymbol = mutableMapOf()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val mainModule = translator.generateModuleFragment(
generatorContext,
environment.getSourceFiles(),
@@ -228,20 +231,11 @@ internal fun Context.psiToIr(
val fakeOverrideChecker = FakeOverrideChecker(KonanManglerIr, KonanManglerDesc)
modules.values.forEach { fakeOverrideChecker.check(it) }
}
irModule = mainModule
irModules = modules
// IR linker deserializes files in the order they lie on the disk, which might be inconvenient,
// so to make the pipeline more deterministic, the files are to be sorted.
// This concerns in the first place global initializers order for the eager initialization strategy,
// where the files are being initialized in order one by one.
irModules.values.forEach { module -> module.files.sortBy { it.fileEntry.name } }
if (!isProducingLibrary)
irLinker = irDeserializer as KonanIrLinker
ir.symbols = symbols
modules.values.forEach { module -> module.files.sortBy { it.fileEntry.name } }
if (!isProducingLibrary) {
// TODO: find out what should be done in the new builtins/symbols about it
@@ -249,7 +243,7 @@ internal fun Context.psiToIr(
(functionIrClassFactory as? BuiltInFictitiousFunctionIrClassFactory)?.buildAllClasses()
}
(functionIrClassFactory as? BuiltInFictitiousFunctionIrClassFactory)?.module =
(modules.values + irModule!!).single { it.descriptor == this.stdlibModule }
(modules.values + mainModule).single { it.descriptor == this.stdlibModule }
}
mainModule.files.forEach { it.metadata = KonanFileMetadataSource(mainModule) }
@@ -257,9 +251,11 @@ internal fun Context.psiToIr(
module.files.forEach { it.metadata = KonanFileMetadataSource(module as KonanIrModuleFragmentImpl) }
}
val originalBindingContext = bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in K/N IR to avoid leaking memory: $bindingContext")
originalBindingContext.clear()
this.bindingContext = BindingContext.EMPTY
return PsiToIrOutput(
modules,
mainModule,
expectDescriptorToSymbol,
symbols,
if (isProducingLibrary) null else irDeserializer as KonanIrLinker
)
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrInput
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
@@ -30,6 +31,8 @@ import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CleanableBindingContext
internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) {
if (!context.config.needVerifyIr) return
@@ -111,9 +114,13 @@ internal val buildCExportsPhase = konanUnitPhase(
internal val psiToIrPhase = konanUnitPhase(
op = {
this.psiToIr(symbolTable!!,
isProducingLibrary = config.produce == CompilerOutputKind.LIBRARY,
useLinkerWhenProducingLibrary = false)
val input = PsiToIrInput(moduleDescriptor, environment, isProducingLibrary = config.produce == CompilerOutputKind.LIBRARY)
val psiToIrOutput = this.psiToIr(input, useLinkerWhenProducingLibrary = false)
this.populateAfterPsiToIr(psiToIrOutput)
val originalBindingContext = bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in K/N IR to avoid leaking memory: $bindingContext")
originalBindingContext.clear()
this.bindingContext = BindingContext.EMPTY
},
name = "Psi2Ir",
description = "Psi to IR conversion and klib linkage",
@@ -0,0 +1,102 @@
/*
* 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.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.KonanReflectionTypes
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.psiToIr
import org.jetbrains.kotlin.backend.konan.serialization.KonanIdSignaturer
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker
import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerDesc
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CleanableBindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
data class PsiToIrInput(
val moduleDescriptor: ModuleDescriptor,
val environment: KotlinCoreEnvironment,
val isProducingLibrary: Boolean,
)
// TODO: Possible improvement: a separate class for klib output (without irModules and irLinker).
internal class PsiToIrOutput(
val irModules: Map<String, IrModuleFragment>,
val irModule: IrModuleFragment,
val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
val symbols: KonanSymbols,
val irLinker: KonanIrLinker?,
)
// TODO: Consider component-based approach
internal interface PsiToIrContext : PhaseContext {
val symbolTable: SymbolTable?
val reflectionTypes: KonanReflectionTypes
val builtIns: KonanBuiltIns
val interopBuiltIns: InteropBuiltIns
val bindingContext: BindingContext
val stdlibModule: ModuleDescriptor
get() = this.builtIns.any.module
}
internal class PsiToIrContextImpl(
config: KonanConfig,
private val moduleDescriptor: ModuleDescriptor,
override val bindingContext: BindingContext,
) : BasicPhaseContext(config), PsiToIrContext {
// TODO: Invalidate properly in dispose method.
override val symbolTable = SymbolTable(KonanIdSignaturer(KonanManglerDesc), IrFactoryImpl)
override val reflectionTypes: KonanReflectionTypes by lazy(LazyThreadSafetyMode.PUBLICATION) {
KonanReflectionTypes(moduleDescriptor)
}
override val builtIns: KonanBuiltIns by lazy(LazyThreadSafetyMode.PUBLICATION) {
moduleDescriptor.builtIns as KonanBuiltIns
}
override val interopBuiltIns by lazy {
InteropBuiltIns(this.builtIns)
}
override fun dispose() {
val originalBindingContext = bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in K/N IR to avoid leaking memory: $bindingContext")
originalBindingContext.clear()
}
}
internal val PsiToIrPhase = createSimpleNamedCompilerPhase<PsiToIrContext, PsiToIrInput, PsiToIrOutput>(
"PsiToIr", "Translate PSI to IR",
outputIfNotEnabled = { _, _, _, _ -> error("PsiToIr phase cannot be disabled") }
) { context, input ->
context.psiToIr(input, useLinkerWhenProducingLibrary = false)
}
internal fun <T : PsiToIrContext> PhaseEngine<T>.runPsiToIr(
frontendResult: FrontendPhaseOutput.Full,
isProducingLibrary: Boolean
): PsiToIrOutput {
val psiToIrInput = PsiToIrInput(frontendResult.moduleDescriptor, frontendResult.environment, isProducingLibrary)
return this.runPhase(PsiToIrPhase, psiToIrInput)
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.llvm.findMainEntryPoint
import org.jetbrains.kotlin.backend.konan.lower.TestProcessor
import org.jetbrains.kotlin.builtins.StandardNames
@@ -40,14 +41,14 @@ internal class KonanIr(context: Context, irModule: IrModuleFragment): Ir<Context
}
internal class KonanSymbols(
val context: Context,
private val descriptorsLookup: DescriptorsLookup,
context: PhaseContext,
descriptorsLookup: DescriptorsLookup,
irBuiltIns: IrBuiltIns,
private val symbolTable: SymbolTable,
lazySymbolTable: ReferenceSymbolTable
): Symbols(irBuiltIns, symbolTable) {
val entryPoint = findMainEntryPoint(context)?.let { symbolTable.referenceSimpleFunction(it) }
val entryPoint = findMainEntryPoint(context, descriptorsLookup.builtIns)?.let { symbolTable.referenceSimpleFunction(it) }
override val externalSymbolTable = lazySymbolTable
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -17,7 +19,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
internal fun findMainEntryPoint(context: PhaseContext, builtIns: KotlinBuiltIns): FunctionDescriptor? {
val config = context.config.configuration
if (config.get(KonanConfigKeys.PRODUCE) != PROGRAM) return null
@@ -27,7 +29,7 @@ internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
val entryName = entryPoint.shortName()
val packageName = entryPoint.parent()
val packageScope = context.builtIns.builtInsModule.getPackage(packageName).memberScope
val packageScope = builtIns.builtInsModule.getPackage(packageName).memberScope
val candidates = packageScope.getContributedFunctions(entryName,
NoLookupLocation.FROM_BACKEND).filter {
@@ -1816,7 +1816,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
return when (value) {
is IrConstantPrimitive -> {
val constructedType = value.value.type
if (context.ir.symbols.getTypeConversion(constructedType, value.type) != null) {
if (context.getTypeConversion(constructedType, value.type) != null) {
if (value.value.kind == IrConstKind.Null) {
Zero(value.type.toLLVMType(llvm))
} else {
@@ -1846,7 +1846,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
val constructedType = value.constructor.owner.constructedClassType
val constructedClass = constructedType.getClass()!!
val needUnBoxing = constructedType.getInlinedClassNative() != null &&
context.ir.symbols.getTypeConversion(constructedType, value.type) == null
context.getTypeConversion(constructedType, value.type) == null
if (needUnBoxing) {
val unboxed = value.valueArguments.singleOrNull()
?: error("Inlined class should have exactly one constructor argument")
@@ -408,7 +408,7 @@ internal class ObjCExportCodeGenerator(
resultLifetime: Lifetime
): LLVMValueRef {
val conversion = symbols.getTypeConversion(actualType, expectedType)
val conversion = context.getTypeConversion(actualType, expectedType)
?: return genValue(resultLifetime)
val value = genValue(Lifetime.ARGUMENT)
@@ -134,7 +134,7 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
}
private fun IrExpression.adaptIfNecessary(actualType: IrType, expectedType: IrType): IrExpression {
val conversion = symbols.getTypeConversion(actualType, expectedType)
val conversion = context.getTypeConversion(actualType, expectedType)
return if (conversion == null) {
this
} else {
@@ -1472,7 +1472,7 @@ internal object DevirtualizationAnalysis {
})
}
// Insert proper unboxing (unreachable code):
+irCoerce(throwExpr, symbols.getTypeConversion(throwExpr.type, type))
+irCoerce(throwExpr, context.getTypeConversion(throwExpr.type, type))
}
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.