0% found this document useful (0 votes)
750 views21 pages

DAG Optimization in Compiler Design

gfgg

Uploaded by

buzztube96
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
750 views21 pages

DAG Optimization in Compiler Design

gfgg

Uploaded by

buzztube96
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Optimization Of Basic Blocks-

Directed Acyclic Graph

Directed Acyclic Graph (DAG) is a tool that depicts the structure of basic blocks, helps to see the flow
of values flowing among the basic blocks, and offers optimization too. DAG provides easy
transformation on basic blocks. DAG can be understood here:

 Leaf nodes represent identifiers, names or constants.

 Interior nodes represent operators.

 Interior nodes also represent the results of expressions or the identifiers/name where the
values are to be stored or assigned.

Example:

t0 = a + b

t1 = t0 + c

d = t0 + t1

[t0 = a + b]
[t1 = t0 + c]

[d = t0 + t1]

DAG is a very useful data structure for implementing transformations on Basic Blocks.

 A DAG is constructed for optimizing the basic block.

 A DAG is usually constructed using Three Address Code.


 Transformations such as dead code elimination and common sub expression elimination are
then applied.

Properties-

 Reachability relation forms a partial order in DAGs.

 Both transitive closure & transitive reduction are uniquely defined for DAGs.

 Topological Orderings are defined for DAGs.

Applications-

DAGs are used for the following purposes-

 To determine the expressions which have been computed more than once (called common sub-
expressions).

 To determine the names whose computation has been done outside the block but used inside
the block.

 To determine the statements of the block whose computed value can be made available outside
the block.

Algorithm for Construction of DAG

There are three possible cases to construct DAG on three address code:

Case 1: x = y op z

Case 2: x = op y

Case 3: x = y

DAG can be constructed as follows:

STEP 1:

If y is undefined then create a node with label y. Similarly create a node with label z.

STEP 2:
For case 1, create a node with label op whose left child is node y, and node z will be the right child. Also,
check for any common sub expressions. For case 2, determine if a node is labelled op. such node will
have a child node y. for case 3 node n will be node y.

STEP 3:

Delete x from list of identifiers for node x. append x to the list of attached identifiers for node n found in
step 2.

Example:

Consider the following three address code statements.

a=b*c

d=b

e=d*c

b=e

f=b+c

g=f+d

Step 1

Consider the first statement, i.e., a = b * c. Create a leaf node with label b and c as left and right child
respectively and parent of it will be *. Append resultant variable a to the node *.

Step 2

For second statement, i.e., d = b, node b is already created. So, append d to this node.
Step 3

For third statement e = d * c, the nodes for d, c and * are already create. Node e is not created, so
append node e to node *.

Step 4

For fourth statement b = e, append b to node e.

Step 5

For fifth statement f = b + c, create a node for operator + whose left child b and right child c and append
f to newly created node +
Step 6

For last statement g = f + d, create a node for operator + whose left child d and right child f and append
g to newly created node +.

Construction of DAGs-

Following rules are used for the construction of DAGs-

Rule-01:

In a DAG,

 Interior nodes always represent the operators.

 Exterior nodes (leaf nodes) always represent the names, identifiers or constants.
Rule-02:

While constructing a DAG,

 A check is made to find if there exists any node with the same value.

 A new node is created only when there does not exist any node with the same value.

 This action helps in detecting the common sub-expressions and avoiding the re-computation of
the same.

Rule-03:

The assignment instructions of the form x:=y are not performed unless they are necessary.
Problem-01:

Consider the following expression and construct a DAG for it-

(a+b)x(a+b+c)

Solution-

Three Address Code for the given expression is-

T1 = a + b

T2 = T1 + c

T3 = T1 x T2

Now, Directed Acyclic Graph is-


NOTE

From the constructed DAG, we observe-

 The common sub-expression (a+b) has been expressed into a single node in the DAG.

 The computation is carried out only once and stored in the identifier T1 and reused later.
This illustrates how the construction scheme of a DAG identifies the common sub-expression and helps
in eliminating its re-computation later.

Problem-02:

Consider the following expression and construct a DAG for it-

(((a+a)+(a+a))+((a+a)+(a+a)))

Solution-

Directed Acyclic Graph for the given expression is-

Problem-03:

Consider the following block and construct a DAG for it-

(1) a = b x c

(2) d = b

(3) e = d x c

(4) b = e

(5) f = b + c
(6) g = f + d

Solution-

Directed Acyclic Graph for the given block is-

Problem-04:

Optimize the block in the Problem-03.

Solution-

Step-01:

Firstly, construct a DAG for the given block (already done above).

Step-02:

Now, the optimized block can be generated by traversing the DAG.

 The common sub-expression e = d x c which is actually b x c (since d = b) is eliminated.

 The dead code b = e is eliminated.


The optimized block is-

(1) a = b x c

(2) d = b

(3) f = a + c

(4) g = f + d

Problem-05:

Consider the following basic block-

B10:

S1 = 4 x I

S2 = addr(A) – 4

S3 = S2[S1]

S4 = 4 x I

S5 = addr(B) – 4

S6 = S5[S4]

S7 = S3 x S6

S8 = PROD + S7

PROD = S8

S9 = I + 1

I = S9
If I <= 20 goto L10

1. Draw a directed acyclic graph and identify local common sub-expressions.

2. After eliminating the common sub-expressions, re-write the basic block.

Solution-

Directed Acyclic Graph for the given basic block is-

In this code fragment,

 4 x I is a common sub-expression. Hence, we can eliminate because S1 = S4.

 We can optimize S8 = PROD + S7 and PROD = S8 as PROD = PROD + S7.

 We can optimize S9 = I + 1 and I = S9 as I = I + 1.

After eliminating S4, S8 and S9, we get the following basic block-

B10:

S1 = 4 x I

S2 = addr(A) – 4

S3 = S2[S1]
S5 = addr(B) – 4

S6 = S5[S1]

S7 = S3 x S6

PROD = PROD + S7

I=I+1

Peephole Optimization
This optimization technique works locally on the source code to transform it into an optimized code. By
locally, we mean a small portion of the code block at hand. These methods can be applied on
intermediate codes as well as on target codes. A bunch of statements is analyzed and are checked for
the following possible optimization:

Redundant instruction elimination

At source code level, the following can be done by the user:

int add_ten(int x) int add_ten(int x) int add_ten(int x) int add_ten(int x)

{ { { {

int y, z; int y; int y = 10; return x + 10;

y = 10; y = 10; return x + y; }

z = x + y; y = x + y; }

return z; return y;

} }

At compilation level, the compiler searches for instructions redundant in nature. Multiple loading and
storing of instructions may carry the same meaning even if some of them are removed. For example:

 MOV x, R0

 MOV R0, R1

We can delete the first instruction and re-write the sentence as:

MOV x, R1

Unreachable code
Unreachable code is a part of the program code that is never accessed because of programming
constructs. Programmers may have accidently written a piece of code that can never be reached.

Example:

void add_ten(int x)

return x + 10;

printf(“value of x is %d”, x);

In this code segment, the printf statement will never be executed as the program control returns back
before it can execute, hence printf can be removed.

Flow of control optimization

There are instances in a code where the program control jumps back and forth without performing any
significant task. These jumps can be removed. Consider the following chunk of code:

...

MOV R1, R2

GOTO L1

...

L1 : GOTO L2

L2 : INC R1

In this code,label L1 can be removed as it passes the control to L2. So instead of jumping to L1 and then
to L2, the control can directly reach L2, as shown below:

...

MOV R1, R2

GOTO L2

...

L2 : INC R1

Algebraic expression simplification


There are occasions where algebraic expressions can be made simple. For example, the expression a = a
+ 0 can be replaced by a itself and the expression a = a + 1 can simply be replaced by INC a.

