dependingOn in 6.6

John Meacham john at repetae.net
Wed Aug 9 19:54:01 EDT 2006


On Wed, Aug 09, 2006 at 08:40:06AM +0100, Simon Peyton-Jones wrote:
> What's the implementation you have in mind?  It's sure to get in the way of some optimisations; e.g.	
> 	((\x.e) `dependingOn` y) arg
> Maybe that doesn’t matter.  I'm reluctant to build in optimisation rules for dependingOn.

getting in the way of optimizations is 100% exactly the purpose of it. 

there are two optimizations in particular it gives the user fine grained
control over, CSE and let floating. it is also very useful for
implementing low level IO stuff, and some special case code in ghc
dealing with the world# can be dropped probably if dependingOn were
used. (I'll get to that)


> Is the argument order right?  dependingOn is very like "seq" except
> that it's lazy.  Shouldn't it have the same type?

I think the similarity to seq is mainly superficial. seq doesn't
actually keep its arguments coupled together, it causes its first
argument to be strict in a given context, but other than that it doesn't
really use its second argument. both evaluations can be floated around
independently after the initial transformation.

dependingOn maintains the relaionship between its arguments throughout
the compilation process right up to code generation.

> When is it ok to inline the function, to reveal its (lack of) implementation?  Can you just implement it in a library of your own?

I am assuming it will be transformed into a primitive that is simply
passed through the opimizer like normal and discarded by the code
generator. the existing touch# might do the job.

> Can you list the "all sorts of applications" you have in mind?

for the user there are a couple of uses, mainly it gets rid of the
caveats of using unsafePerformIO so you no longer need to turn of
let-floating or cse to use it safely in certain cases.

-- no need for CSE

> data Dummy1 = Dummy1
> intVar1 :: IORef Int
> intVar1 = unsafePerformIO (newIORef 0 `dependingOn` Dummy1)

> data Dummy2 = Dummy2
> intVar2 :: IORef Int
> intVar2 = unsafePerformIO (newIORef 0 `dependingOn` Dummy2)

normally, you would have to compile with -fno-cse to keep these two
variables from being turned into one. however, since Dummy1 and Dummy2
will never be the same, the two terms cannot be considered the same by
he optimizer so there is no problem.


-- no need for turning off let-floating
f x = log `seq` ... where
        log = unsafePerformIO (putErrLn "f called" `dependingOn` x)

normally, the log function will be floated out so the rouinte is only
called once when you actually want it to be executed every time f x is
entered. the 'dependingOn' ensures this is the case.

similarly, it can be used to quash space leaks

bigAndFast is something that is trivial to compute, but would take up a
ton of space if kept around, like an expanded string. you want to ensure
it is not floated to the top level.

f x = ... bigAndFast ... where
        bigAndFast = unpackString (constantString `dependingOn` x)

now it cannot be floated to the top level.



Now for some of the low level goodness:

right now, ghc has to be careful to never inline unsafePerformIO, this
gets in the way of optimizations, like if what it computes is a good
producer, you still can't fusion it with a consumer.


-- current definition
{-# NOINLINE unsafePerformIO #-}
unsafePerformIO	:: IO a -> a
unsafePerformIO (IO m) = case m realWorld# of (# _, r #)   -> r


-- new definition
unsafePerformIO	:: IO a -> a
unsafePerformIO (IO m) = case m (realWorld# `dependingOn` m) of (# _, r #)   -> r

now the problem goes away, realWord# is no longer a consant that might
lead to CSEing with something else, but now depends on the argumen to
unsafePerformIO. now unsafePerformIO can safely be inlined.

similarly, runST and array creation can be simplified in the same way.


it allows sequence points to be implemented, right now there is
special case code in the compiler to ensure certain strict values are
never floated 'ahead' in the IO monad. you can implement something like

sequence :: a -> IO a
sequence x = IO \world -> (# world, x `dependingOn` world #)

now, any use of thunks passed through sequence are guarenteed to not be
evaluated until after all IO actions happening before it have
completed.


other uses have popped up. I implemented it just as a hack to get array
creation to work, but have found it to be very useful in general.


        John
-- 
John Meacham - ⑆repetae.net⑆john⑈


More information about the Glasgow-haskell-users mailing list