JVM_IR: make codegen a phase

This commit is contained in:
Georgy Bronnikov
2020-11-09 21:18:22 +03:00
parent b68fee9cbc
commit fe4d33dcb1
4 changed files with 69 additions and 39 deletions
@@ -6,14 +6,12 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.analyzer.hasJdkCapability
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -25,7 +23,6 @@ import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrLinker
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
@@ -37,7 +34,6 @@ 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
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.psi2ir.generators.generateTypicalIrProviderList
import org.jetbrains.kotlin.resolve.CleanableBindingContext
@@ -182,37 +178,16 @@ open class JvmIrCodegenFactory(
fun doGenerateFilesInternal(input: JvmIrBackendInput) {
val (state, irModuleFragment, symbolTable, phaseConfig, irProviders, extensions, backendExtension, notifyCodegenStart) = input
val context = JvmBackendContext(
state, irModuleFragment.irBuiltins, irModuleFragment, symbolTable, phaseConfig, extensions, backendExtension
state, irModuleFragment.irBuiltins, irModuleFragment, symbolTable, phaseConfig, extensions, backendExtension,
notifyCodegenStart
)
/* JvmBackendContext creates new unbound symbols, have to resolve them. */
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
context.state.factory.registerSourceFiles(irModuleFragment.files.map(IrFile::getKtFile))
JvmLower(context).lower(irModuleFragment)
jvmPhases.invokeToplevel(phaseConfig, context, irModuleFragment)
notifyCodegenStart()
for (generateMultifileFacade in listOf(true, false)) {
for (irFile in irModuleFragment.files) {
// Generate multifile facades first, to compute and store JVM signatures of const properties which are later used
// when serializing metadata in the multifile parts.
// TODO: consider dividing codegen itself into separate phases (bytecode generation, metadata serialization) to avoid this
val isMultifileFacade = irFile.fileEntry is MultifileFacadeFileEntry
if (isMultifileFacade != generateMultifileFacade) continue
try {
for (loweredClass in irFile.declarations) {
if (loweredClass !is IrClass) {
throw AssertionError("File-level declaration should be IrClass after JvmLower, got: " + loweredClass.render())
}
ClassCodegen.getOrCreate(loweredClass, context).generate()
}
} catch (e: Throwable) {
CodegenUtil.reportBackendException(e, "code generation", irFile.fileEntry.name)
}
}
}
// TODO: split classes into groups connected by inline calls; call this after every group
// and clear `JvmBackendContext.classCodegens`
state.afterIndependentPart()
@@ -224,7 +199,7 @@ open class JvmIrCodegenFactory(
symbolTable: SymbolTable,
extensions: JvmGeneratorExtensionsImpl,
backendExtension: JvmBackendExtension,
notifyCodegenStart: () -> Unit
notifyCodegenStart: () -> Unit = {}
) {
val irProviders = configureBuiltInsAndGenerateIrProvidersInFrontendIRMode(irModuleFragment, symbolTable, extensions)
doGenerateFilesInternal(
@@ -52,6 +52,7 @@ class JvmBackendContext(
val phaseConfig: PhaseConfig,
val generatorExtensions: JvmGeneratorExtensions,
val backendExtension: JvmBackendExtension,
val notifyCodegenStart: () -> Unit,
) : CommonBackendContext {
// If the JVM fqname of a class differs from what is implied by its parent, e.g. if it's a file class
// annotated with @JvmPackageName, the correct name is recorded here.
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.shouldContainSuspendMarkers
import org.jetbrains.kotlin.backend.jvm.lower.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -21,6 +22,7 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -273,6 +275,42 @@ private val kotlinNothingValueExceptionPhase = makeIrFilePhase<CommonBackendCont
description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'"
)
private val notifyCodegenStartPhase = makeCustomPhase<JvmBackendContext, IrModuleFragment>(
op = { context, _ -> context.notifyCodegenStart() },
name = "NotifyCodegenStart",
description = "Notify time measuring subsystem that code generation is being started",
)
private fun codegenPhase(generateMultifileFacade: Boolean): NamedCompilerPhase<JvmBackendContext, IrModuleFragment> {
val suffix = if (generateMultifileFacade) "MultifileFacades" else "Regular"
val descriptionSuffix = if (generateMultifileFacade) ", multifile facades" else ", regular files"
return performByIrFile(
name = "CodegenByIrFile$suffix",
description = "Code generation by IrFile$descriptionSuffix",
lower = listOf(
makeIrFilePhase(
{ context ->
object : FileLoweringPass {
override fun lower(irFile: IrFile) {
val isMultifileFacade = irFile.fileEntry is MultifileFacadeFileEntry
if (isMultifileFacade != generateMultifileFacade) return
for (loweredClass in irFile.declarations) {
if (loweredClass !is IrClass) {
throw AssertionError("File-level declaration should be IrClass after JvmLower, got: " + loweredClass.render())
}
ClassCodegen.getOrCreate(loweredClass, context).generate()
}
}
}
},
name = "Codegen$suffix",
description = "Code generation"
)
)
)
}
private val jvmFilePhases = listOf(
typeAliasAnnotationMethodsPhase,
stripTypeAliasDeclarationsPhase,
@@ -379,11 +417,10 @@ private val jvmFilePhases = listOf(
makePatchParentsPhase(3)
)
val jvmPhases = NamedCompilerPhase(
private val jvmLoweringPhases = NamedCompilerPhase(
name = "IrLowering",
description = "IR lowering",
nlevels = 1,
actions = setOf(defaultDumper, validationAction),
lower = validateIrBeforeLowering then
processOptionalAnnotationsPhase then
expectDeclarationsRemovingPhase then
@@ -398,9 +435,25 @@ val jvmPhases = NamedCompilerPhase(
validateIrAfterLowering
)
class JvmLower(val context: JvmBackendContext) {
fun lower(irModuleFragment: IrModuleFragment) {
// TODO run lowering passes as callbacks in bottom-up visitor
jvmPhases.invokeToplevel(context.phaseConfig, context, irModuleFragment)
}
}
// Generate multifile facades first, to compute and store JVM signatures of const properties which are later used
// when serializing metadata in the multifile parts.
// TODO: consider dividing codegen itself into separate phases (bytecode generation, metadata serialization) to avoid this
private val jvmCodegenPhases = NamedCompilerPhase(
name = "Codegen",
description = "Code generation",
nlevels = 1,
lower = codegenPhase(generateMultifileFacade = true) then
codegenPhase(generateMultifileFacade = false)
)
val jvmPhases = NamedCompilerPhase(
name = "IrBackend",
description = "IR Backend for JVM",
nlevels = 1,
actions = setOf(defaultDumper, validationAction),
lower = jvmLoweringPhases then
notifyCodegenStartPhase then
jvmCodegenPhases
)
@@ -87,7 +87,8 @@ class Fir2IrResultsConverter(
irProviders,
extensions,
FirJvmBackendExtension(inputArtifact.session, components),
) {}
notifyCodegenStart = {},
)
)
}
}