Announce: ghc-core, command line pager for reading GHC Core

Just a quick announcement, I've uploaded to hackage 'ghc-core' , a wrapper over ghc for displaying the optimised core and assembly language ghc produces from your programs. The code is colourised by hscolour, and displayed in a pager, git-log style. This will be useful for those who like looking at optimised Haskell. You can quickly get an idea of what kind of core and assembly your code is turning into, and the effect of various flags on the result. Usage: $ ghc-core Foo.hs $ ghc-core Foo.hs -optc-O2 -fvia-C Get it here, http://hackage.haskell.org/cgi-bin/hackage-scripts/package/ghc-core Screenshot, http://galois.com/~dons/images/ghc-core.png Cheers, Don

Hi Don,
Just a quick announcement, I've uploaded to hackage 'ghc-core' , a wrapper over ghc for displaying the optimised core and assembly language ghc produces from your programs.
This is cool, but it still lags behind the facilities found in yhc-core. http://yhc06.blogspot.com/2006/12/yhccorehtml.html I have found that any effort put into improving Core viewing tools repays itself rather quickly, and that HTML output with hyperlinks is incredibly handy! Thanks Neil

On Fri, 2 May 2008, Neil Mitchell wrote:
Hi Don,
Just a quick announcement, I've uploaded to hackage 'ghc-core' , a wrapper over ghc for displaying the optimised core and assembly language ghc produces from your programs.
This is cool, but it still lags behind the facilities found in yhc-core.
http://yhc06.blogspot.com/2006/12/yhccorehtml.html
I have found that any effort put into improving Core viewing tools repays itself rather quickly, and that HTML output with hyperlinks is incredibly handy!
I have tried ghc-core now. I find the idea really good to make Core more readable, but although my examples are moderately small the Core already spans several pages. Thus highlighting alone is of limitted help. Indeed, if the output would be HTML as Neil proposed, this would be cool. Then I could quickly find out, what value 'lit_r3gZ' refers to.

On Fri, 2 May 2008, Neil Mitchell wrote:
Hi Don,
Just a quick announcement, I've uploaded to hackage 'ghc-core' , a wrapper over ghc for displaying the optimised core and assembly language ghc produces from your programs.
This is cool, but it still lags behind the facilities found in yhc-core.
http://yhc06.blogspot.com/2006/12/yhccorehtml.html
I have found that any effort put into improving Core viewing tools repays itself rather quickly, and that HTML output with hyperlinks is incredibly handy!
An even more advanced tool could show differences between two Core listings. Say I have a program which runs too slow. But if I change a small detail it runs significantly faster - I want to know, how did my change in the Haskell file modified the Core and why the speedup. Showing differences between Core files will certainly be complicated because the generated identifiers are completely different. I don't know whether the order of declarations is a problem. I have an example here where a program becomes faster by a factor of 10 with a rather small change. First I thought the slow thing must be the higher order function which occurs in the Core and is not inlined, but it is present in the slow and the fast variant of the program.

On Fri, 23 May 2008, Henning Thielemann wrote:
An even more advanced tool could show differences between two Core listings. Say I have a program which runs too slow. But if I change a small detail it runs significantly faster - I want to know, how did my change in the Haskell file modified the Core and why the speedup. Showing differences between Core files will certainly be complicated because the generated identifiers are completely different. I don't know whether the order of declarations is a problem.
I have an example here where a program becomes faster by a factor of 10 with a rather small change. First I thought the slow thing must be the higher order function which occurs in the Core and is not inlined, but it is present in the slow and the fast variant of the program.
In this special example, actually simple 'diff' spotted the critical difference, namely a polymorphic function was called. Now I'm in a dilemma: I can either INLINE the function, then it's whole body is copied into main program, which is not necessary here. However this solution would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.

lemming:
On Fri, 23 May 2008, Henning Thielemann wrote:
An even more advanced tool could show differences between two Core listings. Say I have a program which runs too slow. But if I change a small detail it runs significantly faster - I want to know, how did my change in the Haskell file modified the Core and why the speedup. Showing differences between Core files will certainly be complicated because the generated identifiers are completely different. I don't know whether the order of declarations is a problem.
I have an example here where a program becomes faster by a factor of 10 with a rather small change. First I thought the slow thing must be the higher order function which occurs in the Core and is not inlined, but it is present in the slow and the fast variant of the program.
In this special example, actually simple 'diff' spotted the critical difference, namely a polymorphic function was called. Now I'm in a dilemma: I can either INLINE the function, then it's whole body is copied into main program, which is not necessary here. However this solution would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
I usually go with inlining, and get GHC as a whole program optimising compiler. -- Don

Hello Henning, Friday, May 23, 2008, 8:31:24 PM, you wrote:
would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
me too. btw, this already present in jhc. inlining doesn't work in any complex case since recursive functions can't be inlined -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Fri, 23 May 2008, Bulat Ziganshin wrote:
Hello Henning,
Friday, May 23, 2008, 8:31:24 PM, you wrote:
would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
me too. btw, this already present in jhc. inlining doesn't work in any complex case since recursive functions can't be inlined
GHC inlines recursive functions, too, otherwise it could not turn 'foldl' and friends into plain machine loops.

