From 9f457dce803e5a87cc001674d3358d561536d56d Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Wed, 16 Sep 2020 14:25:31 +0700 Subject: [PATCH] Add global accessors to companion objects. It fixes IrGetObjectValue evaluation in case of Lazy IR. --- .../jetbrains/kotlin/backend/konan/Context.kt | 1 - .../konan/EnumSpecialDescriptorsFactory.kt | 37 ++----- .../kotlin/backend/konan/InternalAbi.kt | 46 +++++--- .../jetbrains/kotlin/backend/konan/PsiToIr.kt | 2 + .../kotlin/backend/konan/ToplevelPhases.kt | 103 +++++++++++++----- 5 files changed, 118 insertions(+), 71 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 3ba5dc135ef..4cd2a830344 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -292,7 +292,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { throw Error("Another IrModule in the context.") } field = module!! - internalAbi.init(module) ir = KonanIr(this, module) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt index 0ea866f3fa1..54a57d1ae02 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.irBlockBody import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irReturn @@ -109,37 +110,19 @@ internal class EnumSpecialDeclarationsFactory(val context: Context) { // We can't move property getter to the top-level scope. // So add a wrapper instead. - private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction { - return WrappedSimpleFunctionDescriptor().let { - val valuesType = valuesArrayType(enumClass) - val origin = if (isExternal) InternalAbi.INTERNAL_ABI_ORIGIN else DECLARATION_ORIGIN_ENUM - val name = context.internalAbi.getMangledNameFor("get-VALUES", enumClass) - IrFunctionImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - origin, - IrSimpleFunctionSymbolImpl(it), - name, - DescriptorVisibilities.PUBLIC, - Modality.FINAL, - valuesType, - isInline = false, - isExternal = isExternal, - isTailrec = false, - isSuspend = false, - isExpect = false, - isFakeOverride = false, - isOperator = false, - isInfix = false - ).apply { - it.bind(this) + private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction = + context.irFactory.buildFun { + name = InternalAbi.getEnumValuesAccessorName(enumClass) + returnType = valuesArrayType(enumClass) + origin = InternalAbi.INTERNAL_ABI_ORIGIN + this.isExternal = isExternal + }.also { if (isExternal) { - context.internalAbi.reference(this, enumClass.module) + context.internalAbi.reference(it, enumClass.module) } else { - context.internalAbi.declare(this) + context.internalAbi.declare(it, enumClass.module) } } - } - } fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum { val enumEntriesMap = enumEntriesMap(enumClass) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt index 81b05c3653d..d38aaed178d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt @@ -10,7 +10,9 @@ import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl +import org.jetbrains.kotlin.ir.util.addFile +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -22,22 +24,24 @@ import org.jetbrains.kotlin.name.Name */ internal class InternalAbi(private val context: Context) { /** - * File that stores all internal ABI declarations. + * Files that stores all internal ABI declarations. + * We use per-module files so that global initializer will be stored + * in the appropriate modules. * * We have to store such declarations in top-level to avoid mangling that * makes referencing harder. * A bit better solution is to add files with proper packages, but it is impossible * during FileLowering (hello, ConcurrentModificationException). */ - private lateinit var internalAbiFile: IrFile + private lateinit var internalAbiFiles: Map /** * Representation of ABI files from external modules. */ - private val externalModules = mutableMapOf() + private val externalAbiFiles = mutableMapOf() - fun init(module: IrModuleFragment) { - internalAbiFile = createAbiFile(module) + fun init(modules: List) { + internalAbiFiles = modules.associate { it.descriptor to createAbiFile(it) } } private fun createAbiFile(module: IrModuleFragment): IrFile = @@ -49,24 +53,16 @@ internal class InternalAbi(private val context: Context) { fun reference(function: IrFunction, module: ModuleDescriptor) { assert(function.isExternal) { "Function that represents external ABI should be marked as external" } context.llvmImports.add(module.llvmSymbolOrigin) - externalModules.getOrPut(module) { + externalAbiFiles.getOrPut(module) { createAbiFile(IrModuleFragmentImpl(module, context.irBuiltIns)) }.addChild(function) } /** - * Generate name for declaration that will be a part of internal ABI. + * Adds [function] to a list of [module]'s publicly available symbols. */ - fun getMangledNameFor(declarationName: String, parent: IrDeclarationParent): Name { - val prefix = parent.fqNameForIrSerialization - return "$prefix.$declarationName".synthesizedName - } - - /** - * Adds [function] to a list of publicly available symbols. - */ - fun declare(function: IrFunction) { - internalAbiFile.addChild(function) + fun declare(function: IrFunction, module: ModuleDescriptor) { + internalAbiFiles.getValue(module).addChild(function) } companion object { @@ -74,5 +70,19 @@ internal class InternalAbi(private val context: Context) { * Allows to distinguish external declarations to internal ABI. */ val INTERNAL_ABI_ORIGIN = object : IrDeclarationOriginImpl("INTERNAL_ABI") {} + + fun getCompanionObjectAccessorName(companion: IrClass): Name = + getMangledNameFor("globalAccessor", companion) + + fun getEnumValuesAccessorName(enum: IrClass): Name = + getMangledNameFor("getValues", enum) + + /** + * Generate name for declaration that will be a part of internal ABI. + */ + private fun getMangledNameFor(declarationName: String, parent: IrDeclarationParent): Name { + val prefix = parent.fqNameForIrSerialization + return "$prefix.$declarationName".synthesizedName + } } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt index db8a6fddf68..61384cc84c7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt @@ -157,6 +157,8 @@ internal fun Context.psiToIr(symbolTable: SymbolTable) { // Note: coupled with [shouldLower] below. irModules = linker.modules.filterValues { llvmModuleSpecification.containsModule(it) } + internalAbi.init(irModules.values + irModule!!) + ir.symbols = symbols functionIrClassFactory.module = 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 bfdcf97a617..8ef97b77dd5 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 @@ -3,19 +3,9 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.common.CheckDeclarationParentsVisitor import org.jetbrains.kotlin.backend.common.IrValidator import org.jetbrains.kotlin.backend.common.IrValidatorConfig -import org.jetbrains.kotlin.backend.common.LoggingContext -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl -import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.* -import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker -import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer -import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule -import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary -import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary -import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols -import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport @@ -23,23 +13,22 @@ import org.jetbrains.kotlin.backend.konan.serialization.* import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport 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.konan.DeserializedKlibModuleOrigin -import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin -import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.irBlockBody +import org.jetbrains.kotlin.ir.builders.irGetObjectValue +import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns -import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +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.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration -import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator -import org.jetbrains.kotlin.utils.DFS import java.util.Collections.emptySet internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) { @@ -320,6 +309,67 @@ internal val entryPointPhase = makeCustomPhase( } ) +internal val exportInternalAbiPhase = makeKonanModuleOpPhase( + name = "exportInternalAbi", + description = "Add accessors to private entities", + prerequisite = emptySet(), + op = { context, module -> + val visitor = object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + declaration.acceptChildrenVoid(this) + if (declaration.isCompanion) { + val function = context.irFactory.buildFun { + name = InternalAbi.getCompanionObjectAccessorName(declaration) + origin = InternalAbi.INTERNAL_ABI_ORIGIN + returnType = declaration.defaultType + } + context.createIrBuilder(function.symbol).apply { + function.body = irBlockBody { + +irReturn(irGetObjectValue(declaration.defaultType, declaration.symbol)) + } + } + context.internalAbi.declare(function, declaration.module) + } + + } + } + module.acceptChildrenVoid(visitor) + } +) + +internal val useInternalAbiPhase = makeKonanModuleOpPhase( + name = "useInternalAbi", + description = "Use internal ABI functions to access private entities", + prerequisite = emptySet(), + op = { context, module -> + val accessors = mutableMapOf() + val transformer = object : IrElementTransformerVoid() { + override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { + val irClass = expression.symbol.owner + if (!irClass.isCompanion || context.llvmModuleSpecification.containsDeclaration(irClass)) { + return expression + } + val accessor = accessors.getOrPut(irClass) { + context.irFactory.buildFun { + name = InternalAbi.getCompanionObjectAccessorName(irClass) + returnType = irClass.defaultType + origin = InternalAbi.INTERNAL_ABI_ORIGIN + isExternal = true + }.also { + context.internalAbi.reference(it, irClass.module) + } + } + return IrCallImpl(expression.startOffset, expression.endOffset, expression.type, accessor.symbol) + } + } + module.transformChildrenVoid(transformer) + } +) + internal val bitcodePhase = NamedCompilerPhase( name = "Bitcode", description = "LLVM Bitcode generation", @@ -346,6 +396,8 @@ private val backendCodegen = namedUnitPhase( dependenciesLowerPhase then // Then lower all libraries in topological order. // With that we guarantee that inline functions are unlowered while being inlined. entryPointPhase then + exportInternalAbiPhase then + useInternalAbiPhase then bitcodePhase then verifyBitcodePhase then printBitcodePhase then @@ -395,6 +447,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY) disableIf(dependenciesLowerPhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM) + disableUnless(exportInternalAbiPhase, config.produce.isCache) disableIf(bitcodePhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage) disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)