GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2024-03-27T17:08:50Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/24462Exponentially increasing compilation w/ Higher-Kinded Data under 9.8.12024-03-27T17:08:50Zm4dc4pExponentially increasing compilation w/ Higher-Kinded Data under 9.8.1## Summary
GHC 9.8.1 takes increasingly longer (and uses radically more memory) to compile a module as fields are added to a data type; the same did not occur with GHC 9.6.
## Steps to reproduce
The repo at https://github.com/m4dc4p/g...## Summary
GHC 9.8.1 takes increasingly longer (and uses radically more memory) to compile a module as fields are added to a data type; the same did not occur with GHC 9.6.
## Steps to reproduce
The repo at https://github.com/m4dc4p/ghc98-bug can demonstrate the issue. You can just run `cabal build` to try it.
The file `AAAX.hs` contains the `HKDType` data type. As you uncomment fields, the compilation times (`cabal build`) take longer and longer.
For example, on my M2, with GHC 9.8, I get these timings & peak memory usage:
* 1 field - 2.5s, 198MB peak
* 2 fields - 7s, 1.0GB peak
* 3 fields - 26.8s, 4.6GB peak
* 4 fields - 82.9s, 14.5GB peak
Note that with -O0, there is no problem - compilation time does not increase. Also, removing the standalone `Eq` instance also causes the problem to go away (but, of course, I want that instance).
Under GHC 9.6, with -O1, compilation times do not increase either (even up to 10 fields on the data type).
I found this on MacOS 14.2.1, using an M2 chip.
(Note I also reported this as a bug against the Barbies library - https://github.com/jcpetruzza/barbies/issues/51)9.8.3Simon Peyton JonesSimon Peyton Joneshttps://gitlab.haskell.org/ghc/ghc/-/issues/24401fixIO can choke, and report a loop where there is none2024-02-06T15:13:14ZSimon Peyton JonesfixIO can choke, and report a loop where there is noneThis program was written by [Matthew Craven](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1907226501):
```
import System.IO( fixIO )
import GHC.Exts (noinline)
main :: IO ()
main = do
r <- fixIO $ \p -> ...This program was written by [Matthew Craven](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1907226501):
```
import System.IO( fixIO )
import GHC.Exts (noinline)
main :: IO ()
main = do
r <- fixIO $ \p -> let
{-# NOINLINE q #-}
q = noinline id p
in pure (True : q)
print $! case r of { _:v:_ -> v ; _ -> False }
```
For any law-abiding MonadFix instance, such a call to `mfix` should be semantically equivalent to `pure (repeat True)`. (Specifically, this follows from the 'Purity' law.)
So the `print` should work fine. But actually we get (with -O)
```
run-it: cyclic evaluation in fixIO
```
## Diagnosis
What is going on? Here is the code after optimisation:
```
Main.main1
= \ (s_aRX :: GHC.Prim.State# GHC.Prim.RealWorld) ->
case GHC.Prim.newMVar#
@GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool] s_aRX
of
{ (# ipv_aUk, ipv1_aUl #) ->
case GHC.IO.Unsafe.unsafeDupableInterleaveIO1
@[Bool]
((\ (eta_aSp [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
GHC.Prim.catch# @GHC.Types.LiftedRep @GHC.Types.Lifted @[Bool]
@GHC.Exception.Type.SomeException
(\ (eta1_aUj [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
GHC.Prim.readMVar#
@GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool] ipv1_aUl eta1_aUj)
(System.IO.fixIO2 @[Bool])
eta_aSp)
`cast` (Sym (GHC.Types.N:IO[0] <[Bool]>_R)
:: (GHC.Prim.State# GHC.Prim.RealWorld
-> (# GHC.Prim.State# GHC.Prim.RealWorld, [Bool] #))
~R# IO [Bool]))
ipv_aUk
of
{ (# ipv2_aUp, ipv3_aUq #) ->
let {
q_s1HX [InlPrag=NOINLINE, Dmd=SL] :: [Bool]
q_s1HX = noinline @(forall a. a -> a) id @[Bool] ipv3_aUq } in
case GHC.Prim.putMVar# @GHC.Types.Lifted @GHC.Prim.RealWorld @[Bool]
ipv1_aUl
(GHC.Types.: @Bool GHC.Types.True q_s1HX)
ipv2_aUp
of s2#_aUw
{ __DEFAULT ->
case q_s1HX of {
[] ->
GHC.IO.Handle.Text.hPutStr2
GHC.IO.Handle.FD.stdout
GHC.Show.$fShowBool5
GHC.Types.True
s2#_aUw;
: v_aJ6 ds2_dRE ->
case v_aJ6 of vx_aV1 { __DEFAULT ->
GHC.IO.Handle.Text.hPutStr2
GHC.IO.Handle.FD.stdout
(case vx_aV1 of {
False -> GHC.Show.$fShowBool5;
True -> GHC.Show.$fShowBool4
})
GHC.Types.True
s2#_aUw
} } } } }
```
* The MVar stuff is to "tie the knot" in `fixIO`.
* The `putMVar#` fills in the MVar
* The `unsafeDupableInterleaveIO1` call returns a thunk in `ipv3` which reads the MVar when forced.
Now, **alas** the strictness analyser works out that `q` i used strictly in the continuation, so
the binding for `q` is done with a `case` not a `let` (look at CorePrep output). So we force `ipv3` too
early, before the `putMVar#` has run.
## Cure
The code for `fixIO` is this:
```
fixIO :: (a -> IO a) -> IO a
fixIO k = do
m <- newEmptyMVar
ans <- unsafeDupableInterleaveIO
(readMVar m `catch` \BlockedIndefinitelyOnMVar ->
throwIO FixIOException)
result <- k ans
putMVar m result
return result
```
Why all this MVar stuff? See `Note [fixST]` in `base:Control.Monad.ST.Imp`.
(And [this comment](https://github.com/haskell/core-libraries-committee/issues/242#issuecomment-1902317732).)
Looking at this code it's clear that we must do the `putMVar` before evaluating `result`.
But if we inline `fixIO`, the consumer will consume the `return result`, and the consumer
may well be strict in `result`. Something like
```
do { result <- fixIO m
; f result } -- where f is strict
```
Two simple solutions
* Do not inline `fixIO`.
* Wrap the result of `fixIO` in `lazy`, thus
```
putMVar m restult
return (lazy result)
```
I am not sure which is best. But the `lazy` solution is the one adopted by `Note [unsafePerformIO and strictness]` in GHC.IO.Unsafe, for a very very similar problem.https://gitlab.haskell.org/ghc/ghc/-/issues/24340Question: Is forcing this value in WHNF avoidable?2024-01-23T15:32:45ZmeooowQuestion: Is forcing this value in WHNF avoidable?## Summary
In `containers` we have
```hs
data Map k a = Bin !Int !k a !(Map k a) !(Map k a)
| Tip
```
And we can find the minimum value in the map as follows.
```hs
data KV k a = KV !k a
lookupMin :: Map k a -> Maybe (K...## Summary
In `containers` we have
```hs
data Map k a = Bin !Int !k a !(Map k a) !(Map k a)
| Tip
```
And we can find the minimum value in the map as follows.
```hs
data KV k a = KV !k a
lookupMin :: Map k a -> Maybe (KV k a)
lookupMin Tip = Nothing
lookupMin (Bin _ k x l _) = Just $! lookupMinSure k x l
lookupMinSure :: k -> a -> Map k a -> KV k a
lookupMinSure k x Tip = KV k x
lookupMinSure _ _ (Bin _ k x l _) = lookupMinSure k x l
```
This leads to this core for `lookupMinSure`.
```hs
Rec {
-- RHS size: {terms: 19, types: 26, coercions: 0, joins: 0/0}
SetMin.$wlookupMinSure [InlPrag=[2], Occ=LoopBreaker]
:: forall {k} {a}. k -> a -> Map k a -> (# k, a #)
[GblId[StrictWorker([~, ~, !])],
Arity=3,
Str=<ML><L><1L>,
Unf=OtherCon []]
SetMin.$wlookupMinSure
= \ (@k) (@a) (k1 :: k) (x :: a) (ds :: Map k a) ->
case ds of {
Bin bx k2 x1 l ds1 -> SetMin.$wlookupMinSure @k @a k2 x1 l;
Tip -> case k1 of conrep { __DEFAULT -> (# conrep, x #) }
}
end Rec }
```
Notice that there is a `case k1 of conrep` which forces the key. This should not be necessary, since the map is strict in the key.
@treeowl suggested trying this alternative:
```hs
data UBox :: Type -> UnliftedType where
UBox :: !a -> UBox a
lookupMin' :: Map k a -> Maybe (KV k a)
lookupMin' Tip = Nothing
lookupMin' (Bin _ k x l _) = Just $! lookupMinSure' (UBox k) x l
lookupMinSure' :: UBox k -> a -> Map k a -> KV k a
lookupMinSure' (UBox k) x Tip = KV k x
lookupMinSure' _ _ (Bin _ k x l _) = lookupMinSure' (UBox k) x l
```
This generates the core
```hs
Rec {
-- RHS size: {terms: 19, types: 26, coercions: 0, joins: 0/0}
SetMin.$wlookupMinSure' [InlPrag=[2], Occ=LoopBreaker]
:: forall {k} {a}. k -> a -> Map k a -> (# k, a #)
[GblId[StrictWorker([!, ~, !])],
Arity=3,
Str=<1L><L><1L>,
Unf=OtherCon []]
SetMin.$wlookupMinSure'
= \ (@k) (@a) (ww :: k) (x :: a) (ds :: Map k a) ->
case ww of ww1 { __DEFAULT ->
case ds of {
Bin ipv ipv1 ipv2 ipv3 ipv4 ->
SetMin.$wlookupMinSure' @k @a ipv1 ipv2 ipv3;
Tip -> (# ww1, x #)
}
}
end Rec }
```
This still forces the key, but now on every iteration.
Questions:
* Why does the `UBox` version force the key on every iteration?
* Is it possible to implement this in a way that GHC realizes that forcing they key is not necessary?
Original discussion on `containers` for reference: [#976](https://github.com/haskell/containers/pull/976).
## Steps to reproduce
Compile the code above.
## Environment
* GHC version used: 9.6.3https://gitlab.haskell.org/ghc/ghc/-/issues/24263Precise exceptions: `stToIO` and `ioToST` can circumvent analysis in Note [Wh...2024-01-22T04:13:40ZSebastian GrafPrecise exceptions: `stToIO` and `ioToST` can circumvent analysis in Note [Which scrutinees may throw precise exceptions]## Summary
The analysis described in `Note [Which scrutinees may throw precise exceptions]` is oblivious wrt. computations in `forall s. ... State# s ...`, even though it triggers for its instance `State# RealWorld#`.
This means we can...## Summary
The analysis described in `Note [Which scrutinees may throw precise exceptions]` is oblivious wrt. computations in `forall s. ... State# s ...`, even though it triggers for its instance `State# RealWorld#`.
This means we can circumvent the analysis by defining most of the code in `ST s r`, parameterising over computations that are actually in `IO`,
and then run the supposedly pure `ST s r` computation with those impure `IO` actions supplied as parameters.
## Steps to reproduce:
Compile and run with -O:
```hs
import GHC.IO
import GHC.ST
foo :: Int -> ST s r -> ST s Int
foo a act = act >> (pure $! a+1)
{-# NOINLINE foo #-}
main = stToIO $ foo (error "Not OK") (ioToST (throwIO (mkUserError "OK")))
```
```
test: Not OK
```
## Expected behavior
```
test: user error (OK)
```
## Discussion
It is questionable whether this is actually desirable. Potentially, the performance of every morally pure `ST s a` computation such as `foo` might be affected for the worse: We may not ever use call-by-value for `foo`, unless it forces `a` before calling `act`.
I'm inclined to simply ignore this issue.
(CC) @clyring and @simonpj with whom I discussed this issue today.https://gitlab.haskell.org/ghc/ghc/-/issues/24251`filter (const False)` leaks with -O22024-03-27T10:38:59Zakegalj`filter (const False)` leaks with -O2See also
* #15631
* [This discussion on !10987](https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10987#note_538951)
* #21741
* #24334
## Summary
Program:
```haskell
main = print $ filter (const False) [0..]
```
leaks when compiled...See also
* #15631
* [This discussion on !10987](https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10987#note_538951)
* #21741
* #24334
## Summary
Program:
```haskell
main = print $ filter (const False) [0..]
```
leaks when compiled with `ghc -O2`. It doesn't leak when compiled with `ghc -O0`
## Steps to reproduce
```bash
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.8.1
$ ghc -O2 Test.hs
$ ./Test
```
## Expected behavior
When compiled with `-O2` and run it should loop with constant space.
## Environment
* GHC version used: 9.8.1, 9.4.8
Optional:
* Operating System: NixOS 23.11.750.7c4c20509c43 (Tapir)
* System Architecture: x86_64 Intel(R) Core(TM) i5-2520Makegaljakegaljhttps://gitlab.haskell.org/ghc/ghc/-/issues/24203GHC is reluctant to unbox Int with -O when returned in a sum field2023-11-22T08:17:59ZmeooowGHC is reluctant to unbox Int with -O when returned in a sum field## Summary
### Example 1
I have some code like
```hs
{-# LANGUAGE BangPatterns #-}
module M where
foo :: [Int] -> Maybe Int
foo = go 1 0
where
go :: Int -> Int -> [Int] -> Maybe Int
go !i !n [] = Just n
go i n (x:xs)
...## Summary
### Example 1
I have some code like
```hs
{-# LANGUAGE BangPatterns #-}
module M where
foo :: [Int] -> Maybe Int
foo = go 1 0
where
go :: Int -> Int -> [Int] -> Maybe Int
go !i !n [] = Just n
go i n (x:xs)
| i < 10 = go (i+1) (n+x) xs
| otherwise = Nothing
```
If I compile it with -O on GHC 9.4.8 or 9.6.3, `n` is not unboxed even with the bang.
<details>
<summary>Core:</summary>
```hs
-- RHS size: {terms: 31, types: 16, coercions: 0, joins: 0/0}
M.$wgo [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Prim.Int# -> Int -> [Int] -> Maybe Int
[GblId[StrictWorker([~, !, !])],
Arity=3,
Str=<L><1L><1L>,
Unf=OtherCon []]
M.$wgo
= \ (ww_sNC :: GHC.Prim.Int#) (n_sNE :: Int) (ds_sNF :: [Int]) ->
case n_sNE of n1_X1 { GHC.Types.I# ipv_sN7 ->
case ds_sNF of {
[] -> GHC.Maybe.Just @Int n1_X1;
: ipv1_sN9 ipv2_sNa ->
case GHC.Prim.<# ww_sNC 10# of {
__DEFAULT -> GHC.Maybe.Nothing @Int;
1# ->
case ipv1_sN9 of { GHC.Types.I# y_aNl ->
M.$wgo
(GHC.Prim.+# ww_sNC 1#)
(GHC.Types.I# (GHC.Prim.+# ipv_sN7 y_aNl))
ipv2_sNa
}
}
}
}
```
</details>
[Haskell playground link](https://play.haskell.org/saved/lOdYT1ZQ)
### Example 2
```hs
module M where
foo :: [Int] -> Maybe Int
foo [] = Nothing
foo xs = Just $! sum xs
```
The accumulator in `sum` remains boxed.
[Haskell playground link](https://play.haskell.org/saved/eM7gcscU)
This is not the case with GHC 9.2.8. Using -O2 with 9.4.8 and 9.6.3 also unboxes it.
Is this intended behavior?
## Steps to reproduce
Compile the snippet above.
## Expected behavior
`n` is unboxed.
## Environment
* GHC version used: 9.4.8, 9.6.3https://gitlab.haskell.org/ghc/ghc/-/issues/23911`noinline` worsens demand signatures when it should not.2023-09-05T14:20:53ZAndreas Klebinger`noinline` worsens demand signatures when it should not.In terms of demands I would have expected the variants of `foo` and `bar` to behave the same:
```haskell
{-# NOINLINE foo #-}
foo x y = (noinline const) x y
{-# NOINLINE bar #-}
bar x y = const x y
{-# NOINLINE sfoo #-}
sfoo x y = (n...In terms of demands I would have expected the variants of `foo` and `bar` to behave the same:
```haskell
{-# NOINLINE foo #-}
foo x y = (noinline const) x y
{-# NOINLINE bar #-}
bar x y = const x y
{-# NOINLINE sfoo #-}
sfoo x y = (noinline (+)) x y :: Int
{-# NOINLINE sbar #-}
sbar x y = x + y :: Int
```
However `noinline` completely destroys the demand information:
```haskell
==================== Tidy Core ====================
Result size of Tidy Core
= {terms: 21, types: 36, coercions: 0, joins: 0/0}
-- RHS size: {terms: 8, types: 11, coercions: 0, joins: 0/0}
foo [InlPrag=NOINLINE] :: forall {t1} {t2}. t1 -> t2 -> t1
[GblId, Arity=2, Str=<L><L>, Unf=OtherCon []]
foo
= \ (@t_aCy) (@t1_aCu) (x_aiJ :: t_aCy) (y_aiK :: t1_aCu) ->
noinline
@(forall a b. a -> b -> a) const @t_aCy @t1_aCu x_aiJ y_aiK
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
bar [InlPrag=NOINLINE] :: forall {a} {b}. a -> b -> a
[GblId, Arity=2, Str=<1L><A>, Unf=OtherCon []]
bar = const
-- RHS size: {terms: 7, types: 9, coercions: 0, joins: 0/0}
sfoo [InlPrag=NOINLINE] :: Int -> Int -> Int
[GblId, Arity=2, Str=<L><L>, Unf=OtherCon []]
sfoo
= \ (x_aiS :: Int) (y_aiT :: Int) ->
noinline
@(forall a. Num a => a -> a -> a)
+
@Int
GHC.Num.$fNumInt
x_aiS
y_aiT
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
sbar [InlPrag=NOINLINE] :: Int -> Int -> Int
[GblId, Arity=2, Str=<1!P(L)><1!P(L)>, Cpr=1, Unf=OtherCon []]
sbar = GHC.Num.$fNumInt_$c+
```
It seems very reasonale to have a special case for noinline wich works something like:
`dmdAnal' env dmd <noinline @type f> = dmdAnal' env dmd f`.https://gitlab.haskell.org/ghc/ghc/-/issues/23910Replace absent lazy function arguments with rubbishLit even without W/W.2023-09-05T14:21:36ZAndreas KlebingerReplace absent lazy function arguments with rubbishLit even without W/W.Currently for something like `const` (assuming it isn't inlined) we often see code like this:
```haskell
foo x y z = .... const x y ....
```
W/W would iirc transform this into:
```haskell
foo x y z = $wfoo x z
$wfoo = ... const x <...Currently for something like `const` (assuming it isn't inlined) we often see code like this:
```haskell
foo x y z = .... const x y ....
```
W/W would iirc transform this into:
```haskell
foo x y z = $wfoo x z
$wfoo = ... const x <rubbishLit> ...
```
Which means y` isn't unnecessarily retained. However there seems little reason to limit this to worker wrapper.
Even without worker wrapper if we have
```haskell
foo x y z = .... const x y ....
```
it should be fine to rewrite this into:
```haskell
foo x y z = .... const x <rubbishLit> ....
```
It's possible I'm missing something but I think that should be fine and allow us to gc `y` as soon as we entered `foo`.https://gitlab.haskell.org/ghc/ghc/-/issues/23699Regression in the optimizer of GHC 9.6 concerning `unsafePerformIO`2024-02-06T18:08:20ZAndreas AbelRegression in the optimizer of GHC 9.6 concerning `unsafePerformIO`I noticed a regression in the optimizer of GHC 9.6 (over 9.4) concerning `unsafePerformIO` (used for debug messages).
The reproducer for this issue is the `master` branch of this repo: https://github.com/andreasabel/regression-ghc-9.6-un...I noticed a regression in the optimizer of GHC 9.6 (over 9.4) concerning `unsafePerformIO` (used for debug messages).
The reproducer for this issue is the `master` branch of this repo: https://github.com/andreasabel/regression-ghc-9.6-unsafePerformIO
With GHC 9.6.2, a debug message that is printed with `-O0` is not printed with `-O1`.
```shellsession
$ cabal run agda -w ghc-9.6.2 --builddir=dist0 -O0
ReduceM SHOULD ALSO PRINT THIS DEBUG MESSAGE!!!!!!!!!!!!! LET'S MAKE IT VERY LONG SO IT CANNOT BE OVERLOOKED!!!!!!!!!!!!!!!!!!!
agda: An internal error has occurred. Please report this as a bug.
Location of the error: __IMPOSSIBLE_VERBOSE__, called at src/full/Agda/ImpossibleTest.hs:22:11 in Agda-2.6.4-inplace-agda:Agda.ImpossibleTest
impossibleTestReduceM, called at src/main/Main.hs:32:5 in Agda-2.6.4-inplace-agda:Main
$ cabal run agda -w ghc-9.6.2 -O1
agda: An internal error has occurred. Please report this as a bug.
Location of the error: __IMPOSSIBLE_VERBOSE__, called at src/full/Agda/ImpossibleTest.hs:22:11 in Agda-2.6.4-inplace-agda:Agda.ImpossibleTest
impossibleTestReduceM, called at src/main/Main.hs:32:5 in Agda-2.6.4-inplace-agda:Main
```
Note that the separation of these builds in to two different `builddir`s is necessary because Cabal might confuse the builds otherwise:
- https://github.com/haskell/cabal/issues/7711
I shrank Agda from 426 modules to 142, it can likely be further shrunk (to 2 or 3 modules), but one has to proceed with care, as inlining code might make the issue go away.
Here are some branches where the issue has disappeared:
1. `lost-issue-inlining-impossibleTest`: inlining the module `Agda.ImpossibleTest`
2. `lost-issue-inlining-reportSLn`: inlining `reportSLn` into `__IMPOSSIBLE_VERBOSE__`
The latter modification was used to fix the motivating issue:
- https://github.com/agda/agda/issues/6728
- fixed in https://github.com/agda/agda/pull/6710/commits/4de3e5353cadd9a8e448af5f781701fa214ce9f3
This GHC issue for 8.8 and up might be related:
- https://gitlab.haskell.org/ghc/ghc/-/issues/198419.10.1https://gitlab.haskell.org/ghc/ghc/-/issues/23463Demand: retry# should have exnDiv, not botDiv2023-06-15T13:50:17ZSebastian GrafDemand: retry# should have exnDiv, not botDivHere's the entry in primops.txt.pp:
```
-- NB: retry#'s strictness information specifies it to diverge.
-- This lets the compiler perform some extra simplifications, since retry#
-- will technically never return.
--
-- This allows the s...Here's the entry in primops.txt.pp:
```
-- NB: retry#'s strictness information specifies it to diverge.
-- This lets the compiler perform some extra simplifications, since retry#
-- will technically never return.
--
-- This allows the simplifier to replace things like:
-- case retry# s1
-- (# s2, a #) -> e
-- with:
-- retry# s1
-- where 'e' would be unreachable anyway. See #8091.
primop RetryOp "retry#" GenPrimOp
State# RealWorld -> (# State# RealWorld, v #)
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
has_side_effects = True
```
This use of `botDiv` is unsound because it appears that the whole computation is strict in every free variable.
We have introduced `exnDiv` for exactly this reason: To enable dead-code elimination while not being strict in free variables. Let's use it on `retry#`, too.
`retry#` kind of is like a precise exception primop anyway (with a pre-defined catch handler), so it makes a lot of sense to treat it as such.
There's more context about `exnDiv` in `Note [Dead ends]` and `Note [Precise exceptions and strictness analysis]`.https://gitlab.haskell.org/ghc/ghc/-/issues/23398Demand analyser should unpack tuple dictionaries2023-05-18T19:38:14ZSimon Peyton JonesDemand analyser should unpack tuple dictionariesConsider
```haskell
{-# OPTIONS_GHC -O -fdicts-strict #-}
module Foo where
type PairDict a = (Eq a, Show a)
foo :: PairDict a => a -> a -> String
foo x y | x==y = show x
| otherwise = show y
```
At the moment, with `-O` we...Consider
```haskell
{-# OPTIONS_GHC -O -fdicts-strict #-}
module Foo where
type PairDict a = (Eq a, Show a)
foo :: PairDict a => a -> a -> String
foo x y | x==y = show x
| otherwise = show y
```
At the moment, with `-O` we generate this:
```
foo :: forall a. PairDict a => a -> a -> String
[GblId,
Str=<SP(SP(1C(1,C(1,L)),A),SP(A,1C(1,L),A))><L><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True]
foo
= \ (@a_aPr)
($d(%,%)_aPs :: PairDict a_aPr)
(eta_B0 :: a_aPr)
(eta1_B1 :: a_aPr) ->
case ==
@a_aPr
(GHC.Classes.$p0(%,%) @(Eq a_aPr) @(Show a_aPr) $d(%,%)_aPs)
eta_B0
eta1_B1
of {
False ->
show
@a_aPr
(GHC.Classes.$p1(%,%) @(Eq a_aPr) @(Show a_aPr) $d(%,%)_aPs)
eta1_B1;
True ->
show
@a_aPr
(GHC.Classes.$p1(%,%) @(Eq a_aPr) @(Show a_aPr) $d(%,%)_aPs)
eta_B0
}
```
Notice that we are strict in the pair dictionary, but we do not unpack it. Why not? Because of `Note [Do not unbox class dictionaries]` in GHC.Core.Opt.DmdAnal.
But for *tuple dictionaries* it would be better to unpack. Then we'd get
```
$wfoo :: (Eq a, Show a) => blah
```
and we might well be able to specialise it for particular `Eq` or `Show` dictionaries. The above `Note` doesn't apply.
Actually for "equality classes" like
```
bar :: (a ~ b) => blah
```
we would also be better off unboxing. Not much point in specialising `bar` for two particular types. All we do is make a copy of `bar`'s RHS with a particular coercion. Unlike notmrla class methods, that does not unlock any new optimisation opportunities in the specialised RHS.
TL;DR: narrow `Note [Do not unbox class dictionaries]` to ignore tuple classes and equality classes.https://gitlab.haskell.org/ghc/ghc/-/issues/23319Missed optimization2023-05-21T20:58:05ZmeooowMissed optimizationPlease feel free to change the title to something that better describes the issue.
## Summary
Here is some code that calculates the sum of vertices over a tree.
```hs
sum1 :: (Int -> [Int]) -> Int -> Int
sum1 neighbors root = go root ...Please feel free to change the title to something that better describes the issue.
## Summary
Here is some code that calculates the sum of vertices over a tree.
```hs
sum1 :: (Int -> [Int]) -> Int -> Int
sum1 neighbors root = go root 0 where
f :: Int -> [Int -> Int] -> Int -> Int
f x ks acc = foldl' (\acc' k -> k acc') (acc + x) ks
go :: Int -> Int -> Int
go x = f x (map go (neighbors x))
{-# NOINLINE sum1 #-}
```
This might seem a bit unnatural, but it's a simplified example so please excuse that.
I expect the `[Int -> Int]` to get optimized away, but unfortunately that doesn't happen according to the core.
However, if I eta-expand `go`, the problem disappears. The same happens if I merge `f` into `go`.
Here are some benchmarks:
```hs
import Criterion.Main
import Data.List
main :: IO ()
main = defaultMain
[ bench "sum1" $ whnf (sum1 binTree) 1
, bench "sum2" $ whnf (sum2 binTree) 1
, bench "sum3" $ whnf (sum3 binTree) 1
]
binTree :: Int -> [Int]
binTree x
| x > 1000000 = []
| otherwise = [2*x + 1, 2*x + 2]
sum1 :: (Int -> [Int]) -> Int -> Int
sum1 neighbors root = go root 0 where
f :: Int -> [Int -> Int] -> Int -> Int
f x ks acc = foldl' (\acc' k -> k acc') (acc + x) ks
go :: Int -> Int -> Int
go x = f x (map go (neighbors x))
{-# NOINLINE sum1 #-}
sum2 :: (Int -> [Int]) -> Int -> Int
sum2 neighbors root = go root 0 where
f :: Int -> [Int -> Int] -> Int -> Int
f x ks acc = foldl' (\acc' k -> k acc') (acc + x) ks
go :: Int -> Int -> Int
go x eta1 = f x (map go (neighbors x)) eta1
{-# NOINLINE sum2 #-}
sum3 :: (Int -> [Int]) -> Int -> Int
sum3 neighbors root = go root 0 where
go :: Int -> Int -> Int
go x = \acc -> foldl' (\acc' k -> k acc') (acc + x) (map go (neighbors x))
{-# NOINLINE sum3 #-}
```
With GHC 9.4.4 and -O2:
```
benchmarking sum1
time 49.07 ms (48.88 ms .. 49.31 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 48.52 ms (48.25 ms .. 48.75 ms)
std dev 473.0 μs (371.5 μs .. 619.1 μs)
benchmarking sum2
time 6.521 ms (6.493 ms .. 6.539 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 6.542 ms (6.538 ms .. 6.548 ms)
std dev 14.12 μs (10.22 μs .. 19.91 μs)
benchmarking sum3
time 6.565 ms (6.505 ms .. 6.647 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 6.544 ms (6.531 ms .. 6.570 ms)
std dev 50.54 μs (26.96 μs .. 91.55 μs)
```
## Expected behavior
`sum1` to be as efficient as `sum2` and `sum3`.
## Environment
* GHC version used: 9.4.4
Optional:
* Operating System: Ubuntu
* System Architecture: x86_64
---
**Edit**: I have simplified the example a bit, the original example was
```hs
depthSum1 :: forall a. (a -> [a]) -> a -> Int
depthSum1 neighbors root = go root 0 0 where
f :: a -> [Int -> Int -> Int] -> Int -> Int -> Int
f _ ks depth acc = foldl' (\acc' k -> k (depth+1) acc') (acc + depth) ks
go :: a -> Int -> Int -> Int
go x = f x (map go (neighbors x))
```https://gitlab.haskell.org/ghc/ghc/-/issues/23233Subsequent strictness defeats pseq2024-02-07T11:08:27ZMatthew Cravenclyring@gmail.comSubsequent strictness defeats pseq## Summary
Today, `pseq x y` expands to `case x of !_ -> lazy y`. We reason that the use of `lazy` will prevent us from moving the evaluation of `y` before the evaluation of `x`. Even if that much is true (CSE could potentially defeat i...## Summary
Today, `pseq x y` expands to `case x of !_ -> lazy y`. We reason that the use of `lazy` will prevent us from moving the evaluation of `y` before the evaluation of `x`. Even if that much is true (CSE could potentially defeat it), if `x` is used strictly later on we may defer the eval on `x`.
Se also #22935
## Steps to reproduce
<details><summary>compile and run with -O</summary>
```haskell
{-# LANGUAGE BangPatterns #-}
module Main (main) where
import GHC.Conc
import GHC.Exts
import Debug.Trace
data StrictPair a b = SP !a !b
deriving Show
f :: a -> Int -> StrictPair a Int
{-# NOINLINE f #-}
f x y = SP x (y * y)
fun :: a -> b -> (b -> Bool) -> StrictPair a Int
fun x y g = case pseq x y of
!u -> case g u of
True -> f x 12
False -> f x 14
p :: Int
{-# NOINLINE p #-}
p = trace "eval p" 3
q :: Int
{-# NOINLINE q #-}
q = trace "eval q" 4
main :: IO ()
main = print (fun p q even)
```
</details>
In this program, `q` should never be evaluated unless `p` has already been evaluated, because it is only ever used as the second arg to `pseq`. But when it is run with optimizations, `q` will be evaluated first.
## Environment
* GHC version used: any version 8.0-9.6.1https://gitlab.haskell.org/ghc/ghc/-/issues/23047atomicModifyMutVar2# demand signature is not great2023-06-28T14:21:52ZDavid FeueratomicModifyMutVar2# demand signature is not great## Summary
`atomicModifyMutVar2#` uses its function argument only once, but that is not reflected in its demand signature.
In particular, I would expect `atomicModifyMutVar2#` to have demand signature `<L><LC(S,L)><L>`, but instead it ...## Summary
`atomicModifyMutVar2#` uses its function argument only once, but that is not reflected in its demand signature.
In particular, I would expect `atomicModifyMutVar2#` to have demand signature `<L><LC(S,L)><L>`, but instead it has signature `<L><L><L>`.
I have no idea how bad this is in practice (if at all), though I can see it leads to an extra `let` binding in `atomicSwapIORef`: the compiler unnecessarily floats the `Box` allocation out of the lambda. Nevertheless, it seems like the sort of thing we want to be precise about.
It looks like this can be fixed by writing an explicit demand signature in `primops.txt.pp` somehow?
## Environment
* GHC version used: 9.6
Optional:
* Operating System:
* System Architecture:https://gitlab.haskell.org/ghc/ghc/-/issues/22718Oops! Entered absent arg Arg: z2023-04-11T05:24:59ZwkoikingOops! Entered absent arg Arg: z## Summary
GHC 9.x.x (including 9.4.4) causes below error when optimizations enabled, while GHC 8.10.7 does not.
~~~
test-exe.exe: internal error: Oops! Entered absent arg Arg: z
Type: [(TrackID, VentilationSectionID)]
In module `SP6....## Summary
GHC 9.x.x (including 9.4.4) causes below error when optimizations enabled, while GHC 8.10.7 does not.
~~~
test-exe.exe: internal error: Oops! Entered absent arg Arg: z
Type: [(TrackID, VentilationSectionID)]
In module `SP6.Data.Layout'
(GHC version 9.4.4 for x86_64_unknown_mingw32)
Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug
~~~
## Steps to reproduce
~~~
% git clone https://github.com/wkoiking/absent-arg-z.git
% cd absent-arg-z
% stack test
~~~
Without optimizations, this error seems to be avoided. i.e., below will not cause the error.
~~~
% stack clean
% stack test --fast
~~~
## Expected behavior
The run time error should not occur.
## Environment
* GHC version used: GHC 9.4.4
Optional:
* Operating System: Windows 10 Home (10.0.19044 build 19044)
* System Architecture: x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/22549Loop with undecidable instances in GHC 9.4.32023-08-04T00:08:51ZDavid FeuerLoop with undecidable instances in GHC 9.4.3## Summary
Certain undecidable instances are compiled into infinite loops in GHC 9.4.3.
## Steps to reproduce
This problem showed up in the `logict-sequence` test suite. Li-yao Xia somehow managed to figure out that the problem was th...## Summary
Certain undecidable instances are compiled into infinite loops in GHC 9.4.3.
## Steps to reproduce
This problem showed up in the `logict-sequence` test suite. Li-yao Xia somehow managed to figure out that the problem was the way certain instances were compiled. Here's a greatly reduced version:
```haskell
-- Everything works fine with -O0 and -O. Only -O2 fails.
{-# OPTIONS_GHC -O2 #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MonoLocalBinds #-}
import Data.Function (on)
import Data.Functor.Identity
data ViewT m a
= Empty
| a :< SeqT m a
newtype SeqT m a = SeqT [m (ViewT m a)]
toViewT :: Monad m => SeqT m a -> m (ViewT m a)
toViewT (SeqT []) = pure Empty
toViewT (SeqT (h : t)) = h >>= \case
Empty -> toViewT (SeqT t)
hi :< SeqT ti -> pure (hi :< SeqT (ti ++ t))
instance (Eq (m (ViewT m a)), Monad m) => Eq (SeqT m a) where
(==) = (==) `on` toViewT
deriving instance (Eq a, Eq (SeqT m a)) => Eq (ViewT m a)
example :: SeqT Identity Int
example = SeqT []
main :: IO ()
main = print (example == example)
```
When compiled with GHC 9.4.3, this prints
```
Buggle: <<loop>>
```
When compiled with versions 7.10.3, 8.0.2, 8.2.2, 8.4.4, 8.6.5, 8.8.4, 8.10.4, 9.0.2, or 9.2.5, it prints
```
True
```
For version 7.8, it works once the source is adjusted appropriately.
## Expected behavior
I expect it to print `True`.
## Environment
* GHC version used: 9.4.3
Optional:
* Operating System:
* System Architecture:9.4.4Sebastian GrafSebastian Grafhttps://gitlab.haskell.org/ghc/ghc/-/issues/22496Not enough boxing in demand analysis2022-11-22T15:43:15ZSimon Peyton JonesNot enough boxing in demand analysisIn GHC.Types.Demand I see
```
plusSubDmd :: SubDemand -> SubDemand -> SubDemand
-- Shortcuts for neutral and absorbing elements.
-- Below we assume that Boxed always wins.
```
But in the code compiled for GHC.Core.Map.Type, I found some...In GHC.Types.Demand I see
```
plusSubDmd :: SubDemand -> SubDemand -> SubDemand
-- Shortcuts for neutral and absorbing elements.
-- Below we assume that Boxed always wins.
```
But in the code compiled for GHC.Core.Map.Type, I found some terribly sub-optimal code,
like this:
```
$wgo_s7Ti [InlPrag=[2], Occ=LoopBreaker]
:: CmEnv -> Type -> CmEnv -> Type -> TypeEquality
[LclId[StrictWorker([])],
Arity=4,
Str=<SL><SL><L><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=NEVER}]
$wgo_s7Ti
= \ (ww_s7T9 [Dmd=SL] :: CmEnv)
(ww_s7Ta [Dmd=SL] :: Type)
(ww_s7Te :: CmEnv)
(ww_s7Tf :: Type) ->
case ww_s7Te of ww_X1 { CME ipv_s817 ipv_s818 ->
case ww_s7T9 of wild_s77i { CME bx_s77j ds_s77k ->
... many cases
gos_s77g wild_s77i ww_X1 tys_a4kW tys'_a4kY
... many cases
```
That is, `$wgo` is strict in both `CmEnv` arguments, and takes them apart,
but still requires them boxed. (This is *after* strictness analysis and worker/wrapper.)
Why? After faffing around for quite a while I found one of the many recursive
calls inside `$wgo` was this, which uses the boxed values.
```
gos_r8bK wild_s77i ww_X1 tys_a4kW tys'_a4kY
```
And `gos` looks like this:
```
gos_s77g [Occ=LoopBreaker]
:: CmEnv -> CmEnv -> [KindOrType] -> [KindOrType] -> TypeEquality
[LclId,
Arity=4,
Str=<L><L><SL><SL> ]
gos_r8bK
= \ (ds_s8n8 :: GHC.Core.Map.Type.CmEnv)
(ds1_s8n9 :: GHC.Core.Map.Type.CmEnv)
(ds2_s8na [Occ=Once1!] :: [GHC.Core.TyCo.Rep.KindOrType])
(ds3_s8nb [Occ=Once2!] :: [GHC.Core.TyCo.Rep.KindOrType]) ->
case ds2_s8na of {
[] ->
case ds3_s8nb of {
[] -> GHC.Core.Map.Type.TEQ;
: _ [Occ=Dead] _ [Occ=Dead] -> GHC.Core.Map.Type.TNEQ
};
: ty1_s8ng [Occ=Once1] tys1_s8nh [Occ=Once2] ->
case ds3_s8nb of {
[] -> GHC.Core.Map.Type.TNEQ;
: ty2_s8nj [Occ=Once1] tys2_s8nk [Occ=Once2] ->
case GHC.Core.Map.Type.$wgo ds_s8n8 ty1_s8ng ds1_s8n9 ty2_s8nj of {
GHC.Core.Map.Type.TNEQ -> GHC.Core.Map.Type.TNEQ;
GHC.Core.Map.Type.TEQ ->
gos_r8bK ds_s8n8 ds1_s8n9 tys1_s8nh tys2_s8nk;
GHC.Core.Map.Type.TEQX ->
case gos_r8bK ds_s8n8 ds1_s8n9 tys1_s8nh tys2_s8nk
of wild3_s8nm [Occ=Once1] {
__DEFAULT -> wild3_s8nm;
GHC.Core.Map.Type.TEQ -> GHC.Core.Map.Type.TEQX
}
}
}
}
```
Aha. It isn't strict on the `CMEnv` arguments, so they are passed boxed.
It happens that this is a cold path of `$wgo`. Moreover, all of the calls to `gos` are in the RHS of `$wgo`.
So it would really be better to pass the arguments to `gos` unboxed.
However even SpecConstr doesn't catch this, because even though all the calls to `gos`
are explicit constructors, `gos` itself does not take them apart.
There is clearly a lost opportunity here. I'm not sure how to grasp it.https://gitlab.haskell.org/ghc/ghc/-/issues/22475Oops! Entered absent arg Arg: lvl2023-12-09T16:43:51Zkazu-yamamotoOops! Entered absent arg Arg: lvl## Summary
GHC 9.4.x causes "Oops! Entered absent arg Arg: lvl" while other GHCs do not.
## Steps to reproduce
```
% git clone https://github.com/kazu-yamamoto/quic
% cd quic
% cabal configure --enable-tests
% cabal build
% cabal tes...## Summary
GHC 9.4.x causes "Oops! Entered absent arg Arg: lvl" while other GHCs do not.
## Steps to reproduce
```
% git clone https://github.com/kazu-yamamoto/quic
% cd quic
% cabal configure --enable-tests
% cabal build
% cabal test
```
## Expected behavior
This failure should not be encountered.
## Environment
* GHC version used: GHC 9.4.3
Optional:
* Operating System: macOS ventura (13.0.1)
* System Architecture: x86_649.4.4Sebastian GrafSebastian Grafhttps://gitlab.haskell.org/ghc/ghc/-/issues/22442Eta-expansion can cause binders in callers to become lazier.2023-02-07T17:24:18ZAndreas KlebingerEta-expansion can cause binders in callers to become lazier.Consider this code:
```haskell
foo !x = ...
bar =
let x = <thunk>
in foo x
```
Given that `foo` is strict in it's first arg one would hope that bar eventually compiles down to code such as:
```haskell
bar =
case <thunk> of x...Consider this code:
```haskell
foo !x = ...
bar =
let x = <thunk>
in foo x
```
Given that `foo` is strict in it's first arg one would hope that bar eventually compiles down to code such as:
```haskell
bar =
case <thunk> of x
_DEFAULT -> foo x
```
And that's generally what happens. However if we eta-expand `foo` instead we get:
```haskell
foo !x y = ...
bar =
let x = <thunk>
in \y -> foo x y
```
This happens since now only once `bar` is applied to an additional argument `x` will be evaluated.
This can be good or bad depending on the code in question, but it's definitely *surprising*.
I observed this happening in https://gitlab.haskell.org/ghc/ghc/-/issues/22425
I think this has a potential bad interaction with when the simplifier drops seqs that I haven't yet seen in the wild.
In particular we might start out with:
```haskell
-- foo has arity 1
bar x =
case x of x' -> foo x'
-- The simplifier sees that foo is strict in x' and will drop the seq
-- foo has arity 1
bar x =
foo x'
-- We eta expand foo
-- foo has arity 2
bar x =
\y -> foo x' y
```
And suddenly forcing `bar x` no longer forces `x` despite the user potentially having explicitly written it to do so.
But as I said I haven't observed the later part in the wild yet and haven't tried to come up with a reproducer. I doubt it happens often in practice.
@sgraf812 You might find this interesting/have more to say about this.https://gitlab.haskell.org/ghc/ghc/-/issues/22388Make W/W aware of the semantics of unboxed tuples.2022-11-11T02:46:51ZAndreas KlebingerMake W/W aware of the semantics of unboxed tuples.Currently W/W treats unboxed tuples the same as any other data type. This makes things simple but causes some odd behaviour.
It interacts badly with `-funbox-small-strict-fields` https://gitlab.haskell.org/ghc/ghc/-/issues/22309
But it...Currently W/W treats unboxed tuples the same as any other data type. This makes things simple but causes some odd behaviour.
It interacts badly with `-funbox-small-strict-fields` https://gitlab.haskell.org/ghc/ghc/-/issues/22309
But it also causes pointless W/W splits to "unbox" the unboxed tuple.
For example in this function:
```
{-# NOINLINE myFunction #-}
myFunction :: (# Int, Int, Int #) -> (# Int, Int, Int #)
myFunction (# x, y, z #) = (# y, z, x #)
```
Where we produce this core:
```haskell
$wmyFunction
= \ ww_sPi ww1_sPj ww2_sPk -> (# ww1_sPj, ww2_sPk, ww_sPi #)
myFunction
= \ ds_sPg ->
case ds_sPg of { (# ww_sPr, ww1_sPs, ww2_sPt #) ->
$wmyFunction ww_sPr ww1_sPs ww2_sPt
}
```
Resulting in this STG code eventually:
```haskell
wmyFunction =
\r [ww_sSk ww1_sSl ww2_sSm] (#,,#) [ww1_sSl ww2_sSm ww_sSk];
myFunction =
\r [us_gSs us_gSt us_gSu] $wmyFunction us_gSs us_gSt us_gSu;
```
Not really high priority as this shouldn't be very harmful. We expect the wrapper to almost always be inlined. But it generates useless work for the simplifier/codeGen so would still be good to fix eventually.