• No results found

2.6 Concluding Remarks

3.1.2 Intermediate Representation

The IR is the final result of a compiler’s frontend and reflects the compiler’s view of the program code. It separates the frontend from the backend and allows thereby arbitrary combinations of front- and backends in case of a common IR. The field of appliance for an IR is by no means restricted to a compiler. In fact, graph-based IR adopts an important role for ISE as well, since application analysis is typically performed on an IR. Hence, this section is not only relevant for compilation, but for ISE as well and therefore, relevant components and algorithms pertinent for both compilation and ISE are explained in this section.

Several types of different IRs have evolved in the past, such that there is not the “only one”. The most prominent and important IR-format is based upon tree and/or graph structures (Figure 3.2). 3.1. Definition (Directed Graph). A directed graph is a graph G = (V, E) consisting of

finite sets of vertices V and ordered pairs of vertices E ⊆ V × V called edges, such that

∀(vi, vj), (ui, uj) ∈ E : (vi, vj) = (ui, uj) ⇔ vi = ui∧ vj = uj.

Directed Acyclic Graphs (DAG) and trees are very similar data structures. Both are directed

graphs in the first place, containing no directed cycles. However, trees additionally satisfy the condition that every vertex v ∈ V has only one successor: ∀(vi, vj) ∈ E : ¬∃(vi, vk) ∈ E ∧ vk 6= vj.

Typically, an IR represents program code in terms of expressions and statements. The statements denote trees of expressions (Figure 3.2 (b)) and are in turn organized in basic blocks. Basic blocks are identified through IR nodes modifying the control flow of the program like goto.

3.2. Definition (Basic Block). A basic block B = hs1, . . . , sni is a maximal sequence of IR statements, for which the following conditions are true: B can only be entered at statement s1 and left at sn. Statement s1 is called leader of the basic block. It can either be a function entry point, a jump destination, or a statement that follows immediately after a jump or a return.

40 Chapter 3. Compilation and Instruction Set Extensions

Figure 3.2– Examples of Data Flow Graph (a) and Data Flow Trees (b).

Basic blocks are an important structure. If the first statement of a basic block is executed, consequently all following statements are executed as well. This observation is the basis for code-selection and ISE as well, since these techniques attempt to find mappings of IR nodes to hardware instructions. In case of mapping multiple IR nodes to the same hardware instruction, it is mandatory that selected IR nodes are either executed completely or not at all during program execution. Code-selection and ISE both rely on data dependencies between statements.

3.3. Definition (Data Dependency). A statement sj of a basic block B = hs1, . . . , sni is said to be data dependent on a statement si, with i<j, if si defines a value that is used by sj, i.e. si needs to be executed before sj.

A dependency analysis in its simplest form evaluates the data dependencies inside a single basic block and is called local Data Flow Analysis (DFA). During DFA, a data flow equation is created for each statement, such that the resulting system of equations provides information on data dependencies for all statements of the basic block. The DFA results in a structure called Data

Flow Graph (Figure 3.2) (a).

Data Flow Graph

The vertices of such trees and graphs represent operators/operands and the edges represent data dependencies. These structures are consequently referred to as Data Flow Tree (DFT) and Data

Flow Graph (DFG), where DFGs are the more important structure, since DFTs usually derive

their structure from an according DFG.

3.4. Definition (Data Flow Graph). A DFG for a basic block B is a DAG GB = (VB, EB), where each leaf node v ∈ VB represents either an input operand (constant, variable) or an output (variable) operand and each interior node an operation. Edges (vi, vj) ∈ EB ⊂ VB × VB indicate that a value defined by vi is used by vj, i.e. vj is data dependent on vi.

3.1. Compilation 41 Vertices of a DFG emanating more than one edge are called Common Subexpressions (CSE). If a DFG does not contain any CSE, it is called a DFT. DFTs are usually constructed by splitting a DFG at its CSEs and by inserting copies of the according CSE into each resulting DFT at appropriate positions, such that each vertex in a tree has not more than one successor.

In practice, a compiler performs a DFA not on the level of a basic block, but for complete procedures. For this purpose (and others as well), a Control Flow Graph (CFG) is computed. Control Flow Graph

