[K/N] Port to the new driver

This commit is contained in:
Sergey Bogolepov
2022-12-28 15:43:19 +02:00
committed by Space Team
parent 21e57b1c54
commit 576e8ba127
53 changed files with 1474 additions and 1165 deletions
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.target.*
@@ -12,11 +14,14 @@ typealias BitcodeFile = String
typealias ObjectFile = String
typealias ExecutableFile = String
internal class BitcodeCompiler(val generationState: NativeGenerationState) {
internal class BitcodeCompiler(
private val context: PhaseContext,
private val temporaryFiles: TempFiles,
) {
private val config = generationState.config
private val config = context.config
private val platform = config.platform
private val optimize = generationState.shouldOptimize()
private val optimize = context.shouldOptimize()
private val debug = config.debug
private val overrideClangOptions =
@@ -28,11 +33,11 @@ internal class BitcodeCompiler(val generationState: NativeGenerationState) {
private fun runTool(vararg command: String) =
Command(*command)
.logWith(generationState::log)
.logWith(context::log)
.execute()
private fun temporary(name: String, suffix: String): String =
generationState.tempFiles.create(name, suffix).absolutePath
temporaryFiles.create(name, suffix).absolutePath
private fun targetTool(tool: String, vararg arg: String) {
val absoluteToolName = if (platform.configurables is AppleConfigurables) {
@@ -58,19 +63,19 @@ internal class BitcodeCompiler(val generationState: NativeGenerationState) {
}
val flags = overrideClangOptions.takeIf(List<String>::isNotEmpty)
?: mutableListOf<String>().apply {
addNonEmpty(configurables.clangFlags)
addNonEmpty(listOf("-triple", targetTriple.toString()))
if (configurables is ZephyrConfigurables) {
addNonEmpty(configurables.constructClangCC1Args())
}
addNonEmpty(when {
optimize -> configurables.clangOptFlags
debug -> configurables.clangDebugFlags
else -> configurables.clangNooptFlags
})
addNonEmpty(BitcodeEmbedding.getClangOptions(config))
addNonEmpty(configurables.currentRelocationMode(generationState).translateToClangCc1Flag())
}
addNonEmpty(configurables.clangFlags)
addNonEmpty(listOf("-triple", targetTriple.toString()))
if (configurables is ZephyrConfigurables) {
addNonEmpty(configurables.constructClangCC1Args())
}
addNonEmpty(when {
optimize -> configurables.clangOptFlags
debug -> configurables.clangDebugFlags
else -> configurables.clangNooptFlags
})
addNonEmpty(BitcodeEmbedding.getClangOptions(config))
addNonEmpty(configurables.currentRelocationMode(context).translateToClangCc1Flag())
}
if (configurables is AppleConfigurables) {
targetTool("clang++", *flags.toTypedArray(), file, "-o", objectFile)
} else {
@@ -0,0 +1,53 @@
/*
* 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
import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
internal class CacheStorage(private val generationState: NativeGenerationState) {
private val outputFiles = generationState.outputFiles
companion object {
fun renameOutput(outputFiles: OutputFiles) {
// For caches the output file is a directory. It might be created by someone else,
// we have to delete it in order for the next renaming operation to succeed.
// TODO: what if the directory is not empty?
java.io.File(outputFiles.mainFileName).delete()
if (!outputFiles.tempCacheDirectory!!.renameTo(outputFiles.mainFile))
outputFiles.tempCacheDirectory.deleteRecursively()
}
}
fun saveAdditionalCacheInfo() {
outputFiles.prepareTempDirectories()
saveCacheBitcodeDependencies()
saveInlineFunctionBodies()
saveClassFields()
saveEagerInitializedProperties()
}
private fun saveCacheBitcodeDependencies() {
outputFiles.bitcodeDependenciesFile!!.writeLines(
DependenciesSerializer.serialize(generationState.dependenciesTracker.immediateBitcodeDependencies))
}
private fun saveInlineFunctionBodies() {
outputFiles.inlineFunctionBodiesFile!!.writeBytes(
InlineFunctionBodyReferenceSerializer.serialize(generationState.inlineFunctionBodies))
}
private fun saveClassFields() {
outputFiles.classFieldsFile!!.writeBytes(
ClassFieldsSerializer.serialize(generationState.classFields))
}
private fun saveEagerInitializedProperties() {
outputFiles.eagerInitializedPropertiesFile!!.writeBytes(
EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles))
}
}
@@ -182,7 +182,7 @@ internal fun checkLlvmModuleExternalCalls(generationState: NativeGenerationState
// otherwise optimiser can inline it
staticData.getGlobal(functionListGlobal)?.setExternallyInitialized(true);
staticData.getGlobal(functionListSizeGlobal)?.setExternallyInitialized(true);
generationState.verifyBitCode()
verifyModule(llvm.module)
}
// this should be a separate pass, to handle DCE correctly
@@ -201,5 +201,5 @@ internal fun addFunctionsListSymbolForChecker(generationState: NativeGenerationS
staticData.getGlobal(functionListSizeGlobal)
?.setInitializer(llvm.constInt32(functions.size))
?: throw IllegalStateException("$functionListSizeGlobal global not found")
generationState.verifyBitCode()
verifyModule(llvm.module)
}
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.createObjCFramework
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.file.isBitcode
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.konan.library.impl.buildLibrary
@@ -78,15 +79,14 @@ val CompilerOutputKind.isCache: Boolean
val KonanConfig.involvesCodegen: Boolean
get() = produce != CompilerOutputKind.LIBRARY && !omitFrameworkBinary
internal fun llvmIrDumpCallback(state: ActionState, module: IrModuleFragment, context: Context) {
module.let{}
if (state.beforeOrAfter == BeforeOrAfter.AFTER && state.phase.name in context.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) {
internal fun llvmIrDumpCallback(state: ActionState, module: LLVMModuleRef, config: KonanConfig, temporaryFiles: TempFiles) {
if (state.phase.name in config.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) {
val moduleName: String = memScoped {
val sizeVar = alloc<size_tVar>()
LLVMGetModuleIdentifier(context.generationState.llvm.module, sizeVar.ptr)!!.toKStringFromUtf8()
LLVMGetModuleIdentifier(module, sizeVar.ptr)!!.toKStringFromUtf8()
}
val output = context.generationState.tempFiles.create("$moduleName.${state.phase.name}", ".ll")
if (LLVMPrintModuleToFile(context.generationState.llvm.module, output.absolutePath, null) != 0) {
val output = temporaryFiles.create("$moduleName.${state.phase.name}", ".ll")
if (LLVMPrintModuleToFile(module, output.absolutePath, null) != 0) {
error("Can't dump LLVM IR to ${output.absolutePath}")
}
}
@@ -16,8 +16,10 @@ import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysisRes
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.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.llvm.CodegenClassMetadata
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportCodeSpec
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportedInterface
@@ -29,6 +31,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
@@ -68,35 +71,18 @@ internal class NativeMapping : DefaultMapping() {
val functionToVolatileField = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrSimpleFunction, IrField>()
}
// TODO: Can be renamed or merged with KonanBackendContext
internal class Context(
config: KonanConfig,
val environment: KotlinCoreEnvironment,
override var bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
) : KonanBackendContext(config), PsiToIrContext {
val sourcesModules: Set<ModuleDescriptor>,
override val builtIns: KonanBuiltIns,
override val irBuiltIns: IrBuiltIns,
val irModules: Map<String, IrModuleFragment>,
val irLinker: KonanIrLinker,
symbols: KonanSymbols,
) : KonanBackendContext(config) {
fun populateAfterPsiToIr(
psiToIrOutput: PsiToIrOutput
) {
irModules = psiToIrOutput.irModules
irModule = psiToIrOutput.irModule
expectDescriptorToSymbol = psiToIrOutput.expectDescriptorToSymbol
ir = KonanIr(this)
ir.symbols = psiToIrOutput.symbols
if (psiToIrOutput.irLinker is KonanIrLinker) {
irLinker = psiToIrOutput.irLinker
}
}
override var symbolTable: SymbolTable? = null
lateinit var cAdapterExportedElements: CAdapterExportedElements
lateinit var expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>
override val builtIns: KonanBuiltIns by lazy(PUBLICATION) {
moduleDescriptor.builtIns as KonanBuiltIns
}
override val ir: KonanIr = KonanIr(this, symbols)
override val configuration get() = config.configuration
@@ -104,20 +90,12 @@ internal class Context(
override val optimizeLoopsOverUnsignedArrays = true
// TODO: Drop it to reduce code coupling and make it possible to have multiple
// generationStates at the same time.
lateinit var generationState: NativeGenerationState
val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) }
val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) }
val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) }
val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) }
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) }
override val reflectionTypes: KonanReflectionTypes by lazy(PUBLICATION) {
KonanReflectionTypes(moduleDescriptor)
}
// TODO: Remove after adding special <userData> property to IrDeclaration.
private val layoutBuilders = mutableMapOf<IrClass, ClassLayoutBuilder>()
@@ -136,93 +114,27 @@ internal class Context(
lateinit var globalHierarchyAnalysisResult: GlobalHierarchyAnalysisResult
val librariesWithDependencies by lazy {
config.librariesWithDependencies(moduleDescriptor)
}
fun needGlobalInit(field: IrField): Boolean {
if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return field.isStatic
// TODO: add some smartness here. Maybe if package of the field is in never accessed
// assume its global init can be actually omitted.
return true
}
lateinit var irModules: Map<String, IrModuleFragment>
// TODO: make lateinit?
var irModule: IrModuleFragment? = null
set(module) {
if (field != null) {
throw Error("Another IrModule in the context.")
}
field = module!!
ir = KonanIr(this)
}
override lateinit var ir: KonanIr
override val irBuiltIns
get() = irModule!!.irBuiltins
override val typeSystem: IrTypeSystemContext
get() = IrTypeSystemContextImpl(irBuiltIns)
override val interopBuiltIns by lazy {
val interopBuiltIns by lazy {
InteropBuiltIns(this.builtIns)
}
var cAdapterExportedElements: CAdapterExportedElements? = null
var objCExportedInterface: ObjCExportedInterface? = null
var objCExportCodeSpec: ObjCExportCodeSpec? = null
lateinit var library: KonanLibraryLayout
fun verifyBitCode() {
if (::generationState.isInitialized)
generationState.verifyBitCode()
}
fun printBitCode() {
if (::generationState.isInitialized)
generationState.printBitCode()
}
fun ghaEnabled() = ::globalHierarchyAnalysisResult.isInitialized
val memoryModel = config.memoryModel
override var inVerbosePhase = false
override fun log(message: () -> String) {
if (inVerbosePhase) {
println(message())
}
}
var moduleDFG: ModuleDFG? = null
val lifetimes = mutableMapOf<IrElement, Lifetime>()
var devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult? = null
var referencedFunctions: Set<IrFunction>? = null
override val stdlibModule
val stdlibModule
get() = this.builtIns.any.module
val declaredLocalArrays: MutableMap<String, LLVMTypeRef> = HashMap()
lateinit var irLinker: KonanIrLinker
val targetAbiInfo = config.target.abiInfo
val targetAbiInfo: TargetAbiInfo by lazy {
when {
config.target == KonanTarget.MINGW_X64 -> {
WindowsX64TargetAbiInfo()
}
!config.target.family.isAppleFamily && config.target.architecture == Architecture.ARM64 -> {
AAPCS64TargetAbiInfo()
}
else -> {
DefaultTargetAbiInfo()
}
}
}
val memoryModel = config.memoryModel
override fun dispose() {}
}
@@ -76,7 +76,7 @@ internal class DependenciesTrackerImpl(private val generationState: NativeGenera
private val usedNativeDependencies = mutableSetOf<KotlinLibrary>()
private val usedBitcodeOfFile = mutableSetOf<LibraryFile>()
private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
private val allLibraries by lazy { context.config.librariesWithDependencies().toSet() }
private fun findStdlibFile(fqName: FqName, fileName: String): LibraryFile {
val stdlib = (context.standardLlvmSymbolsOrigin as? DeserializedKlibModuleOrigin)?.library
@@ -276,7 +276,7 @@ internal class DependenciesTrackerImpl(private val generationState: NativeGenera
val allBitcodeDependencies: List<DependenciesTracker.ResolvedDependency> = run {
val allBitcodeDependencies = mutableMapOf<KonanLibrary, DependenciesTracker.ResolvedDependency>()
for (library in context.librariesWithDependencies) {
for (library in context.config.librariesWithDependencies()) {
if (context.config.cachedLibraries.getLibraryCache(library) == null || library == context.config.libraryToCache?.klib)
allBitcodeDependencies[library] = DependenciesTracker.ResolvedDependency.wholeModule(library)
}
@@ -352,4 +352,15 @@ internal object DependenciesSerializer {
}
private const val DEPENDENCIES_DELIMITER = '|'
}
}
/**
* Result of dependency tracking during LLVM module production.
* Elements of this class should be easily serializable/deserializable,
* so the late compiler phases could be easily executed separately.
*/
data class DependenciesTrackingResult(
val nativeDependenciesToLink: List<KonanLibrary>,
val allNativeDependencies: List<KonanLibrary>,
val allCachedBitcodeDependencies: List<DependenciesTracker.ResolvedDependency>
)
@@ -19,15 +19,9 @@ import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.library.metadata.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.library.toUnresolvedLibraries
internal fun Context.getExportedDependencies(): List<ModuleDescriptor> =
moduleDescriptor.getExportedDependencies(config)
internal fun ModuleDescriptor.getExportedDependencies(konanConfig: KonanConfig): List<ModuleDescriptor> =
getDescriptorsFromLibraries((konanConfig.resolve.exportedLibraries + konanConfig.resolve.includedLibraries).toSet())
internal fun Context.getIncludedLibraryDescriptors(): List<ModuleDescriptor> =
moduleDescriptor.getIncludedLibraryDescriptors(config)
internal fun ModuleDescriptor.getIncludedLibraryDescriptors(konanConfig: KonanConfig): List<ModuleDescriptor> =
getDescriptorsFromLibraries(konanConfig.resolve.includedLibraries.toSet())
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
@@ -23,7 +24,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.getPackageFragment
internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBackendContext {
internal abstract class KonanBackendContext(config: KonanConfig) : BasicPhaseContext(config), CommonBackendContext {
abstract override val builtIns: KonanBuiltIns
abstract override val ir: KonanIr
@@ -36,17 +37,6 @@ internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBac
KonanSharedVariablesManager(this)
}
val messageCollector: MessageCollector
get() = config.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) {
val location = element?.getCompilerMessageLocation(irFile ?: error("irFile should be not null for $element"))
this.messageCollector.report(
if (isError) CompilerMessageSeverity.ERROR else CompilerMessageSeverity.WARNING,
message, location
)
}
override val internalPackageFqn = KonanFqNames.internalPackageName
override val mapping: NativeMapping = NativeMapping()
@@ -239,9 +239,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
val shortModuleName: String?
get() = configuration.get(KonanConfigKeys.SHORT_MODULE_NAME)
fun librariesWithDependencies(moduleDescriptor: ModuleDescriptor?): List<KonanLibrary> {
if (moduleDescriptor == null) error("purgeUnneeded() only works correctly after resolve is over, and we have successfully marked package files as needed or not needed.")
fun librariesWithDependencies(): List<KonanLibrary> {
return resolvedLibraries.filterRoots { (!it.isDefault && !this.purgeUserLibs) || it.isNeededForLink }.getFullList(TopologicalLibraryOrder).map { it as KonanLibrary }
}
@@ -1,26 +1,21 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLambdasLowering
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator
import org.jetbrains.kotlin.backend.konan.llvm.redundantCoercionsCleaningPhase
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -32,7 +27,7 @@ private val filePhaseActions = setOfNotNull(
)
private val modulePhaseActions = setOfNotNull(
defaultDumper,
::llvmIrDumpCallback,
// ::llvmIrDumpCallback,
::moduleValidationCallback.takeIf { validateAll }
)
@@ -66,42 +61,8 @@ internal fun makeKonanFileOpPhase(
actions = filePhaseActions
)
internal fun makeKonanModuleOpPhase(
op: (Context, IrModuleFragment) -> Unit,
name: String,
description: String,
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet()
) = SameTypeNamedCompilerPhase(
name, description, prerequisite, nlevels = 0,
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
op(context, input)
return input
}
},
actions = modulePhaseActions
)
internal val specialBackendChecksPhase = makeKonanModuleLoweringPhase(
{ SpecialBackendChecksTraversal(it, it.interopBuiltIns, it.ir.symbols, it.irBuiltIns) },
name = "SpecialBackendChecks",
description = "Special backend checks"
)
internal val propertyAccessorInlinePhase = makeKonanModuleLoweringPhase(
::PropertyAccessorInlineLowering,
name = "PropertyAccessorInline",
description = "Property accessor inline lowering"
)
/* IrFile phases */
internal val createFileLowerStatePhase = makeKonanFileOpPhase(
{ context, _ -> context.generationState.fileLowerState = FileLowerState() },
name = "CreateFileLowerState",
description = "Create FileLowerState"
)
internal val removeExpectDeclarationsPhase = makeKonanFileLoweringPhase(
::ExpectDeclarationsRemoving,
name = "RemoveExpectDeclarations",
@@ -149,12 +110,6 @@ internal val sharedVariablesPhase = makeKonanFileLoweringPhase(
prerequisite = setOf(lateinitPhase)
)
internal val inventNamesForLocalClasses = makeKonanFileLoweringPhase(
{ NativeInventNamesForLocalClasses(it.generationState) },
name = "InventNamesForLocalClasses",
description = "Invent names for local classes and anonymous objects"
)
internal val extractLocalClassesFromInlineBodies = makeKonanFileOpPhase(
{ context, irFile ->
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
@@ -184,15 +139,6 @@ internal val wrapInlineDeclarationsWithReifiedTypeParametersLowering = makeKonan
description = "Wrap inline declarations with reified type parameters"
)
internal val inlinePhase = makeKonanFileOpPhase(
{ context, irFile ->
FunctionInlining(context, NativeInlineFunctionResolver(context, context.generationState)).lower(irFile)
},
name = "Inline",
description = "Functions inlining",
prerequisite = setOf(lowerBeforeInlinePhase, arrayConstructorPhase, extractLocalClassesFromInlineBodies)
)
internal val postInlinePhase = makeKonanFileLoweringPhase(
{ PostInlineLowering(it) },
name = "PostInline",
@@ -250,12 +196,6 @@ internal val initializersPhase = makeKonanFileLoweringPhase(
prerequisite = setOf(enumConstructorsPhase)
)
internal val objectClassesPhase = makeKonanFileOpPhase(
op = { context, irFile -> ObjectClassLowering(context.generationState).lower(irFile) },
name = "ObjectClasses",
description = "Object classes lowering"
)
internal val localFunctionsPhase = makeKonanFileOpPhase(
op = { context, irFile ->
LocalDelegatedPropertiesLowering().lower(irFile)
@@ -339,39 +279,25 @@ internal val testProcessorPhase = makeKonanFileOpPhase(
description = "Unit test processor"
)
internal val delegationPhase = makeKonanFileLoweringPhase(
{ PropertyDelegationLowering(it.generationState) },
name = "Delegation",
description = "Delegation lowering",
prerequisite = setOf(volatilePhase)
)
internal val functionReferencePhase = makeKonanFileLoweringPhase(
{ FunctionReferenceLowering(it.generationState) },
name = "FunctionReference",
description = "Function references lowering",
prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase`
)
internal val enumWhenPhase = makeKonanFileLoweringPhase(
::NativeEnumWhenLowering,
name = "EnumWhen",
description = "Enum when lowering",
prerequisite = setOf(enumConstructorsPhase, functionReferencePhase)
// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase)
)
internal val enumClassPhase = makeKonanFileLoweringPhase(
::EnumClassLowering,
name = "Enums",
description = "Enum classes lowering",
prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumWhenPhase) // TODO: make weak dependency on `testProcessorPhase`
// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumWhenPhase) // TODO: make weak dependency on `testProcessorPhase`
)
internal val enumUsagePhase = makeKonanFileLoweringPhase(
::EnumUsageLowering,
name = "EnumUsage",
description = "Enum usage lowering",
prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumClassPhase)
// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumClassPhase)
)
@@ -379,7 +305,7 @@ internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase(
::NativeSingleAbstractMethodLowering,
name = "SingleAbstractMethod",
description = "Replace SAM conversions with instances of interface-implementing classes",
prerequisite = setOf(functionReferencePhase)
// prerequisite = setOf(functionReferencePhase)
)
internal val builtinOperatorPhase = makeKonanFileLoweringPhase(
@@ -389,37 +315,18 @@ internal val builtinOperatorPhase = makeKonanFileLoweringPhase(
prerequisite = setOf(defaultParameterExtentPhase, singleAbstractMethodPhase, enumWhenPhase)
)
internal val interopPhase = makeKonanFileLoweringPhase(
{ InteropLowering(it.generationState) },
name = "Interop",
description = "Interop lowering",
prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase)
)
internal val varargPhase = makeKonanFileLoweringPhase(
::VarargInjectionLowering,
name = "Vararg",
description = "Vararg lowering",
prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck)
)
internal val coroutinesPhase = makeKonanFileOpPhase(
{ context, irFile ->
NativeSuspendFunctionsLowering(context.generationState).lower(irFile)
AddContinuationToNonLocalSuspendFunctionsLowering(context).lower(irFile)
NativeAddContinuationToFunctionCallsLowering(context).lower(irFile)
AddFunctionSupertypeToSuspendFunctionLowering(context).lower(irFile)
},
name = "Coroutines",
description = "Coroutines lowering",
prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase)
// prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck)
)
internal val typeOperatorPhase = makeKonanFileLoweringPhase(
::TypeOperatorLowering,
name = "TypeOperators",
description = "Type operators lowering",
prerequisite = setOf(coroutinesPhase)
// prerequisite = setOf(coroutinesPhase)
)
internal val bridgesPhase = makeKonanFileOpPhase(
@@ -429,21 +336,14 @@ internal val bridgesPhase = makeKonanFileOpPhase(
},
name = "Bridges",
description = "Bridges building",
prerequisite = setOf(coroutinesPhase)
// prerequisite = setOf(coroutinesPhase)
)
internal val autoboxPhase = makeKonanFileLoweringPhase(
::Autoboxing,
name = "Autobox",
description = "Autoboxing of primitive types",
prerequisite = setOf(bridgesPhase, coroutinesPhase)
)
internal val unboxInlinePhase = makeKonanModuleLoweringPhase(
::UnboxInlineLowering,
name = "UnboxInline",
description = "Unbox functions inline lowering",
prerequisite = setOf(autoboxPhase, redundantCoercionsCleaningPhase)
// prerequisite = setOf(bridgesPhase, coroutinesPhase)
)
internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase(
@@ -488,10 +388,4 @@ internal val exportInternalAbiPhase = makeKonanFileLoweringPhase(
::ExportCachesAbiVisitor,
name = "ExportInternalAbi",
description = "Add accessors to private entities"
)
internal val useInternalAbiPhase = makeKonanFileLoweringPhase(
{ ImportCachesAbiTransformer(it.generationState) },
name = "UseInternalAbi",
description = "Use internal ABI functions to access private entities"
)
@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.KonanLibrary
@@ -36,68 +37,36 @@ internal fun determineLinkerOutput(context: PhaseContext): LinkerOutputKind =
else -> TODO("${context.config.produce} should not reach native linker stage")
}
internal class CacheStorage(val generationState: NativeGenerationState) {
private val config = generationState.config
private val outputFiles = generationState.outputFiles
fun renameOutput() {
// For caches the output file is a directory. It might be created by someone else,
// we have to delete it in order for the next renaming operation to succeed.
// TODO: what if the directory is not empty?
java.io.File(outputFiles.mainFileName).delete()
if (!outputFiles.tempCacheDirectory!!.renameTo(outputFiles.mainFile))
outputFiles.tempCacheDirectory.deleteRecursively()
}
fun saveAdditionalCacheInfo() {
outputFiles.prepareTempDirectories()
saveCacheBitcodeDependencies()
saveInlineFunctionBodies()
saveClassFields()
saveEagerInitializedProperties()
}
private fun saveCacheBitcodeDependencies() {
outputFiles.bitcodeDependenciesFile!!.writeLines(
DependenciesSerializer.serialize(generationState.dependenciesTracker.immediateBitcodeDependencies))
}
private fun saveInlineFunctionBodies() {
outputFiles.inlineFunctionBodiesFile!!.writeBytes(
InlineFunctionBodyReferenceSerializer.serialize(generationState.inlineFunctionBodies))
}
private fun saveClassFields() {
outputFiles.classFieldsFile!!.writeBytes(
ClassFieldsSerializer.serialize(generationState.classFields))
}
private fun saveEagerInitializedProperties() {
outputFiles.eagerInitializedPropertiesFile!!.writeBytes(
EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles))
}
}
// TODO: We have a Linker.kt file in the shared module.
internal class Linker(val generationState: NativeGenerationState) {
private val config = generationState.config
internal class Linker(
private val context: PhaseContext,
private val isCoverageEnabled: Boolean = false,
private val tempFiles: TempFiles,
private val outputFiles: OutputFiles,
) {
private val config = context.config
private val platform = config.platform
private val linkerOutput = determineLinkerOutput(generationState)
private val linkerOutput = determineLinkerOutput(context)
private val linker = platform.linker
private val target = config.target
private val optimize = generationState.shouldOptimize()
private val optimize = context.shouldOptimize()
private val debug = config.debug || config.lightDebug
fun link(objectFiles: List<ObjectFile>) {
val nativeDependencies = generationState.dependenciesTracker.nativeDependenciesToLink
fun link(
outputFile: String,
objectFiles: List<ObjectFile>,
dependenciesTrackingResult: DependenciesTrackingResult
) {
val nativeDependencies = dependenciesTrackingResult.nativeDependenciesToLink
val includedBinariesLibraries = config.libraryToCache?.let { listOf(it.klib) }
?: nativeDependencies.filterNot { config.cachedLibraries.isLibraryCached(it) }
val includedBinaries = includedBinariesLibraries.map { (it as? KonanLibrary)?.includedPaths.orEmpty() }.flatten()
val libraryProvidedLinkerFlags = generationState.dependenciesTracker.allNativeDependencies.map { it.linkerOpts }.flatten()
val libraryProvidedLinkerFlags = dependenciesTrackingResult.allNativeDependencies.map { it.linkerOpts }.flatten()
runLinker(objectFiles, includedBinaries, libraryProvidedLinkerFlags)
runLinker(outputFile, objectFiles, includedBinaries, libraryProvidedLinkerFlags, dependenciesTrackingResult)
}
private fun asLinkerArgs(args: List<String>): List<String> {
@@ -117,11 +86,13 @@ internal class Linker(val generationState: NativeGenerationState) {
return result
}
private fun runLinker(objectFiles: List<ObjectFile>,
includedBinaries: List<String>,
libraryProvidedLinkerFlags: List<String>): ExecutableFile? {
val outputFiles = generationState.outputFiles
private fun runLinker(
outputFile: String,
objectFiles: List<ObjectFile>,
includedBinaries: List<String>,
libraryProvidedLinkerFlags: List<String>,
dependenciesTrackingResult: DependenciesTrackingResult,
): ExecutableFile {
val additionalLinkerArgs: List<String>
val executable: String
@@ -137,7 +108,7 @@ internal class Linker(val generationState: NativeGenerationState) {
}
executable = outputFiles.nativeBinaryFile
} else {
val framework = File(generationState.outputFile)
val framework = File(outputFile)
val dylibName = framework.name.removeSuffix(".framework")
val dylibRelativePath = when (target.family) {
Family.IOS,
@@ -152,10 +123,9 @@ internal class Linker(val generationState: NativeGenerationState) {
executable = dylibPath.absolutePath
}
val needsProfileLibrary = generationState.coverage.enabled
val mimallocEnabled = config.allocationMode == AllocationMode.MIMALLOC
val linkerInput = determineLinkerInput(objectFiles, linkerOutput)
val linkerInput = determineLinkerInput(objectFiles, linkerOutput, dependenciesTrackingResult)
try {
File(executable).delete()
val linkerArgs = asLinkerArgs(config.configuration.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
@@ -172,12 +142,12 @@ internal class Linker(val generationState: NativeGenerationState) {
debug = debug,
kind = linkerOutput,
outputDsymBundle = outputFiles.symbolicInfoFile,
needsProfileLibrary = needsProfileLibrary,
needsProfileLibrary = isCoverageEnabled,
mimallocEnabled = mimallocEnabled,
sanitizer = config.sanitizer
)
(linkerInput.preLinkCommands + finalOutputCommands).forEach {
it.logWith(generationState::log)
it.logWith(context::log)
it.execute()
}
} catch (e: KonanExternalToolFailure) {
@@ -191,7 +161,7 @@ internal class Linker(val generationState: NativeGenerationState) {
Also, consider filing an issue with full Gradle log here: https://kotl.in/issue
""".trimIndent()
else ""
generationState.reportCompilationError("${e.toolName} invocation reported errors\n$extraUserInfo\n${e.message}")
context.reportCompilationError("${e.toolName} invocation reported errors\n$extraUserInfo\n${e.message}")
}
return executable
}
@@ -205,8 +175,12 @@ internal class Linker(val generationState: NativeGenerationState) {
return isStaticLibrary && enabled && nonEmptyCaches
}
private fun determineLinkerInput(objectFiles: List<ObjectFile>, linkerOutputKind: LinkerOutputKind): LinkerInput {
val caches = determineCachesToLink(generationState)
private fun determineLinkerInput(
objectFiles: List<ObjectFile>,
linkerOutputKind: LinkerOutputKind,
dependenciesTrackingResult: DependenciesTrackingResult,
): LinkerInput {
val caches = determineCachesToLink(context, dependenciesTrackingResult)
// Since we have several linker stages that involve caching,
// we should detect cache usage early to report errors correctly.
val cachingInvolved = caches.static.isNotEmpty() || caches.dynamic.isNotEmpty()
@@ -216,7 +190,7 @@ internal class Linker(val generationState: NativeGenerationState) {
LinkerInput(objectFiles, CachesToLink(emptyList(), caches.dynamic), emptyList(), cachingInvolved)
}
shouldPerformPreLink(caches, linkerOutputKind) -> {
val preLinkResult = generationState.tempFiles.create("withStaticCaches", ".o").absolutePath
val preLinkResult = tempFiles.create("withStaticCaches", ".o").absolutePath
val preLinkCommands = linker.preLinkCommands(objectFiles + caches.static, preLinkResult)
LinkerInput(listOf(preLinkResult), CachesToLink(emptyList(), caches.dynamic), preLinkCommands, cachingInvolved)
}
@@ -234,20 +208,18 @@ private class LinkerInput(
private class CachesToLink(val static: List<String>, val dynamic: List<String>)
private fun determineCachesToLink(generationState: NativeGenerationState): CachesToLink {
private fun determineCachesToLink(
context: PhaseContext,
dependenciesTrackingResult: DependenciesTrackingResult,
): CachesToLink {
val staticCaches = mutableListOf<String>()
val dynamicCaches = mutableListOf<String>()
generationState.dependenciesTracker.allCachedBitcodeDependencies.forEach { dependency ->
dependenciesTrackingResult.allCachedBitcodeDependencies.forEach { dependency ->
val library = dependency.library
val currentBinaryContainsLibrary = generationState.llvmModuleSpecification.containsLibrary(library)
val cache = generationState.config.cachedLibraries.getLibraryCache(library)
val cache = context.config.cachedLibraries.getLibraryCache(library)
?: error("Library $library is expected to be cached")
// Consistency check. Generally guaranteed by implementation.
if (currentBinaryContainsLibrary)
error("Library ${library.libraryName} is found in both cache and current binary")
val list = when (cache.kind) {
CachedLibraries.Kind.DYNAMIC -> dynamicCaches
CachedLibraries.Kind.STATIC -> staticCaches
@@ -97,43 +97,14 @@ internal class NativeGenerationState(
val cStubsManager = CStubsManager(config.target, this)
lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String
lateinit var compilerOutput: List<ObjectFile>
val coverage by lazy { CoverageManager(this) }
lateinit var objCExport: ObjCExport
fun hasDebugInfo() = debugInfoDelegate.isInitialized()
fun verifyBitCode() {
if (!llvmDelegate.isInitialized()) return
verifyModule(llvm.module)
}
// TODO: Do we need this function?
fun printBitCode() {
if (!llvmDelegate.isInitialized()) return
separator("BitCode:")
LLVMDumpModule(llvm.module)
}
private fun separator(title: String) {
println("\n\n--- ${title} ----------------------\n")
}
private var isDisposed = false
// A little hack to make logging work when executing this phase in its parent context.
// TODO: A better solution would be a separate logger object or something like that.
// PhaseContext should not be responsible for logging.
override var inVerbosePhase: Boolean
get() = context.inVerbosePhase
set(value) {
context.inVerbosePhase = value
}
override fun dispose() {
if (isDisposed) return
@@ -116,36 +116,40 @@ internal fun createLTOPipelineConfigForRuntime(generationState: NativeGeneration
* In case of debug we do almost nothing (that's why we need [createLTOPipelineConfigForRuntime]),
* but for release binaries we rely on "closed" world and enable a lot of optimizations.
*/
internal fun createLTOFinalPipelineConfig(generationState: NativeGenerationState): LlvmPipelineConfig {
val config = generationState.config
internal fun createLTOFinalPipelineConfig(
context: PhaseContext,
targetTriple: String,
closedWorld: Boolean
): LlvmPipelineConfig {
val config = context.config
val target = config.target
val configurables: Configurables = config.platform.configurables
val cpuModel = getCpuModel(generationState)
val cpuFeatures = getCpuFeatures(generationState)
val cpuModel = getCpuModel(context)
val cpuFeatures = getCpuFeatures(context)
val optimizationLevel: LlvmOptimizationLevel = when {
generationState.shouldOptimize() -> LlvmOptimizationLevel.AGGRESSIVE
generationState.shouldContainDebugInfo() -> LlvmOptimizationLevel.NONE
context.shouldOptimize() -> LlvmOptimizationLevel.AGGRESSIVE
context.shouldContainDebugInfo() -> LlvmOptimizationLevel.NONE
else -> LlvmOptimizationLevel.DEFAULT
}
val sizeLevel: LlvmSizeLevel = when {
// We try to optimize code as much as possible on embedded targets.
target is KonanTarget.ZEPHYR ||
target == KonanTarget.WASM32 -> LlvmSizeLevel.AGGRESSIVE
generationState.shouldOptimize() -> LlvmSizeLevel.NONE
generationState.shouldContainDebugInfo() -> LlvmSizeLevel.NONE
context.shouldOptimize() -> LlvmSizeLevel.NONE
context.shouldContainDebugInfo() -> LlvmSizeLevel.NONE
else -> LlvmSizeLevel.NONE
}
val codegenOptimizationLevel: LLVMCodeGenOptLevel = when {
generationState.shouldOptimize() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive
generationState.shouldContainDebugInfo() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelNone
context.shouldOptimize() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive
context.shouldContainDebugInfo() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelNone
else -> LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault
}
val relocMode: LLVMRelocMode = configurables.currentRelocationMode(generationState).translateToLlvmRelocMode()
val relocMode: LLVMRelocMode = configurables.currentRelocationMode(context).translateToLlvmRelocMode()
val codeModel: LLVMCodeModel = LLVMCodeModel.LLVMCodeModelDefault
val globalDce = true
// Since we are in a "closed world" internalization can be safely used
// to reduce size of a bitcode with global dce.
val internalize = generationState.llvmModuleSpecification.isFinal
val internalize = closedWorld
// Hidden visibility makes symbols internal when linking the binary.
// When producing dynamic library, this enables stripping unused symbols from binary with -dead_strip flag,
// similar to DCE enabled by internalize but later:
@@ -157,13 +161,13 @@ internal fun createLTOFinalPipelineConfig(generationState: NativeGenerationState
// Null value means that LLVM should use default inliner params
// for the provided optimization and size level.
val inlineThreshold: Int? = when {
generationState.shouldOptimize() -> tryGetInlineThreshold(generationState)
generationState.shouldContainDebugInfo() -> null
context.shouldOptimize() -> tryGetInlineThreshold(context)
context.shouldContainDebugInfo() -> null
else -> null
}
return LlvmPipelineConfig(
generationState.llvm.targetTriple,
targetTriple,
cpuModel,
cpuFeatures,
optimizationLevel,
@@ -66,14 +66,15 @@ internal fun PsiToIrContext.psiToIr(
val forwardDeclarationsModuleDescriptor = moduleDescriptor.allDependencyModules.firstOrNull { it.isForwardDeclarationModule }
val libraryToCacheModule = config.libraryToCache?.klib?.let {
val libraryToCache = config.libraryToCache
val libraryToCacheModule = libraryToCache?.klib?.let {
moduleDescriptor.allDependencyModules.single { module -> module.konanLibrary == it }
}
val stdlibIsCached = stdlibModule.konanLibrary?.let { config.cachedLibraries.isLibraryCached(it) } == true
val stdlibIsBeingCached = libraryToCacheModule == stdlibModule
require(!(stdlibIsCached && stdlibIsBeingCached)) { "The cache for stdlib is already built" }
val kFunctionImplIsBeingCached = stdlibIsBeingCached && config.libraryToCache?.strategy.containsKFunctionImpl
val kFunctionImplIsBeingCached = stdlibIsBeingCached && libraryToCache?.strategy.containsKFunctionImpl
val shouldUseLazyFunctionClasses = (stdlibIsCached || stdlibIsBeingCached) && !kFunctionImplIsBeingCached
val stubGenerator = DeclarationStubGeneratorImpl(
@@ -152,7 +153,7 @@ internal fun PsiToIrContext.psiToIr(
while (true) {
// context.config.librariesWithDependencies could change at each iteration.
val dependencies = moduleDescriptor.allDependencyModules.filter {
config.librariesWithDependencies(moduleDescriptor).contains(it.konanLibrary)
config.librariesWithDependencies().contains(it.konanLibrary)
}
fun sortDependencies(dependencies: List<ModuleDescriptor>): Collection<ModuleDescriptor> {
@@ -251,11 +252,13 @@ internal fun PsiToIrContext.psiToIr(
module.files.forEach { it.metadata = KonanFileMetadataSource(module as KonanIrModuleFragmentImpl) }
}
return PsiToIrOutput(
modules,
mainModule,
expectDescriptorToSymbol,
symbols,
if (isProducingLibrary) null else irDeserializer as KonanIrLinker
)
return if (isProducingLibrary) {
PsiToIrOutput.ForKlib(mainModule, symbols, expectDescriptorToSymbol)
} else if (libraryToCache == null) {
PsiToIrOutput.ForBackend(modules, mainModule, symbols, irDeserializer as KonanIrLinker)
} else {
val libraryName = libraryToCache.klib.libraryName
val libraryModule = modules[libraryName] ?: error("No module for the library being cached: $libraryName")
PsiToIrOutput.ForBackend(modules.filterKeys { it != libraryName }, libraryModule, symbols, irDeserializer as KonanIrLinker)
}
}
@@ -131,3 +131,16 @@ fun KonanTarget.getARCRetainAutoreleasedReturnValueMarker(): String? = when (thi
Architecture.ARM32 -> "mov\tr7, r7\t\t// marker for objc_retainAutoreleaseReturnValue"
else -> null
}
val KonanTarget.abiInfo: TargetAbiInfo
get() = when {
this == KonanTarget.MINGW_X64 -> {
WindowsX64TargetAbiInfo()
}
!family.isAppleFamily && architecture == Architecture.ARM64 -> {
AAPCS64TargetAbiInfo()
}
else -> {
DefaultTargetAbiInfo()
}
}
@@ -64,23 +64,6 @@ internal fun konanUnitPhase(
op: Context.() -> Unit
) = namedOpUnitPhase(name, description, prerequisite, op)
internal val buildAdditionalCacheInfoPhase = konanUnitPhase(
op = {
val module = irModules[config.libraryToCache!!.klib.libraryName]
?: error("No module for the library being cached: ${config.libraryToCache!!.klib.libraryName}")
val moduleDeserializer = irLinker.moduleDeserializers[module.descriptor]
if (moduleDeserializer == null) {
require(module.descriptor.isFromInteropLibrary()) { "No module deserializer for ${module.descriptor}" }
} else {
CacheInfoBuilder(this.generationState, moduleDeserializer, module).build()
}
},
name = "BuildAdditionalCacheInfo",
description = "Build additional cache info (inline functions bodies and fields of classes)",
// prerequisites generally do not work in dynamic driver.
// prerequisite = setOf(psiToIrPhase)
)
/*
* Sometimes psi2ir produces IR with non-trivial variance in super types of SAM conversions (this is a language design issue).
* Earlier this was solved with just erasing all such variances but this might lead to some other hard to debug problems,
@@ -88,209 +71,26 @@ internal val buildAdditionalCacheInfoPhase = konanUnitPhase(
* even if they do, then it's better to throw an error right away than to dig out weird crashes down the pipeline or even at runtime.
* We explicitly check this, also fixing older klibs built with previous compiler versions by applying the same trick as before.
*/
internal val checkSamSuperTypesPhase = konanUnitPhase(
op = {
// Handling types in current module not recursively:
// psi2ir can produce SAM conversions with variances in type arguments of type arguments.
// See https://youtrack.jetbrains.com/issue/KT-49384.
// So don't go deeper than top-level arguments to avoid the compiler emitting false-positive errors.
// Lowerings can handle this.
// Also such variances are allowed in the language for manual implementations of interfaces.
irModule!!.files
.forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.THROW, recurse = false).run() }
// TODO: This is temporary for handling klibs produced with earlier compiler versions.
// Handling types in dependencies recursively, just to be extra safe: don't change something that works.
irModules.values
.flatMap { it.files }
.forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.ERASE, recurse = true).run() }
},
name = "CheckSamSuperTypes",
description = "Check SAM conversions super types"
)
//internal val checkSamSuperTypesPhase = konanUnitPhase(
// op = {
// // Handling types in current module not recursively:
// // psi2ir can produce SAM conversions with variances in type arguments of type arguments.
// // See https://youtrack.jetbrains.com/issue/KT-49384.
// // So don't go deeper than top-level arguments to avoid the compiler emitting false-positive errors.
// // Lowerings can handle this.
// // Also such variances are allowed in the language for manual implementations of interfaces.
// irModule!!.files
// .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.THROW, recurse = false).run() }
// // TODO: This is temporary for handling klibs produced with earlier compiler versions.
// // Handling types in dependencies recursively, just to be extra safe: don't change something that works.
// irModules.values
// .flatMap { it.files }
// .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.ERASE, recurse = true).run() }
// },
// name = "CheckSamSuperTypes",
// description = "Check SAM conversions super types"
//)
internal val saveAdditionalCacheInfoPhase = konanUnitPhase(
op = { CacheStorage(generationState).saveAdditionalCacheInfo() },
name = "SaveAdditionalCacheInfo",
description = "Save additional cache info (inline functions bodies and fields of classes)"
)
internal val objectFilesPhase = konanUnitPhase(
op = { this.generationState.compilerOutput = BitcodeCompiler(this.generationState).makeObjectFiles(this.generationState.bitcodeFileName) },
name = "ObjectFiles",
description = "Bitcode to object file"
)
internal val linkerPhase = konanUnitPhase(
op = { Linker(this.generationState).link(this.generationState.compilerOutput) },
name = "Linker",
description = "Linker"
)
internal val finalizeCachePhase = konanUnitPhase(
op = { CacheStorage(this.generationState).renameOutput() },
name = "FinalizeCache",
description = "Finalize cache (rename temp to the final dist)"
)
internal val allLoweringsPhase = SameTypeNamedCompilerPhase(
name = "IrLowering",
description = "IR Lowering",
// TODO: The lowerings before inlinePhase should be aligned with [NativeInlineFunctionResolver.kt]
lower = performByIrFile(
name = "IrLowerByFile",
description = "IR Lowering by file",
lower = listOf(
createFileLowerStatePhase,
removeExpectDeclarationsPhase,
stripTypeAliasDeclarationsPhase,
lowerBeforeInlinePhase,
arrayConstructorPhase,
lateinitPhase,
sharedVariablesPhase,
inventNamesForLocalClasses,
extractLocalClassesFromInlineBodies,
wrapInlineDeclarationsWithReifiedTypeParametersLowering,
inlinePhase,
provisionalFunctionExpressionPhase,
postInlinePhase,
contractsDslRemovePhase,
annotationImplementationPhase,
rangeContainsLoweringPhase,
forLoopsPhase,
flattenStringConcatenationPhase,
foldConstantLoweringPhase,
computeStringTrimPhase,
stringConcatenationPhase,
stringConcatenationTypeNarrowingPhase,
enumConstructorsPhase,
initializersPhase,
localFunctionsPhase,
volatilePhase,
tailrecPhase,
defaultParameterExtentPhase,
innerClassPhase,
dataClassesPhase,
ifNullExpressionsFusionPhase,
testProcessorPhase,
delegationPhase,
functionReferencePhase,
singleAbstractMethodPhase,
enumWhenPhase,
builtinOperatorPhase,
finallyBlocksPhase,
enumClassPhase,
enumUsagePhase,
interopPhase,
varargPhase,
kotlinNothingValueExceptionPhase,
coroutinesPhase,
typeOperatorPhase,
expressionBodyTransformPhase,
objectClassesPhase,
constantInliningPhase,
staticInitializersPhase,
bridgesPhase,
exportInternalAbiPhase,
useInternalAbiPhase,
autoboxPhase,
)
),
actions = setOf(defaultDumper, ::moduleValidationCallback)
)
internal val dependenciesLowerPhase = SameTypeNamedCompilerPhase(
name = "LowerLibIR",
description = "Lower library's IR",
prerequisite = emptySet(),
lower = object : CompilerPhase<Context, IrModuleFragment, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
val files = mutableListOf<IrFile>()
files += input.files
input.files.clear()
// TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order.
context.librariesWithDependencies
.reversed()
.forEach {
val libModule = context.irModules[it.libraryName]
if (libModule == null || !context.generationState.llvmModuleSpecification.containsModule(libModule))
return@forEach
input.files += libModule.files
allLoweringsPhase.invoke(phaseConfig, phaserState, context, input)
input.files.clear()
}
// Save all files for codegen in reverse topological order.
// This guarantees that libraries initializers are emitted in correct order.
context.librariesWithDependencies
.forEach {
val libModule = context.irModules[it.libraryName]
if (libModule == null || !context.generationState.llvmModuleSpecification.containsModule(libModule))
return@forEach
input.files += libModule.files
}
input.files += files
return input
}
})
internal val entryPointPhase = makeCustomPhase<Context, IrModuleFragment>(
name = "addEntryPoint",
description = "Add entry point for program",
prerequisite = emptySet(),
op = { context, _ ->
require(context.config.produce == CompilerOutputKind.PROGRAM)
val entryPoint = context.ir.symbols.entryPoint!!.owner
val file = if (context.generationState.llvmModuleSpecification.containsDeclaration(entryPoint)) {
entryPoint.file
} else {
// `main` function is compiled to other LLVM module.
// For example, test running support uses `main` defined in stdlib.
context.irModule!!.addFile(NaiveSourceBasedFileEntryImpl("entryPointOwner"), FqName("kotlin.native.internal.abi"))
}
file.addChild(makeEntryPoint(context.generationState))
}
)
internal val bitcodePhase = SameTypeNamedCompilerPhase(
name = "Bitcode",
description = "LLVM Bitcode generation",
lower = returnsInsertionPhase then
buildDFGPhase then
devirtualizationAnalysisPhase then
dcePhase then
removeRedundantCallsToStaticInitializersPhase then
devirtualizationPhase then
propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields
// from dependencies can be changed during lowerings.
inlineClassPropertyAccessorsPhase then
redundantCoercionsCleaningPhase then
unboxInlinePhase then
createLLVMDeclarationsPhase then
ghaPhase then
RTTIPhase then
escapeAnalysisPhase then
codegenPhase then
cStubsPhase
)
internal val bitcodePostprocessingPhase = SameTypeNamedCompilerPhase(
name = "BitcodePostprocessing",
description = "Optimize and rewrite bitcode",
lower = checkExternalCallsPhase then
bitcodeOptimizationPhase then
coveragePhase then
removeRedundantSafepointsPhase then
optimizeTLSDataLoadsPhase then
rewriteExternalCallsCheckerGlobals
)
internal fun PhaseConfigurationService.disableIf(phase: AnyNamedPhase, condition: Boolean) {
if (condition) disable(phase)
@@ -306,41 +106,9 @@ internal fun PhaseConfigurationService.konanPhasesConfig(config: KonanConfig) {
// (which doesn't report error for these corner cases), we turn off the checker for now (the problem with variances
// is workarounded in [FunctionReferenceLowering] by taking erasure of SAM conversion type).
// Also see https://youtrack.jetbrains.com/issue/KT-50399 for more details.
disable(checkSamSuperTypesPhase)
disableUnless(buildAdditionalCacheInfoPhase, config.produce.isCache)
disableUnless(saveAdditionalCacheInfoPhase, config.produce.isCache)
disableUnless(finalizeCachePhase, config.produce.isCache)
// disable(checkSamSuperTypesPhase)
disableUnless(exportInternalAbiPhase, config.produce.isCache)
disableUnless(functionsWithoutBoundCheck, config.involvesCodegen)
disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled)
disableUnless(optimizeTLSDataLoadsPhase, config.optimizationsEnabled)
if (!config.involvesLinkStage) {
disable(bitcodePostprocessingPhase)
disable(linkBitcodeDependenciesPhase)
disable(objectFilesPhase)
disable(linkerPhase)
}
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
if (!config.optimizationsEnabled) {
disable(buildDFGPhase)
disable(devirtualizationAnalysisPhase)
disable(devirtualizationPhase)
disable(escapeAnalysisPhase)
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken
// debug information.
disable(propertyAccessorInlinePhase)
disable(unboxInlinePhase)
disable(inlineClassPropertyAccessorsPhase)
disable(dcePhase)
disable(removeRedundantCallsToStaticInitializersPhase)
disable(ghaPhase)
}
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
disableUnless(removeRedundantSafepointsPhase, config.memoryModel == MemoryModel.EXPERIMENTAL)
}
}
@@ -263,8 +263,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod
}
}
internal fun IrField.requiredAlignment(context: Context) : Int {
val llvm = context.generationState.llvm
internal fun IrField.requiredAlignment(llvm: Llvm): Int {
val llvmType = type.toLLVMType(llvm)
val abiAlignment = if (llvmType == llvm.vector128Type) {
8 // over-aligned objects are not supported now, and this worked somehow, so let's keep it as it for now
@@ -284,10 +283,10 @@ internal fun IrField.requiredAlignment(context: Context) : Int {
internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
private fun IrField.toFieldInfo(): FieldInfo {
private fun IrField.toFieldInfo(llvm: Llvm): FieldInfo {
val isConst = correspondingPropertySymbol?.owner?.isConst ?: false
require(!isConst || initializer?.expression is IrConst<*>) { "A const val field ${render()} must have constant initializer" }
return FieldInfo(name.asString(), type, isConst, symbol, requiredAlignment(context))
return FieldInfo(name.asString(), type, isConst, symbol, requiredAlignment(llvm))
}
val vtableEntries: List<OverriddenFunctionInfo> by lazy {
@@ -441,7 +440,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
if (mappedField == fieldInfo.irField)
fieldInfo
else
mappedField!!.toFieldInfo()
mappedField!!.toFieldInfo(llvm)
}
private var fields: List<FieldInfo>? = null
@@ -452,7 +451,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
val superClass = irClass.getSuperClassNotAny()
val superFields = if (superClass != null) context.getLayoutBuilder(superClass).getFieldsInternal(llvm) else emptyList()
val declaredFields = getDeclaredFields()
val declaredFields = getDeclaredFields(llvm)
val sortedDeclaredFields = if (irClass.hasAnnotation(KonanFqNames.noReorderFields))
declaredFields
else
@@ -504,7 +503,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
/**
* Fields declared in the class.
*/
fun getDeclaredFields(): List<FieldInfo> {
fun getDeclaredFields(llvm: Llvm): List<FieldInfo> {
val outerThisField = if (irClass.isInner)
context.innerClassesSupport.getOuterThisField(irClass)
else null
@@ -517,7 +516,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) {
"No IR and no cache for ${irClass.render()}"
}
return moduleDeserializer.deserializeClassFields(irClass, outerThisField?.toFieldInfo())
return moduleDeserializer.deserializeClassFields(irClass, outerThisField?.toFieldInfo(llvm))
}
val declarations = irClass.declarations.toMutableList()
@@ -527,8 +526,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
}
return declarations.mapNotNull {
when (it) {
is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo()
is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo()
is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo(llvm)
is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo(llvm)
else -> null
}
}
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
import org.jetbrains.kotlin.backend.konan.ir.getSuperInterfaces
import org.jetbrains.kotlin.backend.konan.llvm.isVoidAsReturnType
import org.jetbrains.kotlin.backend.konan.llvm.longName
import org.jetbrains.kotlin.backend.konan.lower.erasedUpperBound
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
@@ -278,4 +277,7 @@ fun IrFunction.externalSymbolOrThrow(): String? {
throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName, @GCUnsafeCall or @ObjCMethod annotation")
}
private val IrFunction.longName: String
get() = "${(parent as? IrClass)?.name?.asString() ?: "<root>"}.${(this as? IrSimpleFunction)?.name ?: "<init>"}"
val IrFunction.isBuiltInOperator get() = origin == IrBuiltIns.BUILTIN_OPERATOR
@@ -9,6 +9,9 @@ import kotlinx.cinterop.usingJvmCInteropCallbacks
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.driver.phases.*
import org.jetbrains.kotlin.backend.konan.getIncludedLibraryDescriptors
import org.jetbrains.kotlin.backend.konan.isCache
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
@@ -55,11 +58,12 @@ internal class DynamicCompilerDriver : CompilerDriver() {
val (psiToIrOutput, objCCodeSpec) = engine.runPsiToIr(frontendOutput, isProducingLibrary = false) {
it.runPhase(CreateObjCExportCodeSpecPhase, objCExportedInterface)
}
require(psiToIrOutput is PsiToIrOutput.ForBackend)
val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput) {
it.objCExportedInterface = objCExportedInterface
it.objCExportCodeSpec = objCCodeSpec
}
engine.runBackend(backendContext)
engine.runBackend(backendContext, psiToIrOutput.irModule)
}
private fun produceCLibrary(engine: PhaseEngine<PhaseContext>, config: KonanConfig, environment: KotlinCoreEnvironment) {
@@ -67,10 +71,11 @@ internal class DynamicCompilerDriver : CompilerDriver() {
val (psiToIrOutput, cAdapterElements) = engine.runPsiToIr(frontendOutput, isProducingLibrary = false) {
it.runPhase(BuildCExports, frontendOutput)
}
require(psiToIrOutput is PsiToIrOutput.ForBackend)
val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput) {
it.cAdapterExportedElements = cAdapterElements
}
engine.runBackend(backendContext)
engine.runBackend(backendContext, psiToIrOutput.irModule)
}
private fun produceKlib(engine: PhaseEngine<PhaseContext>, config: KonanConfig, environment: KotlinCoreEnvironment) {
@@ -105,6 +110,7 @@ internal class DynamicCompilerDriver : CompilerDriver() {
} else {
engine.runPsiToIr(frontendOutput, isProducingLibrary = true)
}
require(psiToIrOutput is PsiToIrOutput.ForKlib)
return engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput)
}
@@ -114,22 +120,25 @@ internal class DynamicCompilerDriver : CompilerDriver() {
private fun produceBinary(engine: PhaseEngine<PhaseContext>, config: KonanConfig, environment: KotlinCoreEnvironment) {
val frontendOutput = engine.runFrontend(config, environment) ?: return
val psiToIrOutput = engine.runPsiToIr(frontendOutput, isProducingLibrary = false)
require(psiToIrOutput is PsiToIrOutput.ForBackend)
val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput)
engine.runBackend(backendContext)
engine.runBackend(backendContext, psiToIrOutput.irModule)
}
private fun createBackendContext(
config: KonanConfig,
frontendOutput: FrontendPhaseOutput.Full,
psiToIrOutput: PsiToIrOutput,
psiToIrOutput: PsiToIrOutput.ForBackend,
additionalDataSetter: (Context) -> Unit = {}
) = Context(
config,
frontendOutput.environment,
frontendOutput.bindingContext,
frontendOutput.moduleDescriptor
frontendOutput.moduleDescriptor.getIncludedLibraryDescriptors(config).toSet() + frontendOutput.moduleDescriptor,
frontendOutput.moduleDescriptor.builtIns as KonanBuiltIns,
psiToIrOutput.irModule.irBuiltins,
psiToIrOutput.irModules,
psiToIrOutput.irLinker,
psiToIrOutput.symbols
).also {
it.populateAfterPsiToIr(psiToIrOutput)
additionalDataSetter(it)
}
}
@@ -122,12 +122,17 @@ internal class PhaseEngine<C : PhaseContext>(
fun <Input, Output, P : AbstractNamedCompilerPhase<C, Input, Output>> runPhase(
phase: P,
input: Input
input: Input,
disable: Boolean = false
): Output {
if (disable) {
return phase.outputIfNotEnabled(phaseConfig, phaserState.changePhaserStateType(), context, input)
}
// We lose sticky postconditions here, but it should be ok, since type is changed.
return phase.invoke(phaseConfig, phaserState.changePhaserStateType(), context, input)
}
fun <Output, P : AbstractNamedCompilerPhase<C, Unit, Output>> runPhase(
phase: P,
): Output = runPhase(phase, Unit)
@@ -7,14 +7,28 @@ package org.jetbrains.kotlin.backend.konan.driver.phases
import org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
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.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.lower.SpecialBackendChecksTraversal
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.makeEntryPoint
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.util.addChild
import org.jetbrains.kotlin.ir.util.addFile
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.name.FqName
internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase<PsiToIrContext, PsiToIrOutput>(
internal data class SpecialBackendChecksInput(
val irModule: IrModuleFragment,
val symbols: KonanSymbols,
)
internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase<PsiToIrContext, SpecialBackendChecksInput>(
"SpecialBackendChecks",
"Special backend checks",
) { context, input ->
@@ -38,13 +52,30 @@ internal val CopyDefaultValuesToActualPhase = createSimpleNamedCompilerPhase<Pha
name = "CopyDefaultValuesToActual",
description = "Copy default values from expect to actual declarations",
) { _, input ->
ExpectToActualDefaultValueCopier(input).process()
ExpectToActualDefaultValueCopier(input).process()
}
internal fun <T : PsiToIrContext> PhaseEngine<T>.runSpecialBackendChecks(psiToIrOutput: PsiToIrOutput) {
runPhase(SpecialBackendChecksPhase, psiToIrOutput)
internal fun <T : PsiToIrContext> PhaseEngine<T>.runSpecialBackendChecks(irModule: IrModuleFragment, symbols: KonanSymbols) {
runPhase(SpecialBackendChecksPhase, SpecialBackendChecksInput(irModule, symbols))
}
internal fun <T : PhaseContext> PhaseEngine<T>.runK2SpecialBackendChecks(fir2IrOutput: Fir2IrOutput) {
runPhase(K2SpecialBackendChecksPhase, fir2IrOutput)
}
internal val EntryPointPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "addEntryPoint",
description = "Add entry point for program"
) { context, module ->
val parent = context.context
val entryPoint = parent.ir.symbols.entryPoint!!.owner
val file: IrFile = if (context.llvmModuleSpecification.containsDeclaration(entryPoint)) {
entryPoint.file
} else {
// `main` function is compiled to other LLVM module.
// For example, test running support uses `main` defined in stdlib.
module.addFile(NaiveSourceBasedFileEntryImpl("entryPointOwner"), FqName("kotlin.native.internal.abi"))
}
file.addChild(makeEntryPoint(context))
}
@@ -5,9 +5,29 @@
package org.jetbrains.kotlin.backend.konan.driver.phases
import llvm.LLVMDumpModule
import llvm.LLVMModuleRef
import llvm.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.common.phaser.Action
import org.jetbrains.kotlin.backend.common.phaser.ActionState
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.checkLlvmModuleExternalCalls
import org.jetbrains.kotlin.backend.konan.createLTOFinalPipelineConfig
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.insertAliasToEntryPoint
import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
import org.jetbrains.kotlin.backend.konan.optimizations.RemoveRedundantSafepointsPass
import org.jetbrains.kotlin.backend.konan.optimizations.removeMultipleThreadDataLoads
private val nativeLLVMDumper =
fun(actionState: ActionState, _: Unit, context: NativeGenerationState) {
llvmIrDumpCallback(actionState, context.llvm.module, context.config, context.tempFiles)
}
private val llvmPhaseActions: Set<Action<Unit, NativeGenerationState>> = setOf(nativeLLVMDumper)
/**
* Write in-memory LLVM module to filesystem as a bitcode.
@@ -16,14 +36,111 @@ import org.jetbrains.kotlin.backend.konan.insertAliasToEntryPoint
* after static driver removal.
*/
internal val WriteBitcodeFilePhase = createSimpleNamedCompilerPhase(
internal val WriteBitcodeFilePhase = createSimpleNamedCompilerPhase<NativeGenerationState, LLVMModuleRef, String>(
"WriteBitcodeFile",
"Write bitcode file",
outputIfNotEnabled = { _, _, _, _ -> }
) { context: NativeGenerationState, _: Unit ->
outputIfNotEnabled = { _, _, _, _ -> error("WriteBitcodeFile be disabled") }
) { context, llvmModule ->
val output = context.tempFiles.nativeBinaryFileName
context.bitcodeFileName = output
// Insert `_main` after pipeline, so we won't worry about optimizations corrupting entry point.
insertAliasToEntryPoint(context)
LLVMWriteBitcodeToFile(context.llvm.module, output)
LLVMWriteBitcodeToFile(llvmModule, output)
output
}
internal val CheckExternalCallsPhase = createSimpleNamedCompilerPhase(
name = "CheckExternalCalls",
description = "Check external calls",
postactions = llvmPhaseActions,
) { context, _ ->
checkLlvmModuleExternalCalls(context)
}
internal val RewriteExternalCallsCheckerGlobals = createSimpleNamedCompilerPhase(
name = "RewriteExternalCallsCheckerGlobals",
description = "Rewrite globals for external calls checker after optimizer run",
postactions = llvmPhaseActions,
) { context, _ ->
addFunctionsListSymbolForChecker(context)
}
internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase(
name = "BitcodeOptimization",
description = "Optimize bitcode",
postactions = llvmPhaseActions,
) { context, _ ->
val config = createLTOFinalPipelineConfig(context, context.llvm.targetTriple, closedWorld = context.llvmModuleSpecification.isFinal)
LlvmOptimizationPipeline(config, context.llvm.module, context).use {
it.run()
}
}
internal val CoveragePhase = createSimpleNamedCompilerPhase(
name = "Coverage",
description = "Produce coverage information",
postactions = llvmPhaseActions,
op = { context, _ -> runCoveragePass(context) }
)
internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase(
name = "RemoveRedundantSafepoints",
description = "Remove function prologue safepoints inlined to another function",
postactions = llvmPhaseActions,
op = { context, _ ->
RemoveRedundantSafepointsPass().runOnModule(
module = context.llvm.module,
isSafepointInliningAllowed = context.shouldInlineSafepoints()
)
}
)
internal val OptimizeTLSDataLoadsPhase = createSimpleNamedCompilerPhase(
name = "OptimizeTLSDataLoads",
description = "Optimize multiple loads of thread data",
postactions = llvmPhaseActions,
op = { context, _ -> removeMultipleThreadDataLoads(context) }
)
internal val CStubsPhase = createSimpleNamedCompilerPhase(
name = "CStubs",
description = "C stubs compilation",
postactions = llvmPhaseActions,
op = { context, _ -> produceCStubs(context) }
)
internal val LinkBitcodeDependenciesPhase = createSimpleNamedCompilerPhase(
name = "LinkBitcodeDependencies",
description = "Link bitcode dependencies",
postactions = llvmPhaseActions,
op = { context, _ -> linkBitcodeDependencies(context) }
)
internal val VerifyBitcodePhase = createSimpleNamedCompilerPhase<PhaseContext, LLVMModuleRef>(
name = "VerifyBitcode",
description = "Verify bitcode",
op = { _, llvmModule -> verifyModule(llvmModule) }
)
internal val PrintBitcodePhase = createSimpleNamedCompilerPhase<PhaseContext, LLVMModuleRef>(
name = "PrintBitcode",
description = "Print bitcode",
op = { _, llvmModule -> LLVMDumpModule(llvmModule) }
)
internal fun PhaseEngine<NativeGenerationState>.runBitcodePostProcessing() {
val checkExternalCalls = context.config.configuration.getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)
if (checkExternalCalls) {
runPhase(CheckExternalCallsPhase)
}
runPhase(BitcodeOptimizationPhase)
runPhase(CoveragePhase)
if (context.config.memoryModel == MemoryModel.EXPERIMENTAL) {
runPhase(RemoveRedundantSafepointsPhase)
}
if (context.config.optimizationsEnabled) {
runPhase(OptimizeTLSDataLoadsPhase)
}
if (checkExternalCalls) {
runPhase(RewriteExternalCallsCheckerGlobals)
}
}
@@ -0,0 +1,65 @@
/*
* 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 llvm.DIFinalize
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.llvm.CodeGeneratorVisitor
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.llvm.RTTIGeneratorVisitor
import org.jetbrains.kotlin.backend.konan.llvm.createLlvmDeclarations
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.acceptVoid
internal val CreateLLVMDeclarationsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "CreateLLVMDeclarations",
description = "Map IR declarations to LLVM",
op = { generationState, module ->
generationState.llvmDeclarations = createLlvmDeclarations(generationState, module)
}
)
internal data class RTTIInput(
val irModule: IrModuleFragment,
val referencedFunctions: Set<IrFunction>?
)
internal val RTTIPhase = createSimpleNamedCompilerPhase<NativeGenerationState, RTTIInput>(
name = "RTTI",
description = "RTTI generation",
op = { generationState, input ->
val visitor = RTTIGeneratorVisitor(generationState, input.referencedFunctions)
input.irModule.acceptVoid(visitor)
visitor.dispose()
}
)
internal data class CodegenInput(
val irModule: IrModuleFragment,
val lifetimes: Map<IrElement, Lifetime>
)
internal val CodegenPhase = createSimpleNamedCompilerPhase<NativeGenerationState, CodegenInput>(
name = "Codegen",
description = "Code generation",
op = { generationState, input ->
val context = generationState.context
generationState.objCExport = ObjCExport(
generationState,
input.irModule.descriptor,
context.objCExportedInterface,
context.objCExportCodeSpec
)
input.irModule.acceptVoid(CodeGeneratorVisitor(generationState, input.irModule.irBuiltins, input.lifetimes))
if (generationState.hasDebugInfo())
DIFinalize(generationState.debugInfo.builder)
}
)
@@ -0,0 +1,47 @@
/*
* 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.CacheStorage
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.OutputFiles
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
internal val BuildAdditionalCacheInfoPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "BuildAdditionalCacheInfo",
description = "Build additional cache info (inline functions bodies and fields of classes)",
) { context, module ->
// TODO: Use explicit parameter
val parent = context.context
val moduleDeserializer = parent.irLinker.moduleDeserializers[module.descriptor]
if (moduleDeserializer == null) {
require(module.descriptor.isFromInteropLibrary()) { "No module deserializer for ${module.descriptor}" }
} else {
CacheInfoBuilder(context, moduleDeserializer, module).build()
}
}
/**
* It is naturally a part of "produce LLVM module", so using NativeGenerationState context should be OK.
*/
internal val SaveAdditionalCacheInfoPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
name = "SaveAdditionalCacheInfo",
description = "Save additional cache info (inline functions bodies and fields of classes)"
) { context, _ ->
// TODO: Extract necessary parts of context into explicit input.
CacheStorage(context).saveAdditionalCacheInfo()
}
internal val FinalizeCachePhase = createSimpleNamedCompilerPhase<PhaseContext, OutputFiles>(
name = "FinalizeCache",
description = "Finalize cache (rename temp to the final dist)"
) { _, outputFiles ->
// TODO: Explicit parameter
CacheStorage.renameOutput(outputFiles)
}
@@ -75,8 +75,4 @@ internal val FrontendPhase = createSimpleNamedCompilerPhase(
} else {
FrontendPhaseOutput.ShouldNotGenerateCode
}
}
internal fun <T : FrontendContext> PhaseEngine<T>.runFrontend(environment: KotlinCoreEnvironment): FrontendPhaseOutput {
return this.runPhase(FrontendPhase, environment)
}
@@ -0,0 +1,179 @@
/*
* 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.phaser.ActionState
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis
import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG
import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG
import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFGBuilder
import org.jetbrains.kotlin.backend.konan.optimizations.dce
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
internal val GHAPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "GHAPhase",
description = "Global hierarchy analysis",
op = { generationState, irModule ->
GlobalHierarchyAnalysis(generationState.context, irModule).run()
}
)
internal val BuildDFGPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment, ModuleDFG>(
name = "BuildDFG",
description = "Data flow graph building",
outputIfNotEnabled = { _, _, generationState, irModule ->
val context = generationState.context
val symbolTable = DataFlowIR.SymbolTable(context, DataFlowIR.Module(irModule.descriptor))
ModuleDFG(emptyMap(), symbolTable)
},
op = { generationState, irModule ->
val context = generationState.context
ModuleDFGBuilder(context, irModule).build()
}
)
internal data class DevirtualizationAnalysisInput(
val irModule: IrModuleFragment,
val moduleDFG: ModuleDFG,
)
internal val DevirtualizationAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerationState, DevirtualizationAnalysisInput, DevirtualizationAnalysis.AnalysisResult>(
name = "DevirtualizationAnalysis",
description = "Devirtualization analysis",
outputIfNotEnabled = { _, _, _, _ ->
DevirtualizationAnalysis.AnalysisResult(
emptyMap(),
DevirtualizationAnalysis.DevirtualizationAnalysisImpl.EmptyTypeHierarchy
)
},
op = { generationState, (irModule, moduleDFG) ->
val context = generationState.context
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
DevirtualizationAnalysis.run(context, irModule, moduleDFG, externalModulesDFG)
}
)
internal data class DCEInput(
val irModule: IrModuleFragment,
val moduleDFG: ModuleDFG,
val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
)
internal val DCEPhase = createSimpleNamedCompilerPhase<NativeGenerationState, DCEInput, Set<IrFunction>?>(
name = "DCEPhase",
description = "Dead code elimination",
outputIfNotEnabled = { _, _, _, _ -> null },
op = { generationState, input ->
val context = generationState.context
dce(context, input.irModule, input.moduleDFG, input.devirtualizationAnalysisResult)
}
)
internal data class DevirtualizationInput(
val irModule: IrModuleFragment,
val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult
)
internal val DevirtualizationPhase = createSimpleNamedCompilerPhase(
name = "Devirtualization",
description = "Devirtualization",
postactions = modulePhaseActions.map { f ->
fun(actionState: ActionState, data: DevirtualizationInput, context: NativeGenerationState) =
f(actionState, data.irModule, context)
}.toSet(),
op = { generationState, input ->
val context = generationState.context
val devirtualizedCallSites = input.devirtualizationAnalysisResult.devirtualizedCallSites
.asSequence()
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
DevirtualizationAnalysis.devirtualize(input.irModule, context,
externalModulesDFG, devirtualizedCallSites)
}
)
internal data class EscapeAnalysisInput(
val irModule: IrModuleFragment,
val moduleDFG: ModuleDFG,
val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
)
internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerationState, EscapeAnalysisInput, Map<IrElement, Lifetime>>(
name = "EscapeAnalysis",
description = "Escape analysis",
outputIfNotEnabled = { _, _, _, _ -> emptyMap() },
op = { generationState, input ->
val lifetimes = mutableMapOf<IrElement, Lifetime>()
val context = generationState.context
val entryPoint = context.ir.symbols.entryPoint?.owner
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val nonDevirtualizedCallSitesUnfoldFactor =
if (entryPoint != null) {
// For a final program it can be safely assumed that what classes we see is what we got,
// so can take those. In theory we can always unfold call sites using type hierarchy, but
// the analysis might converge much, much slower, so take only reasonably small for now.
5
} else {
// Can't tolerate any non-devirtualized call site for a library.
// TODO: What about private virtual functions?
// Note: 0 is also bad - this means that there're no inheritors in the current source set,
// but there might be some provided by the users of the library being produced.
-1
}
val callGraph = CallGraphBuilder(
context,
input.irModule,
input.moduleDFG,
externalModulesDFG,
input.devirtualizationAnalysisResult,
nonDevirtualizedCallSitesUnfoldFactor
).build()
EscapeAnalysis.computeLifetimes(context, generationState, input.moduleDFG, externalModulesDFG, callGraph, lifetimes)
lifetimes
}
)
internal data class RedundantCallsInput(
val moduleDFG: ModuleDFG,
val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
val irModule: IrModuleFragment,
)
internal val RemoveRedundantCallsToStaticInitializersPhase = createSimpleNamedCompilerPhase(
name = "RemoveRedundantCallsToStaticInitializersPhase",
description = "Redundant static initializers calls removal",
postactions = modulePhaseActions.map { f ->
fun(actionState: ActionState, data: RedundantCallsInput, context: NativeGenerationState) =
f(actionState, data.irModule, context)
}.toSet(),
op = { generationState, input ->
val context = generationState.context
val moduleDFG = input.moduleDFG
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val callGraph = CallGraphBuilder(
context,
input.irModule,
moduleDFG,
externalModulesDFG,
input.devirtualizationAnalysisResult,
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
).build()
val rootSet = DevirtualizationAnalysis.computeRootSet(context, input.irModule, moduleDFG, externalModulesDFG)
.mapNotNull { it.irFunction }
.toSet()
StaticInitializersOptimization.removeRedundantCalls(context, input.irModule, callGraph, rootSet)
}
)
@@ -0,0 +1,28 @@
/*
* 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.*
import org.jetbrains.kotlin.backend.konan.Linker
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.konan.TempFiles
data class LinkerPhaseInput(
val outputFile: String,
val objectFiles: List<ObjectFile>,
val dependenciesTrackingResult: DependenciesTrackingResult,
val outputFiles: OutputFiles,
val temporaryFiles: TempFiles,
val isCoverageEnabled: Boolean,
)
internal val LinkerPhase = createSimpleNamedCompilerPhase<PhaseContext, LinkerPhaseInput>(
name = "Linker",
description = "Linker"
) { context, input ->
val linker = Linker(context, input.isCoverageEnabled, input.temporaryFiles, input.outputFiles)
linker.link(input.outputFile, input.objectFiles, input.dependenciesTrackingResult)
}
@@ -0,0 +1,246 @@
/*
* 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.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.ImportCachesAbiTransformer
import org.jetbrains.kotlin.backend.konan.lower.InlineClassPropertyAccessorsLowering
import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner
import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering
import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering
/**
* Run whole IR lowering pipeline over [irModuleFragment].
*/
internal fun PhaseEngine<NativeGenerationState>.runAllLowerings(irModuleFragment: IrModuleFragment) {
val lowerings = getAllLowerings()
irModuleFragment.files.forEach { file ->
context.fileLowerState = FileLowerState()
lowerings.fold(file) { loweredFile, lowering ->
runPhase(lowering, loweredFile)
}
}
}
private val validateAll = false
private val fileLoweringActions: Set<Action<IrFile, NativeGenerationState>> = setOfNotNull(
nativeStateDumper,
nativeStateIrValidator.takeIf { validateAll }
)
internal val modulePhaseActions: Set<Action<IrModuleFragment, NativeGenerationState>> = setOfNotNull(
nativeStateDumper,
nativeStateIrValidator.takeIf { validateAll }
)
internal val ReturnsInsertionPhase = createSimpleNamedCompilerPhase(
name = "ReturnsInsertion",
description = "Returns insertion for Unit functions",
postactions = fileLoweringActions,
//prerequisite = setOf(autoboxPhase, coroutinesPhase, enumClassPhase), TODO: if there are no files in the module, this requirement fails.
op = { context, irFile -> ReturnsInsertionLowering(context.context).lower(irFile) }
)
internal val InlineClassPropertyAccessorsPhase = createSimpleNamedCompilerPhase(
name = "InlineClassPropertyAccessorsLowering",
description = "Inline class property accessors",
postactions = fileLoweringActions,
op = { context, irFile -> InlineClassPropertyAccessorsLowering(context.context).lower(irFile) }
)
internal val RedundantCoercionsCleaningPhase = createSimpleNamedCompilerPhase(
name = "RedundantCoercionsCleaning",
description = "Redundant coercions cleaning",
postactions = fileLoweringActions,
op = { context, irFile -> RedundantCoercionsCleaner(context.context).lower(irFile) }
)
internal val PropertyAccessorInlinePhase = createSimpleNamedCompilerPhase(
name = "PropertyAccessorInline",
description = "Property accessor inline lowering",
postactions = fileLoweringActions
) { context, irFile ->
PropertyAccessorInlineLowering(context.context).lower(irFile)
}
internal val UnboxInlinePhase = createSimpleNamedCompilerPhase(
name = "UnboxInline",
description = "Unbox functions inline lowering",
postactions = fileLoweringActions
) { context, irFile ->
UnboxInlineLowering(context.context).lower(irFile)
}
private val InlinePhase = createFileLoweringPhase(
lowering = { context ->
object : FileLoweringPass {
override fun lower(irFile: IrFile) {
FunctionInlining(context.context, NativeInlineFunctionResolver(context.context, context)).lower(irFile)
}
}
},
name = "Inline",
description = "Functions inlining",
// prerequisite = setOf(lowerBeforeInlinePhase, arrayConstructorPhase, extractLocalClassesFromInlineBodies)
)
private val DelegationPhase = createFileLoweringPhase(
lowering = ::PropertyDelegationLowering,
name = "Delegation",
description = "Delegation lowering"
// prerequisite = setOf(volatilePhase)
)
private val FunctionReferencePhase = createFileLoweringPhase(
lowering = ::FunctionReferenceLowering,
name = "FunctionReference",
description = "Function references lowering",
// prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase`
)
private val InventNamesForLocalClasses = createFileLoweringPhase(
lowering = ::NativeInventNamesForLocalClasses,
name = "InventNamesForLocalClasses",
description = "Invent names for local classes and anonymous objects",
)
private val UseInternalAbiPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile>(
name = "UseInternalAbi",
description = "Use internal ABI functions to access private entities",
outputIfNotEnabled = { _, _, _, irFile -> irFile },
) { context, file ->
ImportCachesAbiTransformer(context).lower(file)
file
}
private val ObjectClassesPhase = createFileLoweringPhase(
lowering = ::ObjectClassLowering,
name = "ObjectClasses",
description = "Object classes lowering"
)
private val CoroutinesPhase = createFileLoweringPhase(
lowering = { context ->
object : FileLoweringPass {
override fun lower(irFile: IrFile) {
NativeSuspendFunctionsLowering(context).lower(irFile)
AddContinuationToNonLocalSuspendFunctionsLowering(context.context).lower(irFile)
NativeAddContinuationToFunctionCallsLowering(context.context).lower(irFile)
AddFunctionSupertypeToSuspendFunctionLowering(context.context).lower(irFile)
}
}
},
name = "Coroutines",
description = "Coroutines lowering",
// prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase)
)
private val InteropPhase = createFileLoweringPhase(
lowering = ::InteropLowering,
name = "Interop",
description = "Interop lowering",
// prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase)
)
private fun PhaseEngine<NativeGenerationState>.getAllLowerings() = listOf<AbstractNamedCompilerPhase<NativeGenerationState, IrFile, IrFile>>(
convertToNativeGeneration(removeExpectDeclarationsPhase),
convertToNativeGeneration(stripTypeAliasDeclarationsPhase),
convertToNativeGeneration(lowerBeforeInlinePhase),
convertToNativeGeneration(arrayConstructorPhase),
convertToNativeGeneration(lateinitPhase),
convertToNativeGeneration(sharedVariablesPhase),
InventNamesForLocalClasses,
convertToNativeGeneration(extractLocalClassesFromInlineBodies),
convertToNativeGeneration(wrapInlineDeclarationsWithReifiedTypeParametersLowering),
InlinePhase,
convertToNativeGeneration(provisionalFunctionExpressionPhase),
convertToNativeGeneration(postInlinePhase),
convertToNativeGeneration(contractsDslRemovePhase),
convertToNativeGeneration(annotationImplementationPhase),
convertToNativeGeneration(rangeContainsLoweringPhase),
convertToNativeGeneration(forLoopsPhase),
convertToNativeGeneration(flattenStringConcatenationPhase),
convertToNativeGeneration(foldConstantLoweringPhase),
convertToNativeGeneration(computeStringTrimPhase),
convertToNativeGeneration(stringConcatenationPhase),
convertToNativeGeneration(stringConcatenationTypeNarrowingPhase),
convertToNativeGeneration(enumConstructorsPhase),
convertToNativeGeneration(initializersPhase),
convertToNativeGeneration(localFunctionsPhase),
convertToNativeGeneration(volatilePhase),
convertToNativeGeneration(tailrecPhase),
convertToNativeGeneration(defaultParameterExtentPhase),
convertToNativeGeneration(innerClassPhase),
convertToNativeGeneration(dataClassesPhase),
convertToNativeGeneration(ifNullExpressionsFusionPhase),
convertToNativeGeneration(testProcessorPhase),
DelegationPhase,
FunctionReferencePhase,
convertToNativeGeneration(singleAbstractMethodPhase),
convertToNativeGeneration(enumWhenPhase),
convertToNativeGeneration(builtinOperatorPhase),
convertToNativeGeneration(finallyBlocksPhase),
convertToNativeGeneration(enumClassPhase),
convertToNativeGeneration(enumUsagePhase),
InteropPhase,
convertToNativeGeneration(varargPhase),
convertToNativeGeneration(kotlinNothingValueExceptionPhase),
CoroutinesPhase,
convertToNativeGeneration(typeOperatorPhase),
convertToNativeGeneration(expressionBodyTransformPhase),
ObjectClassesPhase,
convertToNativeGeneration(constantInliningPhase),
convertToNativeGeneration(staticInitializersPhase),
convertToNativeGeneration(bridgesPhase),
convertToNativeGeneration(exportInternalAbiPhase),
UseInternalAbiPhase,
convertToNativeGeneration(autoboxPhase),
)
/**
* Simplify porting of lowerings from static driver (where lowerings were executed in one big Context)
* to dynamic with NativeGenerationState. It can be done manual rewriting, but it is an enormous work.
*/
private fun PhaseEngine<NativeGenerationState>.convertToNativeGeneration(
phase: SameTypeNamedCompilerPhase<Context, IrFile>,
): SimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile> {
return createSimpleNamedCompilerPhase(
phase.name,
phase.description,
postactions = fileLoweringActions,
outputIfNotEnabled = { _, _, _, irFile -> irFile },
op = { context, irFile -> phase.phaseBody(phaseConfig, phaserState.changePhaserStateType(), context.context, irFile) }
)
}
private fun createFileLoweringPhase(
name: String,
description: String,
lowering: (NativeGenerationState) -> FileLoweringPass
): SimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile> = createSimpleNamedCompilerPhase(
name,
description,
postactions = fileLoweringActions,
outputIfNotEnabled = { _, _, _, irFile -> irFile },
op = { context, irFile ->
lowering(context).lower(irFile)
irFile
}
)
@@ -0,0 +1,24 @@
/*
* 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.BitcodeCompiler
import org.jetbrains.kotlin.backend.konan.ObjectFile
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.konan.TempFiles
internal data class ObjectFilesPhaseInput(
val bitcodeFileName: String,
val temporaryFiles: TempFiles
)
internal val ObjectFilesPhase = createSimpleNamedCompilerPhase<PhaseContext, ObjectFilesPhaseInput, List<ObjectFile>>(
name = "ObjectFiles",
description = "Bitcode to object file",
outputIfNotEnabled = { _, _, _, _ -> emptyList() }
) { context, input ->
BitcodeCompiler(context, input.temporaryFiles).makeObjectFiles(input.bitcodeFileName)
}
@@ -6,31 +6,47 @@
package org.jetbrains.kotlin.backend.konan.driver.phases
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfigurationService
import org.jetbrains.kotlin.backend.common.phaser.PhaserState
import org.jetbrains.kotlin.backend.common.phaser.SimpleNamedCompilerPhase
import org.jetbrains.kotlin.backend.common.phaser.*
internal fun <Context : LoggingContext, Input, Output> createSimpleNamedCompilerPhase(
name: String,
description: String,
preactions: Set<Action<Input, Context>> = emptySet(),
postactions: Set<Action<Output, Context>> = emptySet(),
outputIfNotEnabled: (PhaseConfigurationService, PhaserState<Input>, Context, Input) -> Output,
phaseBody: (Context, Input) -> Output
): SimpleNamedCompilerPhase<Context, Input, Output> = object : SimpleNamedCompilerPhase<Context, Input, Output>(name, description) {
op: (Context, Input) -> Output
): SimpleNamedCompilerPhase<Context, Input, Output> = object : SimpleNamedCompilerPhase<Context, Input, Output>(
name,
description,
preactions = preactions,
postactions = postactions.map { f ->
fun(actionState: ActionState, data: Pair<Input, Output>, context: Context) = f(actionState, data.second, context)
}.toSet(),
) {
override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input): Output =
outputIfNotEnabled(phaseConfig, phaserState, context, input)
override fun phaseBody(context: Context, input: Input): Output =
phaseBody(context, input)
op(context, input)
}
internal fun <Context : LoggingContext, Input> createSimpleNamedCompilerPhase(
name: String,
description: String,
phaseBody: (Context, Input) -> Unit
): SimpleNamedCompilerPhase<Context, Input, Unit> = object : SimpleNamedCompilerPhase<Context, Input, Unit>(name, description) {
preactions: Set<Action<Input, Context>> = emptySet(),
postactions: Set<Action<Input, Context>> = emptySet(),
op: (Context, Input) -> Unit
): SimpleNamedCompilerPhase<Context, Input, Unit> = object : SimpleNamedCompilerPhase<Context, Input, Unit>(
name,
description,
preactions = preactions,
postactions = postactions.map { f ->
fun(actionState: ActionState, data: Pair<Input, Unit>, context: Context) = f(actionState, data.first, context)
}.toSet(),
) {
override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input) {}
override fun phaseBody(context: Context, input: Input): Unit =
phaseBody(context, input)
op(context, input)
}
@@ -33,14 +33,23 @@ data class PsiToIrInput(
val isProducingLibrary: Boolean,
)
// TODO: Possible improvement: a separate class for klib output (without irModules and irLinker).
internal class PsiToIrOutput(
val irModules: Map<String, IrModuleFragment>,
internal sealed class PsiToIrOutput(
val irModule: IrModuleFragment,
val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
val symbols: KonanSymbols,
val irLinker: KonanIrLinker?,
)
) {
class ForBackend(
val irModules: Map<String, IrModuleFragment>,
irModule: IrModuleFragment,
symbols: KonanSymbols,
val irLinker: KonanIrLinker,
) : PsiToIrOutput(irModule, symbols)
class ForKlib(
irModule: IrModuleFragment,
symbols: KonanSymbols,
val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
): PsiToIrOutput(irModule, symbols)
}
// TODO: Consider component-based approach
internal interface PsiToIrContext : PhaseContext {
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.library.SerializedMetadata
internal data class SerializerInput(
val moduleDescriptor: ModuleDescriptor,
val psiToIrOutput: PsiToIrOutput?,
val psiToIrOutput: PsiToIrOutput.ForKlib?,
)
data class SerializerOutput(
@@ -58,13 +58,13 @@ internal val SerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, Seri
exportKDoc = context.shouldExportKDoc(),
!expectActualLinker, includeOnlyModuleContent = true)
val serializedMetadata = serializer.serializeModule(input.moduleDescriptor)
val neededLibraries = config.librariesWithDependencies(input.moduleDescriptor)
val neededLibraries = config.librariesWithDependencies()
SerializerOutput(serializedMetadata, serializedIr, null, neededLibraries)
}
internal fun <T : PhaseContext> PhaseEngine<T>.runSerializer(
moduleDescriptor: ModuleDescriptor,
psiToIrResult: PsiToIrOutput?,
psiToIrResult: PsiToIrOutput.ForKlib?,
): SerializerOutput {
val input = SerializerInput(moduleDescriptor, psiToIrResult)
return this.runPhase(SerializerPhase, input)
@@ -8,20 +8,20 @@ package org.jetbrains.kotlin.backend.konan.driver.phases
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.driver.runPhaseInParentContext
import org.jetbrains.kotlin.backend.konan.llvm.linkBitcodeDependenciesPhase
import org.jetbrains.kotlin.backend.konan.llvm.printBitcodePhase
import org.jetbrains.kotlin.backend.konan.llvm.verifyBitcodePhase
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
internal fun PhaseEngine<PhaseContext>.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? {
val frontendOutput = useContext(FrontendContextImpl(config)) { it.runFrontend(environment) }
val frontendOutput = useContext(FrontendContextImpl(config)) { it.runPhase(FrontendPhase, environment) }
return frontendOutput as? FrontendPhaseOutput.Full
}
@@ -41,20 +41,23 @@ internal fun <T> PhaseEngine<PhaseContext>.runPsiToIr(
val additionalOutput = produceAdditionalOutput(psiToIrEngine)
val psiToIrInput = PsiToIrInput(frontendOutput.moduleDescriptor, frontendOutput.environment, isProducingLibrary)
val output = psiToIrEngine.runPhase(PsiToIrPhase, psiToIrInput)
psiToIrEngine.runSpecialBackendChecks(output)
psiToIrEngine.runSpecialBackendChecks(output.irModule, output.symbols)
output to additionalOutput
}
runPhase(CopyDefaultValuesToActualPhase, psiToIrOutput.irModule)
return psiToIrOutput to additionalOutput
}
internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Context) {
internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Context, irModule: IrModuleFragment) {
useContext(backendContext) { backendEngine ->
backendEngine.runPhase(functionsWithoutBoundCheck)
backendEngine.processModuleFragments(backendEngine.context.irModule!!) { generationState, fragment ->
val fragments = backendEngine.splitIntoFragments(irModule)
fragments.forEach { (generationState, fragment) ->
backendEngine.useContext(generationState) { generationStateEngine ->
// TODO: We can run compile part in parallel if we get rid of context.generationState.
generationStateEngine.runLowerAndCompile(fragment)
// TODO: Make this work if we first compile all the fragments and only after that run the link phases.
val it = generationStateEngine.compileModule(fragment)
// Split here
compileAndLink(it, it.outputFiles.mainFileName, it.outputFiles, it.temporaryFiles, isCoverageEnabled = false)
}
}
}
@@ -68,35 +71,26 @@ private fun isReferencedByNativeRuntime(declarations: List<IrDeclaration>): Bool
it is IrClass && isReferencedByNativeRuntime(it.declarations)
}
internal fun PhaseEngine<out Context>.processModuleFragments(
private fun PhaseEngine<out Context>.splitIntoFragments(
input: IrModuleFragment,
action: (NativeGenerationState, IrModuleFragment) -> Unit
): Unit = if (context.config.producePerFileCache) {
val module = context.irModules[context.config.libraryToCache!!.klib.libraryName]
?: error("No module for the library being cached: ${context.config.libraryToCache!!.klib.libraryName}")
): Sequence<Pair<NativeGenerationState, IrModuleFragment>> = if (context.config.producePerFileCache) {
val module = input
val files = module.files.toList()
module.files.clear()
val stdlibIsBeingCached = module.descriptor == context.stdlibModule
val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached }
?.filter { it.isFunctionInterfaceFile }.orEmpty()
val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached }
?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty()
for (file in files) {
if (file.isFunctionInterfaceFile) continue
files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file ->
val generationState = NativeGenerationState(
context.config,
context,
CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString())
)
context.generationState = generationState
module.files += file
val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file))
if (generationState.shouldDefineFunctionClasses)
module.files += functionInterfaceFiles
fragment.files += functionInterfaceFiles
if (generationState.shouldLinkRuntimeNativeLibraries) {
filesReferencedByNativeRuntime.forEach {
@@ -104,56 +98,132 @@ internal fun PhaseEngine<out Context>.processModuleFragments(
}
}
action(context.generationState, input)
module.files.clear()
context.irModule!!.files.clear() // [dependenciesLowerPhase] puts all files to [context.irModule] for codegen.
fragment.files.filterIsInstance<IrFileImpl>().forEach {
it.module = fragment
}
generationState to fragment
}
module.files += files
} else {
context.generationState = NativeGenerationState(context.config, context, context.config.libraryToCache?.strategy)
action(context.generationState, input)
val nativeGenerationState = NativeGenerationState(context.config, context, context.config.libraryToCache?.strategy)
sequenceOf(nativeGenerationState to input)
}
internal data class ModuleCompilationOutput(
val bitcodeFile: String,
val dependenciesTrackingResult: DependenciesTrackingResult,
// Passing tempFiles and output files through this file looks silly and incorrect.
// TODO: Refactor these classes and remove them from here.
val temporaryFiles: TempFiles,
val outputFiles: OutputFiles,
)
/**
* Performs all the hard work:
* 1. Runs IR lowerings
* 2. Runs LTO.
* 3. Translates IR to LLVM IR.
* 4. Optimizes it.
* 5. Serializes it to a bitcode file.
* 6. Compiles bitcode to an object file.
* 7. Performs binary linkage.
* ... And stores additional cache info.
*
* TODO: Split into more granular phases with explicit inputs and outputs.
*/
internal fun PhaseEngine<NativeGenerationState>.runLowerAndCompile(module: IrModuleFragment) {
internal fun PhaseEngine<NativeGenerationState>.compileModule(module: IrModuleFragment): ModuleCompilationOutput {
if (context.config.produce.isCache) {
runPhaseInParentContext(buildAdditionalCacheInfoPhase)
runPhase(BuildAdditionalCacheInfoPhase, module)
}
if (context.config.produce == CompilerOutputKind.PROGRAM) {
runPhaseInParentContext(entryPointPhase, module)
runPhase(EntryPointPhase, module)
}
runBackendCodegen(module)
runBitcodePostProcessing()
if (context.config.produce.isCache) {
runPhaseInParentContext(saveAdditionalCacheInfoPhase)
runPhase(SaveAdditionalCacheInfoPhase)
}
runPhase(WriteBitcodeFilePhase)
runPhaseInParentContext(objectFilesPhase)
runPhaseInParentContext(linkerPhase)
val bitcodeFile = runPhase(WriteBitcodeFilePhase, context.llvm.module)
val dependenciesTrackingResult = DependenciesTrackingResult(
context.dependenciesTracker.bitcodeToLink,
context.dependenciesTracker.allNativeDependencies,
context.dependenciesTracker.allCachedBitcodeDependencies,
)
return ModuleCompilationOutput(bitcodeFile, dependenciesTrackingResult, context.tempFiles, context.outputFiles)
}
internal fun <C : PhaseContext> PhaseEngine<C>.compileAndLink(
moduleCompilationOutput: ModuleCompilationOutput,
linkerOutputFile: String,
outputFiles: OutputFiles,
temporaryFiles: TempFiles,
isCoverageEnabled: Boolean,
) {
val objectFiles = runPhase(ObjectFilesPhase, ObjectFilesPhaseInput(moduleCompilationOutput.bitcodeFile, temporaryFiles))
val linkerPhaseInput = LinkerPhaseInput(linkerOutputFile, objectFiles, moduleCompilationOutput.dependenciesTrackingResult,
outputFiles, temporaryFiles, isCoverageEnabled = isCoverageEnabled)
runPhase(LinkerPhase, linkerPhaseInput)
if (context.config.produce.isCache) {
runPhaseInParentContext(finalizeCachePhase)
runPhase(FinalizeCachePhase, outputFiles)
}
}
internal fun PhaseEngine<NativeGenerationState>.runBackendCodegen(module: IrModuleFragment) {
runPhaseInParentContext(allLoweringsPhase, module)
runPhaseInParentContext(dependenciesLowerPhase, module)
runPhaseInParentContext(bitcodePhase, module)
runPhaseInParentContext(verifyBitcodePhase, module)
runPhaseInParentContext(printBitcodePhase, module)
runPhaseInParentContext(linkBitcodeDependenciesPhase, module)
runPhaseInParentContext(bitcodePostprocessingPhase, module)
runAllLowerings(module)
val dependenciesToCompile = findDependenciesToCompile()
// TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order.
// TODO: Does the order of files really matter with the new MM?
dependenciesToCompile.reversed().forEach { irModule ->
runAllLowerings(irModule)
}
mergeDependencies(module, dependenciesToCompile)
runCodegen(module)
runPhase(CStubsPhase)
// TODO: Consider extracting llvmModule and friends from nativeGenerationState and pass them explicitly.
// Motivation: possibility to run LTO on bitcode level after separate IR compilation.
val llvmModule = context.llvm.module
if (context.config.needCompilerVerification || context.config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)) {
runPhase(VerifyBitcodePhase, llvmModule)
}
if (context.shouldPrintBitCode()) {
runPhase(PrintBitcodePhase, llvmModule)
}
runPhase(LinkBitcodeDependenciesPhase)
}
/**
* Compile lowered [module] to object file.
* @return absolute path to object file.
*/
private fun PhaseEngine<NativeGenerationState>.runCodegen(module: IrModuleFragment) {
val optimize = context.shouldOptimize()
module.files.forEach {
runPhase(ReturnsInsertionPhase, it)
}
val moduleDFG = runPhase(BuildDFGPhase, module, disable = !optimize)
val devirtualizationAnalysisResults = runPhase(DevirtualizationAnalysisPhase, DevirtualizationAnalysisInput(module, moduleDFG), disable = !optimize)
val dceResult = runPhase(DCEPhase, DCEInput(module, moduleDFG, devirtualizationAnalysisResults), disable = !optimize)
runPhase(RemoveRedundantCallsToStaticInitializersPhase, RedundantCallsInput(moduleDFG, devirtualizationAnalysisResults, module), disable = !optimize)
runPhase(DevirtualizationPhase, DevirtualizationInput(module, devirtualizationAnalysisResults), disable = !optimize)
// Have to run after link dependencies phase, because fields from dependencies can be changed during lowerings.
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken debug information.
module.files.forEach {
runPhase(PropertyAccessorInlinePhase, it, disable = !optimize)
runPhase(InlineClassPropertyAccessorsPhase, it, disable = !optimize)
runPhase(RedundantCoercionsCleaningPhase, it)
// depends on redundantCoercionsCleaningPhase
runPhase(UnboxInlinePhase, it, disable = !optimize)
}
runPhase(CreateLLVMDeclarationsPhase, module)
runPhase(GHAPhase, module, disable = !optimize)
runPhase(RTTIPhase, RTTIInput(module, dceResult))
val lifetimes = runPhase(EscapeAnalysisPhase, EscapeAnalysisInput(module, moduleDFG, devirtualizationAnalysisResults), disable = !optimize)
runPhase(CodegenPhase, CodegenInput(module, lifetimes))
}
private fun PhaseEngine<NativeGenerationState>.findDependenciesToCompile(): List<IrModuleFragment> {
return context.config.librariesWithDependencies()
.mapNotNull { context.context.irModules[it.libraryName] }
.filter { context.llvmModuleSpecification.containsModule(it) }
}
// Save all files for codegen in reverse topological order.
// This guarantees that libraries initializers are emitted in correct order.
private fun mergeDependencies(targetModule: IrModuleFragment, dependencies: List<IrModuleFragment>) {
targetModule.files.addAll(0, dependencies.flatMap { it.files })
}
@@ -0,0 +1,64 @@
/*
* 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.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.backend.common.checkDeclarationParents
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
import org.jetbrains.kotlin.ir.IrElement
internal fun irValidationCallback(state: ActionState, element: IrElement, context: Context) {
if (!context.config.needVerifyIr) return
val validatorConfig = IrValidatorConfig(
abortOnError = false,
ensureAllNodesAreDifferent = true,
checkTypes = true,
checkDescriptors = false
)
try {
element.accept(IrValidator(context, validatorConfig), null)
element.checkDeclarationParents()
} catch (t: Throwable) {
// TODO: Add reference to source.
if (validatorConfig.abortOnError)
throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t)
else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}")
}
}
internal val nativeStateIrValidator =
fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) {
irValidationCallback(actionState, data, context.context)
}
internal val nativeStateDumper =
fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) {
defaultDumper(actionState, data, context.context)
}
internal fun <C : PhaseContext, Input, Output> SimpleNamedCompilerPhase<C, Input, Output>.copy(
preactions: Set<Action<Input, C>> = emptySet(),
postactions: Set<Action<Output, C>> = emptySet(),
) = object : SimpleNamedCompilerPhase<C, Input, Output>(
name,
description,
preactions = preactions,
postactions = postactions.map { f ->
fun(actionState: ActionState, data: Pair<Input, Output>, context: C) { f(actionState, data.second, context) }
}.toSet(),
) {
override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: C, input: Input): Output =
this@copy.outputIfNotEnabled(phaseConfig, phaserState, context, input)
override fun phaseBody(context: C, input: Input): Output =
this@copy.phaseBody(context, input)
}
@@ -41,9 +41,7 @@ object KonanNameConventions {
}
// This is what Context collects about IR.
internal class KonanIr(context: Context): Ir<Context>(context) {
override var symbols: KonanSymbols by Delegates.notNull()
}
internal class KonanIr(context: Context, override val symbols: KonanSymbols): Ir<Context>(context)
internal abstract class KonanSymbols(
context: PhaseContext,
@@ -1,338 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis
import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass
import org.jetbrains.kotlin.backend.konan.lower.InlineClassPropertyAccessorsLowering
import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner
import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.util.OperatorNameConventions
internal val createLLVMDeclarationsPhase = makeKonanModuleOpPhase(
name = "CreateLLVMDeclarations",
description = "Map IR declarations to LLVM",
op = { context, _ -> context.generationState.llvmDeclarations = createLlvmDeclarations(context.generationState, context.irModule!!) }
)
internal val RTTIPhase = makeKonanModuleOpPhase(
name = "RTTI",
description = "RTTI generation",
op = { context, irModule ->
val visitor = RTTIGeneratorVisitor(context.generationState)
irModule.acceptVoid(visitor)
visitor.dispose()
}
)
internal val buildDFGPhase = makeKonanModuleOpPhase(
name = "BuildDFG",
description = "Data flow graph building",
op = { context, irModule ->
context.moduleDFG = ModuleDFGBuilder(context, irModule).build()
}
)
internal val returnsInsertionPhase = makeKonanModuleOpPhase(
name = "ReturnsInsertion",
description = "Returns insertion for Unit functions",
//prerequisite = setOf(autoboxPhase, coroutinesPhase, enumClassPhase), TODO: if there are no files in the module, this requirement fails.
op = { context, irModule -> irModule.files.forEach { ReturnsInsertionLowering(context).lower(it) } }
)
internal val inlineClassPropertyAccessorsPhase = makeKonanModuleOpPhase(
name = "InlineClassPropertyAccessorsLowering",
description = "Inline class property accessors",
op = { context, irModule -> irModule.files.forEach { InlineClassPropertyAccessorsLowering(context).lower(it) } }
)
internal val devirtualizationAnalysisPhase = makeKonanModuleOpPhase(
name = "DevirtualizationAnalysis",
description = "Devirtualization analysis",
prerequisite = setOf(buildDFGPhase),
op = { context, _ ->
context.devirtualizationAnalysisResult = DevirtualizationAnalysis.run(
context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
)
}
)
internal val redundantCoercionsCleaningPhase = makeKonanModuleOpPhase(
name = "RedundantCoercionsCleaning",
description = "Redundant coercions cleaning",
op = { context, irModule -> irModule.files.forEach { RedundantCoercionsCleaner(context).lower(it) } }
)
internal val ghaPhase = makeKonanModuleOpPhase(
name = "GHAPhase",
description = "Global hierarchy analysis",
op = { context, irModule -> GlobalHierarchyAnalysis(context, irModule).run() }
)
internal val IrFunction.longName: String
get() = "${(parent as? IrClass)?.name?.asString() ?: "<root>"}.${(this as? IrSimpleFunction)?.name ?: "<init>"}"
internal val dcePhase = makeKonanModuleOpPhase(
name = "DCEPhase",
description = "Dead code elimination",
prerequisite = setOf(devirtualizationAnalysisPhase),
op = { context, _ ->
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val callGraph = CallGraphBuilder(
context, context.moduleDFG!!,
externalModulesDFG,
context.devirtualizationAnalysisResult!!,
// For DCE we don't wanna miss any potentially reachable function.
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
).build()
val referencedFunctions = mutableSetOf<IrFunction>()
callGraph.rootExternalFunctions.forEach {
if (!it.isStaticFieldInitializer)
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
}
for (node in callGraph.directEdges.values) {
if (!node.symbol.isStaticFieldInitializer)
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
node.callSites.forEach {
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}"))
}
}
context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
// TODO: Generalize somehow, not that graceful.
if (declaration.name == OperatorNameConventions.INVOKE
&& declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) {
referencedFunctions.add(declaration)
}
super.visitFunction(declaration)
}
override fun visitConstructor(declaration: IrConstructor) {
// TODO: NativePointed is the only inline class for which the field's type and
// the constructor parameter's type are different.
// Thus we need to conserve the constructor no matter if it was actually referenced somehow or not.
// See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why.
if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary)
referencedFunctions.add(declaration)
super.visitConstructor(declaration)
}
})
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitFile(declaration: IrFile): IrFile {
declaration.declarations.removeAll {
(it is IrFunction && !referencedFunctions.contains(it))
}
return super.visitFile(declaration)
}
override fun visitClass(declaration: IrClass): IrStatement {
if (declaration == context.ir.symbols.nativePointed)
return super.visitClass(declaration)
declaration.declarations.removeAll {
(it is IrFunction && it.isReal && !referencedFunctions.contains(it))
}
return super.visitClass(declaration)
}
override fun visitProperty(declaration: IrProperty): IrStatement {
if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.getter = null
}
if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.setter = null
}
return super.visitProperty(declaration)
}
})
context.referencedFunctions = referencedFunctions
}
)
internal val removeRedundantCallsToStaticInitializersPhase = makeKonanModuleOpPhase(
name = "RemoveRedundantCallsToStaticInitializersPhase",
description = "Redundant static initializers calls removal",
prerequisite = setOf(devirtualizationAnalysisPhase),
op = { context, _ ->
val moduleDFG = context.moduleDFG!!
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val callGraph = CallGraphBuilder(
context, moduleDFG,
externalModulesDFG,
context.devirtualizationAnalysisResult!!,
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
).build()
val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG)
.mapNotNull { it.irFunction }
.toSet()
StaticInitializersOptimization.removeRedundantCalls(context, callGraph, rootSet)
}
)
internal val devirtualizationPhase = makeKonanModuleOpPhase(
name = "Devirtualization",
description = "Devirtualization",
prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase),
op = { context, irModule ->
val devirtualizedCallSites =
context.devirtualizationAnalysisResult!!.devirtualizedCallSites
.asSequence()
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
DevirtualizationAnalysis.devirtualize(irModule, context,
ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()), devirtualizedCallSites)
}
)
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
name = "EscapeAnalysis",
description = "Escape analysis",
prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase),
op = { context, _ ->
val entryPoint = context.ir.symbols.entryPoint?.owner
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val nonDevirtualizedCallSitesUnfoldFactor =
if (entryPoint != null) {
// For a final program it can be safely assumed that what classes we see is what we got,
// so can take those. In theory we can always unfold call sites using type hierarchy, but
// the analysis might converge much, much slower, so take only reasonably small for now.
5
}
else {
// Can't tolerate any non-devirtualized call site for a library.
// TODO: What about private virtual functions?
// Note: 0 is also bad - this means that there're no inheritors in the current source set,
// but there might be some provided by the users of the library being produced.
-1
}
val callGraph = CallGraphBuilder(
context, context.moduleDFG!!,
externalModulesDFG,
context.devirtualizationAnalysisResult!!,
nonDevirtualizedCallSitesUnfoldFactor
).build()
EscapeAnalysis.computeLifetimes(
context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes
)
}
)
internal val codegenPhase = makeKonanModuleOpPhase(
name = "Codegen",
description = "Code generation",
op = { context, irModule ->
context.generationState.objCExport = ObjCExport(
context.generationState,
context.moduleDescriptor,
context.objCExportedInterface,
context.objCExportCodeSpec
)
irModule.acceptVoid(CodeGeneratorVisitor(context.generationState, context.lifetimes))
if (context.generationState.hasDebugInfo())
DIFinalize(context.generationState.debugInfo.builder)
}
)
internal val cStubsPhase = makeKonanModuleOpPhase(
name = "CStubs",
description = "C stubs compilation",
op = { context, _ -> produceCStubs(context.generationState) }
)
internal val linkBitcodeDependenciesPhase = makeKonanModuleOpPhase(
name = "LinkBitcodeDependencies",
description = "Link bitcode dependencies",
op = { context, _ -> linkBitcodeDependencies(context.generationState) }
)
internal val checkExternalCallsPhase = makeKonanModuleOpPhase(
name = "CheckExternalCalls",
description = "Check external calls",
op = { context, _ -> checkLlvmModuleExternalCalls(context.generationState) }
)
internal val rewriteExternalCallsCheckerGlobals = makeKonanModuleOpPhase(
name = "RewriteExternalCallsCheckerGlobals",
description = "Rewrite globals for external calls checker after optimizer run",
op = { context, _ -> addFunctionsListSymbolForChecker(context.generationState) }
)
internal val bitcodeOptimizationPhase = makeKonanModuleOpPhase(
name = "BitcodeOptimization",
description = "Optimize bitcode",
op = { context, _ ->
val generationState = context.generationState
val config = createLTOFinalPipelineConfig(context.generationState)
LlvmOptimizationPipeline(config, context.generationState.llvm.module, generationState).use {
it.run()
}
}
)
internal val coveragePhase = makeKonanModuleOpPhase(
name = "Coverage",
description = "Produce coverage information",
op = { context, _ -> runCoveragePass(context.generationState) }
)
internal val optimizeTLSDataLoadsPhase = makeKonanModuleOpPhase(
name = "OptimizeTLSDataLoads",
description = "Optimize multiple loads of thread data",
op = { context, _ -> removeMultipleThreadDataLoads(context.generationState) }
)
internal val removeRedundantSafepointsPhase = makeKonanModuleOpPhase(
name = "RemoveRedundantSafepoints",
description = "Remove function prologue safepoints inlined to another function",
op = { context, _ ->
RemoveRedundantSafepointsPass().runOnModule(
module = context.generationState.llvm.module,
isSafepointInliningAllowed = context.shouldInlineSafepoints()
)
}
)
internal val verifyBitcodePhase = makeKonanModuleOpPhase(
name = "VerifyBitcode",
description = "Verify bitcode",
op = { context, _ -> context.verifyBitCode() }
)
internal val printBitcodePhase = makeKonanModuleOpPhase(
name = "PrintBitcode",
description = "Print bitcode",
op = { context, _ ->
if (context.shouldPrintBitCode()) {
context.printBitCode()
}
}
)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.lower.inline.InlinerExpressionLocatio
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.cexport.CAdapterApiExporter
import org.jetbrains.kotlin.backend.konan.cexport.CAdapterCodegen
import org.jetbrains.kotlin.backend.konan.cexport.CAdapterExportedElements
import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
@@ -22,6 +23,8 @@ import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_STANDA
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -83,8 +86,8 @@ internal fun IrField.isGlobalNonPrimitive(context: Context) = when {
internal fun IrField.shouldBeFrozen(context: Context): Boolean =
this.storageKind(context) == FieldStorageKind.SHARED_FROZEN
internal class RTTIGeneratorVisitor(generationState: NativeGenerationState) : IrElementVisitorVoid {
val generator = RTTIGenerator(generationState)
internal class RTTIGeneratorVisitor(generationState: NativeGenerationState, referencedFunctions: Set<IrFunction>?) : IrElementVisitorVoid {
val generator = RTTIGenerator(generationState, referencedFunctions)
val kotlinObjCClassInfoGenerator = KotlinObjCClassInfoGenerator(generationState)
@@ -201,7 +204,11 @@ private interface CodeContext {
//-------------------------------------------------------------------------//
internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, val lifetimes: Map<IrElement, Lifetime>) : IrElementVisitorVoid {
internal class CodeGeneratorVisitor(
val generationState: NativeGenerationState,
val irBuiltins: IrBuiltIns,
val lifetimes: Map<IrElement, Lifetime>
) : IrElementVisitorVoid {
private val context = generationState.context
private val llvm = generationState.llvm
private val debugInfo: DebugInfo
@@ -338,8 +345,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
return block()
}
}
private fun appendCAdapters() {
val elements = context.cAdapterExportedElements
private fun appendCAdapters(elements: CAdapterExportedElements) {
CAdapterCodegen(codegen, generationState).buildAllAdaptersRecursively(elements)
// TODO: It is not a part of IrToBitcode. Maybe move it somewhere?
CAdapterApiExporter(generationState, elements).makeGlobalStruct()
@@ -442,7 +448,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
appendLlvmUsed("llvm.used", llvm.usedFunctions + llvm.usedGlobals)
appendLlvmUsed("llvm.compiler.used", llvm.compilerUsedGlobals)
if (context.config.produce.isNativeLibrary) {
appendCAdapters()
context.cAdapterExportedElements?.let { appendCAdapters(it) }
}
}
@@ -899,12 +905,17 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
declaration.backingField?.acceptVoid(this)
}
//-------------------------------------------------------------------------//
private fun needGlobalInit(field: IrField): Boolean {
if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return field.isStatic
// TODO: add some smartness here. Maybe if package of the field is in never accessed
// assume its global init can be actually omitted.
return true
}
override fun visitField(declaration: IrField) {
context.log{"visitField : ${ir2string(declaration)}"}
debugFieldDeclaration(declaration)
if (context.needGlobalInit(declaration)) {
if (needGlobalInit(declaration)) {
val type = declaration.type.toLLVMType(llvm)
val globalPropertyAccess = generationState.llvmDeclarations.forStaticField(declaration).storageAddressAccess
val initializer = declaration.initializer?.expression
@@ -1684,7 +1695,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
when {
!value.symbol.owner.isStatic -> {
fieldAddress = fieldPtrOfClass(evaluateExpression(value.receiver!!), value.symbol.owner)
alignment = context.generationState.llvmDeclarations.forField(value.symbol.owner).alignment
alignment = generationState.llvmDeclarations.forField(value.symbol.owner).alignment
}
value.symbol.owner.correspondingPropertySymbol?.owner?.isConst == true -> {
// TODO: probably can be removed, as they are inlined.
@@ -1762,7 +1773,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
functionGenerationContext.call(llvm.checkLifetimesConstraint, listOf(thisPtr, valueToAssign))
}
address = fieldPtrOfClass(thisPtr, value.symbol.owner)
alignment = context.generationState.llvmDeclarations.forField(value.symbol.owner).alignment
alignment = generationState.llvmDeclarations.forField(value.symbol.owner).alignment
} else {
assert(value.receiver == null)
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL)
@@ -2537,7 +2548,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
private fun evaluateOperatorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
context.log{"evaluateOperatorCall : origin:${ir2string(callee)}"}
val function = callee.symbol.owner
val ib = context.irModule!!.irBuiltins
val ib = context.irBuiltIns
with(functionGenerationContext) {
val functionSymbol = function.symbol
@@ -339,7 +339,7 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
} else {
// Fields are module-private, so we use internal name:
val name = "kvar:" + qualifyInternalName(declaration)
val alignmnet = declaration.requiredAlignment(context)
val alignmnet = declaration.requiredAlignment(llvm)
val storage = if (declaration.storageKind(context) == FieldStorageKind.THREAD_LOCAL) {
addKotlinThreadLocal(name, declaration.type.toLLVMType(llvm), alignmnet)
} else {
@@ -17,7 +17,10 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
internal class RTTIGenerator(override val generationState: NativeGenerationState) : ContextUtils {
internal class RTTIGenerator(
override val generationState: NativeGenerationState,
private val referencedFunctions: Set<IrFunction>?,
) : ContextUtils {
private val acyclicCache = mutableMapOf<IrType, Boolean>()
private val safeAcyclicFieldTypes = setOf(
@@ -51,7 +54,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState
private fun flagsFromClass(irClass: IrClass): Int {
var result = 0
if (irClass.isFrozen(context))
result = result or TF_IMMUTABLE
result = result or TF_IMMUTABLE
// TODO: maybe perform deeper analysis to find surely acyclic types.
if (!irClass.isInterface && !irClass.isAbstract() && !irClass.isAnnotationClass) {
if (checkAcyclicClass(irClass)) {
@@ -294,7 +297,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
val implementation = it.implementation
if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) {
if (implementation == null || implementation.isExternalObjCClassMethod() || referencedFunctions?.contains(implementation) == false) {
NullPointer(llvm.int8Type)
} else {
implementation.entryPointAddress
@@ -375,7 +378,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState
else {
val vtableEntries = iface.interfaceVTableEntries.map { ifaceFunction ->
val impl = layoutBuilder.overridingOf(ifaceFunction)
if (impl == null || context.referencedFunctions?.contains(impl) == false)
if (impl == null || referencedFunctions?.contains(impl) == false)
NullPointer(llvm.int8Type)
else impl.entryPointAddress
}
@@ -567,7 +570,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState
associatedObjects = null,
processObjectInMark = genProcessObjectInMark(bodyType),
requiredAlignment = runtime.objectAlignment
), vtable)
), vtable)
typeInfoWithVtableGlobal.setInitializer(typeInfoWithVtable)
typeInfoWithVtableGlobal.setConstant(true)
@@ -62,11 +62,14 @@ internal class CoverageManager(val generationState: NativeGenerationState) {
filesRegionsInfo.flatMap { it.functions }.firstOrNull { it.function == irFunction }
private val coveredModules: Set<ModuleDescriptor> by lazy {
val coveredUserCode = if (shouldCoverSources) setOf(context.moduleDescriptor) else emptySet()
val coveredSources = if (shouldCoverSources) {
context.sourcesModules
} else {
emptySet()
}
val coveredLibs = context.irModules.filter { it.key in librariesToCover }.values
.map { it.descriptor }.toSet()
val coveredIncludedLibs = if (shouldCoverSources) context.getIncludedLibraryDescriptors().toSet() else emptySet()
coveredLibs + coveredUserCode + coveredIncludedLibs
coveredLibs + coveredSources
}
private fun fileCoverageFilter(file: IrFile) =
@@ -218,7 +218,8 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
val runtime get() = codegen.runtime
val staticData get() = codegen.staticData
val rttiGenerator = RTTIGenerator(generationState)
// TODO: pass referenced functions explicitly
val rttiGenerator = RTTIGenerator(generationState, referencedFunctions = null)
fun dispose() {
rttiGenerator.dispose()
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
*/
internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, IrBuildingTransformer(context) {
private val irBuiltins = context.irModule!!.irBuiltins
private val irBuiltins = context.irBuiltIns
private val symbols = context.ir.symbols
override fun lower(irFile: IrFile) {
@@ -37,7 +37,7 @@ internal class CacheInfoBuilder(
if (!declaration.isInterface && !declaration.isLocal
&& declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS
) {
val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields()
val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields(generationState.llvm)
generationState.classFields.add(moduleDeserializer.buildClassFields(declaration, declaredFields))
}
}
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
internal class DataClassOperatorsLowering(val context: Context) : FileLoweringPass, IrElementTransformer<IrFunction?> {
private val irBuiltins = context.irModule!!.irBuiltins
private val irBuiltins = context.irBuiltIns
override fun lower(irFile: IrFile) {
irFile.transformChildren(this, null)
@@ -680,7 +680,7 @@ internal class TestProcessor (val context: Context) {
private fun shouldProcessFile(irFile: IrFile): Boolean = irFile.packageFragmentDescriptor.module.let {
// Process test annotations in source libraries too.
it == context.moduleDescriptor || it in context.getIncludedLibraryDescriptors()
it in context.sourcesModules
}
fun process(irFile: IrFile) {
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.DirectedGraph
import org.jetbrains.kotlin.backend.konan.DirectedGraphNode
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol.Declared)
: DirectedGraphNode<DataFlowIR.FunctionSymbol.Declared> {
@@ -53,6 +54,7 @@ internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol.Declared
internal class CallGraphBuilder(
val context: Context,
val irModule: IrModuleFragment,
val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG,
val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
@@ -77,7 +79,7 @@ internal class CallGraphBuilder(
private val functionStack = mutableListOf<HandleFunctionParams>()
fun build(): CallGraph {
val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG)
val rootSet = DevirtualizationAnalysis.computeRootSet(context, irModule, moduleDFG, externalModulesDFG)
for (symbol in rootSet) {
val function = moduleDFG.functions[symbol]
if (function == null)
@@ -0,0 +1,110 @@
/*
* 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.optimizations
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.InteropFqNames
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.parentAsClass
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.util.OperatorNameConventions
internal fun dce(
context: Context,
irModule: IrModuleFragment,
moduleDFG: ModuleDFG,
devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
): Set<IrFunction> {
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val callGraph = CallGraphBuilder(
context,
irModule,
moduleDFG,
externalModulesDFG,
devirtualizationAnalysisResult,
// For DCE we don't wanna miss any potentially reachable function.
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
).build()
val referencedFunctions = mutableSetOf<IrFunction>()
callGraph.rootExternalFunctions.forEach {
if (!it.isStaticFieldInitializer)
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
}
for (node in callGraph.directEdges.values) {
if (!node.symbol.isStaticFieldInitializer)
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
node.callSites.forEach {
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}"))
}
}
irModule.acceptChildrenVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
// TODO: Generalize somehow, not that graceful.
if (declaration.name == OperatorNameConventions.INVOKE
&& declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) {
referencedFunctions.add(declaration)
}
super.visitFunction(declaration)
}
override fun visitConstructor(declaration: IrConstructor) {
// TODO: NativePointed is the only inline class for which the field's type and
// the constructor parameter's type are different.
// Thus we need to conserve the constructor no matter if it was actually referenced somehow or not.
// See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why.
if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary)
referencedFunctions.add(declaration)
super.visitConstructor(declaration)
}
})
irModule.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitFile(declaration: IrFile): IrFile {
declaration.declarations.removeAll {
(it is IrFunction && !referencedFunctions.contains(it))
}
return super.visitFile(declaration)
}
override fun visitClass(declaration: IrClass): IrStatement {
if (declaration == context.ir.symbols.nativePointed)
return super.visitClass(declaration)
declaration.declarations.removeAll {
(it is IrFunction && it.isReal && !referencedFunctions.contains(it))
}
return super.visitClass(declaration)
}
override fun visitProperty(declaration: IrProperty): IrStatement {
if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.getter = null
}
if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.setter = null
}
return super.visitProperty(declaration)
}
})
return referencedFunctions
}
@@ -161,7 +161,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null
private val module = DataFlowIR.Module(irModule.descriptor)
private val symbolTable = DataFlowIR.SymbolTable(context, irModule, module)
private val symbolTable = DataFlowIR.SymbolTable(context, module)
// Possible values of a returnable block.
private val returnableBlockValues = mutableMapOf<IrReturnableBlock, MutableList<IrExpression>>()
@@ -172,6 +172,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
private val expressionValuesExtractor = ExpressionValuesExtractor(context, returnableBlockValues, suspendableExpressionValues)
fun build(): ModuleDFG {
symbolTable.populateWith(irModule)
val functions = mutableMapOf<DataFlowIR.FunctionSymbol, DataFlowIR.Function>()
irModule.accept(object : IrElementVisitorVoid {
@@ -548,8 +550,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
values = mutableListOf(),
type = symbolTable.mapType(irVariable.type),
kind = if (catchParameters.contains(irVariable))
DataFlowIR.VariableKind.CatchParameter
else DataFlowIR.VariableKind.Ordinary
DataFlowIR.VariableKind.CatchParameter
else DataFlowIR.VariableKind.Ordinary
)
scope.nodes += node
variables[irVariable] = Scoped(node, scope)
@@ -240,7 +240,7 @@ internal object DataFlowIR {
: Call(constructor, arguments, constructedType, irCallSite)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type, returnType: Type, override val irCallSite: IrCall?)
val receiverType: Type, returnType: Type, override val irCallSite: IrCall?)
: Call(callee, arguments, returnType, irCallSite)
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
@@ -430,7 +430,7 @@ internal object DataFlowIR {
}
}
class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) {
class SymbolTable(val context: Context, val module: Module) {
private val TAKE_NAMES = true // Take fqNames for all functions and types (for debug purposes).
@@ -454,7 +454,7 @@ internal object DataFlowIR {
var privateTypeIndex = 1 // 0 for [Virtual]
var privateFunIndex = 0
init {
fun populateWith(irModule: IrModuleFragment) {
irModule.accept(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
@@ -492,11 +492,11 @@ internal object DataFlowIR {
val placeToClassTable = true
val symbolTableIndex = if (placeToClassTable) module.numberOfClasses++ else -1
val type = if (irClass.isExported())
Type.Public(localHash(name.toByteArray()), privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, irClass, takeName { name })
else
Type.Private(privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, irClass, takeName { name })
Type.Public(localHash(name.toByteArray()), privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, irClass, takeName { name })
else
Type.Private(privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, irClass, takeName { name })
classMap[irClass] = type
@@ -642,8 +642,8 @@ internal object DataFlowIR {
functionMap[it] = symbol
symbol.parameters = function.allParameters.map { it.type }
.map { mapTypeToFunctionParameter(it) }
.toTypedArray()
.map { mapTypeToFunctionParameter(it) }
.toTypedArray()
symbol.returnParameter = mapTypeToFunctionParameter(function.returnType)
return symbol
@@ -55,7 +55,7 @@ internal object DevirtualizationAnalysis {
private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null
fun computeRootSet(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
fun computeRootSet(context: Context, irModule: IrModuleFragment, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
: List<DataFlowIR.FunctionSymbol> {
fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
@@ -97,7 +97,7 @@ internal object DevirtualizationAnalysis {
// At this point all function references are lowered except those leaking to the native world.
// Conservatively assume them belonging of the root set.
val leakingThroughFunctionReferences = mutableListOf<DataFlowIR.FunctionSymbol>()
context.irModule!!.acceptChildrenVoid(object : IrElementVisitorVoid {
irModule.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
@@ -128,6 +128,7 @@ internal object DevirtualizationAnalysis {
private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual].
internal class DevirtualizationAnalysisImpl(val context: Context,
val irModule: IrModuleFragment,
val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG) {
@@ -234,7 +235,21 @@ internal object DevirtualizationAnalysis {
private fun IntArray.getEdge(v: Int, id: Int) = this[this[v] + id]
private fun IntArray.edgeCount(v: Int) = this[v + 1] - this[v]
inner class TypeHierarchy(val allTypes: Array<DataFlowIR.Type.Declared>) {
interface TypeHierarchy {
val allTypes: Array<DataFlowIR.Type.Declared>
fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet
}
object EmptyTypeHierarchy : TypeHierarchy {
override val allTypes: Array<DataFlowIR.Type.Declared> = emptyArray()
override fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet {
return BitSet()
}
}
inner class TypeHierarchyImpl(override val allTypes: Array<DataFlowIR.Type.Declared>) : TypeHierarchy {
private val typesSubTypes = Array(allTypes.size) { mutableListOf<DataFlowIR.Type.Declared>() }
private val allInheritors = Array(allTypes.size) { BitSet() }
@@ -256,7 +271,7 @@ internal object DevirtualizationAnalysis {
allTypes.forEach { processType(it) }
}
fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet {
override fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet {
val typeId = type.index
val inheritors = allInheritors[typeId]
if (!inheritors.isEmpty || type == DataFlowIR.Type.Virtual) return inheritors
@@ -432,8 +447,8 @@ internal object DevirtualizationAnalysis {
val allTypes = Array<DataFlowIR.Type.Declared>(allDeclaredTypes.size) { DataFlowIR.Type.Virtual }
for (type in allDeclaredTypes)
allTypes[type.index] = type
val typeHierarchy = TypeHierarchy(allTypes)
val rootSet = computeRootSet(context, moduleDFG, externalModulesDFG)
val typeHierarchy = TypeHierarchyImpl(allTypes)
val rootSet = computeRootSet(context, irModule, moduleDFG, externalModulesDFG)
val nodesMap = mutableMapOf<DataFlowIR.Node, Node>()
@@ -695,7 +710,7 @@ internal object DevirtualizationAnalysis {
// and by that we're only holding references to two out of three of them.
private fun buildConstraintGraph(nodesMap: MutableMap<DataFlowIR.Node, Node>,
functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
typeHierarchy: TypeHierarchy,
typeHierarchy: TypeHierarchyImpl,
rootSet: List<DataFlowIR.FunctionSymbol>
): ConstraintGraphBuildResult {
val precursor = buildConstraintGraphPrecursor(nodesMap, functions, typeHierarchy, rootSet)
@@ -728,7 +743,7 @@ internal object DevirtualizationAnalysis {
private fun buildConstraintGraphPrecursor(nodesMap: MutableMap<DataFlowIR.Node, Node>,
functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
typeHierarchy: TypeHierarchy,
typeHierarchy: TypeHierarchyImpl,
rootSet: List<DataFlowIR.FunctionSymbol>
): ConstraintGraphPrecursor {
val constraintGraphBuilder = ConstraintGraphBuilder(nodesMap, functions, typeHierarchy, rootSet, true)
@@ -765,7 +780,7 @@ internal object DevirtualizationAnalysis {
private inner class ConstraintGraphBuilder(val functionNodesMap: MutableMap<DataFlowIR.Node, Node>,
val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
val typeHierarchy: TypeHierarchy,
val typeHierarchy: TypeHierarchyImpl,
val rootSet: List<DataFlowIR.FunctionSymbol>,
val useTypes: Boolean) {
@@ -962,9 +977,9 @@ internal object DevirtualizationAnalysis {
symbol.parameters.forEachIndexed { index, type ->
val resolvedType = type.type.resolved()
val node = if (!resolvedType.isFinal)
constraintGraph.virtualNode // TODO: OBJC-INTEROP-GENERATED-CLASSES
else
concreteClass(resolvedType)
constraintGraph.virtualNode // TODO: OBJC-INTEROP-GENERATED-CLASSES
else
concreteClass(resolvedType)
addEdge(node, parameters[index])
}
}
@@ -1308,8 +1323,8 @@ internal object DevirtualizationAnalysis {
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>,
val typeHierarchy: DevirtualizationAnalysisImpl.TypeHierarchy)
fun run(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) =
DevirtualizationAnalysisImpl(context, moduleDFG, externalModulesDFG).analyze()
fun run(context: Context, irModule: IrModuleFragment, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) =
DevirtualizationAnalysisImpl(context, irModule, moduleDFG, externalModulesDFG).analyze()
fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
@@ -1331,8 +1346,8 @@ internal object DevirtualizationAnalysis {
fun <T : IrElement> IrStatementsBuilder<T>.irTemporary(value: IrExpression, tempName: String, type: IrType): IrVariable {
val temporary = IrVariableImpl(
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(),
Name.identifier(tempName), type, isVar = false, isConst = false, isLateinit = false
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(),
Name.identifier(tempName), type, isVar = false, isConst = false, isLateinit = false
).apply {
this.initializer = value
}
@@ -1361,7 +1376,7 @@ internal object DevirtualizationAnalysis {
if (actualType.boxFunction == null && targetType.boxFunction == null) return null
if (actualType.boxFunction != null && targetType.boxFunction != null) {
assert (actualType.type.resolved() == targetType.type.resolved())
{ "Inconsistent types: ${actualType.type} and ${targetType.type}" }
{ "Inconsistent types: ${actualType.type} and ${targetType.type}" }
return null
}
if (actualType.boxFunction == null)
@@ -470,6 +470,7 @@ internal object EscapeAnalysis {
private class InterproceduralAnalysis(
val context: Context,
val generationState: NativeGenerationState,
val callGraph: CallGraph,
val intraproceduralAnalysisResults: Map<DataFlowIR.FunctionSymbol, FunctionAnalysisResult>,
val externalModulesDFG: ExternalModulesDFG,
@@ -729,7 +730,7 @@ internal object EscapeAnalysis {
?: (node as? DataFlowIR.Node.Variable)
?.values?.singleOrNull()?.let { arrayLengthOf(it.node) }
private val pointerSize = context.generationState.runtime.pointerSize
private val pointerSize = generationState.runtime.pointerSize
private fun arrayItemSizeOf(irClass: IrClass): Int? = when (irClass.symbol) {
symbols.array -> pointerSize
@@ -1796,14 +1797,20 @@ internal object EscapeAnalysis {
}
}
fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
callGraph: CallGraph, lifetimes: MutableMap<IrElement, Lifetime>) {
fun computeLifetimes(
context: Context,
generationState: NativeGenerationState,
moduleDFG: ModuleDFG,
externalModulesDFG: ExternalModulesDFG,
callGraph: CallGraph,
lifetimes: MutableMap<IrElement, Lifetime>
) {
assert(lifetimes.isEmpty())
try {
val intraproceduralAnalysisResult =
IntraproceduralAnalysis(context, moduleDFG, externalModulesDFG, callGraph).analyze()
InterproceduralAnalysis(context, callGraph, intraproceduralAnalysisResult, externalModulesDFG, lifetimes,
InterproceduralAnalysis(context, generationState, callGraph, intraproceduralAnalysisResult, externalModulesDFG, lifetimes,
// TODO: This is a bit conservative, but for more aggressive option some support from runtime is
// needed (namely, determining that a pointer is from the stack; this is easy for x86 or x64,
// but what about all other platforms?).
@@ -1811,7 +1818,7 @@ internal object EscapeAnalysis {
).analyze()
} catch (t: Throwable) {
val extraUserInfo =
"""
"""
Please try to disable escape analysis and rerun the build. To do it add the following snippet to the gradle script:
kotlin.targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
@@ -240,7 +240,7 @@ internal object StaticInitializersOptimization {
}
private val IrFunction.calledInitializer get() =
(body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer?
(body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer?
private fun intraproceduralAnalysis(
node: CallGraphNode,
@@ -520,7 +520,7 @@ internal object StaticInitializersOptimization {
}
}
fun removeRedundantCalls(context: Context, callGraph: CallGraph, rootSet: Set<IrFunction>) {
fun removeRedundantCalls(context: Context, irModule: IrModuleFragment, callGraph: CallGraph, rootSet: Set<IrFunction>) {
val analysisResult = InterproceduralAnalysis(context, callGraph, rootSet).analyze()
var numberOfFunctionsWithGlobalInitializerCall = 0
@@ -532,7 +532,7 @@ internal object StaticInitializersOptimization {
var numberOfCallSitesWithExtractedGlobalInitializerCall = 0
var numberOfCallSitesWithExtractedThreadLocalInitializerCall = 0
context.irModule!!.transformChildren(object : IrElementTransformer<IrBuilderWithScope?> {
irModule.transformChildren(object : IrElementTransformer<IrBuilderWithScope?> {
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrBuilderWithScope?): IrStatement {
return super.visitDeclaration(declaration, context.createIrBuilder(declaration.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET))
}
@@ -574,7 +574,7 @@ internal object StaticInitializersOptimization {
}
}, data = null)
context.irModule!!.transformChildrenVoid(object : IrElementTransformerVoid() {
irModule.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitFunction(declaration: IrFunction): IrStatement {
val body = declaration.body ?: return declaration
val statements = (body as IrBlockBody).statements
@@ -328,8 +328,8 @@ object KonanFakeOverrideClassFilter : FakeOverrideClassFilter {
// This is an alternative to .isObjCClass that doesn't need to walk up all the class heirarchy,
// rather it only looks at immediate super class symbols.
private fun IrClass.hasInteropSuperClass() = this.superTypes
.mapNotNull { it.classOrNull }
.any { it.isInterop() }
.mapNotNull { it.classOrNull }
.any { it.isInterop() }
override fun needToConstructFakeOverrides(clazz: IrClass): Boolean {
return !clazz.hasInteropSuperClass() && clazz !is IrLazyClass
@@ -736,7 +736,7 @@ internal class KonanIrLinker(
}
val signature = function.symbol.signature ?: descriptorSignatures[function.descriptor]
?: error("No signature for ${function.render()}")
?: error("No signature for ${function.render()}")
val inlineFunctionReference = inlineFunctionReferences[signature]
?: error("No inline function reference for ${function.render()}, sig = ${signature.render()}")
val fileDeserializationState = fileDeserializationStates[inlineFunctionReference.file]
@@ -924,7 +924,7 @@ internal class KonanIrLinker(
private val fileMap = mutableMapOf<PackageFragmentDescriptor, IrFile>()
private fun getIrFile(packageFragment: PackageFragmentDescriptor): IrFile = fileMap.getOrPut(packageFragment) {
IrFileImpl(NaiveSourceBasedFileEntryImpl(IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName), packageFragment).also {
IrFileImpl(NaiveSourceBasedFileEntryImpl(IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName), packageFragment, moduleFragment).also {
moduleFragment.files.add(it)
}
}