module LLVM.Util.Optimize(optimizeModule) where

import LLVM.Core.Util (Module, withModule, getObjList)

import qualified LLVM.FFI.Transforms.PassManagerBuilder as PMB
import qualified LLVM.FFI.Core as FFI
import LLVM.FFI.Transforms.Scalar (addVerifierPass)

import Control.Exception (bracket, bracket_)


{- |
Result tells whether the module was modified by any of the passes.
-}
optimizeModule :: Int -> Module -> IO Bool
optimizeModule optLevel mdl =
    withModule mdl $ \ m ->
    {-
    Core.Util.createPassManager would provide a finalizer for us,
    but I think it is better here to immediately dispose the manager
    when we need it no longer.
    -}
    bracket FFI.createPassManager FFI.disposePassManager $ \mpasses ->
    bracket (FFI.createFunctionPassManagerForModule m)
            FFI.disposePassManager $ \fpasses -> do
        addVerifierPass mpasses

        bracket PMB.create PMB.dispose $ \passBuilder -> do
            PMB.setOptLevel passBuilder $ fromIntegral optLevel
            {-
            PMB.setSizeLevel passBuilder 1
            PMB.setDisableUnitAtATime passBuilder false
            PMB.setDisableUnrollLoops passBuilder (optLevel <= 1)
            PMB.setDisableSimplifyLibCalls passBuilder false
            PMB.setHaveExceptions passBuilder true
            PMB.setInliningPass passBuilder true
            -}

            PMB.populateFunctionPassManager passBuilder fpasses
            PMB.populateModulePassManager passBuilder mpasses

        fchanged <-
            bracket_
                (FFI.initializeFunctionPassManager fpasses)
                (FFI.finalizeFunctionPassManager fpasses)
                (mapM (FFI.runFunctionPassManager fpasses) =<<
                 getObjList withModule
                    FFI.getFirstFunction FFI.getNextFunction mdl)
        mchanged <- FFI.runPassManager mpasses m
        return $ any FFI.deconsBool $ mchanged : fchanged

{-
ToDo:
Function that adds passes according to a list of opt-options.
This would simplify to get consistent behaviour between opt and optimizeModule.

-adce                      addAggressiveDCEPass
-deadargelim               addDeadArgEliminationPass
-deadtypeelim              addDeadTypeEliminationPass
-dse                       addDeadStoreEliminationPass
-functionattrs             addFunctionAttrsPass
-globalopt                 addGlobalOptimizerPass
-indvars                   addIndVarSimplifyPass
-instcombine               addInstructionCombiningPass
-ipsccp                    addIPSCCPPass
-jump-threading            addJumpThreadingPass
-licm                      addLICMPass
-loop-deletion             addLoopDeletionPass
-loop-rotate               addLoopRotatePass
-memcpyopt                 addMemCpyOptPass
-prune-eh                  addPruneEHPass
-reassociate               addReassociatePass
-scalarrepl                addScalarReplAggregatesPass
-sccp                      addSCCPPass
-simplifycfg               addCFGSimplificationPass
-simplify-libcalls         addSimplifyLibCallsPass
-strip-dead-prototypes     addStripDeadPrototypesPass
-tailcallelim              addTailCallEliminationPass
-verify                    addVerifierPass
-}