While DFGs mirror program behavior internally of a basic block, the CFG reflects the global control flow of a function or procedure. Basically the CFG provides a different view than DFGs and consequently extends the compiler’s perspective on the source code.

3.5. Definition (Control Flow Graph). A CFG of a function F is a directed graph GF = (VF, EF). Each node v ∈ VF represents a basic block, and EF contains an edge (v, v′) ∈

VF × VF, iff vmight be directly executed after v. The set of successors succ of a basic block B is given by succB = {v ∈ VF | (b, v) ∈ EF} and the set of predecessors pred of a basic block B is given by predB = {v ∈ VF | (v, b) ∈ EF}

The obvious edges are those resulting from jumps to explicit labels like the last statement sn of a

basic block. If sn is a conditional jump or a conditional return, an additional fallthrough edge to

the successor basic block is created. Blocks without any outgoing edges have a return statement at the end. In case the CFG contains unconnected basic blocks, there is so called unreachable

code, which can be eliminated by dead code elimination without changing the semantics of the

program code. Dominators

The notion of dominators is widely applied in the context of compilation and ISE. Especially for ISE (pertinent for this thesis), the enumeration of dominators adopts an important role, because subgraph enumeration is realized by enumerating multiple-vertex dominators. For compilation, probably one of the most prominent applications of dominators is loop analysis. Loops are of high interest, since they usually represent execution hotspots of an application and therefore offer beneficial optimization potential.

3.6. Definition (Dominator). Given a rooted graph1 G = (V, E, r), a vertex v ∈ V dominates a vertex w ∈ V in G (v ¹Gw), iff every path hr, . . . , wi emanating at the graph’s root r leading to w includes v as well. Accordingly, a vertex v ∈ V post-dominates a vertex w ∈ V , iff every path

1

A graph G is called a rooted graph, if one vertex r has been designated the root, in which case the edges have a natural orientation, towards or away from the root r. If all paths are leading towards r, it is sometimes called the sink of G.

42 Chapter 3. Compilation and Instruction Set Extensions

hw, . . . , ri emanating at w leading to the graph’s root r includes v as well. The vertex v is called dominator of w and Dom(w) designates the set of all dominators of w.

The binary dominance relation ¹G is reflexive (a ¹G a), transitive (a ¹G b ∧ b ¹G c ⇒ a ¹G c)

and antisymmetric (a ¹Gb ∧ b ¹Ga ⇒ b = a). Furthermore, in case the underlying flow graph G

is obvious from the context ¹ is used instead of ¹G.

While the dominance relation captures every node that dominates a certain different node, it is often useful to know the immediate dominator of a certain node.

3.7. Definition (Immediate Dominator). Given a rooted graph G = (V, E, r), a vertex v ∈ V immediately dominates a vertex w ∈ V (v idom w), iff every other dominator of w

also dominates v

v idom w ⇒ Dom(w) − {w} = Dom(v).

The vertex v is called the immediate dominator of w (v = IDom(w)).

Intuitively, the immediate dominator of a node w is the node, which is closest to w and dominates it. The immediate dominance relation forms a tree of nodes — called dominator tree — whose root is the entry node, whose edges represent immediate dominance between nodes and whose paths display all dominance relationships. In the dominator tree, each node is a child of its imme- diate dominator. The analysis of dominators has been extensively studied in the past literature [148, 149, 182, 227, 253]. In general, the set of dominators can be represented as

Dom(r) = {r} Dom(v) = {v} ∪   \ w∈pred(v) Dom(w)   (3.1)

However, solving these equations as a forward dataflow problem [149], results in quadratic runtime. Nevertheless, the algorithm described in [182]2 is capable of computing the dominators for a flow

graph in O(n · log(n)) time. It is one of the best known and widely used algorithm for fast dominator computation. By traversing the vertices of an underlying flow graph G = (V, E, r) in depth-first order, the algorithm constructs a spanning tree T and an enumeration (df num(v)) of all vertices v ∈ V (Figure 3.3). The tree features several helpful attributes for the computation of dominators. For all vertices v 6= r and their according path Phr,vi in the spanning tree T , the

