[JS IC] Implement test infra for incremental caches

This commit is contained in:
Roman Artemev
2021-09-14 15:29:37 +07:00
committed by teamcityserver
parent c692b0b2e0
commit 0bfc88fad0
3 changed files with 165 additions and 56 deletions
@@ -5,13 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
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.backend.js.ic.SerializedIcData
import org.jetbrains.kotlin.ir.backend.js.ic.ModuleCache
import org.jetbrains.kotlin.ir.backend.js.ic.icCompile
import org.jetbrains.kotlin.ir.backend.js.lower.generateTests
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
@@ -188,6 +185,14 @@ fun lowerPreservingIcData(module: IrModuleFragment, context: JsIrBackendContext,
controller.currentStage = pirLowerings.size + 1
}
@Suppress("UNUSED_PARAMETER")
fun generateJsFromAst(
mainModule: String,
caches: Map<String, ModuleCache>
): CompilerResult {
TODO(">> EP to generate Js from BinaryAST with root $mainModule")
}
fun generateJsCode(
context: JsIrBackendContext,
moduleFragment: IrModuleFragment,
@@ -195,7 +195,7 @@ abstract class BasicBoxTest(
}
val testModuleName = if (runPlainBoxFunction) null else mainModuleName
val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"")
val icCache = mutableMapOf<String, ICCache>()
val icCache = mutableMapOf<String, TestModuleCache>()
val generatedJsFiles = orderedModules.asReversed().mapNotNull { module ->
val dependencies = module.dependenciesSymbols.map { modules[it]?.outputFileName(outputDir) + ".meta.js" }
@@ -510,7 +510,7 @@ abstract class BasicBoxTest(
skipMangleVerification: Boolean,
abiVersion: KotlinAbiVersion,
checkIC: Boolean,
icCache: MutableMap<String, ICCache>
icCache: MutableMap<String, TestModuleCache>
) {
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
val testFiles = kotlinFiles.map { it.fileName }
@@ -614,7 +614,7 @@ abstract class BasicBoxTest(
testFunction: String,
needsFullIrRuntime: Boolean,
expectActualLinker: Boolean,
icCaches: MutableMap<String, ICCache>
icCaches: MutableMap<String, TestModuleCache>
) {
val sourceToTranslationUnit = hashMapOf<File, TranslationUnit>()
for (testFile in kotlinFiles) {
@@ -756,7 +756,7 @@ abstract class BasicBoxTest(
abiVersion: KotlinAbiVersion,
incrementalCompilation: Boolean,
recompile: Boolean,
icCache: MutableMap<String, ICCache>
icCache: MutableMap<String, TestModuleCache>
) {
val translator = K2JSTranslator(config, false)
val translationResult = translator.translateUnits(ExceptionThrowingReporter, units, mainCallParameters)
@@ -14,16 +14,21 @@ import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.ic.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.js.facade.MainCallParameters
import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.parsing.parseBoolean
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.util.DummyLogger
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
import java.io.File
import java.lang.Boolean.getBoolean
@@ -33,12 +38,106 @@ private val defaultRuntimeKlib = System.getProperty("kotlin.js.reduced.stdlib.pa
private val kotlinTestKLib = System.getProperty("kotlin.js.kotlin.test.path")
// TODO Cache on FS (requires bootstrap)
private val predefinedKlibHasIcCache = mutableMapOf<String, ICCache?>(
private val predefinedKlibHasIcCache = mutableMapOf<String, TestModuleCache?>(
File(fullRuntimeKlib).absolutePath to null,
File(kotlinTestKLib).absolutePath to null,
File(defaultRuntimeKlib).absolutePath to null
)
class TestModuleCache(val moduleName: String, val files: MutableMap<String, FileCache>) {
constructor(moduleName: String) : this(moduleName, mutableMapOf())
fun cacheProvider(): PersistentCacheProvider {
return object : PersistentCacheProvider {
override fun fileFingerPrint(path: String): Hash {
return 0L
}
override fun serializedParts(path: String): SerializedIcDataForFile {
error("Is not supported")
}
override fun inlineGraphForFile(path: String, sigResolver: (Int) -> IdSignature): Collection<Pair<IdSignature, TransHash>> {
error("Is not supported")
}
override fun inlineHashes(path: String, sigResolver: (Int) -> IdSignature): Map<IdSignature, TransHash> {
error("Is not supported")
}
override fun allInlineHashes(sigResolver: (String, Int) -> IdSignature): Map<IdSignature, TransHash> {
error("Is not supported")
}
override fun binaryAst(path: String): ByteArray? {
return files[path]?.ast ?: ByteArray(0)
}
override fun dts(path: String): ByteArray? {
return files[path]?.dts
}
override fun sourceMap(path: String): ByteArray? {
return files[path]?.sourceMap
}
}
}
fun cacheConsumer(): PersistentCacheConsumer {
return object : PersistentCacheConsumer {
override fun commitInlineFunctions(
path: String,
hashes: Collection<Pair<IdSignature, TransHash>>,
sigResolver: (IdSignature) -> Int
) {
}
override fun commitFileFingerPrint(path: String, fingerprint: Hash) {
}
override fun commitInlineGraph(
path: String,
hashes: Collection<Pair<IdSignature, TransHash>>,
sigResolver: (IdSignature) -> Int
) {
}
override fun commitICCacheData(path: String, icData: SerializedIcDataForFile) {
}
override fun commitBinaryAst(path: String, astData: ByteArray) {
val storage = files.getOrPut(path) { FileCache(path, null, null, null) }
storage.ast = astData
}
override fun commitBinaryDts(path: String, dstData: ByteArray) {
val storage = files.getOrPut(path) { FileCache(path, null, null, null) }
storage.dts = dstData
}
override fun commitSourceMap(path: String, mapData: ByteArray) {
val storage = files.getOrPut(path) { FileCache(path, null, null, null) }
storage.sourceMap = mapData
}
override fun invalidateForFile(path: String) {
files.remove(path)
}
override fun commitLibraryPath(libraryPath: String) {
}
}
}
fun createModuleCache(): ModuleCache = ModuleCache(moduleName, files)
}
abstract class BasicIrBoxTest(
pathToTestDir: String,
testGroupOutputDirPrefix: String,
@@ -114,7 +213,7 @@ abstract class BasicIrBoxTest(
abiVersion: KotlinAbiVersion,
incrementalCompilation: Boolean,
recompile: Boolean,
icCache: MutableMap<String, ICCache>
icCache: MutableMap<String, TestModuleCache>
) {
val filesToCompile = units.mapNotNull { (it as? TranslationUnit.SourceFile)?.file }
@@ -133,7 +232,7 @@ abstract class BasicIrBoxTest(
}
if (incrementalCompilationChecksEnabled && incrementalCompilation) {
runtimeKlibs.forEach { createIcCache(it, runtimeKlibs, config, icCache) }
runtimeKlibs.forEach { createIcCache(it, null, runtimeKlibs, config, icCache) }
}
if (!recompile) { // In case of incremental recompilation we only rebuild caches, not klib itself
@@ -157,7 +256,8 @@ abstract class BasicIrBoxTest(
val klibCannonPath = File(klibPath).canonicalPath
if (incrementalCompilation) {
icCache[klibCannonPath] = createIcCache(klibCannonPath, allKlibPaths + klibCannonPath, config, icCache)
icCache[klibCannonPath] =
createIcCache(klibCannonPath, filesToCompile.map { it.virtualFilePath }, allKlibPaths + klibCannonPath, config, icCache)
}
compilationCache[outputFile.name.replace(".js", ".meta.js")] = actualOutputFile
@@ -190,7 +290,7 @@ abstract class BasicIrBoxTest(
emptyList(),
icUseGlobalSignatures = runIcMode,
icUseStdlibCache = runIcMode,
icCache = icCache
icCache = emptyMap()
)
if (!skipRegularMode) {
@@ -212,6 +312,11 @@ abstract class BasicIrBoxTest(
verifySignatures = !skipMangleVerification,
)
// if (incrementalCompilation) {
// // TODO: enable once incremental js generation is done
// generateJsFromAst(klibPath, icCache.map { it.key to it.value.createModuleCache() }.toMap())
// }
val jsOutputFile = if (recompile) File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
else outputFile
@@ -268,7 +373,7 @@ abstract class BasicIrBoxTest(
testFunction: String,
needsFullIrRuntime: Boolean,
expectActualLinker: Boolean,
icCaches: MutableMap<String, ICCache>
icCaches: MutableMap<String, TestModuleCache>
) {
val isMainModule = module.name == DEFAULT_MODULE || module.name == TEST_MODULE
val cacheKey = outputFile.canonicalPath.replace("_v5.js", "")
@@ -278,11 +383,14 @@ abstract class BasicIrBoxTest(
val dirtyFiles = mutableListOf<String>()
val oldBinaryAsts = mutableMapOf<String, ByteArray>()
val dataProvider = icCache.cacheProvider()
val dataConsumer = icCache.cacheConsumer()
for (testFile in kotlinFiles) {
if (testFile.recompile) {
val fileName = testFile.fileName
oldBinaryAsts[fileName] = icCache.dataProvider.binaryAst(fileName)
icCache.dataConsumer.invalidateForFile(fileName)
oldBinaryAsts[fileName] = dataProvider.binaryAst(fileName) ?: error("No AST found for $fileName")
dataConsumer.invalidateForFile(fileName)
dirtyFiles.add(fileName)
}
}
@@ -330,7 +438,8 @@ abstract class BasicIrBoxTest(
icCaches
)
val newBinaryAsts = dirtyFiles.associateWith { icCache.dataProvider.binaryAst(it) }
val cacheProvider = icCache.cacheProvider()
val newBinaryAsts = dirtyFiles.associateWith { cacheProvider.binaryAst(it) }
for (file in dirtyFiles) {
val oldBinaryAst = oldBinaryAsts[file]
@@ -346,49 +455,44 @@ abstract class BasicIrBoxTest(
}
}
private fun createPirCache(path: String, allKlibPaths: Collection<String>, config: JsConfig, icCache: Map<String, ICCache>): ICCache {
val icData = predefinedKlibHasIcCache[path] ?: prepareSingleLibraryIcCache(
project = project,
configuration = config.configuration,
libPath = path,
dependencies = allKlibPaths,
icCache = icCache
)
private fun createIcCache(
path: String,
dirtyFiles: Collection<String>?,
allKlibPaths: Collection<String>,
config: JsConfig,
icCache: MutableMap<String, TestModuleCache>
): TestModuleCache {
if (path in predefinedKlibHasIcCache) {
predefinedKlibHasIcCache[path] = icData
val cannonicalPath = File(path).canonicalPath
var moduleCache = predefinedKlibHasIcCache[cannonicalPath]
if (moduleCache == null) {
moduleCache = icCache[cannonicalPath] ?: TestModuleCache(cannonicalPath)
val allResolvedDependencies = jsResolveLibraries(allKlibPaths, emptyList(), DummyLogger)
val libs = allResolvedDependencies.getFullList().associateBy { File(it.libraryFile.path).canonicalPath }
val nameToKotlinLibrary: Map<ModuleName, KotlinLibrary> = libs.values.associateBy { it.moduleName }
val dependencyGraph = libs.values.associateWith {
it.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { depName ->
nameToKotlinLibrary[depName] ?: error("No Library found for $depName")
}
}
val currentLib = libs[File(cannonicalPath).canonicalPath] ?: error("Expected library at $cannonicalPath")
rebuildCacheForDirtyFiles(currentLib, config.configuration, dependencyGraph, dirtyFiles, moduleCache.cacheConsumer())
if (cannonicalPath in predefinedKlibHasIcCache) {
predefinedKlibHasIcCache[cannonicalPath] = moduleCache
}
}
return icData
}
icCache[cannonicalPath] = moduleCache
@Suppress("UNUSED_PARAMETER")
private fun createIcCache(path: String, allKlibPaths: Collection<String>, config: JsConfig, icCache: Map<String, ICCache>): ICCache {
fun prepare(): ICCache {
return ICCache(PersistentCacheProvider.EMPTY, PersistentCacheConsumer.EMPTY, SerializedIcData(emptyList()))
}
val icData = predefinedKlibHasIcCache[path] ?: prepare()
if (path in predefinedKlibHasIcCache) {
predefinedKlibHasIcCache[path] = icData
}
return icData
}
private fun prepareRuntimePirCaches(config: JsConfig, icCache: MutableMap<String, ICCache>) {
if (!runIcMode) return
val defaultRuntimePath = File(defaultRuntimeKlib).canonicalPath
icCache[defaultRuntimePath] = createPirCache(defaultRuntimePath, listOf(defaultRuntimePath), config, icCache)
val fullRuntimePath = File(fullRuntimeKlib).canonicalPath
icCache[fullRuntimePath] = createPirCache(fullRuntimePath, listOf(fullRuntimePath), config, icCache)
val testKlibPath = File(kotlinTestKLib).canonicalPath
icCache[testKlibPath] = createPirCache(testKlibPath, listOf(fullRuntimePath, testKlibPath), config, icCache)
return moduleCache
}
private fun fromSysPropertyOrAll(key: String, all: Set<AnyNamedPhase>): Set<AnyNamedPhase> {