Skip to content

Adding explicit optimization flags for interaction with GHC#11716

Open
zlonast wants to merge 8 commits into
haskell:masterfrom
zlonast:zlonast/optimizationCFlags
Open

Adding explicit optimization flags for interaction with GHC#11716
zlonast wants to merge 8 commits into
haskell:masterfrom
zlonast:zlonast/optimizationCFlags

Conversation

@zlonast
Copy link
Copy Markdown
Collaborator

@zlonast zlonast commented Apr 8, 2026

Cabal now adds explicit -optc-O2, -optcxx-O2 and -opta-O2 flags when
invoking GHC for C, C++ and assembler source files that are built with
optimization. These explicit flags are only inserted when the user has not
already provided an optimization flag (-O, -O0, -O1, -O2, -O3),
so that existing user choices are never overridden or duplicated.

Here are some examples

Example 1

no leakage between modules

library
  cc-options: -O3 -g3

executable Deduplication
  build-depends:
    , base
    , Deduplication
  cxx-options: -O1 -g1

to

Build profile: -w ghc-<GHCVER> -O1
exe: "-O1","-g0", ... ,"-optc-O2","-optc-g0","-optcxx-O1","-optcxx-g1","-opta-O2","-opta-g0"
lib: "-O1","-g0", ... ,"-optc-O3","-optc-g3","-optcxx-O2","-optcxx-g0","-opta-O2","-opta-g0",

expected behavior

Example 2

optimization: False
debug-info: True

to

Build profile: -w ghc-<GHCVER> -O1
exe/lib:"-O0","-g2", ... ,"-optc-O0","-optc-g2","-optcxx-O0","-optcxx-g2","-opta-O0","-opta-g2"

Example 3

implicit behavior -g0
to

"-O1","-g0" ... ,"-optc-O2","-optc-g0","-optcxx-O2","-optcxx-g0","-opta-O2","-opta-g0"

but

Build profile: -w ghc-<GHCVER> -O1

because
I don't want to contribute to all cabal tests.

Example 4

ghc-options: -O0

to

Build profile: -w ghc-<GHCVER> -O1
exe:"-O0","-g0", ... ,"-optc-O2","-optc-g0","-optcxx-O2","-optcxx-g0","-opta-O2","-opta-g0"
lib:"-O1","-g0", ... ,"-optc-O2","-optc-g0","-optcxx-O2","-optcxx-g0","-opta-O2","-opta-g0"

because it's local optimization

showBuildProfile: "-O" ++ fromOptimisationLevel (globalOptimization <> localOptimization)

Fan fact cabal codebase is all in OptimiSation and OptimiZation

Template Α: This PR modifies behaviour or interface

Include the following checklist in your PR:

Comment thread Cabal/src/Distribution/Simple/GHC/Internal.hs Outdated
@zlonast zlonast force-pushed the zlonast/optimizationCFlags branch 2 times, most recently from 374f8bf to 50720f6 Compare May 9, 2026 15:06
Comment thread cabal-install/src/Distribution/Client/ProjectOrchestration.hs
@zlonast zlonast force-pushed the zlonast/optimizationCFlags branch 12 times, most recently from 840a960 to c0b8ed7 Compare May 11, 2026 19:37
@zlonast
Copy link
Copy Markdown
Collaborator Author

zlonast commented May 11, 2026

How strange, I don't know what the problem is with BuildToolPaths and ghc 9.14.1

