Chapter 8
The simplifier
The densest part of GHC: 450 Notes of inlining, case-of-case, strictness and rewrite rules, run to a fixed point. Where three list traversals become one loop that allocates nothing.
Where this lives in the tree
-
GHC/Core/Opt/Simplify.hsthe simplifier proper -
GHC/Core/Opt/OccurAnal.hsoccurrence analysis (what may be inlined, and what is a join point) -
GHC/Core/Opt/DmdAnal.hsstrictness and demand analysis -
GHC/Core/Opt/WorkWrap.hsturning demand information into unboxed workers
GHC.Core.Opt.* holds 450 Notes: more than any other part of the compiler, more
than the entire back end. This is where GHC’s reputation was earned, and it is
also where the compiler is least like a textbook.
The middle end is not one pass. It is a sequence, configured by optimisation level, in which the simplifier runs several times with analyses interleaved between its runs. Each analysis annotates the program; the simplifier exploits the annotations; the result enables the next analysis.
The simplifier
At its heart is a set of local rewrites applied everywhere, repeatedly, until
nothing changes: beta reduction, let floating, inlining, case-of-known-constructor,
dead code elimination.
The most consequential is case-of-case. Given
case (case x of { A -> e1; B -> e2 }) of { ... alts ... }
the outer case can be pushed into both branches of the inner one, duplicating
alts. This looks like it makes the program bigger, and sometimes it does. But
it is what makes short-circuiting boolean operators, && chains, and fused
pipelines compile to straight-line tests instead of building and immediately
scrutinising intermediate values.
Deciding when to inline is the hardest judgement in the compiler. Inline too little and nothing else fires; inline too much and code size explodes, and because inlining enables further inlining, the feedback is nonlinear. GHC’s answer is a mass of heuristics informed by occurrence analysis: a binder used exactly once in a non-recursive position is nearly always worth inlining, because it cannot duplicate work.
Occurrence analysis
OccurAnal runs before each simplifier pass and answers how each binder is
used: once, many times, under a lambda, in a tail position. That last one is
how join points are found: a let-bound function only ever tail-called becomes a
label rather than a closure.
It also breaks recursive groups into strongly-connected components and picks loop breakers, without which the simplifier would inline a recursive function into itself forever.
Strictness, and unboxing
Demand analysis asks: if this function is called, will it definitely evaluate
this argument? A yes is licence to do something valuable: pass the argument
evaluated and unboxed, rather than as a thunk.
Worker/wrapper is the transformation that cashes it in. A function f becomes a
small wrapper with the original type that unpacks its arguments, plus a
worker taking unboxed values. The wrapper is inlined at every call site, so the
boxing usually disappears entirely.
If we have
{-# INLINABLE f # This is why a strict accumulator loop over Int in Haskell can run with no
allocation at all: after worker/wrapper the accumulator is an Int# in a
register, and the I# box never exists.
Rewrite rules and fusion
RULES pragmas let library authors state equations that GHC applies as
left-to-right rewrites. This is how fusion works, and it lives in libraries
rather than in the compiler.
The canonical example is map f (map g xs) = map (f . g) xs. Modern base uses
a more general scheme built on build/foldr, but the principle is the same:
the library says which rewrites are valid, and the simplifier applies them.
Specialisation is the same idea applied to dictionaries. A polymorphic function called at a known type can be cloned with the dictionary inlined, turning indirect calls into direct ones:
Note June 2023: This has proved to be quite a tricky optimisation to get right see (#23469, #23109, #21229, #23445) so it is now guarded by a flag `-fpolymorphic-specialisation`. Consider class M a where { foo :: a -> Int } instance M (ST s) where ... dMST :: forall s. M (ST s) wimwam :: forall a. M a => a -> Int wimwam = /\a \(d::M a). body f :: ST s -> Int f = /\s \(x::ST s). wimwam @(ST s) (dMST @s) dx + 1 We'd like to specialise wimwam at (ST s), thus $swimwam :: forall s. ST s -> Int $swimwam = /\s. body[ST s/a, (dMST @s)/d] RULE forall s (d :: M (ST s)). wimwam @(ST s) d = $swimwam @s Here are the moving parts:
Show the rest of this Note (76 more lines)
(MP1) We must /not/ dump the CallInfo
CIS wimwam (CI { ci_key = [@(ST s), dMST @s]
, ci_fvs = {dMST} })
when we come to the /\s. Instead, we simply let it continue to float
upwards. Hence ci_fvs is an IdSet, listing the /Ids/ that
are free in the call, but not the /TyVars/. Hence using specArgFreeIds
in singleCall.
NB to be fully kosher we should explicitly quantifying the CallInfo
over 's', but we don't bother. This would matter if there was an
enclosing binding of the same 's', which I don't expect to happen.
(MP2) When we come to specialise the call, we must remember to quantify
over 's'. That is done in the SpecType case of specHeader, where
we add 's' (called qvars) to the binders of the RULE and the specialised
function.
(MP3) If we have f :: forall m. Monoid m => blah, and two calls
(f @(Endo b) (d1 :: Monoid (Endo b))
(f @(Endo (c->c)) (d2 :: Monoid (Endo (c->c)))
we want to generate a specialisation only for the first. The second
is just a substitution instance of the first, with no greater specialisation.
Hence the use of `removeDupCalls` in `filterCalls`.
You might wonder if `d2` might be more specialised than `d1`; but no.
This `removeDupCalls` thing is at the definition site of `f`, and both `d1`
and `d2` are in scope. So `d1` is simply more polymorphic than `d2`, but
is just as specialised.
This distinction is sadly lost once we build a RULE, so `alreadyCovered`
can't be so clever. E.g if we have an existing RULE
forall @a (d1:Ord Int) (d2: Eq a). f @a @Int d1 d2 = ...
and a putative new rule
forall (d1:Ord Int) (d2: Eq Int). f @Int @Int d1 d2 = ...
we /don't/ want the existing rule to subsume the new one.
So we sadly put up with having two rather different places where we
eliminate duplicates: `alreadyCovered` and `removeDupCalls`.
All this arose in #13873, in the unexpected form that a SPECIALISE
pragma made the program slower! The reason was that the specialised
function $sinsertWith arising from the pragma looked rather like `f`
above, and failed to specialise a call in its body like wimwam.
Without the pragma, the original call to `insertWith` was completely
monomorpic, and specialised in one go.
Wrinkles.
* See Note [Weird special case for SpecDict]
* With -XOverlappingInstances you might worry about this:
class C a where ...
instance C (Maybe Int) where ... -- $df1 :: C (Maybe Int)
instance C (Maybe a) where ... -- $df2 :: forall a. C (Maybe a)
f :: C a => blah
f = rhs
g = /\a. ...(f @(Maybe a) ($df2 a))...
h = ...f @(Maybe Int) $df1
There are two calls to f, but with different evidence. This patch will
combine them into one. But it's OK: this code will never arise unless you
use -XIncoherentInstances. Even with -XOverlappingInstances, GHC tries hard
to keep dictionaries as singleton types. But that goes out of the window
with -XIncoherentInstances -- and that is true even with ordianry type-class
specialisation (at least if any inlining has taken place).
GHC makes very few guarantees when you use -XIncoherentInstances, and its
not worth crippling the normal case for the incoherent corner. (The best
thing might be to switch off specialisation altogether if incoherence is
involved... but incoherence is a property of an instance, not a class, so
it's a hard test to make.)
But see Note [Specialisation and overlapping instances]. Seeing it happen
This is what the Fusion.hs example exists for.
What you wrote.
-- | What the optimiser is actually for.
--
-- `pipeline` reads as three traversals building two intermediate lists. Compare
-- the desugared Core with the optimised Core: rewrite rules and the simplifier
-- fuse the whole thing into a single loop that allocates no list at all.
module Fusion where
pipeline :: [Int] -> Int
pipeline = sum . map (* 2) . filter even
countdown :: Int -> Int
countdown n = go n 0
where
go 0 acc = acc
go k acc = go (k - 1) (acc + k)All of Haskell reduced to Core, before any optimisation.
Result size of Desugar (after optimization)
= {terms: 58, types: 41, coercions: 0, joins: 0/3}
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module (TrNameS "main"#) (TrNameS "Fusion"#)
-- RHS size: {terms: 19, types: 23, coercions: 0, joins: 0/2}
pipeline :: [Int] -> Int
pipeline
= . (sum $fFoldableList $fNumInt)
(. (map
(let {
v :: Int -> Int -> Int
v = * $fNumInt } in
let {
v :: Int
v = I# 2# } in
\ (v :: Int) -> v v v))
(filter (even $fIntegralInt)))
-- RHS size: {terms: 31, types: 12, coercions: 0, joins: 0/1}
countdown :: Int -> Int
countdown
= \ (n :: Int) ->
letrec {
go :: Int -> Int -> Int
go
= \ (ds :: Int) (acc :: Int) ->
case == $fEqInt ds (fromInteger $fNumInt (IS 0#)) of {
False ->
go
(- $fNumInt ds (fromInteger $fNumInt (IS 1#))) (+ $fNumInt acc ds);
True -> acc
}; } in
go n (I# 0#)Result size of Desugar (after optimization)
= {terms: 58, types: 41, coercions: 0, joins: 0/3}
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule :: GHC.Internal.Types.Module
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 80 10}]
Fusion.$trModule
= GHC.Internal.Types.Module
(GHC.Internal.Types.TrNameS "main"#)
(GHC.Internal.Types.TrNameS "Fusion"#)
-- RHS size: {terms: 19, types: 23, coercions: 0, joins: 0/2}
pipeline :: [Int] -> Int
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=False, Expandable=False,
Guidance=IF_ARGS [] 240 60}]
pipeline
= . @[Int]
@Int
@[Int]
(sum
@[]
GHC.Internal.Data.Foldable.$fFoldableList
@Int
GHC.Internal.Num.$fNumInt)
(. @[Int]
@[Int]
@[Int]
(map
@Int
@Int
(let {
v :: Int -> Int -> Int
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=False, ConLike=False, WorkFree=False, Expandable=True,
Guidance=IF_ARGS [] 20 0}]
v = * @Int GHC.Internal.Num.$fNumInt } in
let {
v :: Int
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
v = GHC.Internal.Types.I# 2# } in
\ (v :: Int) -> v v v))
(filter @Int (even @Int GHC.Internal.Real.$fIntegralInt)))
-- RHS size: {terms: 31, types: 12, coercions: 0, joins: 0/1}
countdown :: Int -> Int
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0] 320 0}]
countdown
= \ (n :: Int) ->
letrec {
go [Occ=LoopBreaker] :: Int -> Int -> Int
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0 0] 250 0}]
go
= \ (ds :: Int) (acc :: Int) ->
case ==
@Int
GHC.Internal.Classes.$fEqInt
ds
(fromInteger
@Int GHC.Internal.Num.$fNumInt (GHC.Internal.Bignum.Integer.IS 0#))
of {
False ->
go
(- @Int
GHC.Internal.Num.$fNumInt
ds
(fromInteger
@Int
GHC.Internal.Num.$fNumInt
(GHC.Internal.Bignum.Integer.IS 1#)))
(+ @Int GHC.Internal.Num.$fNumInt acc ds);
True -> acc
}; } in
go n (GHC.Internal.Types.I# 0#)The same program after the simplifier has run.
Result size of Tidy Core
= {terms: 93, types: 48, coercions: 0, joins: 0/0}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule4 :: Addr#
$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule3 :: TrName
$trModule3 = TrNameS $trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule2 :: Addr#
$trModule2 = "Fusion"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule1 :: TrName
$trModule1 = TrNameS $trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module $trModule3 $trModule1
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
countdown_z0 :: Int
countdown_z0 = I# 0#
Rec {
-- RHS size: {terms: 27, types: 11, coercions: 0, joins: 0/0}
$wgo1 :: [Int] -> Int# -> Int
$wgo1
= \ (ds :: [Int]) (ww :: Int#) ->
case ds of {
[] -> I# ww;
: y ys ->
case y of { I# ipv ->
case remInt# ipv 2# of {
__DEFAULT -> $wgo1 ys ww;
0# -> $wgo1 ys (+# ww (*# ipv 2#))
}
}
}
end Rec }
-- RHS size: {terms: 4, types: 2, coercions: 0, joins: 0/0}
pipeline :: [Int] -> Int
pipeline = \ (x :: [Int]) -> $wgo1 x 0#
Rec {
-- RHS size: {terms: 14, types: 3, coercions: 0, joins: 0/0}
$wgo :: Int# -> Int# -> Int#
$wgo
= \ (ww :: Int#) (ww1 :: Int#) ->
case ww of wild {
__DEFAULT -> $wgo (-# wild 1#) (+# ww1 wild);
0# -> ww1
}
end Rec }
-- RHS size: {terms: 15, types: 7, coercions: 0, joins: 0/0}
countdown_go :: Int -> Int -> Int
countdown_go
= \ (ds :: Int) (acc :: Int) ->
case ds of { I# ww ->
case acc of { I# ww1 ->
case $wgo ww ww1 of ww2 { __DEFAULT -> I# ww2 }
}
}
-- RHS size: {terms: 11, types: 4, coercions: 0, joins: 0/0}
countdown :: Int -> Int
countdown
= \ (n :: Int) ->
case n of { I# ww ->
case $wgo ww 0# of ww1 { __DEFAULT -> I# ww1 }
}Result size of Tidy Core
= {terms: 93, types: 48, coercions: 0, joins: 0/0}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule4 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 20 0}]
Fusion.$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule3 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Fusion.$trModule3 = GHC.Internal.Types.TrNameS Fusion.$trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule2 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Fusion.$trModule2 = "Fusion"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule1 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Fusion.$trModule1 = GHC.Internal.Types.TrNameS Fusion.$trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Fusion.$trModule :: GHC.Internal.Types.Module
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Fusion.$trModule
= GHC.Internal.Types.Module Fusion.$trModule3 Fusion.$trModule1
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Fusion.countdown_z0 :: Int
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Fusion.countdown_z0 = GHC.Internal.Types.I# 0#
Rec {
-- RHS size: {terms: 27, types: 11, coercions: 0, joins: 0/0}
$wgo1 :: [Int] -> GHC.Internal.Prim.Int# -> Int
[GblId[StrictWorker([!])], Arity=2, Str=<1L><L>, Unf=OtherCon []]
$wgo1
= \ (ds :: [Int]) (ww :: GHC.Internal.Prim.Int#) ->
case ds of {
[] -> GHC.Internal.Types.I# ww;
: y ys ->
case y of { GHC.Internal.Types.I# ipv ->
case GHC.Internal.Prim.remInt# ipv 2# of {
__DEFAULT -> $wgo1 ys ww;
0# ->
$wgo1 ys (GHC.Internal.Prim.+# ww (GHC.Internal.Prim.*# ipv 2#))
}
}
}
end Rec }
-- RHS size: {terms: 4, types: 2, coercions: 0, joins: 0/0}
pipeline :: [Int] -> Int
[GblId,
Arity=1,
Str=<1L>,
Cpr=1,
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x [Occ=Once1] :: [Int]) ->
joinrec {
go1 [InlPrag=[2], Occ=T[2], Dmd=LC(S,C(1,!P(L)))]
:: [Int] -> Int -> Int
[LclId[JoinId(2)(Just [!, !])],
Arity=2,
Str=<SL><S!P(L)>,
Unf=Unf{Src=StableSystem, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
Tmpl= \ (ds [Occ=Once1] :: [Int])
(eta [Occ=Once1!, OS=OneShot] :: Int) ->
case eta of { GHC.Internal.Types.I# ww [Occ=Once1] ->
jump $wgo2 ds ww
}}]
go1 (ds [Occ=Once1] :: [Int]) (eta [Occ=Once1!, OS=OneShot] :: Int)
= case eta of { GHC.Internal.Types.I# ww [Occ=Once1] ->
jump $wgo2 ds ww
};
$wgo2 [InlPrag=[2], Occ=LoopBreakerT[2]]
:: [Int] -> GHC.Internal.Prim.Int# -> Int
[LclId[JoinId(2)(Just [!])], Arity=2, Str=<SL><L>, Unf=OtherCon []]
$wgo2 (ds [Occ=Once1!] :: [Int])
(ww [Occ=Once3] :: GHC.Internal.Prim.Int#)
= case ds of {
[] -> GHC.Internal.Types.I# ww;
: y [Occ=Once1!] ys [Occ=Once2] ->
case y of { GHC.Internal.Types.I# ipv ->
case GHC.Internal.Prim.remInt# ipv 2# of {
__DEFAULT -> jump go1 ys (GHC.Internal.Types.I# ww);
0# ->
jump go1
ys
(GHC.Internal.Types.I#
(GHC.Internal.Prim.+# ww (GHC.Internal.Prim.*# ipv 2#)))
}
}
}; } in
jump go1 x Fusion.countdown_z0}]
pipeline = \ (x :: [Int]) -> $wgo1 x 0#
Rec {
-- RHS size: {terms: 14, types: 3, coercions: 0, joins: 0/0}
Fusion.$wgo [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int#
-> GHC.Internal.Prim.Int# -> GHC.Internal.Prim.Int#
[GblId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
Fusion.$wgo
= \ (ww :: GHC.Internal.Prim.Int#)
(ww1 :: GHC.Internal.Prim.Int#) ->
case ww of wild {
__DEFAULT ->
Fusion.$wgo
(GHC.Internal.Prim.-# wild 1#) (GHC.Internal.Prim.+# ww1 wild);
0# -> ww1
}
end Rec }
-- RHS size: {terms: 15, types: 7, coercions: 0, joins: 0/0}
Fusion.countdown_go [InlPrag=[2]] :: Int -> Int -> Int
[GblId[StrictWorker([!, !])],
Arity=2,
Str=<1!P(1L)><1!P(L)>,
Cpr=1,
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
Tmpl= \ (ds [Occ=Once1!] :: Int) (acc [Occ=Once1!] :: Int) ->
case ds of { GHC.Internal.Types.I# ww [Occ=Once1] ->
case acc of { GHC.Internal.Types.I# ww1 [Occ=Once1] ->
case Fusion.$wgo ww ww1 of ww2 [Occ=Once1] { __DEFAULT ->
GHC.Internal.Types.I# ww2
}
}
}}]
Fusion.countdown_go
= \ (ds :: Int) (acc [OS=OneShot] :: Int) ->
case ds of { GHC.Internal.Types.I# ww ->
case acc of { GHC.Internal.Types.I# ww1 ->
case Fusion.$wgo ww ww1 of ww2 { __DEFAULT ->
GHC.Internal.Types.I# ww2
}
}
}
-- RHS size: {terms: 11, types: 4, coercions: 0, joins: 0/0}
countdown :: Int -> Int
[GblId,
Arity=1,
Str=<1!P(1L)>,
Cpr=1,
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (n [Occ=Once1] :: Int) ->
Fusion.countdown_go n Fusion.countdown_z0}]
countdown
= \ (n :: Int) ->
case n of { GHC.Internal.Types.I# ww ->
case Fusion.$wgo ww 0# of ww1 { __DEFAULT ->
GHC.Internal.Types.I# ww1
}
}Allocation and evaluation made explicit, ready for code generation.
$trModule2 :: Addr# = "Fusion"#;
$trModule4 :: Addr# = "main"#;
$trModule3 :: TrName = TrNameS! [$trModule4];
$trModule1 :: TrName = TrNameS! [$trModule2];
$trModule :: Module = Module! [$trModule3 $trModule1];
countdown_z0 :: Int = I#! [0#];
Rec {
$wgo1 :: [Int] -> Int# -> Int =
{} \r [ds ww]
case ds<TagProper> of wild {
[] -> I# [ww];
: y ys ->
case y of a1 {
I# ipv ->
case remInt# [ipv 2#] of wild1 {
__DEFAULT -> case ys of ys { __DEFAULT -> $wgo1 ys ww; };
0# ->
case *# [ipv 2#] of $wgo1_sat {
__DEFAULT ->
case +# [ww $wgo1_sat] of $wgo1_sat {
__DEFAULT -> case ys of ys { __DEFAULT -> $wgo1 ys $wgo1_sat; };
};
};
};
};
};
end Rec }
pipeline :: [Int] -> Int =
{} \r [x] case x of x { __DEFAULT -> $wgo1 x 0#; };
Rec {
$wgo :: Int# -> Int# -> Int# =
{} \r [ww ww1]
case ww<TagProper> of wild {
__DEFAULT ->
case +# [ww1 wild] of $wgo_sat {
__DEFAULT ->
case -# [wild 1#] of $wgo_sat {
__DEFAULT -> $wgo $wgo_sat $wgo_sat;
};
};
0# -> ww1<TagProper>;
};
end Rec }
countdown_go :: Int -> Int -> Int =
{} \r [ds acc]
case ds<TagProper> of wild {
I# ww ->
case acc<TagProper> of wild1 {
I# ww1 -> case $wgo ww ww1 of ww2 { __DEFAULT -> I# [ww2]; };
};
};
countdown :: Int -> Int =
{} \r [n]
case n of wild {
I# ww -> case $wgo ww 0# of ww1 { __DEFAULT -> I# [ww1]; };
};Fusion.$trModule2 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"Fusion"#;
Fusion.$trModule4 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"main"#;
Fusion.$trModule3 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Fusion.$trModule4];
Fusion.$trModule1 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Fusion.$trModule2];
Fusion.$trModule :: GHC.Internal.Types.Module
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.Module! [Fusion.$trModule3 Fusion.$trModule1];
Fusion.countdown_z0 :: GHC.Internal.Types.Int
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.I#! [0#];
Rec {
$wgo1
:: [GHC.Internal.Types.Int]
-> GHC.Internal.Prim.Int# -> GHC.Internal.Types.Int
[GblId[StrictWorker([!])], Arity=2, Str=<1L><L>, Unf=OtherCon []] =
{} \r [ds ww]
case ds<TagProper> of wild {
[] -> GHC.Internal.Types.I# [ww];
: y [Occ=Once1!] ys [Occ=Once2] ->
case y of a1 {
GHC.Internal.Types.I# ipv ->
case remInt# [ipv 2#] of wild1 {
__DEFAULT -> case ys of ys { __DEFAULT -> $wgo1 ys ww; };
0# ->
case *# [ipv 2#] of $wgo1_sat {
__DEFAULT ->
case +# [ww $wgo1_sat] of $wgo1_sat {
__DEFAULT -> case ys of ys { __DEFAULT -> $wgo1 ys $wgo1_sat; };
};
};
};
};
};
end Rec }
Fusion.pipeline
:: [GHC.Internal.Types.Int] -> GHC.Internal.Types.Int
[GblId, Arity=1, Str=<1L>, Cpr=1, Unf=OtherCon []] =
{} \r [x] case x of x { __DEFAULT -> $wgo1 x 0#; };
Rec {
Fusion.$wgo [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int#
-> GHC.Internal.Prim.Int# -> GHC.Internal.Prim.Int#
[GblId, Arity=2, Str=<1L><L>, Unf=OtherCon []] =
{} \r [ww ww1]
case ww<TagProper> of wild {
__DEFAULT ->
case +# [ww1 wild] of $wgo_sat {
__DEFAULT ->
case -# [wild 1#] of $wgo_sat [Dmd=1L] {
__DEFAULT -> Fusion.$wgo $wgo_sat $wgo_sat;
};
};
0# -> ww1<TagProper>;
};
end Rec }
Fusion.countdown_go [InlPrag=[2]]
:: GHC.Internal.Types.Int
-> GHC.Internal.Types.Int -> GHC.Internal.Types.Int
[GblId[StrictWorker([!, !])],
Arity=2,
Str=<1!P(1L)><1!P(L)>,
Cpr=1,
Unf=OtherCon []] =
{} \r [ds acc]
case ds<TagProper> of wild {
GHC.Internal.Types.I# ww [Occ=Once1] ->
case acc<TagProper> of wild1 {
GHC.Internal.Types.I# ww1 [Occ=Once1] ->
case Fusion.$wgo ww ww1 of ww2 {
__DEFAULT -> GHC.Internal.Types.I# [ww2];
};
};
};
Fusion.countdown
:: GHC.Internal.Types.Int -> GHC.Internal.Types.Int
[GblId, Arity=1, Str=<1!P(1L)>, Cpr=1, Unf=OtherCon []] =
{} \r [n]
case n of wild {
GHC.Internal.Types.I# ww [Occ=Once1] ->
case Fusion.$wgo ww 0# of ww1 {
__DEFAULT -> GHC.Internal.Types.I# [ww1];
};
};In Core (desugared) you can see the composition and the intermediate lists. In Core (optimised) they are gone: fusion has collapsed the pipeline into a single recursive worker over the input list, with no intermediate list allocated.
Worker/wrapper has a demonstration of its own:
What you wrote.
{-# LANGUAGE BangPatterns #-}
-- | What demand analysis and worker/wrapper are for.
--
-- `sumStrict` is a strict accumulator loop. Demand analysis proves the
-- accumulator is always forced, and worker/wrapper splits the function into a
-- wrapper with the original boxed type and a worker taking an unboxed `Int#`.
-- Look for `$wsumStrict` in the optimised Core, and note the loop allocates
-- nothing.
--
-- `sumLazy` is the same fold without the bang. Compare the two in STG: the lazy
-- version builds a thunk per iteration, and every `let` in STG is an allocation.
module Strict where
sumStrict :: [Int] -> Int
sumStrict = go 0
where
go !acc [] = acc
go !acc (x : xs) = go (acc + x) xs
sumLazy :: [Int] -> Int
sumLazy = go 0
where
go acc [] = acc
go acc (x : xs) = go (acc + x) xs
-- A strict data type: the bangs let GHC unpack the fields, so a Point is two
-- unboxed Ints in one heap object rather than two pointers to two boxes.
data Point = Point !Int !Int
shift :: Int -> Point -> Point
shift d (Point x y) = Point (x + d) (y + d)
-- Constructed product result: `minMax` returns a pair, and CPR analysis lets the
-- caller receive the components directly rather than allocating the tuple.
minMax :: [Int] -> (Int, Int)
minMax [] = (0, 0)
minMax (z : zs) = go z z zs
where
go !lo !hi [] = (lo, hi)
go !lo !hi (w : ws) = go (min lo w) (max hi w) wsAll of Haskell reduced to Core, before any optimisation.
Result size of Desugar (after optimization)
= {terms: 138, types: 88, coercions: 0, joins: 0/3}
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module (TrNameS "main"#) (TrNameS "Strict"#)
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepTyConApp $tcInt []
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
$tcPoint :: TyCon
$tcPoint
= TyCon
15270500999396957839#Word64
1310266649928625514#Word64
$trModule
(TrNameS "Point"#)
0#
krep$*
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepTyConApp $tcPoint []
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepFun $krep $krep
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepFun $krep $krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
$tc'Point :: TyCon
$tc'Point
= TyCon
209391528505640325#Word64
16890207377952019736#Word64
$trModule
(TrNameS "'Point"#)
0#
$krep
-- RHS size: {terms: 16, types: 7, coercions: 0, joins: 0/0}
shift :: Int -> Point -> Point
shift
= \ (d :: Int) (ds :: Point) ->
case ds of { Point bx bx ->
$WPoint (+ $fNumInt (I# bx) d) (+ $fNumInt (I# bx) d)
}
-- RHS size: {terms: 17, types: 13, coercions: 0, joins: 0/1}
sumLazy :: [Int] -> Int
sumLazy
= letrec {
go :: Int -> [Int] -> Int
go
= \ (acc :: Int) (ds :: [Int]) ->
case ds of {
[] -> acc;
: x xs -> go (+ $fNumInt acc x) xs
}; } in
go (I# 0#)
-- RHS size: {terms: 20, types: 14, coercions: 0, joins: 0/1}
sumStrict :: [Int] -> Int
sumStrict
= letrec {
go :: Int -> [Int] -> Int
go
= \ (acc :: Int) (ds :: [Int]) ->
case acc of acc { __DEFAULT ->
case ds of {
[] -> acc;
: x xs -> go (+ $fNumInt acc x) xs
}
}; } in
go (I# 0#)
-- RHS size: {terms: 41, types: 31, coercions: 0, joins: 0/1}
minMax :: [Int] -> (Int, Int)
minMax
= \ (ds :: [Int]) ->
case ds of {
[] -> (I# 0#, I# 0#);
: z zs ->
letrec {
go :: Int -> Int -> [Int] -> (Int, Int)
go
= \ (lo :: Int) (hi :: Int) (ds :: [Int]) ->
case lo of lo { __DEFAULT ->
case hi of hi { __DEFAULT ->
case ds of {
[] -> (lo, hi);
: w ws -> go (min $fOrdInt lo w) (max $fOrdInt hi w) ws
}
}
}; } in
go z z zs
}Result size of Desugar (after optimization)
= {terms: 138, types: 88, coercions: 0, joins: 0/3}
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule :: GHC.Internal.Types.Module
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 80 10}]
Strict.$trModule
= GHC.Internal.Types.Module
(GHC.Internal.Types.TrNameS "main"#)
(GHC.Internal.Types.TrNameS "Strict"#)
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep
= GHC.Internal.Types.KindRepTyConApp
GHC.Internal.Types.$tcInt
(GHC.Internal.Types.[] @GHC.Internal.Types.KindRep)
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
Strict.$tcPoint :: GHC.Internal.Types.TyCon
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 50 10}]
Strict.$tcPoint
= GHC.Internal.Types.TyCon
15270500999396957839#Word64
1310266649928625514#Word64
Strict.$trModule
(GHC.Internal.Types.TrNameS "Point"#)
0#
GHC.Internal.Types.krep$*
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep
= GHC.Internal.Types.KindRepTyConApp
Strict.$tcPoint (GHC.Internal.Types.[] @GHC.Internal.Types.KindRep)
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep = GHC.Internal.Types.KindRepFun $krep $krep
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep = GHC.Internal.Types.KindRepFun $krep $krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
Strict.$tc'Point :: GHC.Internal.Types.TyCon
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 50 10}]
Strict.$tc'Point
= GHC.Internal.Types.TyCon
209391528505640325#Word64
16890207377952019736#Word64
Strict.$trModule
(GHC.Internal.Types.TrNameS "'Point"#)
0#
$krep
-- RHS size: {terms: 16, types: 7, coercions: 0, joins: 0/0}
shift :: Int -> Point -> Point
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0 20] 140 0}]
shift
= \ (d :: Int) (ds :: Point) ->
case ds of { Point bx bx ->
Strict.$WPoint
(+ @Int GHC.Internal.Num.$fNumInt (GHC.Internal.Types.I# bx) d)
(+ @Int GHC.Internal.Num.$fNumInt (GHC.Internal.Types.I# bx) d)
}
-- RHS size: {terms: 17, types: 13, coercions: 0, joins: 0/1}
sumLazy :: [Int] -> Int
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=False, ConLike=False, WorkFree=False, Expandable=False,
Guidance=IF_ARGS [] 150 0}]
sumLazy
= letrec {
go [Occ=LoopBreaker] :: Int -> [Int] -> Int
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0 30] 90 0}]
go
= \ (acc :: Int) (ds :: [Int]) ->
case ds of {
[] -> acc;
: x xs -> go (+ @Int GHC.Internal.Num.$fNumInt acc x) xs
}; } in
go (GHC.Internal.Types.I# 0#)
-- RHS size: {terms: 20, types: 14, coercions: 0, joins: 0/1}
sumStrict :: [Int] -> Int
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=False, ConLike=False, WorkFree=False, Expandable=False,
Guidance=IF_ARGS [] 160 0}]
sumStrict
= letrec {
go [Occ=LoopBreaker] :: Int -> [Int] -> Int
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [20 30] 100 0}]
go
= \ (acc :: Int) (ds :: [Int]) ->
case acc of acc { __DEFAULT ->
case ds of {
[] -> acc;
: x xs -> go (+ @Int GHC.Internal.Num.$fNumInt acc x) xs
}
}; } in
go (GHC.Internal.Types.I# 0#)
-- RHS size: {terms: 41, types: 31, coercions: 0, joins: 0/1}
minMax :: [Int] -> (Int, Int)
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [60] 300 10}]
minMax
= \ (ds :: [Int]) ->
case ds of {
[] -> (GHC.Internal.Types.I# 0#, GHC.Internal.Types.I# 0#);
: z zs ->
letrec {
go [Occ=LoopBreaker] :: Int -> Int -> [Int] -> (Int, Int)
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=False,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [20 20 40] 170 10}]
go
= \ (lo :: Int) (hi :: Int) (ds :: [Int]) ->
case lo of lo { __DEFAULT ->
case hi of hi { __DEFAULT ->
case ds of {
[] -> (lo, hi);
: w ws ->
go
(min @Int GHC.Internal.Classes.$fOrdInt lo w)
(max @Int GHC.Internal.Classes.$fOrdInt hi w)
ws
}
}
}; } in
go z z zs
}The same program after the simplifier has run.
Result size of Tidy Core
= {terms: 180, types: 132, coercions: 0, joins: 2/2}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule4 :: Addr#
$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule3 :: TrName
$trModule3 = TrNameS $trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule2 :: Addr#
$trModule2 = "Strict"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule1 :: TrName
$trModule1 = TrNameS $trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module $trModule3 $trModule1
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepTyConApp $tcInt []
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$tcPoint2 :: Addr#
$tcPoint2 = "Point"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$tcPoint1 :: TrName
$tcPoint1 = TrNameS $tcPoint2
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
$tcPoint :: TyCon
$tcPoint
= TyCon
15270500999396957839#Word64
1310266649928625514#Word64
$trModule
$tcPoint1
0#
krep$*
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep1 :: KindRep
$krep1 = KindRepTyConApp $tcPoint []
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep2 :: KindRep
$krep2 = KindRepFun $krep $krep1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$tc'Point1 :: KindRep
$tc'Point1 = KindRepFun $krep $krep2
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$tc'Point3 :: Addr#
$tc'Point3 = "'Point"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$tc'Point2 :: TrName
$tc'Point2 = TrNameS $tc'Point3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
$tc'Point :: TyCon
$tc'Point
= TyCon
209391528505640325#Word64
16890207377952019736#Word64
$trModule
$tc'Point2
0#
$tc'Point1
-- RHS size: {terms: 15, types: 7, coercions: 0, joins: 0/0}
shift :: Int -> Point -> Point
shift
= \ (d :: Int) (ds :: Point) ->
case ds of { Point bx bx1 ->
case d of { I# y -> Point (+# bx y) (+# bx1 y) }
}
Rec {
-- RHS size: {terms: 15, types: 10, coercions: 0, joins: 0/0}
$wgo1 :: Int# -> [Int] -> Int#
$wgo1
= \ (ww :: Int#) (ds :: [Int]) ->
case ds of {
[] -> ww;
: x xs -> case x of { I# y -> $wgo1 (+# ww y) xs }
}
end Rec }
-- RHS size: {terms: 8, types: 3, coercions: 0, joins: 0/0}
sumLazy :: [Int] -> Int
sumLazy
= \ (ds :: [Int]) -> case $wgo1 0# ds of ww { __DEFAULT -> I# ww }
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
sumStrict :: [Int] -> Int
sumStrict = sumLazy
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
minMax2 :: Int
minMax2 = I# 0#
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
minMax1 :: (Int, Int)
minMax1 = (minMax2, minMax2)
Rec {
-- RHS size: {terms: 41, types: 31, coercions: 0, joins: 2/2}
$wgo :: Int# -> Int# -> [Int] -> (# Int#, Int# #)
$wgo
= \ (ww :: Int#) (ww1 :: Int#) (ds :: [Int]) ->
case ds of {
[] -> (# ww, ww1 #);
: w ws ->
case w of { I# y1 ->
join {
$j :: Int# -> (# Int#, Int# #)
$j (ww2 :: Int#)
= join {
$j1 :: Int# -> (# Int#, Int# #)
$j1 (ww3 :: Int#) = $wgo ww2 ww3 ws } in
case <=# ww1 y1 of {
__DEFAULT -> jump $j1 ww1;
1# -> jump $j1 y1
} } in
case <=# ww y1 of {
__DEFAULT -> jump $j y1;
1# -> jump $j ww
}
}
}
end Rec }
-- RHS size: {terms: 20, types: 17, coercions: 0, joins: 0/0}
minMax_go :: Int -> Int -> [Int] -> (Int, Int)
minMax_go
= \ (lo :: Int) (hi :: Int) (ds :: [Int]) ->
case lo of { I# ww ->
case hi of { I# ww1 ->
case $wgo ww ww1 ds of { (# ww2, ww3 #) -> (I# ww2, I# ww3) }
}
}
-- RHS size: {terms: 10, types: 7, coercions: 0, joins: 0/0}
minMax :: [Int] -> (Int, Int)
minMax
= \ (ds :: [Int]) ->
case ds of {
[] -> minMax1;
: z zs -> minMax_go z z zs
}Result size of Tidy Core
= {terms: 180, types: 132, coercions: 0, joins: 2/2}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule4 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 20 0}]
Strict.$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule3 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$trModule3 = GHC.Internal.Types.TrNameS Strict.$trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule2 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Strict.$trModule2 = "Strict"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule1 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$trModule1 = GHC.Internal.Types.TrNameS Strict.$trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Strict.$trModule :: GHC.Internal.Types.Module
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$trModule
= GHC.Internal.Types.Module Strict.$trModule3 Strict.$trModule1
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep
= GHC.Internal.Types.KindRepTyConApp
GHC.Internal.Types.$tcInt
(GHC.Internal.Types.[] @GHC.Internal.Types.KindRep)
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Strict.$tcPoint2 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Strict.$tcPoint2 = "Point"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Strict.$tcPoint1 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$tcPoint1 = GHC.Internal.Types.TrNameS Strict.$tcPoint2
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
Strict.$tcPoint :: GHC.Internal.Types.TyCon
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$tcPoint
= GHC.Internal.Types.TyCon
15270500999396957839#Word64
1310266649928625514#Word64
Strict.$trModule
Strict.$tcPoint1
0#
GHC.Internal.Types.krep$*
-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
$krep1 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep1
= GHC.Internal.Types.KindRepTyConApp
Strict.$tcPoint (GHC.Internal.Types.[] @GHC.Internal.Types.KindRep)
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep2 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep2 = GHC.Internal.Types.KindRepFun $krep $krep1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Strict.$tc'Point1 [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
Strict.$tc'Point1 = GHC.Internal.Types.KindRepFun $krep $krep2
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Strict.$tc'Point3 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Strict.$tc'Point3 = "'Point"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Strict.$tc'Point2 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$tc'Point2 = GHC.Internal.Types.TrNameS Strict.$tc'Point3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
Strict.$tc'Point :: GHC.Internal.Types.TyCon
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.$tc'Point
= GHC.Internal.Types.TyCon
209391528505640325#Word64
16890207377952019736#Word64
Strict.$trModule
Strict.$tc'Point2
0#
Strict.$tc'Point1
-- RHS size: {terms: 15, types: 7, coercions: 0, joins: 0/0}
shift :: Int -> Point -> Point
[GblId,
Arity=2,
Str=<1!P(L)><1!P(L,L)>,
Cpr=1,
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
Tmpl= \ (d [Occ=Once1!] :: Int) (ds [Occ=Once1!] :: Point) ->
case ds of { Point bx [Occ=Once1] bx1 [Occ=Once1] ->
case d of { GHC.Internal.Types.I# y ->
Strict.Point
(GHC.Internal.Prim.+# bx y) (GHC.Internal.Prim.+# bx1 y)
}
}}]
shift
= \ (d :: Int) (ds :: Point) ->
case ds of { Point bx bx1 ->
case d of { GHC.Internal.Types.I# y ->
Strict.Point
(GHC.Internal.Prim.+# bx y) (GHC.Internal.Prim.+# bx1 y)
}
}
Rec {
-- RHS size: {terms: 15, types: 10, coercions: 0, joins: 0/0}
Strict.$wgo1 [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int# -> [Int] -> GHC.Internal.Prim.Int#
[GblId[StrictWorker([~, !])],
Arity=2,
Str=<L><1L>,
Unf=OtherCon []]
Strict.$wgo1
= \ (ww :: GHC.Internal.Prim.Int#) (ds :: [Int]) ->
case ds of {
[] -> ww;
: x xs ->
case x of { GHC.Internal.Types.I# y ->
Strict.$wgo1 (GHC.Internal.Prim.+# ww y) xs
}
}
end Rec }
-- RHS size: {terms: 8, types: 3, coercions: 0, joins: 0/0}
sumLazy :: [Int] -> Int
[GblId,
Arity=1,
Str=<1L>,
Cpr=1,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [0] 50 10}]
sumLazy
= \ (ds :: [Int]) ->
case Strict.$wgo1 0# ds of ww { __DEFAULT ->
GHC.Internal.Types.I# ww
}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
sumStrict :: [Int] -> Int
[GblId,
Arity=1,
Str=<1L>,
Cpr=1,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
sumStrict = sumLazy
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Strict.minMax2 :: Int
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.minMax2 = GHC.Internal.Types.I# 0#
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
Strict.minMax1 :: (Int, Int)
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Strict.minMax1 = (Strict.minMax2, Strict.minMax2)
Rec {
-- RHS size: {terms: 41, types: 31, coercions: 0, joins: 2/2}
Strict.$wgo [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int#
-> GHC.Internal.Prim.Int#
-> [Int]
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[GblId[StrictWorker([~, ~, !])],
Arity=3,
Str=<L><L><1L>,
Unf=OtherCon []]
Strict.$wgo
= \ (ww :: GHC.Internal.Prim.Int#)
(ww1 :: GHC.Internal.Prim.Int#)
(ds :: [Int]) ->
case ds of {
[] -> (# ww, ww1 #);
: w ws ->
case w of { GHC.Internal.Types.I# y1 ->
join {
$j [Dmd=1C(1,!P(L,L))]
:: GHC.Internal.Prim.Int#
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[LclId[JoinId(1)(Nothing)], Arity=1, Str=<L>, Unf=OtherCon []]
$j (ww2 [OS=OneShot] :: GHC.Internal.Prim.Int#)
= join {
$j1 [Dmd=1C(1,!P(L,L))]
:: GHC.Internal.Prim.Int#
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[LclId[JoinId(1)(Nothing)], Arity=1, Str=<L>, Unf=OtherCon []]
$j1 (ww3 [OS=OneShot] :: GHC.Internal.Prim.Int#)
= Strict.$wgo ww2 ww3 ws } in
case GHC.Internal.Prim.<=# ww1 y1 of {
__DEFAULT -> jump $j1 ww1;
1# -> jump $j1 y1
} } in
case GHC.Internal.Prim.<=# ww y1 of {
__DEFAULT -> jump $j y1;
1# -> jump $j ww
}
}
}
end Rec }
-- RHS size: {terms: 20, types: 17, coercions: 0, joins: 0/0}
Strict.minMax_go [InlPrag=[2]] :: Int -> Int -> [Int] -> (Int, Int)
[GblId[StrictWorker([!, !, !])],
Arity=3,
Str=<1!P(L)><1!P(L)><1L>,
Cpr=1(1, 1),
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=3,unsat_ok=True,boring_ok=False)
Tmpl= \ (lo [Occ=Once1!] :: Int)
(hi [Occ=Once1!] :: Int)
(ds [Occ=Once1] :: [Int]) ->
case lo of { GHC.Internal.Types.I# ww [Occ=Once1] ->
case hi of { GHC.Internal.Types.I# ww1 [Occ=Once1] ->
case Strict.$wgo ww ww1 ds of
{ (# ww2 [Occ=Once1], ww3 [Occ=Once1] #) ->
(GHC.Internal.Types.I# ww2, GHC.Internal.Types.I# ww3)
}
}
}}]
Strict.minMax_go
= \ (lo :: Int)
(hi [OS=OneShot] :: Int)
(ds [OS=OneShot] :: [Int]) ->
case lo of { GHC.Internal.Types.I# ww ->
case hi of { GHC.Internal.Types.I# ww1 ->
case Strict.$wgo ww ww1 ds of { (# ww2, ww3 #) ->
(GHC.Internal.Types.I# ww2, GHC.Internal.Types.I# ww3)
}
}
}
-- RHS size: {terms: 10, types: 7, coercions: 0, joins: 0/0}
minMax :: [Int] -> (Int, Int)
[GblId,
Arity=1,
Str=<1L>,
Cpr=1(1, 1),
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (ds [Occ=Once1!] :: [Int]) ->
case ds of {
[] -> Strict.minMax1;
: z zs [Occ=Once1] -> Strict.minMax_go z z zs
}}]
minMax
= \ (ds :: [Int]) ->
case ds of {
[] -> Strict.minMax1;
: z zs -> Strict.minMax_go z z zs
}Allocation and evaluation made explicit, ready for code generation.
$tc'Point3 :: Addr# = "'Point"#;
$tcPoint2 :: Addr# = "Point"#;
$trModule2 :: Addr# = "Strict"#;
$trModule4 :: Addr# = "main"#;
$WPoint :: Int %1 -> Int %1 -> Point =
{} \r [conrep conrep]
case conrep of conrep {
I# unbx -> case conrep of conrep { I# unbx -> Point [unbx unbx]; };
};
Point :: Int# %1 -> Int# %1 -> Point =
{} \r [eta eta] Point [eta eta];
$trModule3 :: TrName = TrNameS! [$trModule4];
$trModule1 :: TrName = TrNameS! [$trModule2];
$trModule :: Module = Module! [$trModule3 $trModule1];
$krep :: KindRep = KindRepTyConApp! [$tcInt []];
$tcPoint1 :: TrName = TrNameS! [$tcPoint2];
$tcPoint :: TyCon =
TyCon! [15270500999396957839#Word64
1310266649928625514#Word64
$trModule
$tcPoint1
0#
krep$*];
$krep1 :: KindRep = KindRepTyConApp! [$tcPoint []];
$krep2 :: KindRep = KindRepFun! [$krep $krep1];
$tc'Point1 :: KindRep = KindRepFun! [$krep $krep2];
$tc'Point2 :: TrName = TrNameS! [$tc'Point3];
$tc'Point :: TyCon =
TyCon! [209391528505640325#Word64
16890207377952019736#Word64
$trModule
$tc'Point2
0#
$tc'Point1];
shift :: Int -> Point -> Point =
{} \r [d ds]
case ds of wild {
Point bx bx1 ->
case d of wild1 {
I# y ->
case +# [bx1 y] of shift_sat {
__DEFAULT ->
case +# [bx y] of shift_sat {
__DEFAULT -> Point [shift_sat shift_sat];
};
};
};
};
Rec {
$wgo1 :: Int# -> [Int] -> Int# =
{} \r [ww ds]
case ds<TagProper> of wild {
[] -> ww<TagProper>;
: x xs ->
case x of wild1 {
I# y ->
case +# [ww y] of $wgo1_sat {
__DEFAULT -> case xs of xs { __DEFAULT -> $wgo1 $wgo1_sat xs; };
};
};
};
end Rec }
sumLazy :: [Int] -> Int =
{} \r [ds]
case case ds of ds { __DEFAULT -> $wgo1 0# ds; } of ww {
__DEFAULT -> I# [ww];
};
sumStrict :: [Int] -> Int = {} \r [eta] sumLazy eta;
minMax2 :: Int = I#! [0#];
minMax1 :: (Int, Int) = (,)! [minMax2 minMax2];
Rec {
$wgo :: Int# -> Int# -> [Int] -> (# Int#, Int# #) =
{} \r [ww ww1 ds]
case ds<TagProper> of wild {
[] -> (#,#) [ww ww1];
: w ws ->
case w of wild1 {
I# y1 ->
let-no-escape {
$j :: Int# -> (# Int#, Int# #) =
{y1, ww1, ws} \j [ww2]
let-no-escape {
$j1 :: Int# -> (# Int#, Int# #) =
{ww2, ws} \j [ww3] case ws of ws { __DEFAULT -> $wgo ww2 ww3 ws; };
} in
case <=# [ww1 y1] of lwild {
__DEFAULT -> $j1 ww1;
1# -> $j1 y1;
};
} in
case <=# [ww y1] of lwild {
__DEFAULT -> $j y1;
1# -> $j ww;
};
};
};
end Rec }
minMax_go :: Int -> Int -> [Int] -> (Int, Int) =
{} \r [lo hi ds]
case lo<TagProper> of wild {
I# ww ->
case hi<TagProper> of wild1 {
I# ww1 ->
case $wgo ww ww1 ds of wild2 {
(#,#) ww2 ww3 ->
let { minMax_go_sat :: Int = I#! [ww3]; } in
let { minMax_go_sat :: Int = I#! [ww2];
} in (,) [minMax_go_sat minMax_go_sat];
};
};
};
minMax :: [Int] -> (Int, Int) =
{} \r [ds]
case ds of wild {
[] -> minMax1<TagProper>;
: z zs ->
case z of z {
__DEFAULT ->
case z of z {
__DEFAULT -> case zs of zs { __DEFAULT -> minMax_go z z zs; };
};
};
};Strict.$tc'Point3 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"'Point"#;
Strict.$tcPoint2 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"Point"#;
Strict.$trModule2 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"Strict"#;
Strict.$trModule4 :: GHC.Internal.Prim.Addr#
[GblId, Unf=OtherCon []] =
"main"#;
Strict.$WPoint [InlPrag=INLINE[final] CONLIKE]
:: GHC.Internal.Types.Int
%1 -> GHC.Internal.Types.Int %1 -> Strict.Point
[GblId[DataConWrapper],
Arity=2,
Caf=NoCafRefs,
Str=<SL><SL>,
Unf=OtherCon []] =
{} \r [conrep conrep]
case conrep of conrep {
GHC.Internal.Types.I# unbx [Occ=Once1] ->
case conrep of conrep {
GHC.Internal.Types.I# unbx [Occ=Once1] -> Strict.Point [unbx unbx];
};
};
Strict.Point [InlPrag=CONLIKE]
:: GHC.Internal.Prim.Int#
%1 -> GHC.Internal.Prim.Int# %1 -> Strict.Point
[GblId[DataCon],
Arity=2,
Caf=NoCafRefs,
Str=<L><L>,
Unf=OtherCon []] =
{} \r [eta eta] Strict.Point [eta eta];
Strict.$trModule3 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Strict.$trModule4];
Strict.$trModule1 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Strict.$trModule2];
Strict.$trModule :: GHC.Internal.Types.Module
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.Module! [Strict.$trModule3 Strict.$trModule1];
$krep :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.KindRepTyConApp! [GHC.Internal.Types.$tcInt
GHC.Internal.Types.[]];
Strict.$tcPoint1 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Strict.$tcPoint2];
Strict.$tcPoint :: GHC.Internal.Types.TyCon
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TyCon! [15270500999396957839#Word64
1310266649928625514#Word64
Strict.$trModule
Strict.$tcPoint1
0#
GHC.Internal.Types.krep$*];
$krep1 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.KindRepTyConApp! [Strict.$tcPoint
GHC.Internal.Types.[]];
$krep2 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.KindRepFun! [$krep $krep1];
Strict.$tc'Point1 [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.KindRepFun! [$krep $krep2];
Strict.$tc'Point2 :: GHC.Internal.Types.TrName
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TrNameS! [Strict.$tc'Point3];
Strict.$tc'Point :: GHC.Internal.Types.TyCon
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.TyCon! [209391528505640325#Word64
16890207377952019736#Word64
Strict.$trModule
Strict.$tc'Point2
0#
Strict.$tc'Point1];
Strict.shift
:: GHC.Internal.Types.Int -> Strict.Point -> Strict.Point
[GblId, Arity=2, Str=<1!P(L)><1!P(L,L)>, Cpr=1, Unf=OtherCon []] =
{} \r [d ds]
case ds of wild {
Strict.Point bx [Occ=Once1] bx1 [Occ=Once1] ->
case d of wild1 {
GHC.Internal.Types.I# y ->
case +# [bx1 y] of shift_sat {
__DEFAULT ->
case +# [bx y] of shift_sat {
__DEFAULT -> Strict.Point [shift_sat shift_sat];
};
};
};
};
Rec {
Strict.$wgo1 [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int#
-> [GHC.Internal.Types.Int] -> GHC.Internal.Prim.Int#
[GblId[StrictWorker([~, !])],
Arity=2,
Str=<L><1L>,
Unf=OtherCon []] =
{} \r [ww ds]
case ds<TagProper> of wild {
[] -> ww<TagProper>;
: x [Occ=Once1!] xs [Occ=Once1] ->
case x of wild1 {
GHC.Internal.Types.I# y [Occ=Once1] ->
case +# [ww y] of $wgo1_sat {
__DEFAULT ->
case xs of xs { __DEFAULT -> Strict.$wgo1 $wgo1_sat xs; };
};
};
};
end Rec }
Strict.sumLazy
:: [GHC.Internal.Types.Int] -> GHC.Internal.Types.Int
[GblId, Arity=1, Str=<1L>, Cpr=1, Unf=OtherCon []] =
{} \r [ds]
case case ds of ds { __DEFAULT -> Strict.$wgo1 0# ds; } of ww {
__DEFAULT -> GHC.Internal.Types.I# [ww];
};
Strict.sumStrict
:: [GHC.Internal.Types.Int] -> GHC.Internal.Types.Int
[GblId, Arity=1, Str=<1L>, Cpr=1, Unf=OtherCon []] =
{} \r [eta] Strict.sumLazy eta;
Strict.minMax2 :: GHC.Internal.Types.Int
[GblId, Unf=OtherCon []] =
GHC.Internal.Types.I#! [0#];
Strict.minMax1 :: (GHC.Internal.Types.Int, GHC.Internal.Types.Int)
[GblId, Unf=OtherCon []] =
(,)! [Strict.minMax2 Strict.minMax2];
Rec {
Strict.$wgo [InlPrag=[2], Occ=LoopBreaker]
:: GHC.Internal.Prim.Int#
-> GHC.Internal.Prim.Int#
-> [GHC.Internal.Types.Int]
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[GblId[StrictWorker([~, ~, !])],
Arity=3,
Str=<L><L><1L>,
Unf=OtherCon []] =
{} \r [ww ww1 ds]
case ds<TagProper> of wild {
[] -> (#,#) [ww ww1];
: w [Occ=Once1!] ws [Occ=Once1] ->
case w of wild1 {
GHC.Internal.Types.I# y1 ->
let-no-escape {
$j [Occ=Once2!T[1], Dmd=1C(1,!P(L,L))]
:: GHC.Internal.Prim.Int#
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[LclId[JoinId(1)(Nothing)], Arity=1, Str=<L>, Unf=OtherCon []] =
{y1, ww1, ws} \j [ww2]
let-no-escape {
$j1 [Occ=Once2!T[1], Dmd=1C(1,!P(L,L))]
:: GHC.Internal.Prim.Int#
-> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
[LclId[JoinId(1)(Nothing)], Arity=1, Str=<L>, Unf=OtherCon []] =
{ww2, ws} \j [ww3]
case ws of ws { __DEFAULT -> Strict.$wgo ww2 ww3 ws; };
} in
case <=# [ww1 y1] of lwild {
__DEFAULT -> $j1 ww1;
1# -> $j1 y1;
};
} in
case <=# [ww y1] of lwild {
__DEFAULT -> $j y1;
1# -> $j ww;
};
};
};
end Rec }
Strict.minMax_go [InlPrag=[2]]
:: GHC.Internal.Types.Int
-> GHC.Internal.Types.Int
-> [GHC.Internal.Types.Int]
-> (GHC.Internal.Types.Int, GHC.Internal.Types.Int)
[GblId[StrictWorker([!, !, !])],
Arity=3,
Str=<1!P(L)><1!P(L)><1L>,
Cpr=1(1, 1),
Unf=OtherCon []] =
{} \r [lo hi ds]
case lo<TagProper> of wild {
GHC.Internal.Types.I# ww [Occ=Once1] ->
case hi<TagProper> of wild1 {
GHC.Internal.Types.I# ww1 [Occ=Once1] ->
case Strict.$wgo ww ww1 ds of wild2 {
(#,#) ww2 [Occ=Once1] ww3 [Occ=Once1] ->
let {
minMax_go_sat [Occ=Once1] :: GHC.Internal.Types.Int
[LclId, Unf=OtherCon []] =
GHC.Internal.Types.I#! [ww3]; } in
let {
minMax_go_sat [Occ=Once1] :: GHC.Internal.Types.Int
[LclId, Unf=OtherCon []] =
GHC.Internal.Types.I#! [ww2];
} in (,) [minMax_go_sat minMax_go_sat];
};
};
};
Strict.minMax
:: [GHC.Internal.Types.Int]
-> (GHC.Internal.Types.Int, GHC.Internal.Types.Int)
[GblId, Arity=1, Str=<1L>, Cpr=1(1, 1), Unf=OtherCon []] =
{} \r [ds]
case ds of wild {
[] -> Strict.minMax1<TagProper>;
: z zs [Occ=Once1] ->
case z of z {
__DEFAULT ->
case z of z {
__DEFAULT ->
case zs of zs { __DEFAULT -> Strict.minMax_go z z zs; };
};
};
};In Core (optimised), sumStrict’s inner loop has become:
$wgo1 :: Int# -> [Int] -> Int#
$wgo1
= \ (ww :: Int#) (ds :: [Int]) ->
case ds of {
[] -> ww;
: x xs -> case x of { I# y -> $wgo1 (+# ww y) xs }
}
The accumulator is an unboxed Int#, the addition is the primop +#, and no
I# box is ever allocated for it. That is demand analysis proving the
accumulator is forced, and worker/wrapper cashing the proof in.
sumLazy is the same source without the bang, and the difference in what the
optimiser can do is the point of the example.
Reading the source yourself
The middle end is large enough that reading it front to back is not a plan. Pick a transformation and follow it.
GHC/Core/Opt/OccurAnal.hsfirst, despite being an analysis rather than a transformation. Nearly every simplifier decision consults its output.GHC/Core/Opt/Simplify/Iteration.hsfor the rewrites themselves.GHC/Core/Opt/DmdAnal.hsandWorkWrap.hsas a pair: the analysis is only interesting because of what the transformation does with it.GHC/Core/Opt/Pipeline.hsto see the order everything runs in, which explains a lot about why a given optimisation did or did not fire.
-ddump-simpl-iterations shows the program after each pass, and
-ddump-rule-firings names every rule that fired. When an expected optimisation
does not happen, those two flags usually explain it faster than reading the code.
Both are pre-run on a minimal fusion pipeline in the trace explorer: watch fold/build fire, pass by pass.