[Git][ghc/ghc][wip/o0-fmax-simplifier-iterations-2] compiler: set -fmax-simplifier-iterations=2 for -O0
Cheng Shao pushed to branch wip/o0-fmax-simplifier-iterations-2 at Glasgow Haskell Compiler / GHC Commits: 0dacd039 by Cheng Shao at 2026-06-04T11:01:34+00:00 compiler: set -fmax-simplifier-iterations=2 for -O0 This patch sets `-fmax-simplifier-iterations=2` as the new default under `-O0` for faster compilation of unoptimized code. See added note for details. Closes #27326. - - - - - 4 changed files: - + changelog.d/ghc-o0-fmax-simplifier-iterations-2 - compiler/GHC/Driver/DynFlags.hs - compiler/GHC/Driver/Session.hs - docs/users_guide/using-optimisation.rst Changes: ===================================== changelog.d/ghc-o0-fmax-simplifier-iterations-2 ===================================== @@ -0,0 +1,13 @@ +section: compiler +synopsis: Set ``-fmax-simplifier-iterations=2`` for ``-O0`` +issues: #27326 +mrs: !16136 + +description: { + The maximum number of simplifier iterations + (``-fmax-simplifier-iterations``) is now tied to the optimisation + level: at most 2 iterations at ``-O0`` (previously 4) and still 4 at + ``-O1``/``-O2``. This speeds up unoptimised compilation. An explicit + ``-fmax-simplifier-iterations=⟨n⟩`` still overrides the default, as + long as it appears after the ``-O`` flag on the command line. +} ===================================== compiler/GHC/Driver/DynFlags.hs ===================================== @@ -576,7 +576,8 @@ defaultDynFlags mySettings = verbosity = 0, debugLevel = 0, simplPhases = 2, - maxSimplIterations = 4, + maxSimplIterations = 2, -- The -O0 default. 'updOptLevel' raises this to 4 at -O1/-O2. See + -- Note [Simplifier iterations and the optimisation level]. ruleCheck = Nothing, binBlobThreshold = Just 500000, -- 500K is a good default (see #16190) maxRelevantBinds = Just 6, ===================================== compiler/GHC/Driver/Session.hs ===================================== @@ -786,15 +786,45 @@ setInteractivePrint f d = d { interactivePrint = Just f} ----------------------------------------------------------------------------- -- Setting the optimisation level +{- Note [Simplifier iterations and the optimisation level] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +At -O0 the only Core-to-Core pass that runs is the final simplifier pass (see +'GHC.Core.Opt.Pipeline.getCoreToDo'); every optimisation-specific pass is gated +off. We therefore want as few simplifier iterations as possible at -O0, since +the later iterations only chase optimisation opportunities the user opted out of +by choosing -O0. + +We cannot drop to a *single* iteration, though. The final simplifier pass is +still load-bearing for correctness: a single iteration can leave a recursive +superclass dictionary with an unreduced superclass selector applied to it, which +then builds an infinite dictionary tower and diverges (<<loop>> at -O, OOM at +-O0). See #21973 and test T21973b. The fix needs (at least) two iterations: the +first inlines the dictionary functions to expose a constructor, and the second +reduces the superclass selector against it, making the recursive dictionary +dead. Empirically two iterations suffice across the known reproducers; the +simplifier offers no proof of a fixed bound, so we keep a small margin rather +than the bare minimum. + +So we tie 'maxSimplIterations' to the optimisation level: 2 iterations at -O0 +and 4 at -O1/-O2. This is a noticeable win for unoptimised compilation, and most +noticeable for the byte-code interpreter (GHCi), which defaults to -O0 and codegen +does less work compared to other backends. + +An explicit -fmax-simplifier-iterations=⟨n⟩ still overrides this, as long as it +appears after the -O flag on the command line (the usual flag-ordering rule for +options that -O adjusts). +-} + updOptLevelChanged :: Int -> DynFlags -> (DynFlags, Bool) -- ^ Sets the 'DynFlags' to be appropriate to the optimisation level and signals if any changes took place updOptLevelChanged n dfs - = (dfs3, changed1 || changed2 || changed3) + = (dfs4, changed1 || changed2 || changed3 || changed4) where final_n = max 0 (min 2 n) -- Clamp to 0 <= n <= 2 (dfs1, changed1) = foldr unset (dfs , False) remove_gopts (dfs2, changed2) = foldr set (dfs1, False) extra_gopts (dfs3, changed3) = setLlvmOptLevel dfs2 + (dfs4, changed4) = setSimplIterations dfs3 extra_gopts = [ f | (ns,f) <- optLevelFlags, final_n `elem` ns ] remove_gopts = [ f | (ns,f) <- optLevelFlags, final_n `notElem` ns ] @@ -814,6 +844,14 @@ updOptLevelChanged n dfs | llvmOptLevel dfs /= llvm_n = (dfs{ llvmOptLevel = llvm_n }, True) | otherwise = (dfs, False) + -- See Note [Simplifier iterations and the optimisation level] + simpl_iters = if final_n == 0 then 2 else 4 + + setSimplIterations dfs + | maxSimplIterations dfs /= simpl_iters = + (dfs{ maxSimplIterations = simpl_iters }, True) + | otherwise = (dfs, False) + updOptLevel :: Int -> DynFlags -> DynFlags -- ^ Sets the 'DynFlags' to be appropriate to the optimisation level updOptLevel n = fst . updOptLevelChanged n ===================================== docs/users_guide/using-optimisation.rst ===================================== @@ -914,11 +914,12 @@ as such you shouldn't need to set any of them explicitly. A flag :ghc-flag:`-Wincomplete-patterns` and :ghc-flag:`-Wincomplete-uni-patterns`. .. ghc-flag:: -fmax-simplifier-iterations=⟨n⟩ - :shortdesc: *default: 4.* Set the max iterations for the simplifier. + :shortdesc: *default: 2 at :ghc-flag:`-O0`, 4 otherwise.* Set the max iterations + for the simplifier. :type: dynamic :category: - :default: 4 + :default: 2 at :ghc-flag:`-O0`, 4 at :ghc-flag:`-O1` and :ghc-flag:`-O2` Sets the maximal number of iterations for the simplifier. View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0dacd03911ce1e8dd38771797e7b56bb... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0dacd03911ce1e8dd38771797e7b56bb... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Cheng Shao (@TerrorJack)