Extract SameTypeNamedCompilerPhase from NamedCompilerPhase

Currently, compiler pipelines are heavily couples with
NamedCompilerPhase. Unfortunately, NamedCompilerPhase uses the same
type for Input and Output, thus it is not applicable to phases that
try to transform some data purely.
Thus, we separate this class into two, allowing to have a new
inheritor of NamedCompilerPhase with different Input and Output types.
This commit is contained in:
Sergey Bogolepov
2022-10-03 14:07:50 +03:00
committed by Space Team
parent 3a500e536a
commit 6a4722188f
9 changed files with 142 additions and 101 deletions
@@ -18,7 +18,7 @@ class PhaserState<Data>(
}
// 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, phaseCount, mutableSetOf())
fun <Input, Output> PhaserState<Input>.changePhaserStateType() = PhaserState<Output>(alreadyDone, depth, phaseCount, mutableSetOf())
inline fun <R, D> PhaserState<D>.downlevel(nlevels: Int, block: () -> R): R {
depth += nlevels
@@ -30,7 +30,7 @@ inline fun <R, D> PhaserState<D>.downlevel(nlevels: Int, block: () -> R): R {
interface CompilerPhase<in Context : LoggingContext, Input, Output> {
fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input): Output
fun getNamedSubphases(startDepth: Int = 0): List<Pair<Int, NamedCompilerPhase<Context, *>>> = emptyList()
fun getNamedSubphases(startDepth: Int = 0): List<Pair<Int, AbstractNamedCompilerPhase<Context, *, *>>> = emptyList()
// In phase trees, `stickyPostconditions` is inherited along the right edge to be used in `then`.
val stickyPostconditions: Set<Checker<Output>> get() = emptySet()
@@ -47,7 +47,7 @@ interface SameTypeCompilerPhase<in Context : LoggingContext, Data> : CompilerPha
// A failing checker should just throw an exception.
typealias Checker<Data> = (Data) -> Unit
typealias AnyNamedPhase = NamedCompilerPhase<*, *>
typealias AnyNamedPhase = AbstractNamedCompilerPhase<*, *, *>
enum class BeforeOrAfter { BEFORE, AFTER }
@@ -66,20 +66,18 @@ infix operator fun <Data, Context> Action<Data, Context>.plus(other: Action<Data
other(phaseState, data, context)
}
class NamedCompilerPhase<in Context : LoggingContext, Data>(
// TODO: A better name would be just `NamedCompilerPhase`, but it is already used (see below).
abstract class AbstractNamedCompilerPhase<in Context : LoggingContext, Input, Output>(
val name: String,
val description: String,
val prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
private val lower: CompilerPhase<Context, Data, Data>,
val preconditions: Set<Checker<Data>> = emptySet(),
val postconditions: Set<Checker<Data>> = emptySet(),
override val stickyPostconditions: Set<Checker<Data>> = emptySet(),
private val actions: Set<Action<Data, Context>> = emptySet(),
private val nlevels: Int = 0
) : SameTypeCompilerPhase<Context, Data> {
override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, input: Data): Data {
val prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
val preconditions: Set<Checker<Input>> = emptySet(),
val postconditions: Set<Checker<Output>> = emptySet(),
protected val nlevels: Int = 0
) : CompilerPhase<Context, Input, Output> {
override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input): Output {
if (!phaseConfig.isEnabled(this)) {
return input
return outputIfNotEnabled(phaseConfig, phaserState, context, input)
}
assert(phaserState.alreadyDone.containsAll(prerequisite)) {
@@ -93,10 +91,10 @@ class NamedCompilerPhase<in Context : LoggingContext, Data>(
runAndProfile(phaseConfig, phaserState, context, input)
} else {
phaserState.downlevel(nlevels) {
lower.invoke(phaseConfig, phaserState, context, input)
phaseBody(phaseConfig, phaserState, context, input)
}
}
runAfter(phaseConfig, phaserState, context, output)
runAfter(phaseConfig, changePhaserStateType(phaserState), context, output)
phaserState.alreadyDone.add(this)
phaserState.phaseCount++
@@ -104,7 +102,57 @@ class NamedCompilerPhase<in Context : LoggingContext, Data>(
return output
}
private fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, input: Data) {
abstract fun phaseBody(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input): Output
abstract fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input): Output
abstract fun changePhaserStateType(phaserState: PhaserState<Input>): PhaserState<Output>
abstract fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, input: Input)
abstract fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Output>, context: Context, output: Output)
private fun runAndProfile(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Input>, context: Context, source: Input): Output {
var result: Output? = null
val msec = measureTimeMillis {
result = phaserState.downlevel(nlevels) {
phaseBody(phaseConfig, phaserState, context, source)
}
}
// TODO: use a proper logger
println("${"\t".repeat(phaserState.depth)}$description: $msec msec")
return result!!
}
override fun toString() = "Compiler Phase @$name"
}
// TODO: This class should be named `SameTypeNamedCompilerPhase`,
// but it would be a breaking change (e.g. there are usages in IntelliJ repo),
// so we introduce a typealias instead as a temporary solution.
class NamedCompilerPhase<in Context : LoggingContext, Data>(
name: String,
description: String,
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
private val lower: CompilerPhase<Context, Data, Data>,
preconditions: Set<Checker<Data>> = emptySet(),
postconditions: Set<Checker<Data>> = emptySet(),
override val stickyPostconditions: Set<Checker<Data>> = emptySet(),
private val actions: Set<Action<Data, Context>> = emptySet(),
nlevels: Int = 0
) : AbstractNamedCompilerPhase<Context, Data, Data>(
name, description, prerequisite, preconditions, postconditions, nlevels
) {
override fun phaseBody(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, input: Data): Data =
lower.invoke(phaseConfig, phaserState, context, input)
override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, input: Data): Data =
input
override fun changePhaserStateType(phaserState: PhaserState<Data>): PhaserState<Data> =
phaserState
override fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, input: Data) {
val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.BEFORE)
for (action in actions) action(state, input, context)
@@ -113,7 +161,7 @@ class NamedCompilerPhase<in Context : LoggingContext, Data>(
}
}
private fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, output: Data) {
override fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, output: Data) {
val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.AFTER)
for (action in actions) action(state, output, context)
@@ -126,20 +174,9 @@ class NamedCompilerPhase<in Context : LoggingContext, Data>(
}
}
private fun runAndProfile(phaseConfig: PhaseConfigurationService, phaserState: PhaserState<Data>, context: Context, source: Data): Data {
var result: Data? = null
val msec = measureTimeMillis {
result = phaserState.downlevel(nlevels) {
lower.invoke(phaseConfig, phaserState, context, source)
}
}
// TODO: use a proper logger
println("${"\t".repeat(phaserState.depth)}$description: $msec msec")
return result!!
}
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, NamedCompilerPhase<Context, *>>> =
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, AbstractNamedCompilerPhase<Context, *, *>>> =
listOf(startDepth to this) + lower.getNamedSubphases(startDepth + nlevels)
override fun toString() = "Compiler Phase @$name"
}
typealias SameTypeNamedCompilerPhase<Context, Data> = NamedCompilerPhase<Context, Data>
@@ -23,7 +23,7 @@ private class CompositePhase<Context : CommonBackendContext, Input, Output>(
for ((previous, next) in phases.zip(phases.drop(1))) {
if (next !is SameTypeCompilerPhase<*, *>) {
// Discard `stickyPostconditions`, they are useless since data type is changing.
currentState = currentState.changeType()
currentState = currentState.changePhaserStateType()
}
currentState.stickyPostconditions.addAll(previous.stickyPostconditions)
result = next.invoke(phaseConfig, currentState, context, result)
@@ -32,7 +32,7 @@ private class CompositePhase<Context : CommonBackendContext, Input, Output>(
return result as Output
}
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, NamedCompilerPhase<Context, *>>> =
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, AbstractNamedCompilerPhase<Context, *, *>>> =
phases.flatMap { it.getNamedSubphases(startDepth) }
override val stickyPostconditions get() = phases.last().stickyPostconditions
@@ -51,14 +51,14 @@ fun <Context : CommonBackendContext, Element : IrElement> makeCustomPhase(
op: (Context, Element) -> Unit,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
preconditions: Set<Checker<Element>> = emptySet(),
postconditions: Set<Checker<Element>> = emptySet(),
stickyPostconditions: Set<Checker<Element>> = emptySet(),
actions: Set<Action<Element, Context>> = setOf(defaultDumper, validationAction),
nlevels: Int = 1
): NamedCompilerPhase<Context, Element> =
NamedCompilerPhase(
): SameTypeNamedCompilerPhase<Context, Element> =
SameTypeNamedCompilerPhase(
name, description, prerequisite, CustomPhaseAdapter(op), preconditions, postconditions, stickyPostconditions, actions, nlevels,
)
@@ -74,11 +74,11 @@ private class CustomPhaseAdapter<Context : CommonBackendContext, Element>(
fun <Context : CommonBackendContext> namedUnitPhase(
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
nlevels: Int = 1,
lower: CompilerPhase<Context, Unit, Unit>
): NamedCompilerPhase<Context, Unit> =
NamedCompilerPhase(
): SameTypeNamedCompilerPhase<Context, Unit> =
SameTypeNamedCompilerPhase(
name, description, prerequisite, lower, nlevels = nlevels
)
@@ -86,9 +86,9 @@ fun <Context : CommonBackendContext> namedUnitPhase(
fun <Context : CommonBackendContext> namedOpUnitPhase(
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>>,
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>>,
op: Context.() -> Unit
): NamedCompilerPhase<Context, Unit> = namedUnitPhase(
): SameTypeNamedCompilerPhase<Context, Unit> = namedUnitPhase(
name, description, prerequisite,
nlevels = 0,
lower = object : SameTypeCompilerPhase<Context, Unit> {
@@ -102,13 +102,13 @@ fun <Context : CommonBackendContext> makeIrFilePhase(
lowering: (Context) -> FileLoweringPass,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
preconditions: Set<Checker<IrFile>> = emptySet(),
postconditions: Set<Checker<IrFile>> = emptySet(),
stickyPostconditions: Set<Checker<IrFile>> = emptySet(),
actions: Set<Action<IrFile, Context>> = setOf(defaultDumper, validationAction)
): NamedCompilerPhase<Context, IrFile> =
NamedCompilerPhase(
): SameTypeNamedCompilerPhase<Context, IrFile> =
SameTypeNamedCompilerPhase(
name, description, prerequisite, FileLoweringPhaseAdapter(lowering), preconditions, postconditions, stickyPostconditions, actions,
nlevels = 0,
)
@@ -126,13 +126,13 @@ fun <Context : CommonBackendContext> makeIrModulePhase(
lowering: (Context) -> FileLoweringPass,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet(),
prerequisite: Set<AbstractNamedCompilerPhase<Context, *, *>> = emptySet(),
preconditions: Set<Checker<IrModuleFragment>> = emptySet(),
postconditions: Set<Checker<IrModuleFragment>> = emptySet(),
stickyPostconditions: Set<Checker<IrModuleFragment>> = emptySet(),
actions: Set<Action<IrModuleFragment, Context>> = setOf(defaultDumper, validationAction)
): NamedCompilerPhase<Context, IrModuleFragment> =
NamedCompilerPhase(
): SameTypeNamedCompilerPhase<Context, IrModuleFragment> =
SameTypeNamedCompilerPhase(
name, description, prerequisite, ModuleLoweringPhaseAdapter(lowering), preconditions, postconditions, stickyPostconditions, actions,
nlevels = 0,
)
@@ -32,8 +32,8 @@ fun <Context : CommonBackendContext> performByIrFile(
description: String = "Perform phases by IrFile",
copyBeforeLowering: Boolean = true,
lower: List<CompilerPhase<Context, IrFile, IrFile>>,
): NamedCompilerPhase<Context, IrModuleFragment> =
NamedCompilerPhase(
): SameTypeNamedCompilerPhase<Context, IrModuleFragment> =
SameTypeNamedCompilerPhase(
name, description, emptySet(), PerformByIrFilePhase(lower, copyBeforeLowering), emptySet(), emptySet(), emptySet(),
setOf(defaultDumper), nlevels = 1,
)
@@ -60,7 +60,7 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
): IrModuleFragment {
for (irFile in input.files) {
try {
val filePhaserState = phaserState.changeType<IrModuleFragment, IrFile>()
val filePhaserState = phaserState.changePhaserStateType<IrModuleFragment, IrFile>()
for (phase in lower) {
phase.invoke(phaseConfig, filePhaserState, context, irFile)
}
@@ -97,7 +97,7 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
for ((irFile, state) in filesAndStates) {
executor.execute {
try {
val filePhaserState = state.changeType<IrModuleFragment, IrFile>()
val filePhaserState = state.changePhaserStateType<IrModuleFragment, IrFile>()
for (phase in lower) {
phase.invoke(phaseConfig, filePhaserState, context, irFile)
}
@@ -134,7 +134,7 @@ private class PerformByIrFilePhase<Context : CommonBackendContext>(
return input
}
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, NamedCompilerPhase<Context, *>>> =
override fun getNamedSubphases(startDepth: Int): List<Pair<Int, AbstractNamedCompilerPhase<Context, *, *>>> =
lower.flatMap { it.getNamedSubphases(startDepth) }
}