mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-24 04:09:47 +00:00
d23ee4dbce
Summary: This is the first patch in a series of patches that will implement data dependence graph in LLVM. Many of the ideas used in this implementation are based on the following paper: D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS. This patch contains support for a basic DDGs containing only atomic nodes (one node for each instruction). The edges are two fold: def-use edges and memory-dependence edges. The implementation takes a list of basic-blocks and only considers dependencies among instructions in those basic blocks. Any dependencies coming into or going out of instructions that do not belong to those basic blocks are ignored. The algorithm for building the graph involves the following steps in order: 1. For each instruction in the range of basic blocks to consider, create an atomic node in the resulting graph. 2. For each node in the graph establish def-use edges to/from other nodes in the graph. 3. For each pair of nodes containing memory instruction(s) create memory edges between them. This part of the algorithm goes through the instructions in lexicographical order and creates edges in reverse order if the sink of the dependence occurs before the source of it. Authored By: bmahjour Reviewer: Meinersbur, fhahn, myhsu, xtian, dmgreen, kbarton, jdoerfert Reviewed By: Meinersbur, fhahn, myhsu Subscribers: ychen, arphaman, simoll, a.elovikov, mgorny, hiraditya, jfb, wuzish, llvm-commits, jsji, Whitney, etiotto Tag: #llvm Differential Revision: https://reviews.llvm.org/D65350 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@372238 91177308-0d34-0410-b5e6-96231b3b80d8
141 lines
6.5 KiB
ReStructuredText
141 lines
6.5 KiB
ReStructuredText
=========================
|
|
Dependence Graphs in LLVM
|
|
=========================
|
|
|
|
.. contents::
|
|
:local:
|
|
|
|
Introduction
|
|
============
|
|
Dependence graphs are useful tools in compilers for analyzing relationships
|
|
between various program elements to help guide optimizations. The ideas
|
|
behind these graphs are described in papers [1]_ and [2]_.
|
|
|
|
The implementation of these ideas in LLVM may be slightly different than
|
|
what is mentioned in the papers. These differences are documented in
|
|
the `implementation details <implementation-details_>`_.
|
|
|
|
.. _DataDependenceGraph:
|
|
|
|
Data Dependence Graph
|
|
=====================
|
|
In its simplest form the Data Dependence Graph (or DDG) represents data
|
|
dependencies between individual instructions. Each node in such a graph
|
|
represents a single instruction and is referred to as an "atomic" node.
|
|
It is also possible to combine some atomic nodes that have a simple
|
|
def-use dependency between them into larger nodes that contain multiple-
|
|
instructions.
|
|
|
|
As described in [1]_ the DDG uses graph abstraction to group nodes
|
|
that are part of a strongly connected component of the graph
|
|
into special nodes called pi-blocks. pi-blocks represent cycles of data
|
|
dependency that prevent reordering transformations. Since any strongly
|
|
connected component of the graph is a maximal subgraph of all the nodes
|
|
that form a cycle, pi-blocks are at most one level deep. In other words,
|
|
no pi-blocks are nested inside another pi-block, resulting in a
|
|
hierarchical representation that is at most one level deep.
|
|
|
|
|
|
For example, consider the following:
|
|
|
|
.. code-block:: c++
|
|
|
|
for (int i = 1; i < n; i++) {
|
|
b[i] = c[i] + b[i-1];
|
|
}
|
|
|
|
This code contains a statement that has a loop carried dependence on
|
|
itself creating a cycle in the DDG. The figure bellow illustrates
|
|
how the cycle of dependency is carried through multiple def-use relations
|
|
and a memory access dependency.
|
|
|
|
.. image:: cycle.png
|
|
|
|
The DDG corresponding to this example would have a pi-block that contains
|
|
all the nodes participating in the cycle, as shown bellow:
|
|
|
|
.. image:: cycle_pi.png
|
|
|
|
Program Dependence Graph
|
|
========================
|
|
|
|
The Program Dependence Graph (or PDG) has a similar structure as the
|
|
DDG, but it is capable of representing both data dependencies and
|
|
control-flow dependencies between program elements such as
|
|
instructions, groups of instructions, basic blocks or groups of
|
|
basic blocks.
|
|
|
|
High-Level Design
|
|
=================
|
|
|
|
The DDG and the PDG are both directed graphs and they extend the
|
|
``DirectedGraph`` class. Each implementation extends its corresponding
|
|
node and edge types resulting in the inheritance relationship depicted
|
|
in the UML diagram bellow:
|
|
|
|
.. image:: uml_nodes_and_edges.png
|
|
|
|
Graph Construction
|
|
------------------
|
|
|
|
The graph build algorithm considers dependencies between elements of
|
|
a given set of instructions or basic blocks. Any dependencies coming
|
|
into or going out of instructions that do not belong to that range
|
|
are ignored. The steps in the build algorithm for the DDG are very
|
|
similar to the steps in the build algorithm for the PDG. As such,
|
|
one of the design goals is to reuse the build algorithm code to
|
|
allow creation of both DDG and PDG representations while allowing
|
|
the two implementations to define their own distinct and independent
|
|
node and edge types. This is achieved by using the well-known builder
|
|
design pattern to isolate the construction of the dependence graph
|
|
from its concrete representation.
|
|
|
|
The following UML diagram depicts the overall structure of the design
|
|
pattern as it applies to the dependence graph implementation.
|
|
|
|
.. image:: uml_builder_pattern.png
|
|
|
|
Notice that the common code for building the two types of graphs are
|
|
provided in the ``DependenceGraphBuilder`` class, while the ``DDGBuilder``
|
|
and ``PDGBuilder`` control some aspects of how the graph is constructed
|
|
by the way of overriding virtual methods defined in ``DependenceGraphBuilder``.
|
|
|
|
Note also that the steps and the names used in this diagram are for
|
|
illustrative purposes and may be different from those in the actual
|
|
implementation.
|
|
|
|
Design Trade-offs
|
|
-----------------
|
|
|
|
Advantages:
|
|
^^^^^^^^^^^
|
|
- Builder allows graph construction code to be reused for DDG and PDG.
|
|
- Builder allows us to create DDG and PDG as separate graphs.
|
|
- DDG nodes and edges are completely disjoint from PDG nodes and edges allowing them to change easily and independently.
|
|
|
|
Disadvantages:
|
|
^^^^^^^^^^^^^^
|
|
- Builder may be perceived as over-engineering at first.
|
|
- There are some similarities between DDG nodes and edges compared to PDG nodes and edges, but there is little reuse of the class definitions.
|
|
|
|
- This is tolerable given that the node and edge types are fairly simple and there is little code reuse opportunity anyway.
|
|
|
|
|
|
.. _implementation-details:
|
|
|
|
Implementation Details
|
|
======================
|
|
|
|
The current implementation of DDG differs slightly from the dependence
|
|
graph described in [1]_ in the following ways:
|
|
|
|
1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the ``store`` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the ``store`` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through ``LoopAnalysis``.
|
|
2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
|
|
3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this impelmentation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
|
|
|
|
|
|
References
|
|
----------
|
|
.. [1] "D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS."
|
|
.. [2] "J. FERRANTE (IBM), K. J. OTTENSTEIN (Michigan Technological University) and JOE D. WARREN (Rice University), 1987. The Program Dependence Graph and Its Use in Optimization."
|