following holds:

• ∀w ∈ Phr,vi∧ w 6= v : df num(w) ≤ df num(v)

• obviously, every dominator of v lies on the Phr,vi, such that

∀d ∈ Dom(v)(d ¹ v ⇒ df num(d) ≤ df num(v))

2

3.1. Compilation 43 • for the computation of IDom(v), only predecessors of v have to be regarded

• if df num(w) ≤ df num(v), u and v have at least one common ancestor3 in the depth-first

tree T

Based on the spanning tree and its implied enumeration, a value called semidominator is computed for each vertex v 6= r (Figure 3.3). A semidominator of a node v can be described as the minimal4

predecessor of v in T , which is originating a path to v including nodes beyond v’s search path Phr,vi.

3.8. Definition (Semidominator). A semidominator is defined as

sdom(v) = min{w|∃hw = v0, v1, . . . vn = vi : vi ≥ v, ∀1 ≤ i ≤ n − 1}.

R

B

C

D

E

F

G

H

I

J

K

L

(2,R)

(3,B)

(4,D)

(1,-)

(5,F)

(6,R)

(7,F)

(8,B)

(9,G)

(10,R)

(11,C)

(12,C)

Figure 3.3 – Example of depth-first enumeration of semi-dominators in a flow graph: Solid red edges represent spanning tree edges; black edges are nontree edges; numbers and letters in parentheses designate depth-first number and semidominator of an according vertex.

3

Node a is an ancestor of node b if a = b or there is a path from a to b in T . Furthermore, a is a proper ancestor of b if a is an ancestor of b and a 6= b.

4

44 Chapter 3. Compilation and Instruction Set Extensions

In order to compute semidominators for the flow graph’s vertices, every vertex v and its prede- cessors w are evaluated in accordance to the following issues:

• If w ∈ Phr,vi of T such that df num(w) ≤ df num(v), w is a candidate for sdom(v).

• If w 6∈ Phr,vi of T (i.e. df num(w) ≥ df num(v)), semidominators of w and its successors u

with df num(u) ≤ df num(w) are candidates for sdom(v).

Afterwards, the candidate featuring the minimal depth-first number is selected. On the path Phsdom(v),vi be u the node whose semidominator sdom(u) features the minimal depth-first number.

Then

IDom(v) = (

sdom(v) : if sdom(u) = sdom(v) IDom(u) : if sdom(u) 6= sdom(v)

Finally, the algorithm explicitly sets IDom(v) for each v, processing the nodes in depth-first order. The asymptotic complexity of this methodology has been further reduced to linearity as described in [42]; these improvements however did not result in reduced runtime. Interestingly, by turning the problem of dominator identification back into a forward data flow problem, [93] presents an algorithm that features significant faster runtimes compared to [182] even for flow graphs with more than 400 nodes.

In the preceding explanations, only single-vertex dominators have been considered. However, the notion of dominator can be generalized to include sets of vertices, which collectively dominate a given vertex [141].

3.9. Definition (Generalized Dominator). Given a rooted graph G = (V, E, r), a set of

vertices U ⊆ V dominates a vertex v ∈ V (U ¹ v), iff the following two conditions are met: 1. all paths from the root r of G to vertex v contain at least one vertex w ∈ U ;

2. for each vertex v ∈ V , there is at least one path from the root r of G to vertex v that contains

w, but not any other vertex in U .

The computation of generalized dominators features in general exponential runtime [141]. In the algorithm described in [141], generalized dominators are computed, similar to Equation 3.1, by taking the intersections of the dominator set of (immediate) predecessors. The algorithm is based upon the observation that, if a vertex v is dominated by another vertex u, then u must also dom- inate all predecessors of v. In accordance to this, first, all single-vertex dominators are computed. Generalized dominators for a certain vertex are determined by considering combinations of domi- nators of its predecessors. In order to verify whether a set of vertices U ⊆ V with cardinality |U | dominates a vertex v, it is ensured that no subset W ⊂ U dominates v. This procedure requires computation of all dominator sets of cardinality less than |U | in advance such that dominator sets are computed by successively increasing the cardinality of the sets.

3.1. Compilation 45