-- Host program optimization. Some passes rely on normalized monadic structure,
-- so we perform an initial normalization pass and then do another final
-- normalization pass after other optimizations have had a chance to run.

optimizeHostProgram :: Exp -> R r Exp
optimizeHostProgram =
    return
    >=> pprStage "Start"
    >=> oPass (norm ExpA)
    -- >=> whenDialect CUDA (oPass (mergeParfor ExpA))
    >=> oPass (shareBindings ExpA)
    >=> aPass (mergeBounds ExpA)
    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 1"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 1"
    >=> cse ExpA
    >=> pprStage "CSE pass 1"
    >=> oPass (occ ExpA)
    >=> pprStage "Occ pass 1"
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 1"

    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 2"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 2"
    >=> cse ExpA
    >=> pprStage "CSE pass 2"
    >=> oPass (occ ExpA)
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 2"

    >=> whenDialect CUDA (constructKernels ExpA)
    >=> oPass (lambdaLift ExpA)
optimizeHostProgram :: Exp -> R r Exp
optimizeHostProgram =
    return
    >=> pprStage "Start"
    >=> oPass (norm ExpA)
    -- >=> whenDialect CUDA (oPass (mergeParfor ExpA))
    >=> oPass (shareBindings ExpA)
    >=> aPass (mergeBounds ExpA)
    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 1"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 1"
    >=> cse ExpA
    >=> pprStage "CSE pass 1"
    >=> oPass (occ ExpA)
    >=> pprStage "Occ pass 1"
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 1"

    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 2"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 2"
    >=> cse ExpA
    >=> pprStage "CSE pass 2"
    >=> oPass (occ ExpA)
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 2"

    >=> whenDialect CUDA (constructKernels ExpA)
    >=> oPass (lambdaLift ExpA)
optimizeHostProgram =
    return
    >=> pprStage "Start"
    >=> oPass (norm ExpA)
    -- >=> whenDialect CUDA (oPass (mergeParfor ExpA))
    >=> oPass (shareBindings ExpA)
    >=> aPass (mergeBounds ExpA)
    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 1"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 1"
    >=> cse ExpA
    >=> pprStage "CSE pass 1"
    >=> oPass (occ ExpA)
    >=> pprStage "Occ pass 1"
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 1"

    >=> oPass (norm ExpA)

    >=> pprStage "Pre-simplify pass 2"
    >=> oPass (simpl ExpA)
    >=> pprStage "Simplify pass 2"
    >=> cse ExpA
    >=> pprStage "CSE pass 2"
    >=> oPass (occ ExpA)
    >=> oPass (inliner ExpA)
    >=> pprStage "Inliner pass 2"

    >=> whenDialect CUDA (constructKernels ExpA)
    >=> oPass (lambdaLift ExpA)