[JS IR] Cleanup pir IC code

This commit is contained in:
Roman Artemev
2021-12-20 22:05:26 +03:00
committed by teamcity
parent 287c23269a
commit 353bfe9018
21 changed files with 27 additions and 1488 deletions
@@ -5,13 +5,11 @@
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.ic.icCompile
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
@@ -20,8 +18,6 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.StageController
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.noUnboundLeft
@@ -53,13 +49,11 @@ fun compile(
phaseConfig: PhaseConfig,
irFactory: IrFactory,
exportedDeclarations: Set<FqName> = emptySet(),
dceDriven: Boolean = false,
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
propertyLazyInitialization: Boolean,
verifySignatures: Boolean = true,
baseClassIntoMetadata: Boolean = false,
lowerPerModule: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
filesToLower: Set<String>? = null,
@@ -67,20 +61,7 @@ fun compile(
icCompatibleIr2Js: Boolean = false,
): LoweredIr {
if (lowerPerModule) {
return icCompile(
depsDescriptors,
exportedDeclarations,
dceRuntimeDiagnostic,
es6mode,
propertyLazyInitialization,
baseClassIntoMetadata,
safeExternalBoolean,
safeExternalBooleanDiagnostic,
)
}
val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer, _) =
val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
loadIr(depsDescriptors, irFactory, verifySignatures, filesToLower, loadFunctionInterfacesIntoStdlib = true)
return compileIr(
@@ -93,12 +74,10 @@ fun compile(
deserializer,
phaseConfig,
exportedDeclarations,
dceDriven,
dceRuntimeDiagnostic,
es6mode,
propertyLazyInitialization,
baseClassIntoMetadata,
lowerPerModule,
safeExternalBoolean,
safeExternalBooleanDiagnostic,
granularity,
@@ -116,12 +95,10 @@ fun compileIr(
deserializer: JsIrLinker,
phaseConfig: PhaseConfig,
exportedDeclarations: Set<FqName>,
dceDriven: Boolean,
dceRuntimeDiagnostic: RuntimeDiagnostic?,
es6mode: Boolean,
propertyLazyInitialization: Boolean,
baseClassIntoMetadata: Boolean,
lowerPerModule: Boolean,
safeExternalBoolean: Boolean,
safeExternalBooleanDiagnostic: RuntimeDiagnostic?,
granularity: JsGenerationGranularity,
@@ -170,55 +147,13 @@ fun compileIr(
// TODO should be done incrementally
generateJsTests(context, allModules.last())
if (dceDriven) {
val controller = MutableController(context, pirLowerings)
check(irFactory is PersistentIrFactory)
irFactory.stageController = controller
controller.currentStage = controller.lowerings.size + 1
eliminateDeadDeclarations(allModules, context)
irFactory.stageController = StageController(controller.currentStage)
} else {
// TODO is this reachable when lowerPerModule == true?
if (lowerPerModule) {
val controller = WholeWorldStageController()
check(irFactory is PersistentIrFactory)
irFactory.stageController = controller
allModules.forEach {
lowerPreservingIcData(it, context, controller)
}
irFactory.stageController = object : StageController(irFactory.stageController.currentStage) {}
} else {
(irFactory.stageController as? WholeWorldStageController)?.let {
lowerPreservingTags(allModules, context, phaseConfig, it)
} ?: jsPhases.invokeToplevel(phaseConfig, context, allModules)
}
}
(irFactory.stageController as? WholeWorldStageController)?.let {
lowerPreservingTags(allModules, context, phaseConfig, it)
} ?: jsPhases.invokeToplevel(phaseConfig, context, allModules)
return LoweredIr(context, moduleFragment, allModules)
}
fun lowerPreservingIcData(module: IrModuleFragment, context: JsIrBackendContext, controller: WholeWorldStageController) {
// Lower all the things
controller.currentStage = 0
pirLowerings.forEachIndexed { i, lowering ->
controller.currentStage = i + 1
when (lowering) {
is DeclarationLowering ->
lowering.declarationTransformer(context).lower(module)
is BodyLowering ->
lowering.bodyLowering(context).lower(module)
is ModuleLowering -> { /*TODO what about other lowerings? */ }
}
}
controller.currentStage = pirLowerings.size + 1
}
fun generateJsCode(
context: JsIrBackendContext,
moduleFragment: IrModuleFragment,
@@ -5,123 +5,8 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.MainModule
import org.jetbrains.kotlin.ir.backend.js.toByteArray
import org.jetbrains.kotlin.name.FqName
import java.io.File
import java.io.PrintWriter
import java.security.MessageDigest
import kotlin.random.Random
import kotlin.random.nextULong
// TODO: Proper version of the compiler (should take changes to lowerings into account)
private val compilerVersion = Random.nextULong()
private fun IcCacheInfo.toICCacheMap(): Map<String, ICCache> {
return data.map { it.key to ICCache(PersistentCacheProvider.EMPTY, PersistentCacheConsumer.EMPTY, it.value) }.toMap()
}
// TODO more parameters for lowerings
// Returns true if caches were built. False if caches were up-to-date.
fun buildCache(
cachePath: String,
project: Project,
mainModule: MainModule.Klib,
configuration: CompilerConfiguration,
dependencies: Collection<String>,
friendDependencies: Collection<String>,
exportedDeclarations: Set<FqName> = emptySet(),
forceClean: Boolean = false,
icCache: IcCacheInfo = IcCacheInfo.EMPTY,
): Boolean {
val dependencyHashes = dependencies.mapNotNull {
val path = File(it).canonicalPath
icCache.md5[path]
} + compilerVersion
val md5 = File(mainModule.libPath).md5(dependencyHashes)
if (!forceClean) {
val oldCacheInfo = CacheInfo.load(cachePath)
if (oldCacheInfo != null && md5 == oldCacheInfo.flatHash) return false
}
val icDir = File(cachePath)
icDir.listFiles { file: File -> file.name.startsWith("ic-") }!!.forEach { it.deleteRecursively() }
File(icDir, "info").delete()
icDir.mkdirs()
val icData = prepareSingleLibraryIcCache(project, configuration, mainModule.libPath, dependencies, friendDependencies, exportedDeclarations, icCache.toICCacheMap())
icData.serializedIcData.writeTo(File(cachePath))
CacheInfo(cachePath, mainModule.libPath, md5, 0UL).save()
return true
}
private fun File.md5(additional: Iterable<ULong> = emptyList()): ULong {
val md5 = MessageDigest.getInstance("MD5")
for (ul in additional) {
md5.update(ul.toLong().toByteArray())
}
fun File.process(prefix: String = "") {
if (isDirectory) {
this.listFiles()!!.sortedBy { it.name }.forEach {
md5.update((prefix + it.name).toByteArray())
it.process(prefix + it.name + "/")
}
} else {
md5.update(readBytes())
}
}
this.process()
val d = md5.digest()
return ((d[0].toULong() and 0xFFUL)
or ((d[1].toULong() and 0xFFUL) shl 8)
or ((d[2].toULong() and 0xFFUL) shl 16)
or ((d[3].toULong() and 0xFFUL) shl 24)
or ((d[4].toULong() and 0xFFUL) shl 32)
or ((d[5].toULong() and 0xFFUL) shl 40)
or ((d[6].toULong() and 0xFFUL) shl 48)
or ((d[7].toULong() and 0xFFUL) shl 56)
)
}
fun checkCaches(
dependencies: Collection<String>,
cachePaths: List<String>,
skipLib: String? = null,
): IcCacheInfo {
val skipLibPath = File(skipLib).canonicalPath
val allLibs = dependencies.map { File(it).canonicalPath }.toSet() - skipLibPath
val caches = cachePaths.map { CacheInfo.load(it) ?: error("Cannot load IC cache from ${it}") }
val missedLibs = allLibs - caches.map { it.libPath }
if (!missedLibs.isEmpty()) {
error("Missing caches for libraries: ${missedLibs}")
}
val result = mutableMapOf<String, SerializedIcData>()
val md5 = mutableMapOf<String, ULong>()
for (c in caches) {
if (c.libPath !in allLibs) error("Missing library: ${c.libPath}")
result[c.libPath] = File(c.path).readIcData()
md5[c.libPath] = c.flatHash
}
return IcCacheInfo(result, md5)
}
// TODO md5 hash
data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong, var transHash: ULong) {
@@ -145,13 +30,3 @@ data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong,
}
}
}
class IcCacheInfo(
val data: Map<String, SerializedIcData>,
val md5: Map<String, ULong>,
) {
companion object {
val EMPTY = IcCacheInfo(emptyMap(), emptyMap())
}
}
@@ -1,242 +0,0 @@
/*
* Copyright 2010-2020 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.ir.backend.js.ic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.KotlinLikeDumpOptions
import org.jetbrains.kotlin.ir.util.dumpKotlinLike
import org.jetbrains.kotlin.ir.util.noUnboundLeft
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.library.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.name.FqName
import java.io.PrintWriter
fun prepareSingleLibraryIcCache(
project: Project,
configuration: CompilerConfiguration,
libPath: String,
dependencies: Collection<String>,
friendDependencies: Collection<String> = emptyList(),
exportedDeclarations: Set<FqName> = emptySet(),
icCache: Map<String, ICCache> = emptyMap(),
): ICCache {
val irFactory = PersistentIrFactory()
val controller = WholeWorldStageController()
irFactory.stageController = controller
val depsDescriptor = ModulesStructure(
project,
MainModule.Klib(libPath),
configuration,
dependencies,
friendDependencies,
true,
true,
icCache
)
val (context, deserializer, allModules) = prepareIr(
depsDescriptor,
exportedDeclarations,
null,
false,
false,
irFactory,
)
val moduleFragment = allModules.last()
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
// generateTests(context, moduleFragment)
lowerPreservingIcData(moduleFragment, context, controller)
return ICCache(
PersistentCacheProvider.EMPTY,
PersistentCacheConsumer.EMPTY,
IcSerializer(
context.irBuiltIns,
context.mapping,
irFactory,
deserializer,
moduleFragment
).serializeDeclarations(irFactory.allDeclarations)
)
}
private fun KotlinResolvedLibrary.allDependencies(): List<KotlinResolvedLibrary> {
val visited = mutableSetOf<KotlinResolvedLibrary>()
val result = mutableListOf<KotlinResolvedLibrary>()
fun KotlinResolvedLibrary.dfs() {
visited += this
resolvedDependencies.forEach {
if (it !in visited) {
it.dfs()
result += it
}
}
}
dfs()
return result
}
private fun dumpIr(module: IrModuleFragment, fileName: String) {
val dumpOptions = KotlinLikeDumpOptions(printElseAsTrue = true)
var actual = ""
for (file in module.files) {
actual += file.path + "\n"
actual += run {
var r = ""
file.declarations.map { it.dumpKotlinLike(dumpOptions) }.sorted().forEach { r += it }
r
}
actual += "\n"
}
PrintWriter("/home/ab/vcs/kotlin/$fileName.txt").use {
it.print(actual)
}
}
fun icCompile(
depsDescriptor: ModulesStructure,
exportedDeclarations: Set<FqName> = emptySet(),
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
propertyLazyInitialization: Boolean,
baseClassIntoMetadata: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
): LoweredIr {
val irFactory = PersistentIrFactory()
val controller = WholeWorldStageController()
irFactory.stageController = controller
val (context, _, allModules, _, loweredIrLoaded) = prepareIr(
depsDescriptor,
exportedDeclarations,
dceRuntimeDiagnostic,
es6mode,
propertyLazyInitialization,
irFactory,
baseClassIntoMetadata,
safeExternalBoolean,
safeExternalBooleanDiagnostic
)
val modulesToLower = allModules.filter { it !in loweredIrLoaded }
if (!modulesToLower.isEmpty()) {
// This won't work incrementally
modulesToLower.forEach { module ->
moveBodilessDeclarationsToSeparatePlace(context, module)
}
generateJsTests(context, modulesToLower.last())
modulesToLower.forEach {
lowerPreservingIcData(it, context, controller)
}
}
// dumpIr(allModules.first(), "simple-dump${if (useStdlibCache) "-actual" else ""}")
return LoweredIr(context, allModules.last(), allModules)
}
fun lowerPreservingIcData(module: IrModuleFragment, context: JsIrBackendContext, controller: WholeWorldStageController) {
// Lower all the things
controller.currentStage = 0
pirLowerings.forEachIndexed { i, lowering ->
controller.currentStage = i + 1
when (lowering) {
is DeclarationLowering ->
lowering.declarationTransformer(context).lower(module)
is BodyLowering ->
lowering.bodyLowering(context).lower(module)
else -> TODO("what about other lowerings?")
}
}
controller.currentStage = pirLowerings.size + 1
}
private fun prepareIr(
depsDescriptor: ModulesStructure,
exportedDeclarations: Set<FqName> = emptySet(),
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
propertyLazyInitialization: Boolean,
irFactory: PersistentIrFactory,
baseClassIntoMetadata: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
): PreparedIr {
val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer, moduleToName, loweredIrLoaded) =
loadIr(depsDescriptor, irFactory, false)
val moduleDescriptor = moduleFragment.descriptor
val allModules = when (depsDescriptor.mainModule) {
is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment)
is MainModule.Klib -> dependencyModules
}
val context = JsIrBackendContext(
moduleDescriptor,
irBuiltIns,
symbolTable,
allModules.first(),
exportedDeclarations,
depsDescriptor.compilerConfiguration,
es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic,
propertyLazyInitialization = propertyLazyInitialization,
baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
mapping = deserializer.mapping,
)
// Load declarations referenced during `context` initialization
val irProviders = listOf(deserializer)
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
deserializer.postProcess()
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
deserializer.loadIcIr { moveBodilessDeclarationsToSeparatePlace(context, it) }
return PreparedIr(context, deserializer, allModules, moduleToName, loweredIrLoaded)
}
data class PreparedIr(
val context: JsIrBackendContext,
val linker: JsIrLinker,
val allModules: List<IrModuleFragment>,
val moduleToName: Map<IrModuleFragment, String>,
val loweredIrLoaded: Set<IrModuleFragment>,
)
@@ -252,7 +252,7 @@ private fun createLinker(
val moduleDescriptor = loadedModules.keys.last()
val typeTranslator = TypeTranslatorImpl(symbolTable, configuration.languageVersionSettings, moduleDescriptor)
val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
return JsIrLinker(null, logger, irBuiltIns, symbolTable, null, null)
return JsIrLinker(null, logger, irBuiltIns, symbolTable, null)
}