All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.backend.common.phaser.CompilerPhase.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2018 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.common.phaser

import org.jetbrains.kotlin.backend.common.LoggingContext
import kotlin.system.measureTimeMillis

/**
 * Represents global compilation context and stores information about phases that were executed.
 *
 * @property alreadyDone A set of already executed phases.
 * @property depth shows The index of the currently running phase.
 * @property phaseCount A unique ID that can show the order in which phases were executed.
 * @property stickyPostconditions A set of conditions that must be checked after each phase.
 * When a condition is added into [stickyPostconditions], it will be executed each time some phase is executed, until we change [Data].
 */
class PhaserState(
    val alreadyDone: MutableSet = mutableSetOf(),
    var depth: Int = 0,
    var phaseCount: Int = 0,
    val stickyPostconditions: MutableSet> = mutableSetOf()
) {
    fun copyOf() = PhaserState(alreadyDone.toMutableSet(), depth, phaseCount, stickyPostconditions)
}

// Copy state, forgetting the sticky postconditions (which will not be applicable to the new type)
fun  PhaserState.changePhaserStateType() = PhaserState(alreadyDone, depth, phaseCount, mutableSetOf())

inline fun  PhaserState.downlevel(nlevels: Int, block: () -> R): R {
    depth += nlevels
    val result = block()
    depth -= nlevels
    return result
}

/**
 * Represents some compiler phase that can be executed.
 */
interface CompilerPhase {
    /**
     * Executes this compiler phase. It accepts some parameter of type [Input] and transforms it into [Output].
     *
     * @param phaseConfig Controls which parts of the compilation pipeline are enabled and how the compiler should validate their invariants.
     * @param phaserState The global context.
     * @param context The local context in which the compiler stores all the necessary information for the given phase.
     */
    fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output

    fun getNamedSubphases(startDepth: Int = 0): List>> = emptyList()

    // In phase trees, `stickyPostconditions` is inherited along the right edge to be used in `then`.
    val stickyPostconditions: Set> get() = emptySet()
}

fun  CompilerPhase.invokeToplevel(
    phaseConfig: PhaseConfig,
    context: Context,
    input: Input
): Output = invoke(phaseConfig, PhaserState(), context, input)

interface SameTypeCompilerPhase : CompilerPhase

// A failing checker should just throw an exception.
typealias Checker = (Data) -> Unit

typealias AnyNamedPhase = AbstractNamedCompilerPhase<*, *, *>

enum class BeforeOrAfter { BEFORE, AFTER }

data class ActionState(
    val config: PhaseConfigurationService,
    val phase: AnyNamedPhase,
    val phaseCount: Int,
    val beforeOrAfter: BeforeOrAfter
)

typealias Action = (ActionState, Data, Context) -> Unit

infix operator fun  Action.plus(other: Action): Action =
    { phaseState, data, context ->
        this(phaseState, data, context)
        other(phaseState, data, context)
    }

// TODO: A better name would be just `NamedCompilerPhase`, but it is already used (see below).
abstract class AbstractNamedCompilerPhase(
    val name: String,
    val description: String,
    val prerequisite: Set> = emptySet(),
    val preconditions: Set> = emptySet(),
    val postconditions: Set> = emptySet(),
    protected val nlevels: Int = 0
) : CompilerPhase {
    override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output {
        if (!phaseConfig.isEnabled(this)) {
            return outputIfNotEnabled(phaseConfig, phaserState, context, input)
        }

        assert(phaserState.alreadyDone.containsAll(prerequisite)) {
            "Lowering $name: phases ${(prerequisite - phaserState.alreadyDone).map { it.name }} are required, but not satisfied"
        }

        context.inVerbosePhase = phaseConfig.isVerbose(this)

        runBefore(phaseConfig, phaserState, context, input)
        val output = if (phaseConfig.needProfiling) {
            runAndProfile(phaseConfig, phaserState, context, input)
        } else {
            phaserState.downlevel(nlevels) {
                phaseBody(phaseConfig, phaserState, context, input)
            }
        }
        runAfter(phaseConfig, changePhaserStateType(phaserState), context, input, output)

        phaserState.alreadyDone.add(this)
        phaserState.phaseCount++

        return output
    }

    abstract fun phaseBody(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output

    abstract fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output

    abstract fun changePhaserStateType(phaserState: PhaserState): PhaserState

    abstract fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input)

    abstract fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input, output: Output)

    private fun runAndProfile(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, 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(
    name: String,
    description: String,
    prerequisite: Set> = emptySet(),
    private val lower: CompilerPhase,
    preconditions: Set> = emptySet(),
    postconditions: Set> = emptySet(),
    override val stickyPostconditions: Set> = emptySet(),
    private val actions: Set> = emptySet(),
    nlevels: Int = 0
) : AbstractNamedCompilerPhase(
    name, description, prerequisite, preconditions, postconditions, nlevels
) {
    override fun phaseBody(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Data): Data =
        lower.invoke(phaseConfig, phaserState, context, input)

    override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Data): Data =
        input

    override fun changePhaserStateType(phaserState: PhaserState): PhaserState =
        phaserState

    override fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Data) {
        val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.BEFORE)
        for (action in actions) action(state, input, context)

        if (phaseConfig.checkConditions) {
            for (pre in preconditions) pre(input)
        }
    }

    override fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Data, output: Data) {
        val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.AFTER)
        for (action in actions) action(state, output, context)

        if (phaseConfig.checkConditions) {
            for (post in postconditions) post(output)
            for (post in stickyPostconditions) post(output)
            if (phaseConfig.checkStickyConditions) {
                for (post in phaserState.stickyPostconditions) post(output)
            }
        }
    }

    override fun getNamedSubphases(startDepth: Int): List>> =
        listOf(startDepth to this) + lower.getNamedSubphases(startDepth + nlevels)
}


typealias SameTypeNamedCompilerPhase = NamedCompilerPhase

/**
 * [AbstractNamedCompilerPhase] with different [Input] and [Output] types (unlike [SameTypeNamedCompilerPhase]).
 * Preffered when data should be explicitly passed between phases.
 * Actively used in a new dynamic Kotlin/Native driver.
 */
abstract class SimpleNamedCompilerPhase(
    name: String,
    description: String,
    prerequisite: Set> = emptySet(),
    preconditions: Set> = emptySet(),
    postconditions: Set> = emptySet(),
    private val preactions: Set> = emptySet(),
    private val postactions: Set, Context>> = emptySet(),
    nlevels: Int = 0,
) : AbstractNamedCompilerPhase(
    name,
    description,
    prerequisite,
    preconditions,
    postconditions,
    nlevels,
) {
    final override fun phaseBody(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output =
        phaseBody(context, input)

    abstract fun phaseBody(context: Context, input: Input): Output

    override fun changePhaserStateType(phaserState: PhaserState): PhaserState =
        phaserState.changePhaserStateType()

    override fun runBefore(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input) {
        val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.BEFORE)
        for (action in preactions) action(state, input, context)

        if (phaseConfig.checkConditions) {
            for (pre in preconditions) pre(input)
        }
    }

    override fun runAfter(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input, output: Output) {
        val state = ActionState(phaseConfig, this, phaserState.phaseCount, BeforeOrAfter.AFTER)
        for (action in postactions) action(state, input to output, context)

        if (phaseConfig.checkConditions) {
            for (post in postconditions) post(output)
            for (post in stickyPostconditions) post(output)
            if (phaseConfig.checkStickyConditions) {
                for (post in phaserState.stickyPostconditions) post(output)
            }
        }
    }

    override fun getNamedSubphases(startDepth: Int): List>> =
        listOf(startDepth to this)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy