
\section{Introduction}
\label{chap:introduction}

We analyze the computational complexity of flow analysis for
higher-order languages, yielding a number of novel insights: $k$CFA is
provably intractable; 0CFA and its approximations are inherently
sequential; and analysis and evaluation of linear programs are
equivalent.

\subsection{Overview}
\label{sec-overview}
%% Danvy, ``On Scientific Writing'': The introduction, It should start
%% with a bang.
% Predicting the future is hard.

% Nevertheless, such is the business of an optimizing compiler: it reads
% in an input program, predicts what will happen when that program is
% run, and then---based on that prediction---outputs an optimized
% program.

Semantics-based program analysis \cite{cousot-cousot-popl77,
  cousot-cousot-jlc92, muchnick-jones-81, nielson-nielson-hankin} aims
to discover the run-time behavior of a program {\em without actually
  running it} [page xv] \cite{muchnick-jones-81}.  But as a natural
consequence of Rice's theorem \shortcite{rice}, a perfect prediction
is almost always impossible.  So {\em tractable} program analysis must
necessarily trade exact evaluation for a safe, computable
approximation to it.  This trade-off induces a fundamental dichotomy
at play in the design of program analyzers and optimizing compilers.
On the one hand, the more an analyzer can discover about what will
happen when the program is run, the more optimizations the compiler
can perform.  On the other, compilers are generally valued not only
for producing fast code, but doing so quickly and efficiently; some
optimizations may be forfeited because the requisite analysis is too
difficult to do in a timely or space-efficient manner.

% We like programming in very expressive languages, where powerful
% computations can be invoked.  We not only allow, but often expect that
% programs can run for a long time.  In contrast, compile-time static
% analysis is valued for being fast, and resource-efficient. Yet, in the
% face of these constraints, it must remain {\em expressive}, able to
% predict rich and useful properties of programs.  This disparity begs
% for a comprehensive, precise answer to the following question:
% \begin{quotation}\it\noindent
% What can we possibly learn about the
% runtime behavior of a computation-intensive program with a
% resource-bounded static analyzer?
% \end{quotation}

As an example in the extreme, if we place {\em no limit} on the
resources consumed by the compiler, it can perfectly predict the
future---the compiler can simply simulate the running of the program,
watching as it goes.  When (and if) the simulation completes, the
compiler can optimize with perfect information about what will happen
when the program is run.  With good reason, this seems a bit like
cheating.

So at a minimum, we typically expect a compiler will eventually finish
working and produce an optimized program.  (In other words, we expect
the compiler to compute within bounded resources of time and space).
%\footnote{This expectation is not typical for Scheme programmers.} 
After all, what good is an optimizing compiler that never finishes?

But by requiring an analyzer to compute within bounded resources, we
have necessarily and implicitly limited its ability to predict the
future.

As the analyzer works, it {\em must} use some form of approximation;
knowledge must be given up for the sake of computing within bounded
resources.  Further resource-efficiency requirements may entail
further curtailing of knowledge that a program analyzer can discover.
%
% For example, what kind of properties can we discover if the analyzer
% is allowed only a fixed number of pointers, or just a polynomial
% amount of time?
%
But the relationship between approximation and efficiency is far from
straightforward.  Perhaps surprisingly, as has been observed
empirically by researchers
\cite{wright-jagannathan-toplas98,jagannathan-etal-popl98,might-shivers-icfp06},
added precision may avoid needless computation induced by
approximation in the analysis, resulting in computational {\em
  savings}---that is, better information can often be produced faster
than poorer information.  So what exactly is the analytic relationship
between forfeited information and resource usage for any given design
decision?

In trading exact evaluation for a safe, computable approximation to
it, analysis negotiates a compromise between complexity and precision.
But what exactly are the trade-offs involved in this negotiation?  For
any given design decision, what is given up and what is gained?  What
makes an analysis rich and expressive?  What makes an analysis fast
and resource-efficient?

