 \documentclass[11pt]{article}
\usepackage{ifthen}
\usepackage{alltt}
% \usepackage{indentfirst}
\usepackage{latexsym}
\usepackage[pdftex]{graphicx}
\usepackage{gdefs}
% \usepackage{logdef}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{setspace}
\usepackage{rotating}
\usepackage{longtable}
\usepackage{hhline}
\usepackage{refs}
\usepackage{graph}
\usepackage{changebar}
\usepackage{array}
\usepackage{wrapfig}
\usepackage[small,compact]{titlesec}
\usepackage{syntax}
\usepackage{color}
\usepackage{tweaklist}
% this sets the spacings in the itemize environment
\renewcommand{\itemhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the enumerate environment
\renewcommand{\enumhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the description environment
\renewcommand{\descripthook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0.0in}
\setlength{\headsep}{0.0in}
\setlength{\textwidth}{6.5in}
\setlength{\oddsidemargin}{0.0in}
\setlength{\evensidemargin}{0.0in}
\setlength{\textheight}{9.0in}

\setcounter{topnumber}{5}
\def\topfraction{0.99}
\def\textfraction{0.05}
\def\floatpagefraction{0.9}
\setcounter{bottomnumber}{5}
\def\bottomfraction{0.99}
\setcounter{totalnumber}{10}
\def\dbltopfraction{0.99}
\def\dblfloatpagefraction{0.8}
\setcounter{dbltopnumber}{5}

\setlength{\marginparwidth}{.95in}
\let\oldmarginpar\marginpar
\renewcommand\marginpar[1]{\-\oldmarginpar[\raggedleft\footnotesize #1]%
{\raggedright\footnotesize #1}}

\let\oldthebibliography=\thebibliography
  \let\endoldthebibliography=\endthebibliography
  \renewenvironment{thebibliography}[1]{%
    \begin{oldthebibliography}{#1}%
      \setlength{\parskip}{0ex}%
      \setlength{\itemsep}{0ex}%
  }%
  {%
    \end{oldthebibliography}%
  }

\newcommand{\main}{\textit{main}}
\newcommand{\Kill}{\textit{Kill}}
\newcommand{\Gen}{\textit{Gen}}
\newcommand{\Id}{\textit{Id}}
\newcommand{\CallSites}{\textit{CallSites}}
\newcommand{\CallIn}{\textit{CallIn}}
\newcommand{\CallOut}{\textit{CallOut}}
\newcommand{\Ret}{\textit{Ret}}
\newcommand{\unbalLeft}{\textit{unbalLeft}}
\newcommand{\matched}{\textit{matched}}
\newcommand{\IVP}{\textit{IVP}}

\newcommand{\pf}{\textit{pf}}
\newcommand{\spf}{\textit{spf}}
\newcommand{\apf}{\textit{apf}}
\newcommand{\SM}{\textit{SM}}
\newcommand{\SSSD}{\textit{SSSD}}
\newcommand{\ESSSD}{\textit{ESSSD}}
\newcommand{\EQ}{\textit{EQ}}
\newcommand{\CS}{\textit{CS}}
\newcommand{\SCS}{\textit{SCS}}
\newcommand{\ECS}{\textit{ECS}}
\newcommand{\EAS}{\textit{EAS}}
\newcommand{\EFAS}{\textit{EFAS}}
\newcommand{\PT}{\textit{PT}}
\newcommand{\APS}{\textit{APS}}
\DeclareMathOperator{\ulambda}{\underline{\lambda}}
\DeclareMathOperator{\op}{\textrm{op}}
\DeclareMathOperator{\extend}{\otimes}
\DeclareMathOperator{\combine}{\oplus}
\DeclareMathOperator{\scomposition}{\tilde{\circ}}
\DeclareMathOperator*{\bigcombine}{\bigoplus}
\DeclareMathOperator*{\bigextend}{\bigotimes}
\DeclareMathOperator*{\Min}{\textrm{\large{Min}}}
\DeclareMathOperator*{\bigcircle}{\bigcirc}
\newcommand{\bbN}{\mathbb{N}}
\newcommand{\bbZ}{\mathbb{Z}}
\newcommand{\len}{\textrm{len}}
\newcommand{\weight}{\textrm{weight}}
\newcommand{\pathWeight}{\textrm{pathWeight}}
\newcommand{\Nodes}{\textrm{Nodes}}
\newcommand{\Edges}{\textrm{Edges}}
\newcommand{\Paths}{\textrm{Paths}}
\newcommand{\Store}{\textrm{Store}}
\newcommand{\AStore}{\textrm{AStore}}
\newcommand{\Var}{\textrm{Var}}
\newcommand{\Val}{\textrm{Val}}
\newcommand{\DVal}{\textrm{DVal}}
\newcommand*{\stacked}[2]{\genfrac{}{}{0pt}{}{#1}{#2}}

\title{Interprocedural Dataflow Analysis, Part II \\
       CS701 \\
}

\author{Thomas Reps \\ \small[Based on notes taken by Cheng Su on October 20, 2015]}
\date{}

\begin{document}           % End of preamble and beginning of text.

\maketitle
\begin{abstract}
This lecture continues the presentation of interprocedural dataflow analysis,
describing the ``functional approach'' defined by Sharir and Pnueli
\cite{PFA:SP81}.
We define the interface for their interprocedural dataflow-analysis framework,
and show how the interface can be instantiated for so-called ``gen/kill problems.''
We also begin to describe a second instantiation of the framework for
so-called ``interprocedural, finite, distributive, subset (IFDS) problems''
\cite{POPL:RHS95}, which can be analyzed via context-free-language reachability
(CFL-reachability).
In this lecture, we show how to use relations to represent distributive
functions over a finite subset, which is the first step in reducing
IFDS problems to CFL-reachability.
\end{abstract}

\section{Review}

Last lecture, we explained the path-problem framework as follows:
\begin{equation}
  \label{Eq:EAS}
  \EAS[s,n] =
    \begin{cases}
      \top       & \quad \text{if } n = s\\
      \bigcombine\limits_{\B{m,n} \in \Edges} M^{\#}(\langle m,n\rangle)(\EAS[s,m]) & \quad \text{otherwise}\\
    \end{cases}
\end{equation}
We pointed out that \eqref{EAS} is not in exactly the right form for a
path problem because it uses function application (i.e.,
$M^{\#}(\langle m,n\rangle)(\cdot)$) rather than the extend operation
(i.e., $(\cdot) \bigextend M^{\#}(\langle m,n\rangle)$).
We fixed this anomaly, and generalized \eqref{EAS} to a functional
view of dataflow analysis by recasting \eqref{EAS} using the following
formulation:

\begin{equation}
  \label{Eq:EFAS}
  \EFAS[s,n] =
    \begin{cases}
      \Id       & \quad \text{if } n = s\\
      \bigcombine\limits_{\B{m,n} \in \Edges} \EFAS[s,m] \bigextend M^{\#}(\B{m,n}) & \quad \text{otherwise}\\
    \end{cases}
\end{equation}
We pointed out that \eqref{EFAS} is similar to the
intraprocedural-propagation equation in Sharir and Pnueli's
``functional approach'' to intraprocedural dataflow analysis
\cite{PFA:SP81}.
To support cross-procedure propagation of information, one more case
needs to be considered, for procedure call.
The equations in \cite{PFA:SP81} are defined as follows:

\begin{eqnarray}
  \phi_{e,e} & = & \Id \label{Eq:SPOne} \\
  \phi_{e,n} & = & \bigcombine\limits_{\B{m,n} \in \Edges} f_{m,n} \circ \phi_{e,m} \quad \text{if } n  \notin \text{Ret} \label{Eq:SPTwo} \\
  \phi_{e,r} & = & \phi_{e',x'} \circ \phi_{e,c} \quad ((c,r) \in \text{CallRetPair}) \label{Eq:SPThree}
\end{eqnarray}
In \eqref{SPOne}, $e$ denotes an entry node of the interprocedural
control flow graph.
$\phi_{e,e}$ denotes the dataflow information propagated
from the entry node to itself.
In \eqref{SPTwo}, we calculate the information for all the nodes
except for return nodes.
In \eqref{SPThree}, $c$ is the node to call procedure $P$.
$P$ has entry node $e'$, and return node $x'$.
$r$ is the node after $c$.
\eqref{SPThree} handles cross-propagation of information.

\section{The Sharir and Pnueli Algorithm: Phase I}

Sharir and Pnueli introduce the concept of \emph{interprocedural valid paths}
(IVP) for describing interprocedural information propagation.
IVP can be viewed as the paths to satisfy a certain condition specified
by a context-free language.
Phase I calculates dataflow information based on the paths that satisfy
the following context-free grammar:
\begin{eqnarray}
    \matched   & ::= & \epsilon \nonumber \\
               &  |  & \matched ~~ e \nonumber \\
               &  |  & \matched ~~ (_i ~~ \matched ~~ )_i \qquad\qquad\text{for $i \in \CallSites$} \label{Eq:MatchedInterprocedural}
\end{eqnarray}
For every call site $i$ in every procedure $p$, we tag the call edge
with ``$(_i$'', and the return edge with ``$)_i$''.
The grammar above describes the paths whose calling stack
returns to exactly the same level as it started, and never
becomes shorter than the stack that we had when the path commenced.

\begin{figure*}
\begin{center}
\begin{tabular}{c}
  \includegraphics[width=.3\textwidth]{figures/SP81-interprocedural.eps}
\end{tabular}
\end{center}
  \vspace*{-2.0ex}
\caption{Illustration of the interprocedural equation from Phase I.}
\label{Fi:SPInterprocedural}
\end{figure*}

To propagate dataflow information across procedures, Sharir and Pnueli
gave \eqref{SPThree};
however, it is a bit more precise---and better illustrates what is going
on---to state it as follows:
\begin{equation}
  \label{Eq:SPThreeAlternative}
  \phi_{e,r} = \CallOut_{x',r} \circ \phi_{e',x'} \circ \CallIn_{c,e'} \circ \phi_{e,c}.
\end{equation}
(Sharir and Pnueli make the assumption that the dataflow transformers for
$\CallIn$ and $\CallOut$ edges are all $\Id$.)
It is also instructive to re-express \eqref{SPThreeAlternative}
with the extend operation:
\begin{equation}
  \label{Eq:SPThreeAlternativeExtend}
  \phi_{e,r} = \phi_{e,c} \extend \CallIn_{c,e'} \extend \phi_{e',x'} \extend \CallOut_{x',r}
\end{equation} 
As shown in \figref{SPInterprocedural}, to obtain the information $\phi_{e,r}$, we
propagate information from entry node $e$ to call site $c$ in the caller, then
from $c$ to entry node $e'$ of the callee, then from $e'$ to
return node $x'$ of the callee, and finally from $x'$ to $r$.
Note the similarity of \eqref{SPThreeAlternativeExtend}
to the form of grammar rule (\ref{Eq:MatchedInterprocedural}):
\[
  \underbrace{\phi_{e,r}}_{\matched}
  =
  \underbrace{\phi_{e,c}}_{\matched}
  \extend
  ~\underbrace{\CallIn_{c,e'}}_{(_i}~
  \extend
  \underbrace{\phi_{e',x'}}_{\matched}
  \extend
  ~\underbrace{\CallOut_{x',r}}_{)_i}.
\]

\begin{figure*}
\begin{center}
\begin{tabular}{c}
  \includegraphics[width=.65\textwidth]{figures/inter-to-intra.eps}
\end{tabular}
\end{center}
  \vspace*{-2.0ex}
\caption{Propagation during Phase I when there are multiple calls to the same procedure.}
\label{Fi:InterToIntra}
\end{figure*}

The situation where there are multiple calls to the same procedure
is shown in {Figure 2}.
(Phase I handles it automatically.)

\paragraph{Discussion.}
\figref{InterToIntra} can also be viewed as showing how
\emph{inter}procedural analysis is ``reduced'' to an
\emph{intra}procedural-analysis problem of analyzing separate
procedures, once \emph{summary functions} are introduced at
call sites---such as call sites $\B{c_1,r_1}$ and $\B{c_2,r_2}$
in procedure $P$, as shown in \figref{InterToIntra}.
However, when a program has recursive procedures,
the problem of finding the appropriate summary functions
itself requires solving a dataflow-analysis problem.
In a sense, the Sharir/Pnueli algorithm interleaves these
two dataflow-analysis problems in a single analysis problem.

Recall the declarative specification of path problems, and
what we have said previously about how the value defined by the
declarative specification relates to the value obtained by
solving the equational formulation---namely, they
coincide as long as $\extend$ distributes over $\combine$.
In this case, as long as $\extend$ distributes over $\combine$, the
$\phi$ functions obtained by solving \eqrefsp{SPOne}{SPTwo}{SPThree}
correspond to the $\psi$ functions specified by the following
declaratively specified path problem, which is defined in terms of
paths that respect the language $L(\matched)$.
\[
  \psi_{e,n} = \bigcombine\limits_{p \in \matched(e,n)}  \apf_p,
\]
where for $p = e_1,e_2,\cdots,e_k$,
\[
  \apf_p = M^{\#}(e_1) \extend M^{\#}(e_2) \extend \cdots \extend M^{\#}(e_k).
\]

\section{The Sharir and Pnueli Algorithm: Phase II}

Sharir and Pnueli describe their algorithm as a two-phase algorithm;
however, I prefer to break up their second phase into two separate
phases.
Thus, part of Sharir and Pnueli's Phase II will be described as
``Phase III'' (see \sectref{SPPhaseIII}).

In both Phase II and Phase III, we need to handle interprocedural valid
paths that end with the stack containing a sequence of called procedures
that have not yet returned.
Also, we are mainly interested in paths that begin at $e_\main$, the entry
node of the main procedure.
The context-free grammar that we need to describe such paths is as follows:
\begin{eqnarray*}
    \unbalLeft & ::= & \epsilon \\
               &  |  & \unbalLeft ~~ \matched ~~ (_i \qquad\qquad\text{for $i \in \CallSites$} \\
               &  |  & \unbalLeft ~~ \matched
\end{eqnarray*}

\begin{figure*}
\begin{center}
\begin{tabular}{c}
  \includegraphics[width=.75\textwidth]{figures/SP81-phase2-interproc.eps}
\end{tabular}
\end{center}
  \vspace*{-2.0ex}
\caption{Propagation during Phase II when there are multiple calls to the same procedure.}
\label{Fi:SPPhaseTwoInterproc}
\end{figure*}

Suppose that we want to calculate the dataflow information
that should hold along all paths from entry node $e_{\main}$ to $n$,
where $n$ is a node in some procedure $p$
(and $p$ is not necessarily procedure $\main$).
Suppose that $e$ is the entry node of $p$.
In Phase III, we will use the fact that if we have the dataflow
information $\Val^{\#}[e]$ for $e$, then we can obtain
$\Val^{\#}[n]$ merely by applying $\phi_{e,n}$:
\[
  \Val^{\#}[n] = \phi_{e,n}(\Val^{\#}[e]).
\]
This observation leaves us with the question, ``How can we obtain
$\Val^{\#}[e]$ (for each of the entry nodes in the program?''
This problem can itself be formulated as a system of dataflow
equations.
The propagation of dataflow information from entry node $e_{\main}$
to entry node $e$ can be expressed by the following system of
equations:

\begin{eqnarray}
  \Val^{\#}[e_{\main}] & = & \top \nonumber \\
  \Val^{\#}[e] & = & \bigcombine\limits_{\B{c_i,e} \in \CallIn} \phi_{e_j,c_i}(\Val^{\#}[e_j])
  \label{Eq:PhaseTwoInterprocedural}
\end{eqnarray} 
where $e_j$ is the entry node of the procedure that contains $c_i$, and
$\CallIn$ is the set of all edges from a call node to an entry node.
Note that the functions of the form $\phi_{e_j,c_i}$ were all calculated
during Phase I, so all the necessary information is available at the
start of Phase.
A graphical depiction of these equations is shown in \figref{SPPhaseTwoInterproc}.

In the discussion of Phase I, we noted a similarity between
\eqref{SPThreeAlternativeExtend} and the form of grammar rule
(\ref{Eq:MatchedInterprocedural}).
For Phase II, there is an analogous similarity between
\eqref{PhaseTwoInterprocedural} and the second grammar rule
that defines $L(\unbalLeft)$:
\[
  \unbalLeft~~::=~~\unbalLeft ~~ \matched ~~ (_i.
\]
The similarity is concealed because
(i)~Sharir and Pnueli make the assumption that the dataflow
transformers for $\CallIn$ edges are all $\Id$, and
(ii)~the values propagated during Phase II are abstractions of
\emph{sets of concrete states}---not abstractions of \emph{transition
relations}---hence, the equations use function \emph{application}
rather than $\circ$ or $\extend$.
Thus, it is a bit more precise---and better illustrates what is going
on---to state \eqref{PhaseTwoInterprocedural} as follows:
\begin{equation}
  \label{Eq:PhaseTwoInterproceduralPlusGrammarRule}
  \underbrace{\Val^{\#}[e]}_{\unbalLeft}
  =
  \bigcombine\limits_{\B{c_i,e} \in \CallIn}
  \underbrace{\CallIn_{c_i,e}}_{(_i}
  (
  \underbrace{\phi_{e_j,c_i}}_{\matched}
  (
  \underbrace{\Val^{\#}[e_j]}_{\unbalLeft}
  )),
\end{equation}
where $e_j$ is the entry node of the procedure that contains $c_i$.
(The symbols on the right-hand side of the grammar rule appear in
reverse order on the right-hand side of
\eqref{PhaseTwoInterproceduralPlusGrammarRule}
because functions that are called \emph{later} as one traverses a path
appear \emph{earlier} in a chain of function applications.)

\section{The Sharir and Pnueli Algorithm: Phase III}
\label{Se:SPPhaseIII}

\begin{figure*}
\begin{center}
\begin{tabular}{c}
  \includegraphics[width=.33\textwidth]{figures/SP81-phase2-intraproc.eps}
\end{tabular}
\end{center}
  \vspace*{-2.0ex}
\caption{Intraprocedural propagation during Phase III.}
\label{Fi:SPPhaseTwoIntraproc}
\end{figure*}

In Phase II, we calculated the information from $e_\main$ to $e$
for each entry node $e$ of the program.
Now we show how to calculate the information from $e$ to $n$.
$n$ is a node in the (intraprocedural) CFG for which $e$ is the entry node.
We merely use the result from Phase I, as shown in following equation:
\begin{equation}
  \Val^{\#}[n] = \phi_{e,n}(\Val^{\#}[e]))
\end{equation} 
This equation is depicted graphically in \figref{SPPhaseTwoIntraproc}.

The combination of Phases I, II, and III provides an algorithm for
solving an interprocedural dataflow-analysis problem.

\paragraph{Discussion.}
It can be shown that as long as $\extend$ distributes over $\combine$,
the values $\{ \Val^{\#}[n] \mid n \in \Nodes \}$ obtained by solving
the equations of Phases I, II, and III correspond exactly to the
values $\{ \Val^{\#}[n] \mid n \in \Nodes \}$ defined via
the following declaratively specified path problem, which is defined
in terms of paths that respect the language $L(\unbalLeft)$:
\[
  \DVal^{\#}[n] = \bigcombine\limits_{p \in \unbalLeft(e,n)} \apf_p(\top),
\]
where for $p = e_1,e_2,\cdots,e_k$,
\[
  \apf_p = M^{\#}(e_1) \extend M^{\#}(e_2) \extend \cdots \extend M^{\#}(e_k).
\]

\section{The Interface(s) for the Sharir/Pnueli Dataflow-Analysis Framework}

There are two different interfaces that one needs to supply
to be able to use the Sharir/Pnueli framework:
\begin{description}
  \item Phase I:
    $\Id, \bigextend, \bigcombine, M^{\#}, =, \sqsubseteq$.
  \item Phases II and III:
    $\top, \combine_{\AStore}, \textrm{function application}, =, \subseteq$.
\end{description}

One family of dataflow-analysis problems for which
we can satisfy our obligations is the family
of gen/kill problems.
Recall that each gen/kill abstract transformer has the form
$\lambda S.(S-\Kill_p) \cup \Gen_p$,
where $\Kill_p$ and $\Gen_p$ are set-valued constants
associated with some program point $p$.
Each such function can be represented using two sets:
$(\Kill_p, \Gen_p)$.
For the Phase I interface, we have
\[
  \begin{array}{rl}
    \Id: & (\phi, \phi) \\
    \combine: & (k_1 \cap k_2, g_1 \cup g_2) \\
    \extend: & (k_1 \cup k_2, (g_1-k_2) \cup g_2) \\
  \end{array}
\]
For the interface for Phases II and III, we have   
\[
  \begin{array}{rl}
    \top: & \mathbb{U} \\
    \combine_{\AStore}: & \cup \\
    \textrm{function application}: & (\lambda S.(S-\Kill_p) \cup \Gen_p)(T) = (T-\Kill_p) \cup \Gen_p)
  \end{array}
\]
where $\mathbb{U}$ is the universe of elements that can go into $\Kill$ and $\Gen$
sets.

\section{Relations to Represent Distributive Functions}

We now begin to describe a second instantiation of the Sharir and
Pnueli framework for so-called ``interprocedural, finite,
distributive, subset (IFDS) problems'' \cite{POPL:RHS95}.

Suppose that we have a function $f: 2^{D} \to 2^{D}$ that distributes
over union---i.e., $f(S_1 \cup S_2) = f(S_1) \cup f(S_2)$.
We can construct a relation
$R \subseteq (D \cup \{\Lambda\}) \times (D \cup \{\Lambda\})$
to represent $f$ as follows:
\begin{equation}
  \label{Eq:RepresentationRelation}
  R_f = \{(\Lambda, \Lambda)\} \cup \{(\Lambda, y) \mid y \in f(\varnothing)\} \cup \{(x, y) \mid y \in f(\{x\}) \land y \notin f(\varnothing)\}
\end{equation}
In essence, $R_f$ tracks $f$'s behavior ``pointwise'' (i.e., on the
singleton sets), with the special symbol $\Lambda$ representing
$f$'s behavior on the empty set.

\begin{Remark}
The construction is sensible because distributivity means that $f$'s
behavior on some set $S$ depends only on its behavior on subsets of $S$.
We can keep breaking the subsets into smaller subsets until we are left
with the singleton sets and the empty set.
\end{Remark}

\begin{figure*}
\begin{center}
\begin{tabular}{c}
  \includegraphics[width=.24\textwidth]{figures/IFDS-gen-kill.eps}
\end{tabular}
\end{center}
  \vspace*{-2.0ex}
\caption{Representation relation for a gen-kill function.}
\label{Fi:IFDSGenKill}
\end{figure*}

All gen/kill functions distribute over $\cup$, so let us consider a
gen/kill problem as an example.
Suppose that the function is
\begin{equation}
  \label{Eq:GenKillExampleOne}
  f \eqdef \lambda S.(S-\{d_4\}) \cup \{d_1,d_2\}.
\end{equation}
The relation $R_f$ that represents $f$ is shown in \figref{IFDSGenKill}.
The graph of $R_f$ illustrates a characteristic of all gen/kill functions:
\begin{itemize}
  \item
    The pre-state $\Lambda$ is connected to the post-state $\Lambda$.
  \item
    There are edges from the pre-state $\Lambda$
    to zero or more nodes $\{ d_i \mid d_i \neq \Lambda \}$.
  \item
    For all $d_i \neq \Lambda$, either there is a gap from
    the pre-state $d_i$ node to the post-state $d_i$ node,
    or there is an edge from the pre-state $d_i$ node to
    the post-state $d_i$ node.
  \item
    There are no other kinds of edges.
\end{itemize}
In other words, if the representation relation $R_f$ for a function $f$
has been constructed according to \eqref{RepresentationRelation},
you can immediate tell whether $f$ is a gen/kill function by
looking at the graph of $R_f$.
In particular, if there is an edge from a
pre-state node $d_i \neq \Lambda$ to a post-state node $d_j \neq \Lambda$
such that $i \neq j$, then $f$ is \emph{not} a gen/kill function.

One should note that the mapping from (syntactic) definitions of
functions to representation relations is many-one.
For instance, the representation relation shown in \figref{IFDSGenKill}
could also be interpreted as the representation relation $R_g$
for the function
\[
  g \eqdef \lambda S.(S-\{d_1,d_2,d_4\}) \cup \{d_1,d_2\}.
\]
That is, the representation relation shown in \figref{IFDSGenKill}
is both $R_g$ and $R_f$ for the function $f$ defined in
\eqref{GenKillExampleOne}.
However, even though $f$ and $g$ are defined in terms of
different kill sets, $f$ and $g$ both compute the same
\emph{mathematical function}.
Two functions $F_1$ and $F_2$ are said to be
\emph{extensionally equal} iff, for all $x$, $F_1(x) = F_2(x)$.
Thus, although the definitions of $f$ and $g$ are syntactically different,
$f$ and $g$ are extensionally equal.
In general, a given representation relation is a canonical form for
the set of distributive functions over a finite set that are
extensionally equal.

{\small
  \bibliography{cs701}
  \bibliographystyle{plain}
}

\end{document}