On Fri, 2008-05-23 at 21:24 +0200, Henning Thielemann wrote:
On Fri, 23 May 2008, Bulat Ziganshin wrote:
Hello Henning,
Friday, May 23, 2008, 8:31:24 PM, you wrote:
would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
me too. btw, this already present in jhc. inlining doesn't work in any complex case since recursive functions can't be inlined
GHC inlines recursive functions, too, otherwise it could not turn 'foldl' and friends into plain machine loops.
Actually ghc's definition of foldl is not recursive, though it does of course contain a local recursion: foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z xs = lgo z xs where lgo z [] = z lgo z (x:xs) = lgo (f z x) xs The lgo recursive call is then specialised at the call site and we can get good code. As I understand it, if foldl was written in the standard directly way then ghc would not inline it. So we have to manually apply the static argument transformation. You'll see that foldr is written in the same way. Duncan

duncan.coutts:
On Fri, 2008-05-23 at 21:24 +0200, Henning Thielemann wrote:
On Fri, 23 May 2008, Bulat Ziganshin wrote:
Hello Henning,
Friday, May 23, 2008, 8:31:24 PM, you wrote:
would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
me too. btw, this already present in jhc. inlining doesn't work in any complex case since recursive functions can't be inlined
GHC inlines recursive functions, too, otherwise it could not turn 'foldl' and friends into plain machine loops.
Actually ghc's definition of foldl is not recursive, though it does of course contain a local recursion:
foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z xs = lgo z xs where lgo z [] = z lgo z (x:xs) = lgo (f z x) xs
The lgo recursive call is then specialised at the call site and we can get good code.
As I understand it, if foldl was written in the standard directly way then ghc would not inline it. So we have to manually apply the static argument transformation. You'll see that foldr is written in the same way.
Similar for length et al. These worker/wrapper things inline quite happily: module B where mylength :: [a] -> Int mylength = go 0 where go :: Int -> [a] -> Int go n [] = n go n (_:xs) = go (n+1) xs {-# INLINE mylength #-} module A where import B main = print (mylength (enumFromTo 1 (10::Int))) ------------------------------------------------------------------------ And it is all inlined into A.hs: A.lvl2 = case A.go1 @ Int A.lvl A.lvl1 of w_axy { I# ww_axA -> $wshowSignedInt 0 ww_axA ([] @ Char) } A.go1 :: forall a1_a5n. Int -> [a1_a5n] -> Int A.go1 = \ (@ a1_a7b) (n_a5p :: Int) (ds_d9N :: [a1_a7b]) -> case ds_d9N of wild_B1 { [] -> n_a5p; : ds1_d9O xs_a5s -> A.go1 @ a1_a7b (case n_a5p of wild1_aqC { I# x_aqE -> I# (+# x_aqE 1) }) xs_a5s -- Don

On Fri, May 23, 2008 at 9:55 PM, Duncan Coutts
On Fri, 2008-05-23 at 21:24 +0200, Henning Thielemann wrote:
On Fri, 23 May 2008, Bulat Ziganshin wrote:
Hello Henning,
Friday, May 23, 2008, 8:31:24 PM, you wrote:
would guarantee speed in every case. Or I can SPECIALISE the function, then the function will only be called, but with polymorphism overhead eliminated. This would only work for a restricted range of types. I'd like to have a pragma, that tells GHC to specialise a function for every type it is called with.
me too. btw, this already present in jhc. inlining doesn't work in any complex case since recursive functions can't be inlined
GHC inlines recursive functions, too, otherwise it could not turn 'foldl' and friends into plain machine loops.
Actually ghc's definition of foldl is not recursive, though it does of course contain a local recursion:
foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z xs = lgo z xs where lgo z [] = z lgo z (x:xs) = lgo (f z x) xs
The lgo recursive call is then specialised at the call site and we can get good code.
As I understand it, if foldl was written in the standard directly way then ghc would not inline it. So we have to manually apply the static argument transformation. You'll see that foldr is written in the same way.
Note that the static argument transformation (SAT) was recently revived in ghc. Info from the patch: Fri Apr 11 18:21:37 CEST 2008 simonpj@microsoft.com * Revive the static argument transformation This patch revives the Static Argument Transformation, thanks to Max Bolingbroke. It is enabled with -fstatic-argument-transformation or -O2 Headline nofib results Size Allocs Runtime Min +0.0% -13.7% -21.4% Max +0.1% +0.0% +5.4% Geometric Mean +0.0% -0.2% -6.9% Haskell programs just keep getting faster and faster. And having ghc do the SAT to enable inlining recursive functions is really nice. Cheers, Josef

On Fri, May 23, 2008 at 05:54:36PM +0200, Henning Thielemann wrote:
An even more advanced tool could show differences between two Core listings.
That would be great. In the meantime, from GHC 6.10, -ddump-simpl -dsuppress-uniques can be good enough to get by (it means you don't get different random names, so diff more-or-less works). Thanks Ian

igloo:
On Fri, May 23, 2008 at 05:54:36PM +0200, Henning Thielemann wrote:
An even more advanced tool could show differences between two Core listings.
That would be great. In the meantime, from GHC 6.10, -ddump-simpl -dsuppress-uniques can be good enough to get by (it means you don't get different random names, so diff more-or-less works).
Great! That's easier than doing the renaming on the tool side. -- Don
participants (7)
-
Bulat Ziganshin
-
Don Stewart
-
Duncan Coutts
-
Henning Thielemann
-
Ian Lynagh
-
Josef Svenningsson
-
Neil Mitchell