We examine these questions in the setting of {\em flow analysis}
\cite{jones81,sestoft-ms,sestoft-fpca89,shivers-pldi88,shivers-phd,midtgaard-07}, a
fundamental and ubiquitous static analysis for higher-order
programming languages.  It forms the basis of almost all other
analyses and is a much-studied component of compiler technology.

Flow analysis answers basic questions such as ``what functions can be
applied?,'' and ``to what arguments?''  These questions specify
well-defined, significant {\em decision problems}, quite apart from
any algorithm proposed to solve them.  
%
This paper examines the inherent computational difficulty of deciding
these problems.


% \dvhnote{Discuss the essence of complexity classes and hardness
% results. (Maybe put in prelims).}

% Control-flow analysis may be feasible and useful for higher-order languages

% Shivers' seminal dissertation, {\em Control-Flow Analysis of
% Higher-Order Languages} \citeyearpar{shivers-phd}, opens with the
% thesis:
% \begin{quote}
% Control-flow analysis is feasible and useful for higher-order languages. 
% \end{quote}
% The subsequent pages explain how flow analysis is computable
% (feasible) and enabling (useful).  But what is the {\em relationship}
% between these two properties?



% In answering the flow analysis question ``does this value flow into
% this point?,'' the most approximate analysis will always answer {\em
% yes}, which takes no resources to compute---and is of little use.  On
% the other hand, the most precise analysis will answer {\em yes} if and
% only if the given value flows into the program point during
% evaluation, which is useful, albeit uncomputable.  In mediating
% between these extremes, every static analysis necessarily gives up
% valuable information for the sake of computing an answer within
% bounded resources.  

% Designing a static analyzer, therefore, requires making trade-offs
% between {\em precision} and {\em complexity}.


% In other words:
% \begin{center}
% {\em What are the computationally potent ingredients in a
% static analysis?}
% \end{center}


If we consider the most useful analysis the one which yields complete
and perfectly accurate information about the running of a program, then
clearly this analysis is intractable---it consumes the same
computational resources as running the program.  At the other end of
the spectrum, if the least useful analysis yields no information about
the running of a program, then this analysis is surely feasible, but
useless.

% In the more than 25 years of research on the subject of flow analysis,
% there has remained a poverty of analytic knowledge concerned with the
% relationship between complexity and precision.  

If the design of software is really a science, we have to understand
the trade-offs between the running time of static analyzers, and the
accuracy of their computations. 

There is substantial empirical experience, which gives a partial
answer to these questions.  However, despite being the fundamental
analysis of higher-order programs, despite being the subject of
investigation for over twenty-five years, and the great deal of
expended effort deriving clever ways to tame the cost, there has
remained a poverty of analytic knowledge on the complexity of flow
analysis, the essence of how it is computed, and where the sources of
approximation occur that make the analysis work.  

This paper is intended to repair such lacunae in our understanding.

%% That knowledge can be used to build better analyzers.

%% They express a goal of analytically relating such techniques,
%% appealing to the relations emphasized by computational complexity,
%% where problems are related via the idea of reduction (which is no more
%% than a carefully resource-bounded compiler).


\subsection{Summary of Results}

\subsubsection{Linearity, Analysis and Normalization}
\begin{itemize}
\item Normalization and analysis are equivalent for linear programs.
\end{itemize}

Although variants of flow analysis abound, we identify a core
language, the linear $\lambda$-calculus, for which all of these
variations coincide.  In other words, for {\em linear}
programs---those written to use each bound variable exactly once---all
known flow analyses will produce equivalent information.  

