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

org.jetbrains.kotlin.backend.konan.driver.Machinery.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC2
Show newest version
/*
 * Copyright 2010-2024 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

import org.jetbrains.kotlin.backend.common.ErrorReportingContext
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.ConfigChecks
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.CommonConfigurationKeys

/**
 * Context is a set of resources that is shared between different phases. PhaseContext is a "minimal context",
 * effectively just a wrapper around [KonanConfig]. Still, it is more than enough in many cases.
 *
 * There is a fuzzy line between phase Input/Output and Context. We can consider them as a spectre:
 * * On the one end there is a [org.jetbrains.kotlin.backend.konan.Context] (circa 1.8.0). It has a lot of properties,
 * some even lateinit, which makes this object hard to construct and phases that depend on it are tightly coupled.
 * But we don't need to pass data between phases explicitly, which makes code easier to write.
 * * One the other end we can pass everything explicitly via I/O types. It will decouple code at the cost of boilerplate.
 *
 * So we have to find a point on this spectre for each phase.
 * We still don't have a rule of thumb for deciding whether object should be a part of context or not.
 * Some notes:
 * * Lifetime of context should be as small as possible: it reduces memory usage and forces a clean architecture.
 * * Frontend and backend are not really tied to IR and its friends, so we can pass more bytes via I/O.
 * * On the other hand, middle- and bitcode phases are hard to decouple due to the way the code was written many years ago.
 * It will take some time to rewrite it properly.
 */
internal interface PhaseContext : LoggingContext, ConfigChecks, ErrorReportingContext {

    /**
     * Called by [PhaseEngine.useContext] after action completion to cleanup resources.
     */
    fun dispose()
}

internal open class BasicPhaseContext(
        override val config: KonanConfig,
) : PhaseContext {
    override var inVerbosePhase = false

    override val messageCollector: MessageCollector
        get() = config.configuration.getNotNull(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY)

    override fun dispose() {

    }
}

/**
 * PhaseEngine is a heart of dynamic compiler driver. Unlike old static compiler driver that relies on predefined list of phases,
 * dynamic one requires user to write a sequence of phases by hand (thus "dynamic"). PhaseEngine provides a framework for that by tracking
 * phase configuration and state under the hood and exposing two methods:
 * * [runPhase], well, executes a given phase.
 * * [useContext] creates a child engine with a more specific [PhaseContext] that will be cleanup at the end of the call.
 * This way, PhaseEngine forces user to create more specialized contexts that have a limited lifetime.
 */
internal class PhaseEngine(
        val phaseConfig: PhaseConfigurationService,
        val phaserState: PhaserState,
        val context: C
) {
    companion object {
        fun startTopLevel(config: KonanConfig, body: (PhaseEngine) -> Unit) {
            val phaserState = PhaserState()
            val phaseConfig = config.flexiblePhaseConfig
            val context = BasicPhaseContext(config)
            val topLevelPhase = object : SimpleNamedCompilerPhase(
                    "Compiler",
                    "The whole compilation process",
            ) {
                override fun phaseBody(context: PhaseContext, input: Any) {
                    val engine = PhaseEngine(phaseConfig, phaserState, context)
                    body(engine)
                }

                override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: PhaseContext, input: Any) {
                    error("Compiler was disabled")
                }
            }
            topLevelPhase.invoke(phaseConfig, phaserState, context, Unit)
        }
    }

    /**
     * Switch to a more specific phase engine.
     */
    inline fun  useContext(newContext: T, action: (PhaseEngine) -> R): R {
        val newEngine = PhaseEngine(phaseConfig, phaserState, newContext)
        try {
            return action(newEngine)
        } finally {
            newContext.dispose()
        }
    }

    /**
     * Create a new PhaseEngine instance for an existing context that should not be disposed after the action.
     * This is useful for creating engines for a sub/super context type.
     */
    inline fun  newEngine(newContext: T, action: (PhaseEngine) -> R): R {
        val newEngine = PhaseEngine(phaseConfig, phaserState, newContext)
        return action(newEngine)
    }

    fun > runPhase(
            phase: P,
            input: Input,
            disable: Boolean = false
    ): Output {
        if (disable) {
            return phase.outputIfNotEnabled(phaseConfig, phaserState.changePhaserStateType(), context, input)
        }
        // We lose sticky postconditions here, but it should be ok, since type is changed.
        return phase.invoke(phaseConfig, phaserState.changePhaserStateType(), context, input)
    }


    fun > runPhase(
            phase: P,
    ): Output = runPhase(phase, Unit)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy