Professional Documents
Culture Documents
Syntax Analysis
Bottom Up Parsing
A bottom-up parser creates the parse tree of the given input starting
from leaves towards the root.
A bottom-up parser tries to find the right-most derivation of the given
input in the reverse order.
Bottom-up parsing is also known as shift-reduce parsing because its
two main actions are shift and reduce.
2 methods in shift reduce parsing
Operator Precedence Parsing
LR Parsing(Left-to-right, Rightmost derivation)
SLR
Canonical LR
LALR
Shift-Reduce Parsing
Grammar: Reducing a sentence: Shift-reduce corresponds
SaABe abbcde to a rightmost derivation:
AAbc|b aAbcde S rm a A B e
Bd aAde rm a A d e
aABe rm a A b c d e
S rm a b b c d e
These match
productions
right-hand sides
4
Handles
A handle is a substring of grammar symbols in a
right-sentential form that matches a right-hand side
of a production
Grammar: a bbcde
SaABe a Abcde
AAbc|b a Ade Handle
Bd a ABe
S
Handle Pruning
6
Stack Implementation of
Shift-Reduce Parsing
input a1 a2 ai an $
stack
Xm
sm1
Xm1 action goto
shift
reduce
s0 accept 11
error
Constructing LR(0) Item
An LR(0) item of a grammar G is a production of G a dot at the
some position of the right side.
.
A aB b
A aBb.
Sets of LR(0) items will be the states of action and goto table of
the SLR parser.
A collection of sets of LR(0) items (the canonical LR(0)
collection) is the basis for constructing SLR parsers.
12
1. Augmented Grammar
G is G with a new production rule SS where S is the
new starting symbol.
2. The Closure Operation
If I is a set of LR(0) items for a grammar G, then closure(I) is the
set of LR(0) items constructed from I by the two rules:
1. Initially, every LR(0) item in I is added to closure(I).
.
2. If A B is in closure(I) and B is a production rule of G; then
.
B will be in the closure(I). We will apply this rule until no more new
LR(0) items can be added to closure(I).
Closure-Example
E E .
closure({E E}) =
E E+T { E E.
ET .
E E+T
T T*F E T.
3. goto Operation
If I is a set of LR(0) items and X is a grammar symbol
(terminal or non-terminal), then goto(I,X) is defined as
follows:
. .
If A X in I then every item in closure({A X })
will be in goto(I,X).
goto-example
Example:
I ={ E . E, E . . E+T, E T,
T . .
T*F, T F,
F . .
(E), F id }
. .
goto(I,E) = { E E , E E +T }
SLR Parsing TableAction Table Goto Table
state id + * ( ) $ E T F
0
1
2
3
4
5
6
7
8
9
10
18
Constructing SLR Parsing Table
(of an augumented grammar G)
input a1 a2 ai an $
stack
Xm
sm1
Xm1 action goto
shift
reduce
s0 accept 22
error
LR Parsing Algorithm
The parsing table consists of two parts: a parsing action function and a
goto function.
The LR parsing program determines sm, the state on top of the stack
and ai, the current input. It then consults action[sm, ai] which can take
one of four values:
Shift
Reduce
Accept
Error
LR Parsing Algorithm
If action[sm, ai] = shift s, where s is a state, then the parser executes a
shift move.
If action[sm, ai] = reduce A , then the parser executes a reduce
move.
pop 2*|| items from the stack;
If action[sm, ai] = accept, parsing is completed
If action[sm, ai] = error, then the parser discovered an error.
LR Parsing Algorithm
set ip to point to the first symbol in w$
initialize stack to 0
repeat forever
let s be top most state on stack & a be symbol pointed to by ip
if action[s, a] = shift s
push a then s onto stack
advance ip to next input symbol
else if action[s, a] = reduce A
pop 2*| | symbols of stack
let s be state now on top of stack
push A then goto[s,A] onto stack
output production A
else if action[s, a] == accept
return success
else
error()
Canonical Collection of Sets of LR(1) Items
The construction of the canonical collection of the sets of LR(1) items are similar to the construction of the
canonical collection of the sets of LR(0) items, except that closure and goto operations work a little bit different.
c d $ S C
0 s3 s4 g1 g2
1 a
2 s6 s7 g5
3 s3 s4 g8
4 r3 r3
5 r1
6 s6 s7 g9
7 r3
8 r2 r2
9 r2
Construction of LR(1) Parsing Tables
1. Construct C{I0,...,In} the canonical collection of sets of LR(1) items for G.
2. Create the parsing action table as follows
.
If a is a terminal, A a,b in Ii and goto(Ii,a)=Ij then action[i,a] is shift j.
.
If A ,a is in Ii , then action[i,a] is reduce A where AS.
If any conflicting actions generated by these rules, the grammar is not LR(1).
34
Error Recovery Strategies
Panic mode
Phrase-level recovery
Error productions
Global correction
35
Error Recovery in Predictive Parsing
An error may occur in the predictive parsing (LL(1) parsing)
if the terminal symbol on the top of stack does not match with
the current input symbol.
if the top of stack is a non-terminal A, the current input symbol is
a, and the parsing table entry M[A,a] is empty.
What should the parser do in an error case?
The parser should be able to give an error message (as much as
possible meaningful error message).
It should be recover from that error case, and it should be able to
continue the parsing with the rest of the input.
36
Panic-Mode Error Recovery in LL(1)
Parsing
In panic-mode error recovery, we skip all the input symbols until a
synchronizing token is found.
What is the synchronizing token?
All the terminal-symbols in the follow set of a non-terminal can be used as a
synchronizing token set for that non-terminal.
So, a simple panic-mode error recovery for the LL(1) parsing:
All the empty entries are marked as synch to indicate that the parser will skip all
the input symbols until a symbol in the follow set of the non-terminal A which on
the top of the stack. Then the parser will pop that non-terminal A from the stack.
The parsing continues from that state.
To handle unmatched terminal symbols, the parser pops that unmatched
terminal symbol from the stack and it issues an error message saying that
unmatched terminal is inserted.
37
Phrase-Level Error Recovery
Each empty entry in the parsing table is filled with a
pointer to a special error routine which will take care
that error case.
These error routines may:
change, insert, or delete input symbols.
issue appropriate error messages
pop items from the stack.
We should be careful when we design these error
routines, because we may put the parser into an infinite
loop.
39
Error Recovery in LR Parsing
An LR parser will detect an error when it consults the parsing
action table and finds an error entry. All empty entries in the
action table are error entries.
Errors are never detected by consulting the goto table.
A canonical LR parser (LR(1) parser) will never make even a
single reduction before announcing an error.
The SLR and LALR parsers may make several reductions before
announcing an error.
But, all LR parsers (LR(1), LALR and SLR parsers) will never
shift an erroneous input symbol onto the stack.
41
Panic Mode Error Recovery in LR
Parsing
Scan down the stack until a state s with a goto on a
particular nonterminal A is found.
Discard zero or more input symbols until a symbol a is
found that can legitimately follow A.
The symbol a is simply in FOLLOW(A), but this may not work for all situations.
42
Phrase-Level Error Recovery in LR
Parsing
Each empty entry in the action table is marked with a
specific error routine.
An error routine reflects the error that the user most
likely will make in that case.
An error routine inserts the symbols into the stack or
the input (or it deletes the symbols from the stack and
the input, or it can do both insertion and deletion).
missing operand(e1)
unbalanced right parenthesis(e2)
Missing operator(e3)
Missing right parenthesis(e4)
43
Example
EE+E
|E*E action goto
|(E)
| id
id + * ( ) $ E
missing operand(e1) 0 s3 e1 e1 s2 e2 e1 1
unbalanced right
parenthesis(e2) 1 e3 s4 s5 e3 e2 acc
Missing operator(e3) 2 s3 e1 e1 s2 e2 e1 6
Missing right
parenthesis(e4) 3 r4 r4 r4 r4 r4 r4
4 s3 e1 e1 s2 e2 e1 7
5 s3 e1 e1 s2 e2 e1 8
6 e3 s4 s5 e3 s9 e4
7 r1 r1 s5 r1 r1 r1
8 r2 r2 r2 r2 r2 r2
YACC
LALR parser generator Yacc
Yet another compiler-compiler
Available on different platforms
UNIX, Linux
Creating an Input/Output Translator with
Yacc
Yacc specification
Yacc compiler y.tab.c
translate.y
47
A Yacc source program has three parts
declarations
%%
translation rules
%%
supporting C functions
Ex:
EE+T|T
TT*F|F
F(E)|digit
%{
#include <stdio.h>
%}
%token DIGIT
%%
expr : expr + term { $$=$1+$3; }
| term
;
term : term * factor { $$=$1*$3; }
| factor
;
factor : ( expr ) { $$ = $2; }
| DIGIT
;
%%
Auxiliary procedures
Unit-IV
SYNTAX DIRECTED TRANSLATION & RUN TIME
ENVIRONMENT
Syllabus
Syntax directed Definitions-Construction of Syntax Tree-Bottom-up
Evaluation of S-Attribute Definitions- Design of predictive translator -
Type Systems-Specification of a simple type checker Equivalence of
Type Expressions-Type Conversions.
RUN-TIME ENVIRONMENT: Source Language Issues-Storage
Organization-Storage Allocation Parameter Passing-Symbol Tables-
Dynamic Storage Allocation-Storage Allocation in FORTAN.
Introduction
Semantic Analysis computes additional information related to the
meaning of the program once the syntactic structure is known.
In typed languages as C, semantic analysis involves adding
information to the symbol table and performing type checking.
Syntax Directed Translation(SDT)
Grammar+Semanticrules=SDT
Usefulfordoingthingsafterparsing(typechecking,codegeneration)
Basicidea:attachattributestogrammarsymbols,thendosomething
withattributeswhentheyappearinparsetree.
Syntax-Directed Definitions
Each grammar symbol has two kinds of associated attributes:
Synthesized attributes
Evaluated bottom-up .
If only synthesized attributes are used, definition is called an
S-attributed definition.
Example
PRODUCTION SEMANTIC RULE
L E n print(E.val)
E E1 + T E.val = E1.val + T.val
ET E.val = T.val
T T1 * F T.val = T1.val * F.val
TF T.val = F.val
F (E) F.val = E.val
F digit F.val = digit .lexval
S-Attributed & L-Attributed Definitions
S-Attributed Definitions An SDD is S-attributed if every attribute is
synthesized.
L-Attributed Definitions An SDD is L-attributed if every attribute is
synthesized or inherited. Then the rule may use
Inherited attributes associated with the parent.
Inherited or synthesized attributes associated with the attributes of left siblings.
Dependency graphs
The interdependencies among the inherited and synthesized attributes
at the nodes in a parse tree can be depicted by a directed graph called a
dependency graph.
If an attribute b depends on attribute c, then attribute b has to be
evaluated AFTER c.
61
Evaluation Order
A TOPOLOGICAL SORT of the dependency graph decides the evaluation order
in a parse tree.
Applications of Syntax-Directed Translations
Construction of Syntax Trees
A syntax tree is a condensed form of parse tree.
Functions used to build the syntax tree:
mknode(op,left,right) constructs an operator node with label op, and two
children, left and right
mkleaf(id,entry) constructs a leaf node with label id and a pointer to a symbol
table entry
mkleaf(num,val) constructs a leaf node with label num and the tokens
numeric value val
a-4+c
65
SDD for syntax tree construction
Production Semantic Rules
E -> E1 + T E.nptr := mknode( +, E1.nptr,T.nptr)
E -> E1 - T E.nptr := mknode( -, E1.nptr,T.nptr)
E -> T E.nptr := T.nptr
T -> ( E ) T.nptr := E.nptr
T -> id T.nptr := mkleaf( id, id.entry )
T -> num T.nptr := mkleaf( num, num.val )
66
Bottom-up Evaluation of S-Attribute
Definitions
Syntax-directed definition with only synthesized attributes is
called S-attributed.
Use LR Parser.
Implementation
Stack to hold information about subtrees that have been parsed.
3*5+4
Example:
69
Production Semantic Values (Code)
L ---> E$ Print (Val[Top])
E ---> E + T Val[nTop]:= Val[Top 2] + Val[Top]
E ---> T
T ---> T * F Val[nTop]:= Val[Top 2] * Val[Top]
T ---> F
F ---> (E) Val[nTop]:=Val[Top 1]
F ---> digit
70
Cont.
3+5$
71
Input Stack Attribute Production Used
3*5+4$ - -
*5+4$ 3 3
Production Semantic Values (Code)
*5+4$ F 3 F ---> digit
L ---> E$ Print (Val[Top])
*5+4$ T 3 T ---> F
5+4$ T* 3 E ---> E + T Val[nTop]:= Val[Top 2] +
Val[Top]
+4$ T*5 3 * 5
E ---> T
+4$ T*F 3 *5 F - digit
T ---> T * F Val[nTop]:= Val[Top 2] *
+4$ T 15 T ---> T * F
Val[Top]
+4$ E 15 E ---> T
T ---> F
4$ E+ 15
F ---> (E) Val[nTop]:=Val[Top 1]
$ E+4 15 + 4
F ---> digit
$ E+F 15 + 4 F ---> digit
$ E+T 15 + 4 T ---> F
$ E 19 E ---> E + T
E 19
72
L 19 L ---> E $
Design of a Predictive Translator
Input: A SDT scheme with grammar suitable for predictive parsing.
Output: Code for Syntax-directed translator.
Method:
1. For each nonterminal A construct a function that has a formal parameter for
inherited attribute of A and that returns the values of the synthesized
attributes of A.
2. The code for nonterminal A decides what production to use based on the
current input symbol.
3. The code associated with each production does the following
i. For token X with synthesized attribute x, save the value of x in the variable declared
for X.x. Then generate call to match the token X and advance the input.
ii. For nonterminal B, generate assignment c=B(b1,b2,.,bk) with the function call on
the right side.
iii. For an action, copy the code into the parser.
TYPE CHECKING
A compiler must check that the source program follows both syntactic and semantic
conventions of the source language.
Semantic Checks
Static done during compilation
Dynamic done during run-time
Type checking is one of these static checking operations.
we may not do all type checking at compile-time.
Some systems also use dynamic type checking too.
A type system is a collection of rules for assigning type expressions to the parts of a
program.
A type checker implements a type system.
A sound type system eliminates run-time type checking for type errors.
74
Some examples of static checks:
Type checks
Flow-of-control checks
75
TYPE SYSTEMS
A type system is a collection of rules for assigning type expressions to
the parts of a program.
For example : if both operands of the arithmetic operators of +,- and
* are of type integer, then the result is of type integer
76
Type Expressions
The type of a language construct is denoted by a type expression.
A type expression can be:
1. A basic type
a primitive data type such as integer, real, char, boolean,
type_error to signal a type error
void : no type
2. A type name
a name can be used to denote a type expression.
3. A type constructor
Constructors include:
Array : If T is a type expression then array (I,T) is a type expression denoting the type of an array with
elements of type T and index set I.
Example: int a[20]; array(019,int)
Products : If T1 and T2 are type expressions, then their Cartesian product T1 X T2 is a type expression.
77
Pointers : The type expression for pointer is given as pointer(T) where T is a data type.
Example int *x;
pointer(int)
Functions : A function in programming languages maps a domain type D to a range type R.
The type of such function is denoted by the type expression D R
78
SPECIFICATION OF A SIMPLE TYPE CHECKER
A type checker for a simple language in which the type of each identifier
must be declared before the identifier is used.
The type checker can handle arrays, pointers, statements and functions.
A Simple Language
Consider the following grammar:
PD;E
D D ; D | id : T
T char | integer | array [ num ] of T | T
E literal | num | id | E mod E | E [ E ] | E
79
Translation scheme:
P D;E
DD;D
D id : T { addtype (id.entry , T.type) }
T char { T.type : = char }
T integer { T.type : = integer }
T T1 { T.type : = pointer(T1.type) }
T array [ num ] of T1 { T.type : = array ( 1 num.val , T1.type) }
83
Equivalence of Type Expressions
The job of a type checker is to find whether two type expressions are
equivalent or not.
Type equivalence is of two categories
Structural Equivalence
Name Equivalence
Structural Equivalence of Type Expressions
When two expressions are the same basic type or formed by applying
the same constructor to structurally equivalent types then those
expressions are called structurally equivalent.
if (s and t are same basic types) then return true
else if (s=array(s1,s2) and t=array(t1,t2)) then return (sequiv(s1,t1) and sequiv(s2,t2))
else if (s = s1 x s2 and t = t1 x t2) then return (sequiv(s1,t1) and sequiv(s2,t2))
else if (s=pointer(s1) and t=pointer(t1)) then return (sequiv(s1,t1))
else if (s = s1 s2 and t = t1 t2) then return (sequiv(s1,t1) and sequiv(s2,t2))
else return false
Name Equivalence of Type Expressions
In name equivalence the type expressions are given the same name.
State
Function that maps a storage location to a value
g: storage location value
g( f(name) ) = value environment state
heap
Activation Record
Information needed by a single returned value
execution of a procedure is managed
using an activation record or frames. actual parameters
Not all compilers use all of the optional control link
fields
Pascal and C push activation record optional access link
on the runtime stack when saved machine status
procedure is called and pop the
activation record off the stack when local data
control returns to the caller
temporaries
Activation Record
1) Temporary values returned value
e.g. those arising in the evaluation of expressions
actual parameters
2) Local data Callers
Data that is local to an execution of the optional control link responsibility
procedure
to initialize
optional access link
3) Saved machine status
State of the machine info before procedure is saved machine status
called. Values of program counter and machine
registers that have to be restored when control Callees
returns from the procedure local data
responsibilit
temporaries y
to initialize
Activation Record
4) Access Link
points to the activation record of the calling procedure. returned value
5) Control link actual parameters
points to the activation record of the caller.
104
Static Allocation
In a static environment (Fortran 77) there are a number
of restrictions:
Size of data objects are known at compile time
No recursive procedures
No dynamic memory allocation
Only one copy of each procedure activation record
exists at time t
We can allocate storage at compile time
Bindings do not change at runtime
Every time a procedure is called, the same bindings occur
Static Allocation code for function 1
inti=10;
code f() code for function n
intf(intj) i (int)
{ global / static area
intk;
main() stack
intm; Activation
record k (int)
}
free space
main() f()
{ Activation
intk; record k (int)
f(k); m (int)
} heap
Stack-based Allocation
In a stack-based allocation, the previous restrictions are
lifted (Pascal, C, etc)
procedures are allowed to be called recursively
Need to hold multiple activation records for the same procedure
Created as required and placed on the stack
Each record will maintain a pointer to the record that activated it
On completion, the current record will be deleted from the stack and
control is passed to the calling record
Dynamic memory allocation is allowed
Pointers to data locations are allowed
Stack-based Allocation
PROGRAMsort(input,output); Position in Activation Records
VARa:array[0..10]ofInteger;
Activation Tree On Stack
PROCEDUREreadarray;
VARi:Integer;
BEGIN
fori:=1to9doread(a[i]);
END; s
FUNCTIONpartition(y,z:Integer): s
Integer;
VARi,j,x,v:Integer; a (array)
BEGIN
END;
PROCEDUREquicksort(m,n:Integer);
VARi:Integer;
BEGIN
if(n>m)thenBEGIN
i:=partition(m,n);
quicksort(m,i1);
quicksort(i+1,n)
END
END;
BEGIN/*ofmain*/
a[0]:=9999;a[10]:=9999;
readarray;
quicksort(1,9)
END.
Stack-based Allocation
PROGRAMsort(input,output);
VARa:array[0..10]ofInteger; Position in Activation Records
PROCEDUREreadarray;
VARi:Integer; Activation Tree On Stack
BEGIN
fori:=1to9doread(a[i]);
END;
FUNCTIONpartition(y,z:Integer):Integer;
VARi,j,x,v:Integer; s
BEGIN
s
END;
PROCEDUREquicksort(m,n:Integer); a (array)
VARi:Integer;
BEGIN
if(n>m)thenBEGIN
r r
i:=partition(m,n);
quicksort(m,i1);
quicksort(i+1,n)
END
i (integer)
END;
BEGIN/*ofmain*/
a[0]:=9999;a[10]:=9999;
readarray;
quicksort(1,9)
END.
Stack-based Allocation
PROGRAMsort(input,output);
VARa:array[0..10]ofInteger; Position in Activation Records
PROCEDUREreadarray;
VARi:Integer; Activation Tree On Stack
BEGIN
fori:=1to9doread(a[i]);
END;
FUNCTIONpartition(y,z:Integer):Integer;
VARi,j,x,v:Integer; s
BEGIN
s
END;
PROCEDUREquicksort(m,n:Integer); a (array)
VARi:Integer;
BEGIN
if(n>m)thenBEGIN
r q(1,9) q(1,9)
i:=partition(m,n);
quicksort(m,i1);
quicksort(i+1,n)
END
i (integer)
END;
BEGIN/*ofmain*/
a[0]:=9999;a[10]:=9999;
readarray;
quicksort(1,9)
END.
Stack-based Allocation
PROGRAMsort(input,output);
VARa:array[0..10]ofInteger; Position in Activation Records
PROCEDUREreadarray;
VARi:Integer; Activation Tree On Stack
BEGIN
fori:=1to9doread(a[i]);
END;
FUNCTIONpartition(y,z:Integer):Integer;
VARi,j,x,v:Integer; s
BEGIN
s
END;
PROCEDUREquicksort(m,n:Integer); a (array)
VARi:Integer;
BEGIN
if(n>m)thenBEGIN
r q(1,9) q(1,9)
i:=partition(m,n);
quicksort(m,i1);
quicksort(i+1,n)
END
p(1,9) q(1,3) i (integer)
END;
BEGIN/*ofmain*/ q(1,3)
a[0]:=9999;a[10]:=9999;
readarray;
quicksort(1,9)
END. i (integer)
Heap Allocation
Symbol Tables
A data structure used by a compiler to keep track of semantics of variables.
Data type.
When is used
Where it is stored: storage address. name attributes
Possible implementations: : :
Unordered list: for a very small set of variables.
Ordered linear list: insertion is expensive, but implementation is relatively easy.
Binary search tree: O(log n) time per operation for n variables.
Hash table: most commonly used, and very efficient provided the memory space is
adequately larger than the number of variables.
DATA STRUCTURE FOR SYMBOL
TABLE
LIST
SELF ORGANIZING LIST
HASH TABLE
HASH TABLE
Search Tree
How to store names
Fixed-length name:
allocate a fixed space for each name allocated.
Variable-length name:
A string of space is used to store all names.
For each name, store the length and starting index of each
name.
Retest-II Batch students
Consider the following grammar
S->AS|b
A->SA|a
Construct the SLR parse table for the grammar .Show the
actions of the parser for the input string abab
Unit-V
CODE OPTIMIZATION AND CODE GENERATION
Principal Sources of Optimization-DAG- Optimization of
Basic Blocks-Global Data Flow Analysis Efficient Data
Flow Algorithms-Issues in Design of a Code Generator - A
Simple Code Generator Algorithm.
Introduction
Intermediate Code undergoes various transformations
called Optimizationsto make the resulting code run
faster and taking less space.
A main goal is to achieve a better performance.
source Intermediate
Front End Code Gen target
Code Code Code
user Machine-
independe Machine-
nt dependent
Compiler Compiler
optimizer optimizer
Two levels
Machine independent code optimization
Control Flow analysis
Data Flow analysis
Transformation
Control- Data-
Transfor-
flow flow
mations
analysis analysis
128
Three address code for quick sort
Basic Blocks and Flow Graphs
The Principal Sources of
Optimization
Local optimization: look within basic block
Global optimization: look across blocks
Function-preserving transformations include
Common subexpression elimination
Copy propagation
Dead-code elimination
Constant-folding
Common sub expression Elimination
An occurrence of an expression E is common sub
expression if E was previously computed and the values
of variables in E have not changed since.
Copy Propagation
Statement of form f := g is called a copy statement
Idea is to use g instead of f in subsequent statements
Dead-Code Elimination
Variable that is no longer live (subsequently used) is
called dead.
Copy propagation often turns copy statement into dead
code.
Constant Folding
Constant Folding is the transformation that substitutes an
expression with a constant.
Constant Folding is useful to discover Dead-Code.
x := 32 becomes x := 64
x := x + 32
ode
Loop Optimization
The running time of a program can be improved if we
decrease the amount of instructions in an inner loop.
Three techniques are useful:
1. Code Motion
2. Reduction in Strength
3. Induction-Variable elimination
Code Motion
Move code outside the loop since there are potential
many iterations
Look for expressions that yield the same result
independent of the iterations.
Example
While ( I <= limit 2)do
Induction Variables
A variable x is an Induction Variable of a loop if every
time the variable x changes values, it is incremented or
decremented by some constant.
B3:
B3:
j=j-1 j = j - 1
t4 = 4 *j t4 = t4 -4
t5 = a [ t4] t5 = a [ t4]
If t5 > v goto B3 If t5 > v goto B3
Reduction in strength
The replacement of an expensive operation by a cheaper one
is termed reduction in strength.
X^2 x * x
X * 4 x << 2
DAG representation
of Basic Block (BB)
A DAG of a basic block is a directed acyclic graph with
following node markings:
Leaves are labeled with unique identifier (varname or const)
Interior nodes are labeled by an operator symbol
Nodes optionally have a list of labels (identifiers)
Edges relates operands to the operator (interior nodes are
operator)
146
Example: DAG for BB
t1
*
t1 := 4 * i
4 i
t1 := 4 * i
t3 := 4 * i
t2 := t1 + t3 if (i <= 20)goto L1
+ t2 <= (L1)
* t1, t3
i 20
4 i
147
Algorithm for construction of DAG
Input: A basic block
Output: A DAG for the basic block containing the following information:
1. A label for each node. For leaves, the label is an identifier. For interior nodes, an
operator symbol.
2. For each node a list of attached identifiers to hold the computed values.
Case (i) x : = y OP z Case (ii) x : = OP y Case (iii) x : = y
Method:
Step 1: If y is undefined then create node(y). If z is undefined, create node(z) for
case(i).
Step 2: For the case(i), create a node(OP) whose left child is node(y) and right child is
node(z). ( Checking for common sub expression). Let n be this node.
For case(ii), determine whether there is node(OP) with one child node(y). If not create
such a node.
For case(iii), node n will be node(y).
Step 3: Delete x from the list of identifiers for node(x).
Example
prod
+
prod0 * t5
(1)
t4
t2
[] [] <=
t1 t3
a b * + t7 i 20
4 i0 1
Representation of Array References
Representation of Array References
Applications of DAGS
Automatically detect common sub
expressions.
Determine which identifiers have their values
used in the block.
Determine which statements compute values
that could be used outside the block.
Optimization of Basic Blocks
Common sub-expression elimination: by
construction of DAG
Note: for common sub-expression elimination,
we are actually targeting for expressions that
compute the same value.
+ c
a = b + c
- b, d
b = a d
c = b + c + a d0
d = a - d
b0 c0
153
DAG representation identifies expressions
that yield the same result
+ e
a := b + c
b := b d
c := c + d
+ a - b + c
e := b + c
b0 c0 d0
154
Dead code elimination: Code generation
from DAG eliminates dead code.
c +
a := b + c
a := b + c
b := a d b,d - d := a - d
d := a d
c := d + c
c := d + c a +
d0
b is not live
b0 c0
155
The Use of Algebraic Identities
Algebraic identities represent another important class of
optimization on basic blocks
x+0=0+x=x
x-0=x
x*1=1*x=x
x/1=x
Another class of algebraic optimization is reduction in
strength.
A third class of optimization is constant folding.
Peephole optimization
Peephole: a small moving window in the instruction sequence
Technique: examine a short sequence of target instructions (peephole)
and try to replace it with a faster or shorter sequence
Goals:
- improve performance
- reduce code size
Methods
Redundant instruction elimination
Unreachable code elimination
Flow of control optimization
Algebraic simplifications
Reduction in strength
Use of machine idioms
Redundant instruction elimination
If debug 1 goto L2
print debugging info
L2:
Flow of control optimization:
goto L1 goto L2
L1: goto L2 L1: goto L2
165
a : =b * - c + b * - c
Postfix notation
a b c uminus * b c uminus * + assign
Target Program
The output of code generator is target program.
Output may take variety of forms
Absolute machine language(executable code)
Relocatable machine language(object files for linker)
Assembly language(facilitates debugging)
Absolute machine language has advantage that it can be placed
in a fixed location in memory and immediately executed.
Relocatable machine language program allows subprograms to
be compiled separately.
Producing assembly language program as o/p makes the
process of code generation somewhat easier.
Memory Management
Mapping names in the source program to addresses of data
objects in run time memory is done by front end & code
generator.
If a machine code is being generated, labels in three address
statements have to be converted to addresses of instructions.
Instruction selection
Uniformity and completeness of the instruction set are
important factors.
Instruction speeds and machine idioms are another
important factor.
If we do not care about the efficiency of the target program,
instruction selection is straightforward.
The quality of the generated code is determined by its speed
and size.
Example
a=b+c
d=a+e
MOV b,R0
ADD c,R0
MOV R0,a
Redundant
MOV a, R0
ADD e,R0
MOV R0,d
Register Allocation
Instructions involving register operands are usually shorter
and faster than those involving operands in memory.
Two sub problems
Register allocation: select the set of variables that will reside in
registers at each point in the program.
Register assignment: select specific register that a variable will reside.
t=a+b
t=t*c
T=t/d
L R1, a
A R1, b
M R0, c
D R0, d
ST R1, t
Choice of evaluation order
The order in which computations are performed
can affect the efficiency of the target code.
When instructions are independent, their
evaluation order can be changed
MOV a,R0
ADD b,R0
MOV R0,t1
t1:=a+b MOV c,R1
t2:=c+d ADD d,R1
a+b-(c+d)*e MOV e,R0
t3:=e*t2
t4:=t1-t3 MUL R1,R0 MOV c,R0
MOV t1,R1 ADD d,R0
reorder SUB R0,R1 MOV e,R1
MOV R1,t4 MUL R0,R1
t2:=c+d MOV a,R0
t3:=e*t2 ADD b,R0
t1:=a+b SUB R1,R0
t4:=t1-t3 MOV R0,t4
Approaches to code generator
Criterion for a code generator is to produce correct code.
Given the premium on correctness, designing a code
generator so it can be easily implemented, tested, and
maintained is an important design goal.
A Code Generator
Generates target code for a sequence of three-address
statements.
Uses new function getreg to assign registers to
variables.
Computed results are kept in registers as long as
possible, which means:
Result is needed in another computation
Register is kept up to a procedure call or end of block
Checks if operands to three-address code are available
in registers
Register and Address Descriptors
A register descriptor keeps track of what is currently
stored in a register at a particular point in the code, e.g.
a local variable, argument, global variable, etc.
MOV a,R0 R0 contains a
An address descriptor keeps track of the location where
the current value of the name can be found at run time,
e.g. a register, stack location, memory address, etc.
MOV a,R0
MOV R0,R1 a in R0 and R1
The Code Generation Algorithm
For each statement x := y op z
1. Set location L = getreg(y, z)
2. If y L then generate
MOV y,L
where y denotes one of the locations where the value of y is
available
3. Generate
OP z,L
where z is one of the locations of z;
Update register/address descriptor of x to include L
4. If y and z has no next use and is stored in register, update
register descriptors to remove y and z
The getreg Algorithm
To compute getreg(y,z)
1. If y is stored in a register R and R only holds the value y, and
y has no next use, then return R;
Update address descriptor: value y no longer in R
2. Else, return a new empty register if available
3. Else, find an occupied register R;
Store contents (register spill) by generating
MOV R,M
for every M in address descriptor of y;
Return register R
4. If x is not used in the block, or no suitable occupied register
can be found, return memory location of x as L.
Code Generation Example
Register Address
Statements CodeGenerated
Descriptor Descriptor
Registersempty
t := a - b MOV a,R0 R0 contains t t in R0
SUB b,R0
181
Example: Paths and Points
d1: i := m 1
d2: j := n B1
d3: a := u1
Path:
p3
d4: i := i + 1 B2 p1, p2, p3, p4,
p4
p5, p6 pn
p5
p6
d5: j := j - 1 B3
B4
p1 pn
p2
d6: a := u2 B5 B6
182
Reaching Definition
Example: Reaching Definition
d1: i := m 1
d2: j := n B1
d3: a := u1
Definition of i (d1)
reaches p1
p1
p2
d4: i := i + 1 B2
Killed as d4, does
d5: j := j - 1 B3 not reach p2.
B4 Definition of i (d1)
does not reach B3,
d6: a := u2 B5 B6 B4, B5 and B6.
184
Data Flow Analysis of Structured
Programs
Data-flow equations for Reaching
Definitions
is of the form
S d: a:=b+c
gen[S] = {d}
kill[S] = Da - {d}
out[S] = gen[S] (in[S] - kill[S])
gen[S] = gen[S1]
kill[S] = kill[S1]
in[S1] = in[S] gen[S1]
out[S] = out[S1]
190
Example Reaching Definitions
d1: i := m-1;
d2: j := n;
gen={d3,d4,d5,d6,d7}
; d3: a := u1;
kill={d1,d2}
do
gen={d1,d2,d3} d4: i := i+1;
; d5: j := j-1;
kill={d4,d5,d6,d7}
if e1 then
gen={d1,d2} gen={d3} gen={d4,d5,d6,d7}do d6: a := u2
; d kill={d1,d2}
kill={d4,d5,d7} kill={d6} 3
else
d7: i := u3
gen={d1} gen={d2} gen={d4,d5,d6,d7}; e2
d d kill={d1,d2} while e2
kill={d4, d7} 1 kill={d5} 2
gen={d4,d5} gen={d6,d7}
; ifkill={}
kill={d1,d2,d7}
0001100 0000011
1100001 ; if 0000000
Estimation:
gen[S] = gen[S1] gen[S2]
kill[S] = kill[S1] kill[S2]
Accurate:
gen[S] = gen[S1] gen[S]
195
kill[S] = kill[S ] kill[S]
Computations of in and out
Iterative Solution of Data-Flow
Equation
Pass 3
FINAL PASS
Definition - Use
Chains
Live Variable (Liveness) Analysis
r4 is dead, as it is redefined.
So is r6. r2, r3, r5 are live
r4 = 4
r6 = 8
r6 = r2 + r3
r7 = r4 r5 What does this mean?
r6 = r4 r5 is useless,
it produces a dead value !!
Get rid of it!
204
Extra Topic
Structure Preserving Transformation
1. Common Sub expression elimination
2. Dead code elimination
3. Renaming of temporary variables
4. Interchange of two independent adjacent statements.
Common-Sub expression Elimination
Remove redundant computations
a := b + c a := b + c
b := a - d b := a - d
c := b + c c := b + c
d := a - d d := b
t1 := b * c
t1 := b * c
t2 := a - t1
t2 := a - t1
t3 := b * c
t4 := t2 + t1
t4 := t2 + t3
207
Dead Code Elimination
Remove unused statements
b := a + 1 b := a + 1
a := b + c
Assuming a is dead (not used)
Renaming Temporary Variables
Temporary variables that are dead at the end of a block
can be safely renamed
t1 := b + c t1 := b + c
t2 := a - t1 t2 := a - t1
t1 := t1 * d t3 := t1 * d
d := t2 + t1 d := t2 + t3
Normal-form block
Interchange of Statements
Independent statements can be reordered
t1 := b + c t1 := b + c
t2 := a - t1 t3 := t1 * d
t3 := t1 * d t2 := a - t1
d := t2 + t3 d := t2 + t3
Algebraic Transformations
Change arithmetic operations computed by basic block
into algebraic equivalent forms
t1 := a - a t1 := 0
t2 := b + t1 t2 := b
t3 := 2 * t2 t3 := t2 << 1