packages feed

tala-core-0.1.0: src/Progmasoft/Tala/Core/Optimizer/Pipeline.hs

-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-- SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1

module Progmasoft.Tala.Core.Optimizer.Pipeline (runOptimizationPipeline) where

import Progmasoft.Tala.Core
import Progmasoft.Tala.Core.Optimizer.Analysis (emptyEffectEnvironment)
import Progmasoft.Tala.Core.Optimizer.Constant
import Progmasoft.Tala.Core.Optimizer.ControlFlow
import Progmasoft.Tala.Core.Optimizer.EffectInference
import Progmasoft.Tala.Core.Optimizer.Inline
import Progmasoft.Tala.Core.Optimizer.Liveness
import Progmasoft.Tala.Core.Optimizer.Types

runOptimizationPipeline :: OptimizerOptions -> CoreModule -> OptimizationResult
runOptimizationPipeline options original =
    let maximumIterations = max 1 (optimizerMaximumIterations options)
        (optimized, iterations, converged, reports, inlineReports) = iteratePipeline maximumIterations 1 original [] []
     in OptimizationResult
            { optimizedCore = optimized
            , optimizationBefore = measureModule original
            , optimizationAfter = measureModule optimized
            , optimizationIterations = iterations
            , optimizationConverged = converged
            , optimizationPassReports = reports
            , optimizationEffectReports = finalEffectReports optimized
            , optimizationInlineReports = inlineReports
            }
    where
        finalEffectReports value
            | optimizerInterproceduralEffects options = snd (inferFunctionEffects value)
            | otherwise = []
        iteratePipeline maximumIterations iteration current reports inlineReports =
            let (next, currentReports, currentInlineReports) = runIteration options iteration current
                accumulated = reports ++ currentReports
                accumulatedInline = inlineReports ++ currentInlineReports
             in if next == current
                    then (next, iteration, True, accumulated, accumulatedInline)
                    else
                        if iteration >= maximumIterations
                            then (next, iteration, False, accumulated, accumulatedInline)
                            else iteratePipeline maximumIterations (iteration + 1) next accumulated accumulatedInline

runIteration :: OptimizerOptions -> Int -> CoreModule -> (CoreModule, [PassReport], [InlineReport])
runIteration options iteration input =
    let (_, effectReports) = inferFunctionEffects input
        (afterInlining, inliningReports, inlineReports) =
            runInlining
                (optimizerInlining options && optimizerInterproceduralEffects options)
                effectReports
                input
        (afterConstants, constantReports) =
            runEnabled
                (optimizerConstantPropagation options)
                ConstantPropagationPass
                propagateConstants
                afterInlining
        controlEnvironment = inferEnvironment afterConstants
        (afterControlFlow, controlFlowReports) =
            runEnabled
                (optimizerControlFlowSimplification options)
                ControlFlowSimplificationPass
                (simplifyControlFlowWith controlEnvironment)
                afterConstants
        deadCodeEnvironment = inferEnvironment afterControlFlow
        (afterDeadCode, deadCodeReports) =
            runEnabled
                (optimizerDeadCodeElimination options)
                DeadCodeEliminationPass
                (eliminateDeadCodeWith deadCodeEnvironment)
                afterControlFlow
     in (afterDeadCode, inliningReports ++ constantReports ++ controlFlowReports ++ deadCodeReports, inlineReports)
    where
        runInlining enabled effects before
            | not enabled = (before, [], [])
            | otherwise =
                let (after, inlineReport) = inlineFunctions (optimizerMaximumInlineExpressionNodes options) effects before
                 in ( after
                    , [PassReport iteration InliningPass (measureModule before) (measureModule after) (before /= after)]
                    , [inlineReport]
                    )
        inferEnvironment value
            | optimizerInterproceduralEffects options = fst (inferFunctionEffects value)
            | otherwise = emptyEffectEnvironment
        runEnabled enabled passName pass before
            | not enabled = (before, [])
            | otherwise =
                let after = pass before
                 in ( after
                    ,
                        [ PassReport
                            { passReportIteration = iteration
                            , passReportPass = passName
                            , passReportBefore = measureModule before
                            , passReportAfter = measureModule after
                            , passReportChanged = before /= after
                            }
                        ]
                    )