From eb0c13793b92cd36ea3d18ccabd07f47094fa33d Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Fri, 18 Jun 2021 14:25:01 +0300 Subject: [PATCH] JS IR: introduce 'lower per module' mode This mode is closer to how IC supposed to work - reusing work from dependencies, not re-lowering them. --- .../backend/js/WholeWorldStageController.kt | 65 +++++++++++++++++++ .../kotlin/ir/backend/js/compiler.kt | 32 ++++++++- .../AbstractSuspendFunctionsLowering.kt | 7 ++ .../kotlin/js/test/BasicIrBoxTest.kt | 10 ++- 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/WholeWorldStageController.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/WholeWorldStageController.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/WholeWorldStageController.kt new file mode 100644 index 00000000000..d4bb8fcb880 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/WholeWorldStageController.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2021 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 + +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.StageController + +// Only allows to apply a lowering to the whole world and save the result +class WholeWorldStageController : StageController() { + override var currentStage: Int = 0 + + // TODO assert lowered + + var declarationListsRestricted: Boolean = false + + override fun withStage(stage: Int, fn: () -> T): T { + val oldStage = currentStage + currentStage = stage + return try { + fn() + } finally { + currentStage = oldStage + } + } + + override fun withInitialIr(block: () -> T): T { + return withStage(0) { + declarationRestriction(true, block) + } + } + + override fun bodyLowering(fn: () -> T): T { + val wasRestricted = declarationListsRestricted + declarationListsRestricted = true + + return try { + fn() + } finally { + declarationListsRestricted = wasRestricted + } + } + + override fun unrestrictDeclarationListsAccess(fn: () -> T): T { + return declarationRestriction(false, fn) + } + + private fun declarationRestriction(value: Boolean, fn: () -> T): T{ + val wasRestricted = declarationListsRestricted + declarationListsRestricted = value + + return try { + fn() + } finally { + declarationListsRestricted = wasRestricted + } + } + + override fun canAccessDeclarationsOf(irClass: IrClass): Boolean { + return !declarationListsRestricted || irClass.visibility == DescriptorVisibilities.LOCAL/* && irClass !in context.extractedLocalClasses*/ + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index b76ae160a2b..e4c0391d91b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -7,6 +7,7 @@ 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 @@ -54,6 +55,7 @@ fun compile( propertyLazyInitialization: Boolean, legacyPropertyAccess: Boolean = false, baseClassIntoMetadata: Boolean = false, + lowerPerModule: Boolean = false, ): CompilerResult { val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer, moduleToName) = loadIr(project, mainModule, analyzer, configuration, allDependencies, friendDependencies, irFactory) @@ -116,7 +118,17 @@ fun compile( ) return transformer.generateModule(allModules) } else { - jsPhases.invokeToplevel(phaseConfig, context, allModules) + if (lowerPerModule) { + val controller = WholeWorldStageController() + check(irFactory is PersistentIrFactory) + irFactory.stageController = controller + allModules.forEach { + lowerPreservingIcData(it, context, controller) + } + } else { + jsPhases.invokeToplevel(phaseConfig, context, allModules) + } + val transformer = IrModuleToJsTransformer( context, mainArguments, @@ -130,6 +142,24 @@ fun compile( } } +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 +} + fun generateJsCode( context: JsIrBackendContext, moduleFragment: IrModuleFragment, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AbstractSuspendFunctionsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AbstractSuspendFunctionsLowering.kt index fd0e3916c67..27c79e0fd03 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AbstractSuspendFunctionsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AbstractSuspendFunctionsLowering.kt @@ -439,6 +439,13 @@ abstract class AbstractSuspendFunctionsLowering(val co coroutineClass.addFakeOverrides(context.irBuiltIns, implementedMembers) + // TODO constructing fake overrides on lowered declaration is tricky. + coroutineClass.declarations.transformFlat { + if (it is IrProperty && it.isFakeOverride) { + listOfNotNull(it.getter, it.setter) + } else null + } + // TODO: find out whether Kotlin/Native needs this call initializeStateMachine(listOf(coroutineConstructor), coroutineClassThis) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index 75b2ed85127..4f3e071edb1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -49,11 +49,13 @@ abstract class BasicIrBoxTest( private fun getBoolean(s: String, default: Boolean) = System.getProperty(s)?.let { parseBoolean(it) } ?: default + private val lowerPerModule: Boolean = getBoolean("kotlin.js.ir.lowerPerModule") + override val skipRegularMode: Boolean = getBoolean("kotlin.js.ir.skipRegularMode") - override val runIrDce: Boolean = getBoolean("kotlin.js.ir.dce", true) + override val runIrDce: Boolean = !lowerPerModule && getBoolean("kotlin.js.ir.dce", true) - override val runIrPir: Boolean = getBoolean("kotlin.js.ir.pir", true) + override val runIrPir: Boolean = !lowerPerModule && getBoolean("kotlin.js.ir.pir", true) val runEs6Mode: Boolean = getBoolean("kotlin.js.ir.es6", false) @@ -131,13 +133,14 @@ abstract class BasicIrBoxTest( } if (!skipRegularMode) { + val irFactory = if (lowerPerModule) PersistentIrFactory() else IrFactoryImpl val compiledModule = compile( project = config.project, mainModule = MainModule.SourceFiles(filesToCompile), analyzer = AnalyzerWithCompilerReport(config.configuration), configuration = config.configuration, phaseConfig = phaseConfig, - irFactory = IrFactoryImpl, + irFactory = irFactory, allDependencies = resolvedLibraries, friendDependencies = emptyList(), mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }, @@ -147,6 +150,7 @@ abstract class BasicIrBoxTest( es6mode = runEs6Mode, multiModule = splitPerModule || perModule, propertyLazyInitialization = propertyLazyInitialization, + lowerPerModule = lowerPerModule ) compiledModule.jsCode!!.writeTo(outputFile, config)