JVM IR: introduce CodegenFactory.convertToIr

The steps of psi2ir and JVM backend need to be separated in the API
because in case of cyclic module dependencies (which are allowed in JPS)
psi2ir should be run first on all sources, and then JVM backend on each
module separately. `CodegenFactory.convertToIr` does nothing in the old
backend.

Also, move the ignoreErrors to GenerationState for simplicity.
This commit is contained in:
Alexander Udalov
2021-09-13 23:42:32 +02:00
parent bfeb9c219d
commit 5d6ca27c93
9 changed files with 79 additions and 38 deletions
@@ -16,15 +16,43 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import com.intellij.openapi.project.Project
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
interface CodegenFactory { interface CodegenFactory {
fun generateModule(state: GenerationState, files: Collection<KtFile>) fun convertToIr(input: IrConversionInput): BackendInput
fun generateModule(state: GenerationState, input: BackendInput)
class IrConversionInput(
val project: Project,
val files: Collection<KtFile>,
val configuration: CompilerConfiguration,
val module: ModuleDescriptor,
val bindingContext: BindingContext,
val languageVersionSettings: LanguageVersionSettings,
val ignoreErrors: Boolean,
) {
companion object {
fun fromGenerationState(state: GenerationState): IrConversionInput =
with(state) {
IrConversionInput(
project, files, configuration, module, originalFrontendBindingContext, languageVersionSettings, ignoreErrors
)
}
}
}
interface BackendInput
companion object { companion object {
fun doCheckCancelled(state: GenerationState) { fun doCheckCancelled(state: GenerationState) {
@@ -36,11 +64,15 @@ interface CodegenFactory {
} }
object DefaultCodegenFactory : CodegenFactory { object DefaultCodegenFactory : CodegenFactory {
override fun generateModule(state: GenerationState, files: Collection<KtFile>) { object DummyOldBackendInput : CodegenFactory.BackendInput
override fun convertToIr(input: CodegenFactory.IrConversionInput): CodegenFactory.BackendInput = DummyOldBackendInput
override fun generateModule(state: GenerationState, input: CodegenFactory.BackendInput) {
val filesInPackages = MultiMap<FqName, KtFile>() val filesInPackages = MultiMap<FqName, KtFile>()
val filesInMultifileClasses = MultiMap<FqName, KtFile>() val filesInMultifileClasses = MultiMap<FqName, KtFile>()
for (file in files) { for (file in state.files) {
val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file) val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
if (fileClassInfo.withJvmMultifileClass) { if (fileClassInfo.withJvmMultifileClass) {
@@ -32,7 +32,12 @@ public class KotlinCodegenFacade {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
state.getCodegenFactory().generateModule(state, state.getFiles()); CodegenFactory.IrConversionInput psi2irInput = CodegenFactory.IrConversionInput.Companion.fromGenerationState(state);
CodegenFactory.BackendInput backendInput = state.getCodegenFactory().convertToIr(psi2irInput);
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
state.getCodegenFactory().generateModule(state, backendInput);
CodegenFactory.Companion.doCheckCancelled(state); CodegenFactory.Companion.doCheckCancelled(state);
state.getFactory().done(); state.getFactory().done();
@@ -67,9 +67,9 @@ class GenerationState private constructor(
private val onIndependentPartCompilationEnd: GenerationStateEventCallback, private val onIndependentPartCompilationEnd: GenerationStateEventCallback,
wantsDiagnostics: Boolean, wantsDiagnostics: Boolean,
val jvmBackendClassResolver: JvmBackendClassResolver, val jvmBackendClassResolver: JvmBackendClassResolver,
val isIrBackend: Boolean val isIrBackend: Boolean,
val ignoreErrors: Boolean,
) { ) {
class Builder( class Builder(
private val project: Project, private val project: Project,
private val builderFactory: ClassBuilderFactory, private val builderFactory: ClassBuilderFactory,
@@ -118,12 +118,16 @@ class GenerationState private constructor(
fun isIrBackend(v: Boolean) = fun isIrBackend(v: Boolean) =
apply { isIrBackend = v } apply { isIrBackend = v }
var ignoreErrors: Boolean = false
fun ignoreErrors(v: Boolean): Builder =
apply { ignoreErrors = v }
fun build() = fun build() =
GenerationState( GenerationState(
project, builderFactory, module, bindingContext, files, configuration, project, builderFactory, module, bindingContext, files, configuration,
generateDeclaredClassFilter, codegenFactory, targetId, generateDeclaredClassFilter, codegenFactory, targetId,
moduleName, outDirectory, onIndependentPartCompilationEnd, wantsDiagnostics, moduleName, outDirectory, onIndependentPartCompilationEnd, wantsDiagnostics,
jvmBackendClassResolver, isIrBackend jvmBackendClassResolver, isIrBackend, ignoreErrors
) )
} }
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.linkage.IrProvider import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
@@ -45,7 +44,6 @@ open class JvmIrCodegenFactory(
private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration), private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration),
) : CodegenFactory { ) : CodegenFactory {
data class JvmIrBackendInput( data class JvmIrBackendInput(
val state: GenerationState,
val irModuleFragment: IrModuleFragment, val irModuleFragment: IrModuleFragment,
val symbolTable: SymbolTable, val symbolTable: SymbolTable,
val phaseConfig: PhaseConfig?, val phaseConfig: PhaseConfig?,
@@ -53,26 +51,20 @@ open class JvmIrCodegenFactory(
val extensions: JvmGeneratorExtensionsImpl, val extensions: JvmGeneratorExtensionsImpl,
val backendExtension: JvmBackendExtension, val backendExtension: JvmBackendExtension,
val notifyCodegenStart: () -> Unit, val notifyCodegenStart: () -> Unit,
) ) : CodegenFactory.BackendInput
override fun generateModule(state: GenerationState, files: Collection<KtFile>) { override fun convertToIr(input: CodegenFactory.IrConversionInput): JvmIrBackendInput {
val input = convertToIr(state, files)
doGenerateFilesInternal(input)
}
@JvmOverloads
fun convertToIr(state: GenerationState, files: Collection<KtFile>, ignoreErrors: Boolean = false): JvmIrBackendInput {
val (mangler, symbolTable) = val (mangler, symbolTable) =
if (externalSymbolTable != null) externalMangler!! to externalSymbolTable if (externalSymbolTable != null) externalMangler!! to externalSymbolTable
else { else {
val mangler = JvmDescriptorMangler(MainFunctionDetector(state.bindingContext, state.languageVersionSettings)) val mangler = JvmDescriptorMangler(MainFunctionDetector(input.bindingContext, input.languageVersionSettings))
val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl, JvmNameProvider) val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl, JvmNameProvider)
mangler to symbolTable mangler to symbolTable
} }
val psi2ir = Psi2IrTranslator(state.languageVersionSettings, Psi2IrConfiguration(ignoreErrors)) val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors))
val messageLogger = state.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None val messageLogger = input.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None
val psi2irContext = psi2ir.createGeneratorContext(state.module, state.bindingContext, symbolTable, jvmGeneratorExtensions) val psi2irContext = psi2ir.createGeneratorContext(input.module, input.bindingContext, symbolTable, jvmGeneratorExtensions)
val pluginExtensions = IrGenerationExtension.getInstances(state.project) val pluginExtensions = IrGenerationExtension.getInstances(input.project)
val stubGenerator = val stubGenerator =
DeclarationStubGeneratorImpl(psi2irContext.moduleDescriptor, symbolTable, psi2irContext.irBuiltIns, jvmGeneratorExtensions) DeclarationStubGeneratorImpl(psi2irContext.moduleDescriptor, symbolTable, psi2irContext.irBuiltIns, jvmGeneratorExtensions)
@@ -111,7 +103,7 @@ open class JvmIrCodegenFactory(
} }
} }
SourceDeclarationsPreprocessor(psi2irContext).run(files) SourceDeclarationsPreprocessor(psi2irContext).run(input.files)
for (extension in pluginExtensions) { for (extension in pluginExtensions) {
psi2ir.addPostprocessingStep { module -> psi2ir.addPostprocessingStep { module ->
@@ -143,7 +135,7 @@ open class JvmIrCodegenFactory(
val irProviders = listOf(irLinker) val irProviders = listOf(irLinker)
val irModuleFragment = val irModuleFragment =
psi2ir.generateModuleFragment(psi2irContext, files, irProviders, pluginExtensions, expectDescriptorToSymbol = null) psi2ir.generateModuleFragment(psi2irContext, input.files, irProviders, pluginExtensions, expectDescriptorToSymbol = null)
irLinker.postProcess() irLinker.postProcess()
stubGenerator.unboundSymbolGeneration = true stubGenerator.unboundSymbolGeneration = true
@@ -151,13 +143,12 @@ open class JvmIrCodegenFactory(
// We need to compile all files we reference in Klibs // We need to compile all files we reference in Klibs
irModuleFragment.files.addAll(dependencies.flatMap { it.files }) irModuleFragment.files.addAll(dependencies.flatMap { it.files })
if (!state.configuration.getBoolean(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT)) { if (!input.configuration.getBoolean(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT)) {
val originalBindingContext = state.originalFrontendBindingContext as? CleanableBindingContext val originalBindingContext = input.bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in JVM IR to avoid leaking memory: ${state.originalFrontendBindingContext}") ?: error("BindingContext should be cleanable in JVM IR to avoid leaking memory: ${input.bindingContext}")
originalBindingContext.clear() originalBindingContext.clear()
} }
return JvmIrBackendInput( return JvmIrBackendInput(
state,
irModuleFragment, irModuleFragment,
symbolTable, symbolTable,
phaseConfig, phaseConfig,
@@ -177,8 +168,9 @@ open class JvmIrCodegenFactory(
return result.toList() return result.toList()
} }
fun doGenerateFilesInternal(input: JvmIrBackendInput) { override fun generateModule(state: GenerationState, input: CodegenFactory.BackendInput) {
val (state, irModuleFragment, symbolTable, customPhaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart) = input val (irModuleFragment, symbolTable, customPhaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart) =
input as JvmIrBackendInput
val irSerializer = if (state.configuration.getBoolean(JVMConfigurationKeys.SERIALIZE_IR)) val irSerializer = if (state.configuration.getBoolean(JVMConfigurationKeys.SERIALIZE_IR))
JvmIrSerializerImpl(state.configuration) JvmIrSerializerImpl(state.configuration)
else null else null
@@ -208,9 +200,10 @@ open class JvmIrCodegenFactory(
notifyCodegenStart: () -> Unit = {} notifyCodegenStart: () -> Unit = {}
) { ) {
val irProviders = configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions) val irProviders = configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions)
doGenerateFilesInternal( generateModule(
state,
JvmIrBackendInput( JvmIrBackendInput(
state, irModuleFragment, symbolTable, phaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart irModuleFragment, symbolTable, phaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart
) )
) )
} }
@@ -6,11 +6,13 @@
package org.jetbrains.kotlin.test.backend.ir package org.jetbrains.kotlin.test.backend.ir
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.test.model.BackendKinds import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact import org.jetbrains.kotlin.test.model.ResultingArtifact
// IR backend (JVM, JS, Native) // IR backend (JVM, JS, Native)
data class IrBackendInput( data class IrBackendInput(
val state: GenerationState,
val backendInput: JvmIrCodegenFactory.JvmIrBackendInput val backendInput: JvmIrCodegenFactory.JvmIrBackendInput
) : ResultingArtifact.BackendInput<IrBackendInput>() { ) : ResultingArtifact.BackendInput<IrBackendInput>() {
override val kind: BackendKinds.IrBackend override val kind: BackendKinds.IrBackend
@@ -22,10 +22,10 @@ class JvmIrBackendFacade(
module: TestModule, module: TestModule,
inputArtifact: IrBackendInput inputArtifact: IrBackendInput
): BinaryArtifacts.Jvm? { ): BinaryArtifacts.Jvm? {
val state = inputArtifact.backendInput.state val state = inputArtifact.state
val codegenFactory = state.codegenFactory as JvmIrCodegenFactory val codegenFactory = state.codegenFactory as JvmIrCodegenFactory
try { try {
codegenFactory.doGenerateFilesInternal(inputArtifact.backendInput) codegenFactory.generateModule(state, inputArtifact.backendInput)
} catch (e: BackendException) { } catch (e: BackendException) {
if (CodegenTestDirectives.IGNORE_ERRORS in module.directives) { if (CodegenTestDirectives.IGNORE_ERRORS in module.directives) {
return null return null
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.test.frontend.classic
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.codegen.ClassBuilderFactories import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
@@ -41,9 +42,9 @@ class ClassicFrontend2IrConverter(
files, configuration files, configuration
).codegenFactory(codegenFactory) ).codegenFactory(codegenFactory)
.isIrBackend(true) .isIrBackend(true)
.ignoreErrors(CodegenTestDirectives.IGNORE_ERRORS in module.directives)
.build() .build()
val ignoreErrors = CodegenTestDirectives.IGNORE_ERRORS in module.directives return IrBackendInput(state, codegenFactory.convertToIr(CodegenFactory.IrConversionInput.fromGenerationState(state)))
return IrBackendInput(codegenFactory.convertToIr(state, files, ignoreErrors))
} }
} }
@@ -75,8 +75,8 @@ class Fir2IrResultsConverter(
val irProviders = codegenFactory.configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions) val irProviders = codegenFactory.configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions)
return IrBackendInput( return IrBackendInput(
generationState,
JvmIrCodegenFactory.JvmIrBackendInput( JvmIrCodegenFactory.JvmIrBackendInput(
generationState,
irModuleFragment, irModuleFragment,
symbolTable, symbolTable,
phaseConfig, phaseConfig,
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollectorBasedReporter
import org.jetbrains.kotlin.cli.common.repl.LineId import org.jetbrains.kotlin.cli.common.repl.LineId
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.ClassBuilderFactories import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
@@ -200,7 +201,10 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
.codegenFactory(codegenFactory) .codegenFactory(codegenFactory)
.build() .build()
generationState.codegenFactory.generateModule(generationState, generationState.files) codegenFactory.generateModule(
generationState,
codegenFactory.convertToIr(CodegenFactory.IrConversionInput.fromGenerationState(generationState)),
)
return generationState return generationState
} }