content: {-
  LLVM does not export its functions
  @createStandardFunctionPasses@ and
  @createStandardModulePasses@ via its C interface
  and interfacing to C-C++ wrappers is not very portable.
  Thus we reimplement these functions
  from @opt.cpp@ and @StandardPasses.h@ in Haskell.
  However this way we risk inconsistencies
  between 'optimizeModule' and the @opt@ shell command.
  -}
  module LLVM.Util.Optimize(optimizeModule) where
  
  import LLVM.Core.Util(Module, withModule)
  import qualified LLVM.FFI.Core as FFI
  import qualified LLVM.FFI.Support as FFI
  import LLVM.FFI.Transforms.Scalar
  import Control.Exception (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 $ \ passes ->
  
  {-
  Note on LLVM-2.6 to 2.8 (at least):
  As far as I understand, if we do not set target data,
  then the optimizer will only perform machine independent optimizations.
  If we set target data
  (e.g. an empty layout string obtained from a module without 'target data' specification.)
  we risk that the optimizer switches to a wrong layout
  (e.g. to 64 bit pointers on a 32 bit machine for empty layout string)
  and thus generates corrupt code.
  
  Currently it seems to be safer to disable
  machine dependent optimization completely.
  
  http://llvm.org/bugs/show_bug.cgi?id=6394
  
      -- Pass the module target data to the pass manager.
      target <- FFI.getDataLayout m >>= createTargetData
      addTargetData target passes
  -}
  
      {-
      opt.cpp does not use a FunctionPassManager for function optimization,
      but a module PassManager.
      Thus we do it the same way.
      I assume that we would need a FunctionPassManager
      only if we wanted to apply individual optimizations to functions.
  
      fPasses <- FFI.createFunctionPassManager mp
      -}
      bracket FFI.createPassManager FFI.disposePassManager $ \ fPasses -> do
      -- add module target data?
  
      -- tools/opt/opt.cpp: AddStandardCompilePasses
      addVerifierPass passes
      addOptimizationPasses passes fPasses optLevel
  
      {- if we wanted to do so, we could loop through all functions and optimize them.
      initializeFunctionPassManager fPasses
      runFunctionPassManager fPasses fcn
      -}
  
      functionsModified <- FFI.runPassManager fPasses m
  
      moduleModified <- FFI.runPassManager passes m
  
      return $
         toEnum (fromIntegral moduleModified) ||
         toEnum (fromIntegral functionsModified)
  
  -- tools/opt/opt.cpp: AddOptimizationPasses
  addOptimizationPasses :: FFI.PassManagerRef -> FFI.PassManagerRef -> Int -> IO ()
  addOptimizationPasses passes fPasses optLevel = do
    createStandardFunctionPasses fPasses optLevel
    createStandardModulePasses passes optLevel True True (optLevel > 1) True True True
  
  createStandardFunctionPasses :: FFI.PassManagerRef -> Int -> IO ()
  createStandardFunctionPasses fPasses optLevel =
      FFI.createStandardFunctionPasses fPasses (fromIntegral optLevel)
  
  -- llvm/Support/StandardPasses.h: createStandardModulePasses
  createStandardModulePasses :: FFI.PassManagerRef -> Int -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> IO ()
  createStandardModulePasses passes optLevel optSize unitAtATime unrollLoops simplifyLibCalls haveExceptions inliningPass =
    FFI.createStandardModulePasses passes (fromIntegral optLevel) (f optSize)
       (f unitAtATime) (f unrollLoops) (f simplifyLibCalls) (f haveExceptions)
       (f (not inliningPass))
    where f True = 1
          f _    = 0
  
  
  {-
  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
  -}
  