[K/N] Port to the new driver
This commit is contained in:
committed by
Space Team
parent
21e57b1c54
commit
576e8ba127
+23
-18
@@ -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 {
|
||||
|
||||
+53
@@ -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))
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
|
||||
+6
-6
@@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
+17
-105
@@ -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() {}
|
||||
}
|
||||
|
||||
+14
-3
@@ -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>
|
||||
)
|
||||
-6
@@ -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())
|
||||
|
||||
|
||||
+2
-12
@@ -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()
|
||||
|
||||
+1
-3
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
+11
-117
@@ -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"
|
||||
)
|
||||
+44
-72
@@ -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
|
||||
|
||||
-29
@@ -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
|
||||
|
||||
|
||||
+19
-15
@@ -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,
|
||||
|
||||
+13
-10
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -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()
|
||||
}
|
||||
}
|
||||
+20
-252
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+17
-8
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -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)
|
||||
|
||||
+35
-4
@@ -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))
|
||||
}
|
||||
+122
-5
@@ -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)
|
||||
}
|
||||
}
|
||||
+65
@@ -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)
|
||||
}
|
||||
)
|
||||
+47
@@ -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)
|
||||
}
|
||||
-4
@@ -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)
|
||||
}
|
||||
+179
@@ -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)
|
||||
}
|
||||
)
|
||||
+28
@@ -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)
|
||||
}
|
||||
+246
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
+24
@@ -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)
|
||||
}
|
||||
+25
-9
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -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 {
|
||||
|
||||
+3
-3
@@ -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)
|
||||
|
||||
+124
-54
@@ -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 })
|
||||
}
|
||||
+64
@@ -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)
|
||||
}
|
||||
+1
-3
@@ -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,
|
||||
|
||||
-338
@@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
+22
-11
@@ -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
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+8
-5
@@ -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)
|
||||
|
||||
+6
-3
@@ -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) =
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+3
-1
@@ -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)
|
||||
|
||||
+110
@@ -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
|
||||
}
|
||||
|
||||
+5
-3
@@ -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)
|
||||
|
||||
+10
-10
@@ -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
|
||||
|
||||
+32
-17
@@ -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)
|
||||
|
||||
+12
-5
@@ -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> {
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
+4
-4
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user