Add global accessors to companion objects.

It fixes IrGetObjectValue evaluation in case of Lazy IR.
This commit is contained in:
Sergey Bogolepov
2020-09-16 14:25:31 +07:00
committed by Sergey Bogolepov
parent 88b2955bb3
commit 9f457dce80
5 changed files with 118 additions and 71 deletions
@@ -292,7 +292,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
throw Error("Another IrModule in the context.") throw Error("Another IrModule in the context.")
} }
field = module!! field = module!!
internalAbi.init(module)
ir = KonanIr(this, module) ir = KonanIr(this, module)
} }
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET 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.irBlockBody
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irReturn 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. // We can't move property getter to the top-level scope.
// So add a wrapper instead. // So add a wrapper instead.
private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction { private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction =
return WrappedSimpleFunctionDescriptor().let { context.irFactory.buildFun {
val valuesType = valuesArrayType(enumClass) name = InternalAbi.getEnumValuesAccessorName(enumClass)
val origin = if (isExternal) InternalAbi.INTERNAL_ABI_ORIGIN else DECLARATION_ORIGIN_ENUM returnType = valuesArrayType(enumClass)
val name = context.internalAbi.getMangledNameFor("get-VALUES", enumClass) origin = InternalAbi.INTERNAL_ABI_ORIGIN
IrFunctionImpl( this.isExternal = isExternal
UNDEFINED_OFFSET, UNDEFINED_OFFSET, }.also {
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)
if (isExternal) { if (isExternal) {
context.internalAbi.reference(this, enumClass.module) context.internalAbi.reference(it, enumClass.module)
} else { } else {
context.internalAbi.declare(this) context.internalAbi.declare(it, enumClass.module)
} }
} }
}
}
fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum { fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum {
val enumEntriesMap = enumEntriesMap(enumClass) val enumEntriesMap = enumEntriesMap(enumClass)
@@ -10,7 +10,9 @@ import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl 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.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -22,22 +24,24 @@ import org.jetbrains.kotlin.name.Name
*/ */
internal class InternalAbi(private val context: Context) { 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 * We have to store such declarations in top-level to avoid mangling that
* makes referencing harder. * makes referencing harder.
* A bit better solution is to add files with proper packages, but it is impossible * A bit better solution is to add files with proper packages, but it is impossible
* during FileLowering (hello, ConcurrentModificationException). * during FileLowering (hello, ConcurrentModificationException).
*/ */
private lateinit var internalAbiFile: IrFile private lateinit var internalAbiFiles: Map<ModuleDescriptor, IrFile>
/** /**
* Representation of ABI files from external modules. * Representation of ABI files from external modules.
*/ */
private val externalModules = mutableMapOf<ModuleDescriptor, IrFile>() private val externalAbiFiles = mutableMapOf<ModuleDescriptor, IrFile>()
fun init(module: IrModuleFragment) { fun init(modules: List<IrModuleFragment>) {
internalAbiFile = createAbiFile(module) internalAbiFiles = modules.associate { it.descriptor to createAbiFile(it) }
} }
private fun createAbiFile(module: IrModuleFragment): IrFile = private fun createAbiFile(module: IrModuleFragment): IrFile =
@@ -49,24 +53,16 @@ internal class InternalAbi(private val context: Context) {
fun reference(function: IrFunction, module: ModuleDescriptor) { fun reference(function: IrFunction, module: ModuleDescriptor) {
assert(function.isExternal) { "Function that represents external ABI should be marked as external" } assert(function.isExternal) { "Function that represents external ABI should be marked as external" }
context.llvmImports.add(module.llvmSymbolOrigin) context.llvmImports.add(module.llvmSymbolOrigin)
externalModules.getOrPut(module) { externalAbiFiles.getOrPut(module) {
createAbiFile(IrModuleFragmentImpl(module, context.irBuiltIns)) createAbiFile(IrModuleFragmentImpl(module, context.irBuiltIns))
}.addChild(function) }.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 { fun declare(function: IrFunction, module: ModuleDescriptor) {
val prefix = parent.fqNameForIrSerialization internalAbiFiles.getValue(module).addChild(function)
return "$prefix.$declarationName".synthesizedName
}
/**
* Adds [function] to a list of publicly available symbols.
*/
fun declare(function: IrFunction) {
internalAbiFile.addChild(function)
} }
companion object { companion object {
@@ -74,5 +70,19 @@ internal class InternalAbi(private val context: Context) {
* Allows to distinguish external declarations to internal ABI. * Allows to distinguish external declarations to internal ABI.
*/ */
val INTERNAL_ABI_ORIGIN = object : IrDeclarationOriginImpl("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
}
} }
} }
@@ -157,6 +157,8 @@ internal fun Context.psiToIr(symbolTable: SymbolTable) {
// Note: coupled with [shouldLower] below. // Note: coupled with [shouldLower] below.
irModules = linker.modules.filterValues { llvmModuleSpecification.containsModule(it) } irModules = linker.modules.filterValues { llvmModuleSpecification.containsModule(it) }
internalAbi.init(irModules.values + irModule!!)
ir.symbols = symbols ir.symbols = symbols
functionIrClassFactory.module = functionIrClassFactory.module =
@@ -3,19 +3,9 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CheckDeclarationParentsVisitor import org.jetbrains.kotlin.backend.common.CheckDeclarationParentsVisitor
import org.jetbrains.kotlin.backend.common.IrValidator import org.jetbrains.kotlin.backend.common.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
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.phaser.* 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.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.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport 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.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin import org.jetbrains.kotlin.ir.builders.irGetObjectValue
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSymbol 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.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.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 import java.util.Collections.emptySet
internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) { internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) {
@@ -320,6 +309,67 @@ internal val entryPointPhase = makeCustomPhase<Context, IrModuleFragment>(
} }
) )
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<IrClass, IrSimpleFunction>()
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( internal val bitcodePhase = NamedCompilerPhase(
name = "Bitcode", name = "Bitcode",
description = "LLVM Bitcode generation", description = "LLVM Bitcode generation",
@@ -346,6 +396,8 @@ private val backendCodegen = namedUnitPhase(
dependenciesLowerPhase then // Then lower all libraries in topological order. dependenciesLowerPhase then // Then lower all libraries in topological order.
// With that we guarantee that inline functions are unlowered while being inlined. // With that we guarantee that inline functions are unlowered while being inlined.
entryPointPhase then entryPointPhase then
exportInternalAbiPhase then
useInternalAbiPhase then
bitcodePhase then bitcodePhase then
verifyBitcodePhase then verifyBitcodePhase then
printBitcodePhase then printBitcodePhase then
@@ -395,6 +447,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
disableIf(dependenciesLowerPhase, config.produce == CompilerOutputKind.LIBRARY) disableIf(dependenciesLowerPhase, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM) disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
disableUnless(exportInternalAbiPhase, config.produce.isCache)
disableIf(bitcodePhase, config.produce == CompilerOutputKind.LIBRARY) disableIf(bitcodePhase, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage) disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage)
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage) disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)