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.")
}
field = module!!
internalAbi.init(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.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)
@@ -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<ModuleDescriptor, IrFile>
/**
* Representation of ABI files from external modules.
*/
private val externalModules = mutableMapOf<ModuleDescriptor, IrFile>()
private val externalAbiFiles = mutableMapOf<ModuleDescriptor, IrFile>()
fun init(module: IrModuleFragment) {
internalAbiFile = createAbiFile(module)
fun init(modules: List<IrModuleFragment>) {
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
}
}
}
@@ -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 =
@@ -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<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(
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)