Phaser: Implement dumper and verifier as general actions
This commit is contained in:
+33
-20
@@ -11,11 +11,12 @@ import kotlin.system.measureTimeMillis
|
||||
class PhaserState<Data>(
|
||||
val alreadyDone: MutableSet<AnyNamedPhase> = mutableSetOf(),
|
||||
var depth: Int = 0,
|
||||
var phaseCount: Int = 0,
|
||||
val stickyPostconditions: MutableSet<Checker<Data>> = mutableSetOf()
|
||||
)
|
||||
|
||||
// Copy state, forgetting the sticky postconditions (which will not be applicable to the new type)
|
||||
fun <Input, Output> PhaserState<Input>.changeType() = PhaserState<Output>(alreadyDone, depth, mutableSetOf())
|
||||
fun <Input, Output> PhaserState<Input>.changeType() = PhaserState<Output>(alreadyDone, depth, phaseCount, mutableSetOf())
|
||||
|
||||
|
||||
fun <R, D> PhaserState<D>.downlevel(nlevels: Int = 1, block: () -> R): R {
|
||||
@@ -51,15 +52,27 @@ interface NamedCompilerPhase<in Context : CommonBackendContext, Input, Output> :
|
||||
val prerequisite: Set<AnyNamedPhase> get() = emptySet()
|
||||
val preconditions: Set<Checker<Input>>
|
||||
val postconditions: Set<Checker<Output>>
|
||||
val actionsBefore: Set<Action<Input, Context>>
|
||||
val actionsAfter: Set<Action<Output, Context>>
|
||||
}
|
||||
|
||||
typealias AnyNamedPhase = NamedCompilerPhase<*, *, *>
|
||||
enum class BeforeOrAfter { BEFORE, AFTER }
|
||||
|
||||
interface PhaseDumperVerifier<in Context : CommonBackendContext, Data> {
|
||||
fun dump(phase: AnyNamedPhase, phaseConfig: PhaseConfig, data: Data, beforeOrAfter: BeforeOrAfter)
|
||||
fun verify(context: Context, data: Data)
|
||||
}
|
||||
data class ActionState(
|
||||
val config: PhaseConfig,
|
||||
val phase: AnyNamedPhase,
|
||||
val phaseCount: Int,
|
||||
val beforeOrAfter: BeforeOrAfter
|
||||
)
|
||||
|
||||
typealias Action<Data, Context> = (ActionState, Data, Context) -> Unit
|
||||
|
||||
infix operator fun <Data, Context> Action<Data, Context>.plus(other: Action<Data, Context>): Action<Data, Context> =
|
||||
{ phaseState, data, context ->
|
||||
this(phaseState, data, context)
|
||||
other(phaseState, data, context)
|
||||
}
|
||||
|
||||
abstract class AbstractNamedPhaseWrapper<in Context : CommonBackendContext, Input, Output>(
|
||||
override val name: String,
|
||||
@@ -69,10 +82,10 @@ abstract class AbstractNamedPhaseWrapper<in Context : CommonBackendContext, Inpu
|
||||
override val preconditions: Set<Checker<Input>> = emptySet(),
|
||||
override val postconditions: Set<Checker<Output>> = emptySet(),
|
||||
override val stickyPostconditions: Set<Checker<Output>> = emptySet(),
|
||||
override val actionsBefore: Set<Action<Input, Context>> = emptySet(),
|
||||
override val actionsAfter: Set<Action<Output, Context>> = emptySet(),
|
||||
private val nlevels: Int = 0
|
||||
) : NamedCompilerPhase<Context, Input, Output> {
|
||||
abstract val inputDumperVerifier: PhaseDumperVerifier<Context, Input>
|
||||
abstract val outputDumperVerifier: PhaseDumperVerifier<Context, Output>
|
||||
|
||||
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Input>, context: Context, input: Input): Output {
|
||||
if (this is SameTypeCompilerPhase<*, *> &&
|
||||
@@ -88,18 +101,20 @@ abstract class AbstractNamedPhaseWrapper<in Context : CommonBackendContext, Inpu
|
||||
|
||||
context.inVerbosePhase = this in phaseConfig.verbose
|
||||
|
||||
runBefore(phaseConfig, context, input)
|
||||
runBefore(phaseConfig, phaserState, context, input)
|
||||
val output = runBody(phaseConfig, phaserState, context, input)
|
||||
runAfter(phaseConfig, phaserState, context, output)
|
||||
|
||||
phaserState.alreadyDone.add(this)
|
||||
phaserState.phaseCount++
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
private fun runBefore(phaseConfig: PhaseConfig, context: Context, input: Input) {
|
||||
checkAndRun(phaseConfig.toDumpStateBefore) { inputDumperVerifier.dump(this, phaseConfig, input, BeforeOrAfter.BEFORE) }
|
||||
checkAndRun(phaseConfig.toValidateStateBefore) { inputDumperVerifier.verify(context, input) }
|
||||
private fun runBefore(phaseConfig: PhaseConfig, phaserState: PhaserState<Input>, context: Context, input: Input) {
|
||||
val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.BEFORE)
|
||||
for (action in actionsBefore) action(state, input, context)
|
||||
|
||||
if (phaseConfig.checkConditions) {
|
||||
for (pre in preconditions) pre(input)
|
||||
}
|
||||
@@ -116,8 +131,9 @@ abstract class AbstractNamedPhaseWrapper<in Context : CommonBackendContext, Inpu
|
||||
}
|
||||
|
||||
private fun runAfter(phaseConfig: PhaseConfig, phaserState: PhaserState<Input>, context: Context, output: Output) {
|
||||
checkAndRun(phaseConfig.toDumpStateAfter) { outputDumperVerifier.dump(this, phaseConfig, output, BeforeOrAfter.AFTER) }
|
||||
checkAndRun(phaseConfig.toValidateStateAfter) { outputDumperVerifier.verify(context, output) }
|
||||
val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.AFTER)
|
||||
for (action in actionsAfter) action(state, output, context)
|
||||
|
||||
if (phaseConfig.checkConditions) {
|
||||
for (post in postconditions) post(output)
|
||||
for (post in stickyPostconditions) post(output)
|
||||
@@ -158,11 +174,8 @@ class SameTypeNamedPhaseWrapper<in Context : CommonBackendContext, Data>(
|
||||
preconditions: Set<Checker<Data>> = emptySet(),
|
||||
postconditions: Set<Checker<Data>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<Data>> = lower.stickyPostconditions,
|
||||
nlevels: Int = 0,
|
||||
val dumperVerifier: PhaseDumperVerifier<Context, Data>
|
||||
actions: Set<Action<Data, Context>> = emptySet(),
|
||||
nlevels: Int = 0
|
||||
) : AbstractNamedPhaseWrapper<Context, Data, Data>(
|
||||
name, description, prerequisite, lower, preconditions, postconditions, stickyPostconditions, nlevels
|
||||
), SameTypeCompilerPhase<Context, Data> {
|
||||
override val inputDumperVerifier get() = dumperVerifier
|
||||
override val outputDumperVerifier get() = dumperVerifier
|
||||
}
|
||||
name, description, prerequisite, lower, preconditions, postconditions, stickyPostconditions, actions, actions, nlevels
|
||||
), SameTypeCompilerPhase<Context, Data>
|
||||
|
||||
+99
-32
@@ -5,48 +5,115 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common.phaser
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.File
|
||||
|
||||
abstract class IrPhaseDumperVerifier<in Context : CommonBackendContext, Data : IrElement>(
|
||||
val verifier: (Context, Data) -> Unit
|
||||
) : PhaseDumperVerifier<Context, Data> {
|
||||
abstract fun Data.getElementName(): String
|
||||
private val IrElement.elementName: String
|
||||
get() = when (this) {
|
||||
is IrModuleFragment ->
|
||||
this.name.asString()
|
||||
|
||||
// TODO: use a proper logger.
|
||||
override fun dump(phase: AnyNamedPhase, phaseConfig: PhaseConfig, data: Data, beforeOrAfter: BeforeOrAfter) {
|
||||
fun separator(title: String) = println("\n\n--- $title ----------------------\n")
|
||||
is IrFile ->
|
||||
this.name
|
||||
|
||||
if (!shouldBeDumped(phaseConfig, data)) return
|
||||
|
||||
val beforeOrAfterStr = beforeOrAfter.name.toLowerCase()
|
||||
val title = "IR for ${data.getElementName()} $beforeOrAfterStr ${phase.description}"
|
||||
separator(title)
|
||||
println(data.dump())
|
||||
else ->
|
||||
this.toString()
|
||||
}
|
||||
|
||||
override fun verify(context: Context, data: Data) = verifier(context, data)
|
||||
private fun ActionState.isDumpNeeded() =
|
||||
phase in when (beforeOrAfter) {
|
||||
BeforeOrAfter.BEFORE -> config.toDumpStateBefore
|
||||
BeforeOrAfter.AFTER -> config.toDumpStateAfter
|
||||
}
|
||||
|
||||
private fun shouldBeDumped(phaseConfig: PhaseConfig, input: Data) =
|
||||
input.getElementName() !in phaseConfig.namesOfElementsExcludedFromDumping
|
||||
private fun ActionState.isValidationNeeded() =
|
||||
phase in when (beforeOrAfter) {
|
||||
BeforeOrAfter.BEFORE -> config.toValidateStateBefore
|
||||
BeforeOrAfter.AFTER -> config.toValidateStateAfter
|
||||
}
|
||||
|
||||
fun <Data, Context> makeDumpAction(dumper: Action<Data, Context>): Action<Data, Context> =
|
||||
{ phaseState, data, context ->
|
||||
if (phaseState.isDumpNeeded())
|
||||
dumper(phaseState, data, context)
|
||||
}
|
||||
|
||||
fun <Data, Context> makeVerifyAction(verifier: (Context, Data) -> Unit): Action<Data, Context> =
|
||||
{ phaseState, data, context ->
|
||||
if (phaseState.isValidationNeeded())
|
||||
verifier(context, data)
|
||||
}
|
||||
|
||||
fun dumpIrElement(actionState: ActionState, data: IrElement, context: Any?): String {
|
||||
val beforeOrAfterStr = actionState.beforeOrAfter.name.toLowerCase()
|
||||
|
||||
var dumpText: String = ""
|
||||
val elementName: String
|
||||
|
||||
val dumpOnlyFqName = actionState.config.dumpOnlyFqName
|
||||
if (dumpOnlyFqName != null) {
|
||||
elementName = dumpOnlyFqName
|
||||
data.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclaration) {
|
||||
if (declaration is IrDeclarationWithName && FqName(dumpOnlyFqName) == declaration.fqNameWhenAvailable) {
|
||||
dumpText += declaration.dump()
|
||||
} else {
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
elementName = data.elementName
|
||||
dumpText = data.dump()
|
||||
}
|
||||
|
||||
val title = "-- IR for $elementName $beforeOrAfterStr ${actionState.phase.description}\n"
|
||||
return title + dumpText
|
||||
}
|
||||
|
||||
class IrFileDumperVerifier<in Context : CommonBackendContext>(verifier: (Context, IrFile) -> Unit) :
|
||||
IrPhaseDumperVerifier<Context, IrFile>(verifier) {
|
||||
override fun IrFile.getElementName() = name
|
||||
}
|
||||
typealias Dumper<Data, Context> = (ActionState, Data, Context) -> String?
|
||||
|
||||
class IrModuleDumperVerifier<in Context : CommonBackendContext>(verifier: (Context, IrModuleFragment) -> Unit) :
|
||||
IrPhaseDumperVerifier<Context, IrModuleFragment>(verifier) {
|
||||
override fun IrModuleFragment.getElementName() = name.asString()
|
||||
}
|
||||
fun <Data, Context> dumpToFile(
|
||||
fileExtension: String,
|
||||
dumper: Dumper<Data, Context>
|
||||
): Action<Data, Context> =
|
||||
fun(actionState: ActionState, data: Data, context: Context) {
|
||||
val directoryPath = actionState.config.dumpToDirectory ?: return
|
||||
val dumpContent = dumper(actionState, data, context) ?: return
|
||||
|
||||
class EmptyDumperVerifier<in Context : CommonBackendContext, Data> : PhaseDumperVerifier<Context, Data> {
|
||||
override fun dump(phase: AnyNamedPhase, phaseConfig: PhaseConfig, data: Data, beforeOrAfter: BeforeOrAfter) {}
|
||||
override fun verify(context: Context, data: Data) {}
|
||||
}
|
||||
val directoryFile = File(directoryPath)
|
||||
if (!directoryFile.isDirectory)
|
||||
if (!directoryFile.mkdirs())
|
||||
error("Can't create directory for IR dumps at $directoryPath")
|
||||
|
||||
// Make dump files in a directory sorted by ID
|
||||
val phaseIdFormatted = "%02d".format(actionState.phaseCount)
|
||||
|
||||
val fileName = "${phaseIdFormatted}_${actionState.phase.name}.$fileExtension"
|
||||
|
||||
File(directoryFile, fileName).writeText(dumpContent)
|
||||
}
|
||||
|
||||
fun <Data, Context> dumpToStdout(
|
||||
dumper: Dumper<Data, Context>
|
||||
): Action<Data, Context> =
|
||||
fun(actionState: ActionState, data: Data, context: Context) {
|
||||
if (actionState.config.dumpToDirectory != null) return
|
||||
val dumpContent = dumper(actionState, data, context) ?: return
|
||||
println("\n\n----------------------------------------------")
|
||||
println(dumpContent)
|
||||
println()
|
||||
}
|
||||
|
||||
val defaultDumper = makeDumpAction(dumpToStdout(::dumpIrElement) + dumpToFile("ir", ::dumpIrElement))
|
||||
+14
-18
@@ -54,7 +54,7 @@ fun <Context : CommonBackendContext> namedIrModulePhase(
|
||||
preconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
postconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<IrModuleFragment>> = lower.stickyPostconditions,
|
||||
verify: (Context, IrModuleFragment) -> Unit = { _, _ -> },
|
||||
actions: Set<Action<IrModuleFragment, Context>> = setOf(defaultDumper),
|
||||
nlevels: Int = 1
|
||||
) = SameTypeNamedPhaseWrapper(
|
||||
name,
|
||||
@@ -64,8 +64,8 @@ fun <Context : CommonBackendContext> namedIrModulePhase(
|
||||
preconditions,
|
||||
postconditions,
|
||||
stickyPostconditions,
|
||||
nlevels,
|
||||
IrModuleDumperVerifier(verify)
|
||||
actions,
|
||||
nlevels
|
||||
)
|
||||
|
||||
fun <Context : CommonBackendContext> namedIrFilePhase(
|
||||
@@ -76,7 +76,7 @@ fun <Context : CommonBackendContext> namedIrFilePhase(
|
||||
preconditions: Set<Checker<IrFile>> = emptySet(),
|
||||
postconditions: Set<Checker<IrFile>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<IrFile>> = lower.stickyPostconditions,
|
||||
verify: (Context, IrFile) -> Unit = { _, _ -> },
|
||||
actions: Set<Action<IrFile, Context>> = setOf(defaultDumper),
|
||||
nlevels: Int = 1
|
||||
) = SameTypeNamedPhaseWrapper(
|
||||
name,
|
||||
@@ -86,8 +86,8 @@ fun <Context : CommonBackendContext> namedIrFilePhase(
|
||||
preconditions,
|
||||
postconditions,
|
||||
stickyPostconditions,
|
||||
nlevels,
|
||||
IrFileDumperVerifier(verify)
|
||||
actions,
|
||||
nlevels
|
||||
)
|
||||
|
||||
fun <Context : CommonBackendContext> namedUnitPhase(
|
||||
@@ -99,8 +99,7 @@ fun <Context : CommonBackendContext> namedUnitPhase(
|
||||
) = SameTypeNamedPhaseWrapper(
|
||||
name, description, prerequisite,
|
||||
lower = lower,
|
||||
nlevels = nlevels,
|
||||
dumperVerifier = EmptyDumperVerifier()
|
||||
nlevels = nlevels
|
||||
)
|
||||
|
||||
fun <Context : CommonBackendContext> namedOpUnitPhase(
|
||||
@@ -125,14 +124,14 @@ fun <Context : CommonBackendContext> performByIrFile(
|
||||
preconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
postconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
verify: (Context, IrModuleFragment) -> Unit = { _, _ -> },
|
||||
actions: Set<Action<IrModuleFragment, Context>> = setOf(defaultDumper),
|
||||
lower: CompilerPhase<Context, IrFile, IrFile>
|
||||
) = namedIrModulePhase(
|
||||
name, description, prerequisite,
|
||||
preconditions = preconditions,
|
||||
postconditions = postconditions,
|
||||
stickyPostconditions = stickyPostconditions,
|
||||
verify = verify,
|
||||
actions = actions,
|
||||
nlevels = 1,
|
||||
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
|
||||
override fun invoke(
|
||||
@@ -161,13 +160,13 @@ fun <Context : CommonBackendContext> makeIrFilePhase(
|
||||
preconditions: Set<Checker<IrFile>> = emptySet(),
|
||||
postconditions: Set<Checker<IrFile>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<IrFile>> = emptySet(),
|
||||
verify: (Context, IrFile) -> Unit = { _, _ -> }
|
||||
actions: Set<Action<IrFile, Context>> = setOf(defaultDumper)
|
||||
) = namedIrFilePhase(
|
||||
name, description, prerequisite,
|
||||
preconditions = preconditions,
|
||||
postconditions = postconditions,
|
||||
stickyPostconditions = stickyPostconditions,
|
||||
verify = verify,
|
||||
actions = actions,
|
||||
nlevels = 0,
|
||||
lower = object : SameTypeCompilerPhase<Context, IrFile> {
|
||||
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrFile>, context: Context, input: IrFile): IrFile {
|
||||
@@ -185,13 +184,13 @@ fun <Context : CommonBackendContext> makeIrModulePhase(
|
||||
preconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
postconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
stickyPostconditions: Set<Checker<IrModuleFragment>> = emptySet(),
|
||||
verify: (Context, IrModuleFragment) -> Unit = { _, _ -> }
|
||||
actions: Set<Action<IrModuleFragment, Context>> = setOf(defaultDumper)
|
||||
) = namedIrModulePhase(
|
||||
name, description, prerequisite,
|
||||
preconditions=preconditions,
|
||||
postconditions = postconditions,
|
||||
stickyPostconditions = stickyPostconditions,
|
||||
verify = verify,
|
||||
actions = actions,
|
||||
nlevels = 0,
|
||||
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
|
||||
override fun invoke(
|
||||
@@ -222,10 +221,7 @@ fun <Context : CommonBackendContext, Input> unitPhase(
|
||||
context.op()
|
||||
}
|
||||
}
|
||||
) {
|
||||
override val inputDumperVerifier = EmptyDumperVerifier<Context, Input>()
|
||||
override val outputDumperVerifier = EmptyDumperVerifier<Context, Unit>()
|
||||
}
|
||||
) {}
|
||||
|
||||
fun <Context : CommonBackendContext, Input> unitSink() = object : CompilerPhase<Context, Input, Unit> {
|
||||
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Input>, context: Context, input: Input) {}
|
||||
|
||||
+2
@@ -19,6 +19,8 @@ class PhaseConfig(
|
||||
val verbose: Set<AnyNamedPhase> = emptySet(),
|
||||
val toDumpStateBefore: Set<AnyNamedPhase> = emptySet(),
|
||||
val toDumpStateAfter: Set<AnyNamedPhase> = emptySet(),
|
||||
val dumpToDirectory: String? = null,
|
||||
val dumpOnlyFqName: String? = null,
|
||||
val toValidateStateBefore: Set<AnyNamedPhase> = emptySet(),
|
||||
val toValidateStateAfter: Set<AnyNamedPhase> = emptySet(),
|
||||
val namesOfElementsExcludedFromDumping: Set<String> = emptySet(),
|
||||
|
||||
Reference in New Issue
Block a user