[K/N] Enable IR validation and dumping for more phases

This commit is contained in:
Sergey Bogolepov
2023-01-24 16:31:07 +02:00
committed by Space Team
parent 10f9000d46
commit 60ff50dc51
8 changed files with 133 additions and 91 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.driver.utilities.KotlinBackendIrHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.lower.SpecialBackendChecksTraversal
@@ -36,6 +37,8 @@ internal data class SpecialBackendChecksInput(
internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase<PsiToIrContext, SpecialBackendChecksInput>(
"SpecialBackendChecks",
"Special backend checks",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
) { context, input ->
SpecialBackendChecksTraversal(context, context.interopBuiltIns, input.symbols, input.irModule.irBuiltins).lower(input.irModule)
}
@@ -56,6 +59,8 @@ internal val K2SpecialBackendChecksPhase = createSimpleNamedCompilerPhase<PhaseC
internal val CopyDefaultValuesToActualPhase = createSimpleNamedCompilerPhase<PhaseContext, IrModuleFragment>(
name = "CopyDefaultValuesToActual",
description = "Copy default values from expect to actual declarations",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
) { _, input ->
ExpectToActualDefaultValueCopier(input).process()
}
@@ -70,7 +75,9 @@ internal fun <T : PhaseContext> PhaseEngine<T>.runK2SpecialBackendChecks(fir2IrO
internal val EntryPointPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "addEntryPoint",
description = "Add entry point for program"
description = "Add entry point for program",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
) { context, module ->
val parent = context.context
val entryPoint = parent.ir.symbols.entryPoint!!.owner
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.driver.phases
import llvm.DIFinalize
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.driver.utilities.KotlinBackendIrHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.llvm.CodeGeneratorVisitor
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.llvm.RTTIGeneratorVisitor
@@ -21,6 +22,8 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
internal val CreateLLVMDeclarationsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "CreateLLVMDeclarations",
description = "Map IR declarations to LLVM",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, module ->
generationState.llvmDeclarations = createLlvmDeclarations(generationState, module)
}
@@ -37,6 +40,8 @@ internal data class RTTIInput(
internal val RTTIPhase = createSimpleNamedCompilerPhase<NativeGenerationState, RTTIInput>(
name = "RTTI",
description = "RTTI generation",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val visitor = RTTIGeneratorVisitor(generationState, input.referencedFunctions)
input.irModule.acceptVoid(visitor)
@@ -55,6 +60,8 @@ internal data class CodegenInput(
internal val CodegenPhase = createSimpleNamedCompilerPhase<NativeGenerationState, CodegenInput>(
name = "Codegen",
description = "Code generation",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val context = generationState.context
generationState.objCExport = ObjCExport(
@@ -10,12 +10,15 @@ import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.OutputFiles
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
internal val BuildAdditionalCacheInfoPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment>(
name = "BuildAdditionalCacheInfo",
description = "Build additional cache info (inline functions bodies and fields of classes)",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
) { context, module ->
// TODO: Use explicit parameter
val parent = context.context
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.phaser.ActionState
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis
import org.jetbrains.kotlin.backend.konan.driver.utilities.KotlinBackendIrHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis
@@ -31,6 +32,8 @@ internal val GHAPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Ir
internal val BuildDFGPhase = createSimpleNamedCompilerPhase<NativeGenerationState, IrModuleFragment, ModuleDFG>(
name = "BuildDFG",
description = "Data flow graph building",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
outputIfNotEnabled = { _, _, generationState, irModule ->
val context = generationState.context
val symbolTable = DataFlowIR.SymbolTable(context, DataFlowIR.Module(irModule.descriptor))
@@ -53,6 +56,8 @@ internal data class DevirtualizationAnalysisInput(
internal val DevirtualizationAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerationState, DevirtualizationAnalysisInput, DevirtualizationAnalysis.AnalysisResult>(
name = "DevirtualizationAnalysis",
description = "Devirtualization analysis",
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
outputIfNotEnabled = { _, _, _, _ ->
DevirtualizationAnalysis.AnalysisResult(
emptyMap(),
@@ -79,6 +84,8 @@ internal val DCEPhase = createSimpleNamedCompilerPhase<NativeGenerationState, DC
name = "DCEPhase",
description = "Dead code elimination",
outputIfNotEnabled = { _, _, _, _ -> null },
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val context = generationState.context
dce(context, input.irModule, input.moduleDFG, input.devirtualizationAnalysisResult)
@@ -93,13 +100,11 @@ internal data class DevirtualizationInput(
get() = irModule
}
internal val DevirtualizationPhase = createSimpleNamedCompilerPhase(
internal val DevirtualizationPhase = createSimpleNamedCompilerPhase<NativeGenerationState, DevirtualizationInput>(
name = "Devirtualization",
description = "Devirtualization",
postactions = modulePhaseActions.map { f ->
fun(actionState: ActionState, data: DevirtualizationInput, context: NativeGenerationState) =
f(actionState, data.irModule, context)
}.toSet(),
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val context = generationState.context
val devirtualizedCallSites = input.devirtualizationAnalysisResult.devirtualizedCallSites
@@ -125,6 +130,8 @@ internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerati
name = "EscapeAnalysis",
description = "Escape analysis",
outputIfNotEnabled = { _, _, _, _ -> emptyMap() },
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val lifetimes = mutableMapOf<IrElement, Lifetime>()
val context = generationState.context
@@ -165,13 +172,11 @@ internal data class RedundantCallsInput(
get() = irModule
}
internal val RemoveRedundantCallsToStaticInitializersPhase = createSimpleNamedCompilerPhase(
internal val RemoveRedundantCallsToStaticInitializersPhase = createSimpleNamedCompilerPhase<NativeGenerationState, RedundantCallsInput>(
name = "RemoveRedundantCallsToStaticInitializersPhase",
description = "Redundant static initializers calls removal",
postactions = modulePhaseActions.map { f ->
fun(actionState: ActionState, data: RedundantCallsInput, context: NativeGenerationState) =
f(actionState, data.irModule, context)
}.toSet(),
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
op = { generationState, input ->
val context = generationState.context
val moduleDFG = input.moduleDFG
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.ImportCachesAbiTransformer
@@ -50,18 +51,6 @@ internal fun PhaseEngine<NativeGenerationState>.runAllLowerings(irModuleFragment
}
}
private val validateAll = false
private val fileLoweringActions: Set<Action<IrFile, NativeGenerationState>> = setOfNotNull(
nativeStateDumper,
nativeStateIrValidator.takeIf { validateAll }
)
internal val modulePhaseActions: Set<Action<IrModuleFragment, NativeGenerationState>> = setOfNotNull(
nativeStateDumper,
nativeStateIrValidator.takeIf { validateAll }
)
internal val functionsWithoutBoundCheck = createSimpleNamedCompilerPhase<Context, Unit>(
name = "FunctionsWithoutBoundCheckGenerator",
description = "Functions without bounds check generation",
@@ -555,7 +544,8 @@ private fun createFileLoweringPhase(
): SimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile> = createSimpleNamedCompilerPhase(
name,
description,
postactions = fileLoweringActions,
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
prerequisite = prerequisite,
outputIfNotEnabled = { _, _, _, irFile -> irFile },
op = { context, irFile ->
@@ -572,7 +562,8 @@ private fun createFileLoweringPhase(
): SimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile> = createSimpleNamedCompilerPhase(
name,
description,
postactions = fileLoweringActions,
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
prerequisite = prerequisite,
outputIfNotEnabled = { _, _, _, irFile -> irFile },
op = { context, irFile ->
@@ -589,7 +580,8 @@ private fun createFileLoweringPhase(
): SimpleNamedCompilerPhase<NativeGenerationState, IrFile, IrFile> = createSimpleNamedCompilerPhase(
name,
description,
postactions = fileLoweringActions,
preactions = getDefaultIrActions(),
postactions = getDefaultIrActions(),
prerequisite = prerequisite,
outputIfNotEnabled = { _, _, _, irFile -> irFile },
op = { context, irFile ->
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.KonanReflectionTypes
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.utilities.KotlinBackendIrHolder
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.psiToIr
import org.jetbrains.kotlin.backend.konan.serialization.KonanIdSignaturer
@@ -102,6 +103,7 @@ internal class PsiToIrContextImpl(
internal val PsiToIrPhase = createSimpleNamedCompilerPhase<PsiToIrContext, PsiToIrInput, PsiToIrOutput>(
"PsiToIr", "Translate PSI to IR",
postactions = getDefaultIrActions(),
outputIfNotEnabled = { _, _, _, _ -> error("PsiToIr phase cannot be disabled") }
) { context, input ->
context.psiToIr(input, useLinkerWhenProducingLibrary = false)
@@ -1,64 +0,0 @@
/*
* Copyright 2010-2022 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.backend.konan.driver.phases
import org.jetbrains.kotlin.backend.common.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.backend.common.checkDeclarationParents
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
import org.jetbrains.kotlin.ir.IrElement
internal fun irValidationCallback(state: ActionState, element: IrElement, context: Context) {
if (!context.config.needVerifyIr) return
val validatorConfig = IrValidatorConfig(
abortOnError = false,
ensureAllNodesAreDifferent = true,
checkTypes = true,
checkDescriptors = false
)
try {
element.accept(IrValidator(context, validatorConfig), null)
element.checkDeclarationParents()
} catch (t: Throwable) {
// TODO: Add reference to source.
if (validatorConfig.abortOnError)
throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t)
else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}")
}
}
internal val nativeStateIrValidator =
fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) {
irValidationCallback(actionState, data, context.context)
}
internal val nativeStateDumper =
fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) {
defaultDumper(actionState, data, context.context)
}
internal fun <C : PhaseContext, Input, Output> SimpleNamedCompilerPhase<C, Input, Output>.copy(
preactions: Set<Action<Input, C>> = emptySet(),
postactions: Set<Action<Output, C>> = emptySet(),
) = object : SimpleNamedCompilerPhase<C, Input, Output>(
name,
description,
preactions = preactions,
postactions = postactions.map { f ->
fun(actionState: ActionState, data: Pair<Input, Output>, context: C) { f(actionState, data.second, context) }
}.toSet(),
) {
override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: C, input: Input): Output =
this@copy.outputIfNotEnabled(phaseConfig, phaserState, context, input)
override fun phaseBody(context: C, input: Input): Output =
this@copy.phaseBody(context, input)
}
@@ -6,6 +6,16 @@
package org.jetbrains.kotlin.backend.konan.driver.utilities
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.backend.common.checkDeclarationParents
import org.jetbrains.kotlin.backend.common.phaser.Action
import org.jetbrains.kotlin.backend.common.phaser.ActionState
import org.jetbrains.kotlin.backend.common.phaser.BeforeOrAfter
import org.jetbrains.kotlin.backend.common.phaser.defaultDumper
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.ir.IrElement
@@ -23,4 +33,84 @@ interface BackendContextHolder<C : CommonBackendContext> {
*/
interface KotlinBackendIrHolder {
val kotlinIr: IrElement
}
}
private fun <Context : PhaseContext> findBackendContext(context: Context): CommonBackendContext? = when {
context is CommonBackendContext -> context
context is BackendContextHolder<*> -> context.backendContext
else -> null
}
private fun <Context : PhaseContext, Data> findKotlinBackendIr(context: Context, data: Data): IrElement? = when {
data is IrElement -> data
data is KotlinBackendIrHolder -> data.kotlinIr
context is KotlinBackendIrHolder -> context.kotlinIr
else -> null
}
private fun ActionState.isDumpNeeded() =
when (beforeOrAfter) {
BeforeOrAfter.BEFORE -> config.shouldDumpStateBefore(phase)
BeforeOrAfter.AFTER -> config.shouldDumpStateAfter(phase)
}
private fun <Context : PhaseContext, Data> getIrValidator(): Action<Data, Context> =
fun(state: ActionState, data: Data, context: Context) {
if (!context.config.needVerifyIr) return
val backendContext: CommonBackendContext? = findBackendContext(context)
if (backendContext == null) {
context.messageCollector.report(CompilerMessageSeverity.WARNING,
"Cannot verify IR ${state.beforeOrAfter} ${state.phase}: insufficient context.")
return
}
val element = findKotlinBackendIr(context, data)
if (element == null) {
context.messageCollector.report(CompilerMessageSeverity.WARNING,
"Cannot verify IR ${state.beforeOrAfter} ${state.phase}: IR not found.")
return
}
val validatorConfig = IrValidatorConfig(
abortOnError = false,
ensureAllNodesAreDifferent = true,
checkTypes = true,
checkDescriptors = false
)
try {
element.accept(IrValidator(backendContext, validatorConfig), null)
element.checkDeclarationParents()
} catch (t: Throwable) {
// TODO: Add reference to source.
if (validatorConfig.abortOnError)
throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t)
else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}")
}
}
private fun <Data, Context : PhaseContext> getIrDumper(): Action<Data, Context> =
fun(state: ActionState, data: Data, context: Context) {
if (!state.isDumpNeeded()) return
val backendContext: CommonBackendContext? = findBackendContext(context)
if (backendContext == null) {
context.messageCollector.report(CompilerMessageSeverity.WARNING,
"Cannot dump IR ${state.beforeOrAfter} ${state.phase}: insufficient context.")
return
}
val element = findKotlinBackendIr(context, data)
if (element == null) {
context.messageCollector.report(CompilerMessageSeverity.WARNING,
"Cannot dump IR ${state.beforeOrAfter} ${state.phase}: IR not found.")
return
}
defaultDumper(state, element, backendContext)
}
private val validateAll = false
/**
* IR dump and verify actions.
*/
internal fun <Data, Context : PhaseContext> getDefaultIrActions(): Set<Action<Data, Context>> = setOfNotNull(
getIrDumper(),
getIrValidator<Context, Data>().takeIf { validateAll }
)