It is straightforward to observe that in a {\em linear}
$\lambda$-term, each abstraction $\lambda x.e$ can be applied to at
most one argument, and hence the abstracted value can be bound to at
most one argument.
% \footnote{Note that this observation is clearly
%   untrue for the {\em nonlinear} $\lambda$-term $(\lambda f.f (a (f
%   b))) (\lambda x.x)$, as $x$ is bound to $b$, and also to $ab$.}
Generalizing this observation, analysis of a linear $\lambda$-term
coincides exactly with its evaluation.  So not only are the varying
analyses equivalent to each other on linear terms, they are all
equivalent to evaluation.

Linearity is an equalizer among variations of static analysis, and a
powerful tool in proving lower bounds.

\subsubsection{Monovariance and \ptime}
\begin{itemize}
\item 0CFA and other monovariant flow analyses are complete for \ptime.
\end{itemize}

By definition, a {\em monovariant} analysis (e.g.~0CFA), does not
distinguish between occurrences of the same variable bound in
different calling contexts.  But the distinction is needless for
linear programs and analysis becomes evaluation under another name.
This opens the door to proving lower bounds on the complexity of the
analysis by writing---to the degree possible---computationally
intensive, linear programs, which will be faithfully executed by the
analyzer rather than the interpreter.

We rely on a symmetric coding of Boolean logic in the linear
$\lambda$-calculus to simulate circuits and reduce the 0CFA decision
problem to the canonical \ptime\ problem, the circuit value problem.
This shows, since the inclusion is well-known, that 0CFA is complete
for \ptime.  Consequently, 0CFA is inherently sequential and there is
no fast parallel algorithm for 0CFA (unless \ptime\ $=$ \nc).
Moreover, this remains true for a number of {\em further
  approximations} to 0CFA.

The best known algorithms for computing 0CFA are often not practical
for large programs. Nonetheless, information can be given up in the
service of quickly computing a necessarily less precise analysis.  For
example, by forfeiting 0CFA's notion of directionality, algorithms for
Henglein's simple closure analysis run in near linear time
\cite{henglein92d}.  Similarly, by explicitly bounding the
number of passes the analyzer is allowed over the program, as in
Ashley and Dybvig's sub-0CFA \cite{ashley-dybvig-toplas98}, we
can recover running times that are linear in the size of the program.
But the question remains: Can we do better?  For example, is it
possible to compute these less precise analyses in logarithmic space?
We show that without profound combinatorial breakthroughs (\ptime\ $=$
\logspace), the answer is no.  Simple closure analysis, sub-0CFA, and
other analyses that approximate or restrict 0CFA, {\em require}---and
are therefore, like 0CFA, complete for---polynomial time.


\subsubsection{0CFA with \texorpdfstring{$\eta$}{Eta}-Expansion and \logspace}
\begin{itemize}
\item 0CFA of typed, $\eta$-expanded programs is complete for \logspace.
\end{itemize}

We identify a restricted class of functional programs whose 0CFA
decision problem may be simpler---namely, complete for
\logspace.  Consider programs that are simply typed, and where a
variable in the function position or the argument position of an
application is fully $\eta$-expanded.  This case---especially, but not
only when the programs are linear---strongly resembles multiplicative
linear logic with {\em atomic} axioms. 

We rely on the resemblance to bring recent results on the complexity
of normalization in linear logic to bear on the analysis of
$\eta$-expanded programs resulting in a \logspace-complete variant of
0CFA.

\subsubsection{\kcfa\ and \exptime}
\begin{itemize}
\item $k$CFA is complete for \exptime\ for all $k>0$.
\end{itemize}

We give an exact characterization of the computational complexity of
the $k$CFA hierarchy.  For any $k > 0$, we prove that the control flow
decision problem is complete for deterministic exponential time.  This
theorem validates empirical observations that such control flow
analysis is intractable.  It also provides more general insight into
the complexity of abstract interpretation.

A fairly straightforward calculation shows that $k$CFA can be computed
in exponential time.  We show that the naive algorithm is essentially
the best one.  There is, in the worst case---and plausibly, in
practice---no way to tame the cost of the analysis.  Exponential time
is required.

