
{-# INLINE f PEEL n #-} inline calls *into* recursive f (called loop peeling for loops) {-# INLINE f UNROLL m #-} inline recursive calls to f *inside* f (called loop unrolling for loops)
{-# INLINE f PEEL n UNROLL m #-} combine the previous two
The problem here is that this only works for directly recursive functions which I, for instance, don't normally use in high- performance code. Most of my loops are pipelines of collective combinators like map, filter, fold etc. because these are the ones that can be fused automatically. Unless I'm misunderstanding something, this approach doesn't handle such cases.
Actually, my first sketch had a problem in that it would work only too well for mutually recursive functions, making it necessary to use loop breakers in spite of the explicit limits (even if we limit unroll to direct recursion, as I intended originally, peeling would then apply to the calls into other functions in the recursion). One way out would be to treat the whole mutual recursion as a single entity, either implicitly, as I indicated, or explicitly, as I interpret Brandon's somewhat ambiguous comment. In other words, the peel/unroll limits would apply to a whole group of mutually recursive definitions, ensuring termination of the inline process without additional loop breakers. If we do that, then it might make sense to talk about peeling/unrolling wrt the whole recursion group. In any case, I need to refine my spec!-) But this discussion is very helpful in finding the issues that need to be addressed and clarified. Another issue that I ran into in manual unrolling is that I sometimes want to unroll wrt a specific parameter of a multi- parameter function, usually because that parameter can only have a very small numer of possible values, or just because the original function encodes multiple loops that I want to disentangle. Claus