llvm-0.9.1.0: LLVM/Util/Optimize.hs
{-
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 Control.Monad(when)
import LLVM.Core.Util(Module, withModule)
import qualified LLVM.FFI.Core as FFI
-- import LLVM.FFI.Target(addTargetData, createTargetData)
import LLVM.FFI.Transforms.IPO
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
addLowerSetJmpPass 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
-- if optLevel > 1 then addFunctionInliningPass else const (return ())
let inline = addFunctionInliningPass
createStandardModulePasses passes optLevel True (optLevel > 1) True True inline
-- llvm/Support/StandardPasses.h: createStandardFunctionPasses
createStandardFunctionPasses :: FFI.PassManagerRef -> Int -> IO ()
createStandardFunctionPasses fPasses optLevel = do
when (optLevel > 0) $ do
addCFGSimplificationPass fPasses
if optLevel == 1
then addPromoteMemoryToRegisterPass fPasses
else addScalarReplAggregatesPass fPasses
addInstructionCombiningPass fPasses
-- llvm/Support/StandardPasses.h: createStandardModulePasses
createStandardModulePasses :: FFI.PassManagerRef -> Int -> Bool -> Bool -> Bool -> Bool -> (FFI.PassManagerRef -> IO()) -> IO ()
createStandardModulePasses passes optLevel unitAtATime unrollLoops simplifyLibCalls haveExceptions inliningPass = do
if optLevel == 0
then inliningPass passes
else do
when unitAtATime $ do
addGlobalOptimizerPass passes -- Optimize out global vars
addIPSCCPPass passes -- IP SCCP
addDeadArgEliminationPass passes -- Dead argument elimination
addInstructionCombiningPass passes -- Clean up after IPCP & DAE
addCFGSimplificationPass passes -- Clean up after IPCP & DAE
-- Start of CallGraph SCC passes.
when (unitAtATime && haveExceptions) $
addPruneEHPass passes -- Remove dead EH info
inliningPass passes
when unitAtATime $
addFunctionAttrsPass passes -- Set readonly/readnone attrs
when (optLevel > 2) $
addArgumentPromotionPass passes -- Scalarize uninlined fn args
-- Start of function pass.
addScalarReplAggregatesPass passes -- Break up aggregate allocas
when simplifyLibCalls $
addSimplifyLibCallsPass passes -- Library Call Optimizations
addInstructionCombiningPass passes -- Cleanup for scalarrepl.
addJumpThreadingPass passes -- Thread jumps.
addCFGSimplificationPass passes -- Merge & remove BBs
addInstructionCombiningPass passes -- Combine silly seq's
addTailCallEliminationPass passes -- Eliminate tail calls
addCFGSimplificationPass passes -- Merge & remove BBs
addReassociatePass passes -- Reassociate expressions
addLoopRotatePass passes -- Rotate Loop
addLICMPass passes -- Hoist loop invariants
-- The C interface does not allow to pass the optimizeForSize parameter
-- addLoopUnswitchPass(optimizeSize || optLevel < 3));
addInstructionCombiningPass passes
addIndVarSimplifyPass passes -- Canonicalize indvars
addLoopDeletionPass passes -- Delete dead loops
when unrollLoops $
addLoopUnrollPass passes -- Unroll small loops
addInstructionCombiningPass passes -- Clean up after the unroller
when (optLevel > 1) $
addGVNPass passes -- Remove redundancies
addMemCpyOptPass passes -- Remove memcpy / form memset
addSCCPPass passes -- Constant prop with SCCP
-- Run instcombine after redundancy elimination to exploit opportunities
-- opened up by them.
addInstructionCombiningPass passes
addJumpThreadingPass passes -- Thread jumps
-- Not available in C interface
-- addCorrelatedValuePropagationPass
addDeadStoreEliminationPass passes -- Delete dead stores
addAggressiveDCEPass passes -- Delete dead instructions
addCFGSimplificationPass passes -- Merge & remove BBs
when unitAtATime $ do
addStripDeadPrototypesPass passes -- Get rid of dead prototypes
addDeadTypeEliminationPass passes -- Eliminate dead types
-- GlobalOpt already deletes dead functions and globals, at -O3 try a
-- late pass of GlobalDCE. It is capable of deleting dead cycles.
when (optLevel > 2) $
addGlobalDCEPass passes -- Remove dead fns and globals.
when (optLevel > 1) $
addConstantMergePass passes -- Merge dup global constants
{-
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
-lowersetjmp addLowerSetJmpPass
-memcpyopt addMemCpyOptPass
-prune-eh addPruneEHPass
-reassociate addReassociatePass
-scalarrepl addScalarReplAggregatesPass
-sccp addSCCPPass
-simplifycfg addCFGSimplificationPass
-simplify-libcalls addSimplifyLibCallsPass
-strip-dead-prototypes addStripDeadPrototypesPass
-tailcallelim addTailCallEliminationPass
-verify addVerifierPass
-}