\section{The Dataflow Engine}
\seclabel{engine}
\seclabel{dfengine}
\simon{Need a linking sentence like ``So far we have discussed clients of 
our library.  Now we turn our attention to the implementation of the
library itself.''  Yes, you'll want to use different vocabulary!}

The dataflow engine is implemented in two layers.
The lower layer is the \emph{dataflow monad}.
It~keeps track of the values of dataflow facts as the engine iterates.
The upper layer is divided into four parts:
a forward solver, a forward rewriter,
a backward solver, and a backward rewriter.

Despite the implicit claim in the title of this paper,
the dataflow engine is not simple.
The benefit of our design is that the \emph{interface} to the dataflow
engine (lattice, transfer functions, rewriting functions) is simple,
and as shown above, compiler passes written \emph{using} the engine
are very simple indeed.
But~the engine itself is complex.

Most of the complexity in the dataflow engine arises because we
implement the ambitious algorithm of
\citet{lerner-grove-chambers:2002}, who write
\begin{quote}
\emph{Previous efforts to exploit [the mutually beneficial
interactions of dataflow analyses] either (1)~iteratively performed
each individual analysis until no further improvements are discovered
or (2)~developed [handwritten] ``super-analyses'' that manually
combine conceptually separate analyses. We have devised a new approach
that allows analyses to be defined independently while still enabling
them to be combined automatically and profitably. Our approach avoids
the loss of precision associated with iterating individual analyses
and the implementation difficulties of manually writing a
super-analysis.}
\end{quote}
Adapting this work to a purely functional setting results in an
implementation that is significantly simpler than the original.
We~sketch that implementation below.


%%  Note that the dataflow engine is the only part of the system that is
%%  hard to get right---this is where all the hair is.
%%  Prime benefit of our system is that once this is right, everything is
%%  easy (and indeed is just logic, strongest postcondition, or weakest
%%  precondition). 
%%  



\subsection{The dataflow monad}

The primary purpose of the dataflow monad is to keep track of 
dataflow facts as the engine iterates.
Dataflow facts are found in three places:
\begin{itemize}
\item
There is a dataflow fact associated with every labelled basic block in
the current graph;
the dataflow monad maintains this association in a finite map.
The functions @getFact@ and @setFact@ query and update this map.
\item
The current graph may be a subgraph of a larger graph, in which case a
forward dataflow pass may produce dataflow facts that flow to labelled
blocks that are outside the current graph.
These facts must be retained and propagated even if the current graph
is abandoned; such facts are added with @addLastOutFact@ and recovered
with @bareLastOuts@.
\item
Finally, a foraward dataflow pass over a subgraph may propagate a fact forward by
``falling off the end;'' such a fact is set with @setExitFact@ and
recovered with @getExitFact@.
\end{itemize}
In addition to keeping track of facts, 
the dataflow monad provides a number of other facilities to manage
changes in state as graphs are rewritten and facts climb the dataflow
lattice:
\begin{itemize}
\item
The monad keeps track of whether any fact has changed.
\item
It provides a @subanalysis@ function which makes it possible to
 analyze a subgraph using the current set of facts, then discard any
 changes in state that may have resulted from the analysis of the
 subgraph.
\item
It provides a supply of fresh @BlockId@s, which are available for use
by rewrite functions.
\item
It tracks the supply of \emph{optimization fuel}.
As~shown below, when fuel runs out, the dataflow engine stops
calling rewriting functions, effectively halting optimization.
Binary search on the size of the fuel supply enables the compiler to
identify unsound rewrites quickly \cite{whalley:isolation}.
\end{itemize}


\subsection{The dataflow engine}

In implementing the dataflow engine, our primary tactic has been to
minimize the amount of duplicate or near-duplicate code.
To~that end, \emph{the dataflow engine implements only composed
analysis and transformation}.
Pure analysis is implemented as a special case in which no node is
ever rewritten.
As explained by \citet{lerner-grove-chambers:2002}, a~composed
analysis is implemented in two phases:
\begin{itemize}
\item
In the first phase, when a rewrite function proposes to replace a
node, the replacement graph is analyzed recursively, and the results
of that analysis are used as the new dataflow
fact(s) flowing out of the original node.
But \emph{the original node is not replaced}; indeed, the replacement
graph is abandoned, and only the facts remain.
If,~during iteration, the original node is analyzed again, perhaps
with a more conservative input fact, the rewrite function may propose
a different replacement or even no replacement at all.
This phase is called the \emph{solver}.
The solve computes a fixed point of the dataflow analysis
\emph{as if} nodes were replaced, while avoiding ever replacing a node
unsafely. 
\item
Once the solver is complete, the resulting fixed point is sound,
and the facts in the fixed point are used by the second phase in which
each replacement proposed by a rewriting function is actually
performed.
This phase is called the \emph{rewriter}.
\end{itemize}

In \citeyear{ramsey-dias:applicative-flow-graph}, two of us
\citeauthor{ramsey-dias:applicative-flow-graph} presented
implementations in Objective Caml of a backward solver and rewriter.
Here, then, as a complement, we sketch implementations of the forward
solver and rewriter used in~GHC. \simon{Oh!  thinks the reader... so this
paper is just a rehash in Haskell of your earlier work?  Can we 
say somethign like ``See Related work for the substantial differences
between this paper and our ealier work''?}


\begin{itemize}
\item
@fwd_pure_anal@ is @forward_sol@ passed the @squash@ function
@\ _ _ -> Nothing@.
It ignores its rewrite, depth, and fuel parameters.
\item
The internal @solve@ function is higher-order in the parameter
@finish@, which extracts from the dataflow monad either the unique
exit fact or the set of @LastOuts@, depending on context.
\item
The function @set_or_save@ calls @setFact@ for @BlockId@s located
within graph~@g@ and calls @addLastOutFact@ for @BlockId@s located
outside graph~@g@.
\end{itemize}




\section{Implementing graphs}
\seclabel{graphs}

Our optimizer represents each procedure using a control-flow graph.
Our representation of control-flow graphs is
\begin{itemize}
\item
Purely applicative, which makes it exceptionally easy to compose
analyses and transformations as described in \secref{engine}
\item
Polymorphic, which enables us not only to reuse the graph for
different low-level intermediate languages, but which forces us to
distinguish generally useful dataflow algorithms from particular
realizations in~GHC
\item
Based on Huet's \citeyearpar{huet:zipper} \emph{zipper},
which makes it easy to adapt existing code improvements written in
imperative style
\end{itemize}
\citet{dias-ramsey:applicative-flow-graph} present our design in
detail, as well as discussing alternatives, advantages, and
disadvantages.
In~this paper we give a high-level view of the data structure, and we
emphasize a significant refinement: the introduction of polymorphism.

\subsection{Basic data structure}

A~basic block is a sequence beginning with a first node, continuing
with zero or more middle nodes, and ending in a last node.
A~first node contains only a unique identifier of type @BlockId@; the
types of middle and last nodes are parameters.
At~any given moment, the dataflow engine may \emph{focus} on a
particular edge, in which case 
it holds the source of that edge plus a
list of its predecessors all the way back to the first node,
and 
it holds the sink of that edge plus a
list of its successors all the way forward to the last node.
These values have types @ZHead m@ and @ZTail m l@, where @m@~and~@l@
are the types of middle and last nodes:
\begin{code}
data ZHead m   = ZFirst BlockId 
               | ZHead (ZHead m) m
data ZTail m l = ZLast (ZLast l) | ZTail m (ZTail m l)
\end{code}
The type @ZLast l@ extends~@l@ with an additional case, @LastExit@,
which represents a subgraph that ends not in a control transfer but by
``falling off the end.''

We~``modify'' a block by creating a new head and tail; to ``insert'',
``remove'', or ``mutate'' a node typically requires a couple of
allocations 
\cite{dias-ramsey:applicative-flow-graph}. 
With these operations it is easy to recast imperative graph-rewriting
code into a pure functional form.


When we are not focusing on a particular edge, we represent a basic
block by pairing its first node (a~@BlockId@ with a @ZTail@):
\begin{code}
data Block m l = Block BlockId (ZTail m l)
\end{code}
An entire graph is represented as a finite map from @BlockId@ to
block, together with the ID of the entry node:\footnote
{The representation of @BlockId@ is chosen so that not only is
  it efficient to create an infinite supply of @BlockId@s, but finite
  maps with @BlockId@ keys can be implemented using a Patricia tree,
  which is even more efficient than a balanced binary tree
  \cite{okasaki-gill:integer-map}.} 
\begin{code}
data Graph m l =
   Graph { g_entry  :: BlockId
         , g_blocks :: BlockEnv (Block m l) }
\end{code}
%%  
%%  A~graph has a single \emph{entry} and at most one \emph{default exit}.
%%  A~graph has a default exit, which we abbreviate just ``exit'', only if
%%  control can ``fall off the end.'' 
%%  A~graph has no exit if control leaves the graph only by an explicit
%%  procedure return or tail call;
%%  for example, the graph for a whole procedure  has no exit.
%%  The most common use of a single-entry, single-exit flow graph is as a
%%  replacement for a middle node during code improvement.

%%  Each control-flow graph is represented as a finite map from @BlockId@ to
%%  basic block, together with a distinguished @BlockId@ that marks the entry
%%  node.
%%  When a graph is ``being modified'',
%%  we \emph{focus} on one internal edge of one basic block.
%%  The focus is represented by a pair.
%%  One element of the pair points to the
%%  source of the edge, which is linked to its predecessor, and so on up
%%  to the block's first node.
%%  The other element of the pair points to the
%%  sink of the edge, which is linked to its successor, and so on down
%%  to the block's last node.

\subsection{Realization in GHC}

The two significant differences between the applicative flow graph
used in Quick~{\PAL} \cite{dias-ramsey:applicative-flow-graph} and the
refined version described in this paper are that the new version is
polymorphic, and the new version stores properties in separate finite
maps, not in mutable property lists.
%We use finite maps in order to avoid mutable reference cells, which
%require that computations be done in the @IO@~monad.
The change from propertly lists to finite maps is inconsequential,
affecting the algorithms in only minor ways and the structure of the
code not at all.
The change from a monomorphic to a polymorphic control-flow graph, by
contrast, has far-reaching implications.

Types related to control flow graph are polymorphic in two parameters:
the type of middle nodes and the type of last nodes.\footnote
{We considered abstracting over types associated with first nodes as
  well, but we preferred to restrict first nodes so that a first node
  carries a @BlockId@ and only a @BlockId@.
  This design gives us fewer type parameters and
  therefore fewer higher-order functions associated with those
  parameters.
  Because we can always use a finite map to associate data with
  each first node, we don't lose any expressive power.}
This design militates toward a modular implementation,
in which the implementation of the dataflow engine is 
decoupled from the representation of computations at individual nodes.
\begin{itemize}
\item
Module @ZipCfg@ exports data structures and algorithms that are
independent of the type of middle and last nodes.
However, in order to enable other algorithms to change the flow graph
without knowing the representation of a last node, @ZipCfg@ exports a
@LastNode@ type class which all last nodes must support.
Ths @LastNode@ type class expresses the minimum required of a type
that claims to repesent a control transfer:
it must be possible to
create a last node that branches unconditionally to a given @BlockId@ (goto);
to test to see if a last node is an unconditional branch, and if so, to
what target;
and to observe what @BlockIds@ designate possible successors of a last
node.
Operations that observe successors are extended to basic
blocks of type @Block m l@ and to values of type @ZTail m l@.

@ZipCfg@ exports a number of basic algorithms on graphs, including the
splicing algorithms described by
\citet{ramsey-dias:applicative-flow-graph}. 
The most important algorithm is postorder depth-first-search
traversal, which orders the basic blocks in a way such that iterative
dataflow analyses converge quickly.
As~a benevolent side effect, this traversal also prunes unreachable
code from the graph.
\item
Module @ZipCfgCmmRep@ depends on @ZipCfg@, but not the converse.
It~exports definitions of the middle- and last-node types needed to
represent GHC's low-level intermediate code, @Cmm@.
For convenience, it also exports instantiations of graph types,
and it exports instance declarations that make it easy to walk @Cmm@
nodes and find out what registers and stack slots are defined and
used.
\item
The \emph{representation} exported by
@ZipCfg@ and @ZipCfgCmmRep@ is useful primarily
for \emph{analyzing} flow graphs.
To~\emph{construct} a flow graph we use ``smart constructors'' which
produce monadic functions from graphs to graphs.
Using these constructors, GHC's front end creates large graphs by
composing smaller ones.

The smart constructors are inspired by Hughes's \citeyearpar{hughes:novel-lists}
representation of lists.
They are similar to the functions of type 
@zgraph -> zgraph@ described by
\citet{ramsey-dias:applicative-flow-graph}, 
but in our Haskell implementation, the function type is monadic and is
hidden from clients.
A~monadic type is necessary in order to plumb through an infinite supply
of @BlockId@ values.
When a client \emph{wants} to use a @BlockId@, for example, to translate
structured control flow into conditional and unconditional branches,
the graph-construction interface provides a constructor of type
analogous to $(\mathtt{BlockId} \arrow \mathit{graph}) \arrow \mathit{graph}$.
%% , as described in the companion paper.
%% \cite{dias-peyton:refactoring}. 
\end{itemize}