in FieldDescr
name
( \f -> case f of
Flag NoOptimisation -> Disp.text "False"
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest abandoning the display as Bool flags

| str == "0" -> ParseOk [] (Flag NoOptimisation)
| str == "1" -> ParseOk [] (Flag NormalOptimisation)
| str == "2" -> ParseOk [] (Flag MaximumOptimisation)
| lstr == "false" -> ParseOk [caseWarning name] (Flag NoOptimisation)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you stop throwing case sensitive warning

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jappeace @leana8959 I think you're knowledgeable about this topic. Perhaps you know why Bool type is a case-sensitive type?

instance Parsec Bool where
  parsec = P.munch1 isAlpha >>= postprocess
    where
      postprocess str
        | str == "True" = pure True
        | str == "False" = pure False
        | lstr == "true" = parsecWarning PWTBoolCase caseWarning *> pure True
        | lstr == "false" = parsecWarning PWTBoolCase caseWarning *> pure False
        | otherwise = fail $ "Not a boolean: " ++ str
        where
          lstr = map toLower str
          caseWarning =
            "Boolean values are case sensitive, use 'True' or 'False'."

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth noting that the current change doesn't remove warnings, but I think they're unnecessary in the cabal ecosystem.

Copy link
Copy Markdown
Collaborator

@jappeace jappeace May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I don't know.

it's introduced here:
92f018c#r185301031

(note that github helpfully collapses the line with my comment pointing it out, so you've to expand legacy to see it)
appears to be copied to other blocks as well.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to know more about the vibe, it looks like the rest of the syntax allows for case-insensitive variants.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can drop the warnings, appears to be a backwards compat scheme:

e92d6573#r185301031


the previous commit I found via UI but now since you asked for vibes I used a claude instance:

image

If you're interested this is the prompts I used: https://gist.github.com/jappeace/5e428936b633b2c6fed1c691d2e1c71e

-- optimisation or requests a different level elsewhere.
sourceOptimization :: OptimisationLevel -> [String]
sourceOptimization = \case
NoOptimisation -> ["-O0"]
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to put a clear flag instead of nothing NoOptimisation -> [], which logically fits the situation NoOptimisation -> ["-O0"]

"n"
(show NoOptimisation, Flag . flagToOptimisationLevel)
( \f -> case f of
Flag NoOptimisation -> []
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

render before: nothing -O -O2
render after: -O0 -O1 -O2

( \f -> case f of
Flag NoDebugInfo -> []
Flag MinimalDebugInfo -> [Just "1"]
Flag NormalDebugInfo -> [Nothing]
Copy link
Copy Markdown
Collaborator Author

@zlonast zlonast May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

render before: nothing -g1 -g -g3
render before: nothing -1 - -3
render after: -g0 -g1 -g2 -g3

| -- | @-O2@
GhcMaximumOptimisation
| -- | e.g. @-Odph@
GhcSpecialOptimisation String
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. ghc-flag:: -O⟨n⟩
:shortdesc: Any -On where n > 2 is the same as -O2.
:type: dynamic
:reverse: -O0
:category: optimization-levels

.. index::
   single: optimise; aggressively

Any -On where n > 2 is the same as -O2.

@zlonast zlonast force-pushed the zlonast/optimizationCFlags branch from c0b8ed7 to f8ee456 Compare May 12, 2026 11:22
@zlonast zlonast force-pushed the zlonast/optimizationCFlags branch from 1a43ec1 to a9db604 Compare May 12, 2026 18:54
@zlonast zlonast force-pushed the zlonast/optimizationCFlags branch from d87d3e0 to 65da48e Compare May 12, 2026 21:29
@zlonast
Copy link
Copy Markdown
Collaborator Author

zlonast commented May 13, 2026

I always try to pass flags, but it's better not to do this when linking, since for some reason cabal hooks die 🤔

@sheaf Could you please tell me if I'm trying to pass extra flags when linking. Is it my fault? Or is it ghc that's incorrectly passing them to gcc? Or is it cabal that's not filtering them? And where should I create an issue?

Because I can't always pass flags, I had to put a lot of NoFlag -> []

The only test where this appears is BuildToolPaths, which uses new cabal hooks.

https://github.com/haskell/cabal/actions/runs/25755559764/job/75643108252

  [1 of 3] Compiling SetupHooks       ( /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.hs, /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.o )
  [2 of 3] Compiling Main             ( /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/hooks.hs, /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/Main.o )
  [3 of 3] Linking /tmp/cabal-testsuite-121237/cabal.dist/work/./dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/hooks
  /usr/bin/ld: /tmp/cabal-testsuite-121237/cabal.dist/work/./dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.o: in function `SetupHooks_init__spt':
  (.text+0x370f): undefined reference to `rcrt_closure'
  /usr/bin/ld: (.text+0x371e): undefined reference to `rcrK_closure'
  collect2: error: ld returned 1 exit status
  ghc-9.14.1: Uncaught exception ghc-9.14.1-c6c3:GHC.Utils.Panic.GhcException:
  
  `gcc' failed in phase `Linker'. (Exit code: 1)
  
  While handling `gcc' failed in phase `Linker'. (Exit code: 1)
  
  HasCallStack backtrace:
    throwIO, called at libraries/exceptions/src/Control/Monad/Catch.hs:343:12 in exceptions-0.10.11-acb5:Control.Monad.Catch
    throwM, called at libraries/exceptions/src/Control/Monad/Catch.hs:855:84 in exceptions-0.10.11-acb5:Control.Monad.Catch
    onException, called at compiler/GHC/Driver/MakeAction.hs:235:23 in ghc-9.14.1-c6c3:GHC.Driver.MakeAction

@zlonast zlonast mentioned this pull request May 13, 2026
4 tasks
@sheaf
Copy link
Copy Markdown
Collaborator

sheaf commented May 13, 2026

I always try to pass flags, but it's better not to do this when linking, since for some reason cabal hooks die 🤔

@sheaf Could you please tell me if I'm trying to pass extra flags when linking. Is it my fault? Or is it ghc that's incorrectly passing them to gcc? Or is it cabal that's not filtering them? And where should I create an issue?

Because I can't always pass flags, I had to put a lot of NoFlag -> []

The only test where this appears is BuildToolPaths, which uses new cabal hooks.

https://github.com/haskell/cabal/actions/runs/25755559764/job/75643108252

  [1 of 3] Compiling SetupHooks       ( /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.hs, /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.o )
  [2 of 3] Compiling Main             ( /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/hooks.hs, /tmp/cabal-testsuite-121237/cabal.dist/work/dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/Main.o )
  [3 of 3] Linking /tmp/cabal-testsuite-121237/cabal.dist/work/./dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/hooks
  /usr/bin/ld: /tmp/cabal-testsuite-121237/cabal.dist/work/./dist/build/x86_64-linux/ghc-9.14.1/pbts-0.1.0.0/setup/SetupHooks.o: in function `SetupHooks_init__spt':
  (.text+0x370f): undefined reference to `rcrt_closure'
  /usr/bin/ld: (.text+0x371e): undefined reference to `rcrK_closure'
  collect2: error: ld returned 1 exit status
  ghc-9.14.1: Uncaught exception ghc-9.14.1-c6c3:GHC.Utils.Panic.GhcException:
  
  `gcc' failed in phase `Linker'. (Exit code: 1)
  
  While handling `gcc' failed in phase `Linker'. (Exit code: 1)
  
  HasCallStack backtrace:
    throwIO, called at libraries/exceptions/src/Control/Monad/Catch.hs:343:12 in exceptions-0.10.11-acb5:Control.Monad.Catch
    throwM, called at libraries/exceptions/src/Control/Monad/Catch.hs:855:84 in exceptions-0.10.11-acb5:Control.Monad.Catch
    onException, called at compiler/GHC/Driver/MakeAction.hs:235:23 in ghc-9.14.1-c6c3:GHC.Driver.MakeAction

Ah yes. I think this is GHC bug #16981/#24773. I think what's happening is that, with this patch, we start to compile the SetupHooks.hs with optimisations, so we trigger the bug that we didn't trigger beforehand.

Let me investigate and suggest a fix, but I imagine it will simply involve implementing the changes described in GHC proposal #732.

@sheaf
Copy link
Copy Markdown
Collaborator

sheaf commented May 13, 2026

I have managed to reproduce the issue with commit f8ee456, but only on Linux (not on Windows).

I tried to mitigate by moving things to the top-level but this didn't fix the isssue. So I think this is a new (different) bug in GHC. I will try to minimise and report it. Do you know which GHC versions are affected; is it only GHC 9.14 and not prior versions?

@zlonast
Copy link
Copy Markdown
Collaborator Author

zlonast commented May 13, 2026

I have managed to reproduce the issue with commit f8ee456, but only on Linux (not on Windows).

I tried to mitigate by moving things to the top-level but this didn't fix the isssue. So I think this is a new (different) bug in GHC. I will try to minimise and report it. Do you know which GHC versions are affected; is it only GHC 9.14 and not prior versions?

I don't know. There's only one complex hooks test here.

CI crashed consistently on all three platforms with ghc 9.14.1.

@zlonast
Copy link
Copy Markdown
Collaborator Author

zlonast commented May 13, 2026

CI windows are also failed c0b8ed7

@zlonast
Copy link
Copy Markdown
Collaborator Author

zlonast commented May 13, 2026

origin:
          case flagToMaybe (ghcOptOptimisation opts) of
            Nothing -> []
            Just GhcNoOptimisation -> ["-O0"]
            Just GhcNormalOptimisation -> ["-O"]
            Just GhcMaximumOptimisation -> ["-O2"]
            Just (GhcSpecialOptimisation s) -> ["-O" ++ s] -- eg -Odph
        , case flagToMaybe (ghcOptDebugInfo opts) of
            Nothing -> []
            Just NoDebugInfo -> []
            Just MinimalDebugInfo -> ["-g1"]
            Just NormalDebugInfo -> ["-g2"]
            Just MaximalDebugInfo -> ["-g3"]

my failed attempt:

         ["-O" ++ fromOptimisationLevel (ghcOptOptimisation opts)]
       , ["-g" ++ fromDebugInfoLevel (ghcOptDebugInfo opts)]

I suspect you can easy to catch it with change Nothing -> [] to Nothing -> ["-O1"] and Nothing -> [] to Nothing -> ["-g2"]

@sheaf
Copy link
Copy Markdown
Collaborator

sheaf commented May 13, 2026

I wrote a reproducer and found that the issue has been fixed on the GHC 10.0 branch; I assume by this commit.

I would recommend adding the following to the SetupHooks.hs module:

{-# LANGUAGE CPP #-}

-- Disable optimisations to work around GHC bug #16981 (fixed with GHC 10.0)
#if __GLASGOW_HASKELL__ < 1000
{-# OPTIONS_GHC -O0 #-}
#endif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants