X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=driver%2Fordering-passes;fp=driver%2Fordering-passes;h=305f3f06b48523101c5101c3306a89cdda80a411;hp=0000000000000000000000000000000000000000;hb=0065d5ab628975892cea1ec7303f968c3338cbe1;hpb=28a464a75e14cece5db40f2765a29348273ff2d2 diff --git a/driver/ordering-passes b/driver/ordering-passes new file mode 100644 index 0000000..305f3f0 --- /dev/null +++ b/driver/ordering-passes @@ -0,0 +1,257 @@ + Ordering the compiler's passes + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Change notes +~~~~~~~~~~~~ +1 Nov 94 * NB: if float-out is done after strictness, remember to + switch off demandedness flags on floated bindings! +13 Oct 94 * Run Float Inwards once more after strictness-simplify [andre] + 4 Oct 94 * Do simplification between float-in and strictness [andre] + * Ignore-inline-pragmas flag for final simplification [andre] + +Aug 94 Original: Simon, Andy, Andre + + + + +This ordering obeys all the constraints except (5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + full laziness + simplify with foldr/build + float-in + simplify + strictness + float-in + +[check FFT2 still gets benefits with this ordering] + +================================= + Constraints +================================= + +1. float-in before strictness. +Reason: floating inwards moves definitions inwards to a site at which +the binding might well be strict. + +Example let x = ... in + y = x+1 + in + ... +===> + let y = let x = ... in x+1 + in ... + +The strictness analyser will do a better job of the latter +than the former. + +2. Don't simplify between float-in and strictness, +unless you disable float-let-out-of-let, otherwise +the simiplifier's local floating might undo some +useful floating-in. + +Example let f = let y = .. in \x-> x+y + in ... +===> + let y = ... + f = \x -> x+y + in ... + +This is a bad move, because now y isn't strict. +In the pre-float case, the binding for y is strict. +Mind you, this isn't a very common case, and +it's easy to disable float-let-from-let. + +3. Want full-laziness before foldr/build. +Reason: Give priority to sharing rather than deforestation. + +Example \z -> let xs = build g + in foldr k z xs +===> + let xs = build g + in \x -> foldr k z xs + +In the post-full-laziness case, xs is shared between all +applications of the function. If we did foldr/build +first, we'd have got + + \z -> g k z + +and now we can't share xs. + + +4. Want strictness after foldr/build. +Reason: foldr/build makes new function definitions which +can benefit from strictness analysis. + +Example: sum [1..10] +===> (f/b) + let g x a | x > 10 = a + | otherwise = g (x+1) (a+x) + +Here we clearly want to get strictness analysis on g. + + +5. Want full laziness after strictness +Reason: absence may allow something to be floated out +which would not otherwise be. + +Example \z -> let x = f (a,z) in ... +===> (absence anal + inline wrapper of f) + \z -> let x = f.wrk a in ... +===> (full laziness) + let x= f.wrk a in \z -> ... + +TOO BAD. This doesn't look a common case to me. + + +6. Want float-in after foldr/build. +Reason: Desugaring list comprehensions + foldr/build +gives rise to new float-in opportunities. + +Example ...some list comp... +==> (foldr/build) + let v = h xs in + case ... of + [] -> v + (y:ys) -> ...(t v)... +==> (simplifier) + let v = h xs in + case ... of + [] -> h xs + (y:ys) -> ...(t v)... + +Now v could usefully be floated into the second branch. + +7. Want simplify after float-inwards. +[Occurred in the prelude, compiling ITup2.hs, function dfun.Ord.(*,*)] +This is due to the following (that happens with dictionaries): + +let a1 = case v of (a,b) -> a +in let m1 = \ c -> case c of I# c# -> case c# of 1 -> a1 5 + 2 -> 6 +in let m2 = \ c -> case c of I# c# -> + case c# +# 1# of cc# -> let cc = I# cc# + in m1 cc + in (m1,m2) + +floating inwards will push the definition of a1 into m1 (supposing +it is only used there): + +in let m1 = let a1 = case v of (a,b) -> a + in \ c -> case c of I# c# -> case c# of 1 -> a1 5 + 2 -> 6 +in let m2 = \ c -> case c of I# c# -> + case c# +# 1# of cc# -> let cc = I# cc# + in m1 cc + in (m1,m2) + +if we do strictness analysis now we will not get a worker-wrapper +for m1, because of the "let a1 ..." (notice that a1 is not strict in +its body). + +Not having this worker wrapper might be very bad, because it might +mean that we will have to rebox arguments to m1 if they are +already unboxed, generating extra allocations, as occurs with m2 (cc) +above. + +To solve this problem we have decided to run the simplifier after +float-inwards, so that lets whose body is a HNF are floated out, +undoing the float-inwards transformation in these cases. +We are then back to the original code, which would have a worker-wrapper +for m1 after strictness analysis and would avoid the extra let in m2. + +What we lose in this case are the opportunities for case-floating +that could be presented if, for example, a1 would indeed be demanded (strict) +after the floating inwards. + +The only way of having the best of both is if we have the worker/wrapper +pass explicitly called, and then we could do with + +float-in +strictness analysis +simplify +strictness analysis +worker-wrapper generation + +as we would +a) be able to detect the strictness of m1 after the + first call to the strictness analyser, and exploit it with the simplifier + (in case it was strict). +b) after the call to the simplifier (if m1 was not demanded) + it would be floated out just like we currently do, before stricness + analysis II and worker/wrapperisation. + +The reason to not do worker/wrapperisation twice is to avoid +generating wrappers for wrappers which could happen. + + +8. If full laziness is ever done after strictness, remember to switch off +demandedness flags on floated bindings! This isn't done at the moment. + + +Ignore-inline-pragmas flag for final simplification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[Occurred in the prelude, compiling ITup2.hs, function dfun.Ord.(*,*)] +Sometimes (e.g. in dictionary methods) we generate +worker/wrappers for functions but the wrappers are never +inlined. In dictionaries we often have + +dict = let f1 = ... + f2 = ... + ... + in (f1,f2,...) + +and if we create worker/wrappers for f1,...,fn the wrappers will not +be inlined anywhere, and we will have ended up with extra +closures (one for the worker and one for the wrapper) and extra +function calls, as when we access the dictionary we will be acessing +the wrapper, which will call the worker. +The simplifier never inlines workers into wrappers, as the wrappers +themselves have INLINE pragmas attached to them (so that they are always +inlined, and we do not know in advance how many times they will be inlined). + +To solve this problem, in the last call to the simplifier we will +ignore these inline pragmas and handle the workers and the wrappers +as normal definitions. This will allow a worker to be inlined into +the wrapper if it satisfies all the criteria for inlining (e.g. it is +the only occurrence of the worker etc.). + +Run Float Inwards once more after strictness-simplify +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[Occurred in the prelude, compiling IInt.hs, function const.Int.index.wrk] +When workers are generated after strictness analysis (worker/wrapper), +we generate them with "reboxing" lets, that simply reboxes the unboxed +arguments, as it may be the case that the worker will need the +original boxed value: + +f x y = case x of + (a,b) -> case y of + (c,d) -> case a == c of + True -> (x,x) + False -> ((1,1),(2,2)) + +==> (worker/wrapper) + +f_wrapper x y = case x of + (a,b) -> case y of + (c,d) -> f_worker a b c d + +f_worker a b c d = let x = (a,b) + y = (c,d) + in case a == c of + True -> (x,x) + False -> ((1,1),(2,2)) + +in this case the simplifier will remove the binding for y as it is not +used (we expected this to happen very often, but we do not know how +many "reboxers" are eventually removed and how many are kept), and +will keep the binding for x. But notice that x is only used in *one* +of the branches in the case, but is always being allocated! The +floating inwards pass would push its definition into the True branch. +A similar benefit occurs if it is only used inside a let definition. +These are basically the advantages of floating inwards, but they are +only exposed after the S.A./worker-wrapperisation of the code! As we +also have reasons to float inwards before S.A. we have to run it +twice. +