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