Strength reduction

There are operations that consume more time and space. Their ‘strength’ can be reduced by replacing
them with other operations that consume less time and space, but produce the same result.

For example, x * 2 can be replaced by x << 1, which involves only one left shift. Though the output of a *
a and a2 is same, a2 is much more efficient to implement.

Accessing machine instructions

The target machine can deploy more sophisticated instructions, which can have the capability to
perform specific operations much efficiently. If the target code can accommodate those instructions
directly, that will not only improve the quality of code, but also yield more efficient results.

Code Generator
A code generator is expected to have an understanding of the target machine’s runtime environment
and its instruction set. The code generator should take the following things into consideration to
generate the code:

 Target language : The code generator has to be aware of the nature of the target language for
which the code is to be transformed. That language may facilitate some machine-specific
instructions to help the compiler generate the code in a more convenient way. The target
machine can have either CISC or RISC processor architecture.

 IR Type : Intermediate representation has various forms. It can be in Abstract Syntax Tree (AST)
structure, Reverse Polish Notation, or 3-address code.

 Selection of instruction : The code generator takes Intermediate Representation as input and
converts (maps) it into target machine’s instruction set. One representation can have many
ways (instructions) to convert it, so it becomes the responsibility of the code generator to
choose the appropriate instructions wisely.

 Register allocation : A program has a number of values to be maintained during the execution.
The target machine’s architecture may not allow all of the values to be kept in the CPU memory
or registers. Code generator decides what values to keep in the registers. Also, it decides the
registers to be used to keep these values.

 Ordering of instructions : At last, the code generator decides the order in which the instruction
will be executed. It creates schedules for instructions to execute them.
Descriptors

The code generator has to track both the registers (for availability) and addresses (location of values)
while generating the code. For both of them, the following two descriptors are used:

 Register descriptor : Register descriptor is used to inform the code generator about the
availability of registers. Register descriptor keeps track of values stored in each register.
Whenever a new register is required during code generation, this descriptor is consulted for
register availability.

 Address descriptor : Values of the names (identifiers) used in the program might be stored at
different locations while in execution. Address descriptors are used to keep track of memory
locations where the values of identifiers are stored. These locations may include CPU registers,
heaps, stacks, memory or a combination of the mentioned locations.

Code generator keeps both the descriptor updated in real-time. For a load statement, LD R1, x, the code
generator:

 updates the Register Descriptor R1 that has value of x and

 updates the Address Descriptor (x) to show that one instance of x is in R1.

Code Generation

Basic blocks comprise of a sequence of three-address instructions. Code generator takes these sequence
of instructions as input.

Note : If the value of a name is found at more than one place (register, cache, or memory), the register’s
value will be preferred over the cache and main memory. Likewise cache’s value will be preferred over
the main memory. Main memory is barely given any preference.

getReg : Code generator uses getReg function to determine the status of available registers and the
location of name values. getReg works as follows:

 If variable Y is already in register R, it uses that register.

 Else if some register R is available, it uses that register.

 Else if both the above options are not possible, it chooses a register that requires minimal
number of load and store instructions.

For an instruction x = y OP z, the code generator may perform the following actions. Let us assume that L
is the location (preferably register) where the output of y OP z is to be saved:

 Call function getReg, to decide the location of L.


 Determine the present location (register or memory) of y by consulting the Address Descriptor
of y. If y is not presently in register L, then generate the following instruction to copy the value
of y to L:

MOV y’, L

where y’ represents the copied value of y.

 Determine the present location of z using the same method used in step 2 for y and generate
the following instruction:

OP z’, L

where z’ represents the copied value of z.

 Now L contains the value of y OP z, that is intended to be assigned to x. So, if L is a register,


update its descriptor to indicate that it contains the value of x. Update the descriptor of x to
indicate that it is stored at location L.

 If y and z has no further use, they can be given back to the system.

You might also like