20210624-80604 Automata and Compiler Design
20210624-80604 Automata and Compiler Design
(Autonomous)
Maisammaguda, Dhulapally (Post Via. Hakimpet), Secunderabad, Telangana-500100 www.mrec.ac.in
Lecture Notes
On
TEXT BOOKS:
1. John C. Martin, “Introduction to Languages and Theory of Computation”, TMH;
Third Edition.
2. Alfred Aho, Ravi Sethi, Jeffrey D Ullman, “Compilers Principles, Techniques and
Tools”, Pearson Education Asia.
REFERENCES:
1. Adesh K. Pandey “An introduction to automata theory and formal
languages”, Publisher: S.K. Kataria and Sons.
2. Deniel I. Cohen, Joh Wiley and Sons, Inc “Introduction to computer theory”.
3. Allen I. Holub “Compiler Design in C”, Prentice Hall of India.
4. J.P. Bennet, “Introduction to Compiler Techniques”, Tata McGraw-Hill,
Second Edition.
E –RESOURCES:
1. https://www.iitg.ernet.in/dgoswami/Flat-Notes.pdf
2. https://books.google.co.in/books?isbn=8184313020
3. http://www.jalc.de/
4. https://arxiv.org/list/cs.FL/0906
5. http://freevideolectures.com/Course/3379/Formal-Languages-and-Automata-Theory
6. http://nptel.ac.in/courses/111103016/
Course Outcomes:
At the end of the course, students will be able to
1. Define the theory of automata types of automata and FA with outputs.
2. Differentiate regular languages and applying pumping lemma.
3. Classify grammars checking ambiguity able to apply pumping lemma for CFL
various types of PDA.
4. Illustrate Turing machine concept and in turn the technique applied in computers.
5. Analyze P vs NP- Class problems and NP-Hard vs NP-complete problems, LBA,
LR Grammar, Counter machines, Decidability of Problems.
UNIT -1
Fundamentals
Examples:
Σ = {0,1}
*
L = {x | x is in Σ and x contains an even number of
0‟s} Σ = {0, 1, 2,…, 9, .}
*
L = {x | x is in Σ and x forms a finite length real number}
Automata &Compiler Design Page 2
= {0, 1.5, 9.326,…}
Σ = {a, b, c,…, z, A, B,…, Z}
*
L = {x | x is in Σ and x is a Pascal reserved word}
= {BEGIN, END, IF,…}
*
Σ = {Pascal reserved words} U { (, ), ., :, ;,…} U {Legal Pascal identifiers} L = {x | x is in Σ and x
is a syntactically correct Pascal program}
Σ = {English words}
*
L = {x | x is in Σ and x is a syntactically correct
Examples:
Let Σ = {0,1}
(0 +1)* All strings of 0‟s and1‟s0(0 +1)* All strings of 0‟s and 1‟s, beginning with
a0 (0 +1)*1 All strings of 0‟s and 1‟s, ending with a1
(0 + 1)*0(0+1)* All strings of 0‟s and 1‟s containing at least one 0 (0 + 1)*0(0 +
1)*0(0+1)* All strings of 0‟s and 1‟s containing at least two
0‟s (0+1)*01*01* All strings of 0‟s and 1‟s containing at least two
0‟s (101*0)* All strings of 0‟s and 1‟s containing an even number of 0‟s
1*(01*01*)* All strings of 0‟s and 1‟s containing an even number of 0‟s
(1*01*0)*1* All strings of 0‟s and 1‟s containing an even number of0‟s
Identities:
3. Ø* =ε
4. ε* =ε
5. u+v =v+u
7. u + u = u
8. u* =(u*)*
9. u(v+w) =uv+uw
=u*(u+v)*
=(u+vu*)*
= (u*v*)*
=u*(vu*)*
=(u*v)*u*
A finite state machine has a set of states and two functions called the next-state
function and the outputfunction
The set of states correspond to all the possible combinations of the internal storage
n
If there are n bits of storage, there are 2 possiblestates
The next state function is a combinational logic function that given the inputs
and the current state, determines the next state of thesystem
The output function produces a set of outputs from the current state and theinputs
Finite Automata
• While NFA‟s are more expressive than DFA‟s, we will see that
addingnondeterminism does not let us define any language that
cannot be defined by aDFA.
• One way to think of this is we might write a program using a NFA, but
then when it is “compiled” we turn the NFA into an equivalentDFA.
• LetM=(Q,Σ,δ,q,F)beaDFAandletwbeinΣ*.ThenwisacceptedbyMiff
0
• Another equivalentdefinition:
L(M) = {w | w is in Σ* and w is accepted by M}
Notes:
• A DFA M = (Q, Σ, δ,q0,F) partitions the set Σ* into two sets:
L(M)and Σ* -L(M).
,mod
Step 1: Q‟ = ɸ
Step 2: Q‟ = {q0}
Step 3: For each state in Q‟, find the states for each input symbol.
Currently, state in Q‟ is q0, find moves from q0 on input symbol a and b using transition
function of NFA and update the transition table of DFA
δ‟ (Transition Function of DFA)
Now { q0, q1 } will be considered as a single state. As its entry is not in Q‟, add it
to Q‟. So Q‟ = { q0, { q0, q1 } }
Now, moves from state { q0, q1 } on different input symbols are not present in transition table of
DFA, we will calculate it like:
δ‟ ( { q0, q1 }, a ) = δ ( q0, a ) ∪ δ ( q1, a ) = { q0, q1 } δ‟ ( { q0, q1 }, b ) = δ ( q0, b ) ∪
Now { q0, q2 } will be considered as a single state. As its entry is not in Q‟, add it
to Q‟. So Q‟ = { q0, { q0, q1 }, { q0, q2 } }
Now, moves from state {q0, q2} on different input symbols are not present in transition table of
DFA, we will calculate it like:
δ‟ ( { q0, q2 }, a ) = δ ( q0, a ) ∪ δ ( q2, a ) = { q0, q1 } δ‟ ( { q0, q2 }, b ) = δ ( q0, b ) ∪ δ ( q2, b ) = { q0 } Now we will update the transition table of DFA.
As there is no new state generated, we are done with the conversion. Final state of DFA will
be state which has q2 as its component i.e., { q0, q2 }
Following are the various parameters for DFA.
Q‟ = { q0, { q0, q1 }, { q0, q2 } }
∑ = ( a, b )
F = { { q0, q2 } } and transition function δ‟ as shown above. The final DFA for above NFA
has been shown in Figure 2.
Note : Sometimes, it is not easy to convert regular expression to DFA. First you can
convert regular expression to NFA and then NFA to DFA
CONTEXT FREE-GRAMMAR
Example :CFG:
kk
• Note that G “generates” the language {0 1 |k>=0}
For the constructed table., fill with synch for rest of the input symbols of FOLLOW set and then
fill the rest of the columns with error term.
A –> X1, X2,…, Xn
Terminals must be a production in P
The first L stands for “Left-to-right scan of input”. The second L stands for “Left-
most derivation”. The „1‟
LL (1) Grammar:
• If a vertex has label ε, then that vertex is a leaf and the only child of its‟parent
• More Generally, a derivation tree can be defined with any non-terminal as theroot.
Notes:
• Root can be anynon-terminal
• Leaf nodes can be terminals ornon-terminals
If there were no multiple entries in the Recursive decent parser table, the given grammar is
LL (1).
If the grammar G is ambiguous, left recursive then the recursive decent table will have
atleast one multiply defined entry.
The weakness of LL(1) (Top-down, predictive) parsing is that, must predict which
production to use.
Error Recovery in Predictive Parser:
Error recovery is based on the idea of skipping symbols on the input until a token in a
selected set of synchronizing tokens appear. Its effectiveness depends on the choice of
• A derivation tree with root S shows the productions used to obtain a
sentential form.
The moves of a parser and error recovery on the erroneous input) id*+id is as follows:
•
FIRSTkFIRSTk: kk terminals that can be at the beginning of a derived non-terminal
•
FOLLOWkFOLLOWk: kk terminals that can come after a derived non-terminal
The basic idea is to create a lookup table using this information from which the parser
can then simply go and check what derivation is to be made given a certain input token.
The class of LL(1)LL(1) grammars are so easily parsed because it is strong. The strong
LL(kLL(k) grammars are a subset of the LL(k)LL(k) grammars that can be parsed without
knowledge of the left-context of the parse. That is, each parsing decision is based only on the
next k tokens of the input for the current nonterminal that is being expanded. Formally,
A grammar (G=N,T,P,S)(G=N,T,P,S) is strong if for any two distinct A-productions in the grammar:
A→αA→α
A→βA→β
FIRSTk(αFOLLOWk(A))∩FIRSTk(βFOLLOWk(A))=∅FIRSTk(αFOLLOWk(A))∩FIRSTk(βFOL LOWk(A))=∅
That looks complicated so we‟ll see it another way. Let‟s take a textbook example to understand,
instead, when is some grammar “weak” or when exactly would we need to know the left-context of
the parse.
S→aAaS→aAa
S→bAbaS→bAba
A→bA→b
A→ϵA→ϵ
Here, you‟ll notice that for an LL(2)LL(2) instance, baba could result from either of
the SSproductions. So the parser needs some left-context to decide whether baba is produced
by S→aAaS→aAa or S→bAbaS→bAba.
Such a grammar is therefore “weak” as opposed to being a strong LL(k)LL(k) grammar.
BOTTOM UPPARSING:
Bottom-up parser builds a derivation by working from the input sentence back towards the
start symbol S. Right most derivation in reverse order is done in bottom-up parsing.
1 rn sentence Bottom-up
Assuming the production A→ , to reduce ri ri-1 match some RHS against ri then replace with
its corresponding LHS, A.In terms of the parse tree, this is working from leaves to root.
Example – 1:
E then S Clse
If S
While E do S
|
true
if E then S
elseS lm
if id then S
elseS lm
if id then while E do S
elseS lm
if id then while true do S
elseS lm
if id then while true do print elseS
lm
HANDLE PRUNING:
Keep removing handles, replacing them with corresponding LHS of production, until we reach S.
Example:
E→E+E/E*E/(E)/id
a+b*c A E→id
E+b*c B E→id
E+E*C C E→id
The grammar is ambiguous, so there are actually two handles at next-to-last step. We can use parser-
generators that compute the handles for us
LR PARSINGINTRODUCTION:
The "L" is for left-to-right scanning of the input and the "R" is for constructing
a rightmost derivation in reverse.
3. The class of grammars that can be parsed using LR methods is a proper subset of
the class of grammars that can be parsed with predictiveparsers.
LR-PARSERS:
LR(k) parsers are most general non-backtracking shift-reduce parsers. Two cases of
interest are k=0 and k=1. LR(1) is of practical relevance.
→ LR(1) items play a key role in the LR(1) and LALR(1) table
constructionalgorithms. LR parsers have more information available
than LL parsers when choosing aproduction:
* LR knowseverything derived fromRHS plus„K‟lookaheadsymbols.
* LL just knows„K‟lookaheadsymbols into what‟sderived fromRHS.
* Deterministic context free languages:
*
*
* LR (1) languages
*
*
LALR PARSING:
Example:
C →cC,c/d/$
C→cC,c/d/$
C→d,c/d/$ I5→some
as previous
I47→C→d,c/d/$
I89→C→cC, c/d/$
Io S36 S47 1 2
1 Accept
2 S36 S47 5
36 S36 S47 89
47 r3 r3
5 r1
89 r2 r2 r2
Ambiguous grammar:
A CFG is said to ambiguous if there exists more than one derivation tree for the given input string i.e.,
more than one LeftMost Derivation Tree (LMDT) or RightMost Derivation Tree (RMDT).
Definition: G = (V,T,P,S) is a CFG is said to be ambiguous if and only if there exist a string in T*
that has more than on parse tree.
where V is a finite set of variables.
T is a finite set of terminals.
P is a finite set of productions of the form -> α, where A is a variable and α ∈ (V ∪ T)* S is a designated variable called the start symbol.
For Example:
Input File:
YACC input file is divided in three parts.
/* definitions */
....
%%
/* rules */
....
%%
/* auxiliary routines */
....
Input File: Definition Part:
• The definition part includes information about the tokens used in the syntax definition:
• %token NUMBER
Semantics
Syntax Directed Translation:
• A formalist called as syntax directed definition is used fort specifying translations for
programming languageconstructs.
• A syntax directed definition is a generalization of a context free grammar in which each
grammar symbol has associated set of attributes and each and each productions is
associated with a set of semanticrules
• SDD is a generalization of CFG in which each grammar productions X->α is associated with it a set of
semantic rules of the form
a: = f(b1,b2…..bk)
• Thissetofattributesforagrammarsymbolispartitionedintotwosubsetscalledsynthesized
and inherited attributes of that grammar symbol.
Inherited attribute:(↑,→)
An inherited attribute is one whose value at parse tree node is determined in terms of attributes at
the parent and | or siblings of thatnode.
The process of computing the attribute values at the node is called annotating or decorating
the parse tree.Terminals can have synthesized attributes, but not inherited attributes.
• The process of computing the attributes values at the nodes is called annotating(or
decorating) of the parse tree.
• Of course, the order of these computations depends on the dependency graph
induced by the semanticrules.
Ex1:1) Synthesized Attributes : Ex: Consider the CFG :
S→ EN
E→E+T
E→E-T
E→ T
T→ T*F
T→T/F
T→F
F→(E)
F→digit N→;
S→EN S.val=E.val
E→E1+T E.val =E1.val +T.val
E→E1-T E.val = E1.val –T.val
E→T E.val=T.val
T→T*F T.val = T.val *F.val
T→T|F T.val =T.val | F.val
F→ (E) F.val=E.val
T→F T.val=F.val
F→digit F.val =digit.lexval
N→; can be ignored by lexical Analyzer as;I
is terminating symbol
For the Non-terminals E,T and F the values can be obtained using the attribute “Val”.
In S→EN, symbol S is the start symbol. This rule is to print the final answer of expressed.
Write the SDD using the appropriate semantic actions for corresponding production rule
of the givenGrammar.
The annotated parse tree is generated and attribute values are computed. The Computation
is done in bottom upmanner.
L-attributed SDT
This form of SDT uses both synthesized and inherited attributes with restriction of not taking values
from right siblings.
In L-attributed SDTs, a non-terminal can get values from its parent, child, and sibling nodes. As
in the following production
S→ABC
S can take values from A, B, and C (synthesized). A can take values from S only. B can take values
from S and A. C can get values from S, A, and B. No non-terminal can get values from the sibling
to its right.
Attributes in L-attributed SDTs are evaluated by depth-first and left-to-right parsing manner.
Analysis + syntheses=translation
code Intermediatecode
In the analysis –synthesis model of a compiler, the front-end translates a source program into an
intermediate representation from which the back-end generates target code, in many compilers the
source code is translated into a language which is intermediate in complexity between a HLL and
machine code .the usual intermediate code introduces symbols to stand for various temporary
quantities.
We assume that the source program has already been parsed and statically checked..the
various intermediate code forms are:
a) Polishnotation
b) Abstract syntax trees(or)syntaxtrees
c) Quadruples
d) Triples three address code
e) Indirecttriples
f) Abstract
machinecode(or)pseudocopde postfix notation:
In general, if e1 and e2 are any postfix expressions, and Ø to the values denoted by e1 and e2 is
indicated in postfix notation nby e1e2Ø.no parentheses are needed in postfix notation because the
position and priority (number of arguments) of the operators permits only one way to decode a
postfixexpression.
• A formalist called as syntax directed definition is used fort specifying translations for
programming languageconstructs.
• A syntax directed definition is a generalization of a context free grammar in which each
grammar symbol has associated set of attributes and each and each productions is
associated with a set of semanticrules
SDD is a generalization of CFG in which each grammar productions X->α is associated with it a
set of semantic rules of the form
a: = f(b1,b2…..bk)
Where a is an attributes obtained from the function f.
Thissetofattributesforagrammarsymbolispartitionedintotwosubsetscalledsynthesized
and inherited attributes of that grammar symbol.
• A parse tree showing the values of attributes at each node is called an Annotated parsetree.
• The process of computing the attributes values at the nodes is called annotating(or
decorating) of the parse tree.Of course, the order of these computations depends on
the dependency graph induced by the
ASSIGNMENT STATEMENTS
Suppose that the context in which an assignment appears is given by the following grammar. P
MD
M ɛ
D D ; D | id : T | proc id ; N D ;
SNɛ
Nonterminal P becomes the new start symbol when these productions are added to those in
the translation scheme shown below.
E →( E1 ) { E.place : = E1.place }
→
E id { p : = lookup ( id.name);
ifp≠nil then
E.place : = p
elseerror }
Flow-of-Control Statements
We now consider the translation of boolean expressions into three -address code in the context of
if-then, if-then-else, and while-do statements such as those generated by the following grammar:
S if E then S1
if E then S1 else
| S2
| while E do S1
In each of these productions, E is the Boolean expression to be translated. In the translation, we
assume that a three-address statement can be symbolically labeled, and that the function
newlabelreturns a new symbolic label each time it is called.
• E.true is the label to which control flows if E is true, and E.false is the label to which
control flows if E is false.
• The semantic rules for translating a flow-of-control statement S allow control to flow from
the translation S.code to the three-address instruction immediately following S.code.
• S.nextis a label that is attached to the first three-address instruction to be executed after the
code for Code for if-then , if-then-else, and while-do statements
E.false: S2.code
E.false: ...
S.next: ...
to E.false
E.true: S1.code
gotoS.begin
E.false: ...
(c) while-do
For example,
Sab –>ba A–
>S.
Here, Variables are S, A and Terminals a, b.
For Example,
S–>AB
AB –>abc
B –> b
In Type 2,
1. First of all it should be Type 1.
2. Left hand side of production can have only one variable.
alpha= 1.
For example,
S–>AB
A –> a B
–> b
V–>VT*/
T*. (or)
V –> T*V /T*
for example :
S –> ab.
TypeChecking:
• A compiler has to do semantic checks in addition to syntactic checks.
• Semantic Checks
• Static –done duringcompilation
• Dynamic –done duringrun-time
• Type checking is one of these static checkingoperations.
• 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.
• A programming language is strongly-typed, if every program its compiler accepts will
execute without type errors.
• –Ex: int x[100]; … x[i] most of the compilers cannot guarantee that i will be between
0and 99
Type Expression:
• The type of a language construct is denoted by a typeexpression.
–A basic type
• void: notype
• arrays: If T is a type expression, then array (I,T)is a type expression where I denotes index
range. Ex: array(0..99,int)
• products: If T1and T2 are type expressions, then their Cartesian product T1 x T2 is a type
expression. Ex: int xint
• pointers: If T is a type expression, then pointer (T) is a type expression. Ex: pointer(int)
error
else S.type=type-error}
else S.type=type-error}
else E.type=type-error}
f: double x char->int
andsequiv(s2,t2))
and sequiv(s2,t2))
In some programming languages, we give a name to a type expression, and we use that name
as a type expressionafterwards.
varr,s : ↑cell
NARROWING THE SET OF POSSIBLE TYPES. The second step in resolving the overloading of
operators and functions in the expression E' consists of
determining if a unique type can be assigned to each subexpression E of
E' and generating the code for evaluating each subexpression E of E'.
This s done by
assigning an inherited attribute E.unique to each subexpression E of E'
such that either E can be assigned a unique type and E.unique is this type,
or E cannot be assigned a unique type and E.unique is $ \bf type\_error$.
assigning a synthesized attribute E.code which is the target code for evaluating E and
executing the following translation scheme (where the attributes E.types have already
been computed).
• The executin•• tar3et program runs in its own logical address space in which each
program value has a location.
• The management and organization of this logical address space is shared between the
complies, operating system and target machine. The operating system maps the logical
address into physical addresses, which are usually spread throughout memory.
CoJe
Static Data
Stack
free memory
Heap
Run-time storage comes in blocks, where a byte is the smallest unit of addressable
bytes and given the address of‘ first byte.
• The storage layout for data objects is strongly influenced by the addressing constraints of
the target machine.
• A character array of length 10 needs only enough bytes to hold 10 characters, a compiler
may allocate 12 bytes to s et alignment, leaving 2 bytes unused.
• This unused space due to alignment considerations 1s referred to as padding.
• The size of sortie program objects may be known at run time and may be placed in an
area cal led static
• The dynamic areas used to maximize the utilization of space at run time are stack and
heap.
Activation records:
• Procedure calls and returns are usually man‹s ed by a run time stack called the cnnli ol
.s‘tavk.
• Each live activation has an activation record on the control stack, with the root of the
activation tree at the bottom, the latter activation has its recoi’d at the top of‘the stuck.
• The contents of the activation record vary with the lans uage being implemented. The
dias ram below shows the contents of activation record.
at
2 as a
3 Heap allocation — allocates and deallocates storage as needed at run time from a data area
known as heap
STATIC ALLOCATION
In static allocation, names are bound to storage as the program is compiled, so there is no
need for a run-time support package.
• Since the bindings do not change at run-time, everytime a procedure is activated, its
names are bound to the same storage locations.
• Therefore values of local names are i elaineJ across activations of a procedure That is,
when control returns to a procedure the values of the locals are the same as they were
when control left the last time.
• Front the type of a name, the compiler decides the amount of storage for the naiTle and
decides where the activation records go. Ai compile time, we can fill in lhe addresses at
which the target code can i’iiid the data it operates on.
Machine independentoptimizations:
Machine independent optimizations are program transformations that improve the
target code without taking into consideration any properties of the targetmachine.
Machine dependantoptimizations:
Machine dependant optimizations are based on register allocation and utilization of
special machine- instruction sequences.
• Simply stated, the best program transformations are those that yield the most benefit for
the leasteffort.
• The transformation must preserve the meaning of programs. That is, the optimization must
not change the output produced by a program for a given input, or cause an error such as
division by zero, that was not present in the original source program. At all times we take
the “safe” approach of missing an opportunity to apply a transformation rather than risk
changing what the programdoes.
• The transformation must be worth the effort. It does not make sense for a compilerwriter
• Data flow analysis (DFA) is the process of ascerting and collecting information prior
to program execution about the possible modification, preservation, and use of certain
entities (such as values or attributes of variables) in a computerprogram
Function-Preserving Transformations
• There are a number of ways in which a compiler can improve a program
without changing the function itcomputes.
• Thetransformations
o Common sub expressionelimination,
o Copypropagation,
o Dead-code elimination,and
o Constant folding, are common examples of such function-preserving
transformations. The other transformations come up primarily when global
optimizations areperformed.
• Frequently, a program will include several calculations of the same value, such as an
offset in an array. Some of the duplicate calculations cannot be avoided by the
programmer because they lie below the level of detail accessible within the source
language.
Common Sub expressionselimination:
• An occurrence of an expression E is called a common sub-expression if E was
previously computed, and the values of variables in E have not changed since the
previous computation. We can avoid recomputing the expression if we can
usethe previously computedvalue.
Forexample
t1: =4*i
t2: =a [t1]
t3: =4*j
t4:=4*i
t5:=n
t 6: =b [t 4] +t 5
The above code can be optimized using the common sub-expression eliminationas t1:=4*i
t2:=a
[t1]t3:=4*j
t5:=n
t6: =b [t1] +t5
The common sub expression t 4: =4*i is eliminated as its computation is already in t1.
And value of i is not been changed from definition to use.
…… A=x*r*r;
The optimization using copy propagation can be done as follows: A=Pi*r*r; Here the
variable x is eliminated
Dead-CodeEliminations:
A variable is live at a point in a program if its value can be used subsequently; otherwise, it is
dead at that point. A related idea is dead or useless code, statements that compute values that
never get used. While the programmer is unlikely to introduce any dead code intentionally, it
may appear as the result of previous transformations. An optimization can be done by
eliminating deadcode.
Example: i=0;
if(i=1)
{
a=b+5;
}
Here,„if‟statement is dead codebecausethis condition will never get satisfied.
Constant folding:
• We can eliminate both the test and printing from the object code. More generally,
deducing at compile time that the value of an expression is a constant and using the
constant instead is known as constantfolding.
• One advantage of copy propagation is that it often turns the copy statement into deadcode.
Forexample,
a=3.14157/2 can be replaced by
a=1.570 there by eliminating a division operation.
Loop Optimizations
• We now give a brief introduction to a very important place for optimizations, namely
loops, especially the inner loops where programs tend to spend the bulk of their time. The
running time of a program may be improved if we decrease the number of instructions in
an inner loop, even if we increase the amount of code outside thatloop.
• Three techniques are important for loopoptimization:
codemotion, which moves code outside aloop;
Induction -variable elimination, which we apply to replace variables from innerloop.
Reduction in strength, which replaces and expensive operation by a cheaper one, such
as a multiplication by anaddition
CodeMotion:
An important modification that decreases the amount of code in a loop is code motion. This
transformation takes an expression that yields the same result independent of the number of times
a loop is executed ( a loop-invariant computation) and places the expression before the loop. Note
Induction Variables
• Loops are usually processed inside out. For example consider the loop aroundB3.
• Note that the values of j and t4 remain in lock-step; every time the value of j decreases by
1, that of t4 decreases by 4 because 4*j is assigned to t4. Such identifiers are called
inductionvariables.
• When there are two or more induction variables in a loop, it may be possible to get rid of
all but one, by the process of induction-variable elimination. For the inner loop around
B3 in Fig. we cannot get rid of either j or t4 completely; t4 is used in B3 and j inB4.
• However, we can illustrate reduction in strength and illustrate a part of the process of
induction-variable elimination. Eventually j will be eliminated when the outer loop of B2
- B5 is considered.
LOOPS IN FLOWGRAPH
• A graph representation of three-address statements, called a flow graph, is useful for
understanding code-generation algorithms, even if the graph is not explicitly constructed
by a code-generation algorithm. Nodes in the flow graph represent computations, and the
edges represent the flow of control.
Dominators:
In a flow graph, a node d dominates node n, if every path from initial node of the flow graph to n
goes through d. This will be denoted by d dom n. Every initial node dominates all the remaining
nodes in the flow graph and the entry of a loop dominates all nodes in the loop.
Similarlyeverynode dominates itself.
Example:
• In the flow graph below,
• Initial node,node1 dominates every node. *node 2 dominatesitself
• node 3 dominates all but 1 and 2. *node 4 dominates all but 1,2 and 3.
• node 5 and 6 dominates only themselves,since flow of control can skip around either by goin
through theother.
• node 7 dominates 7,8 ,9 and 10. *node 8 dominates 8,9 and 10.
D(1)={1}
D(2)={1,2}
D(3)={1,3}
D(4)={1,3,4}
D(5)={1,3,4,5}
(6)={1,3,4,6}
D(7)={1,3,4,7}
D(8)={1,3,4,7,8}
D(10)={1,3,4,7,8,10}
NaturalLoop
One way to find all the loops in a flow graph is to search for edges in the flow graph whose
heads dominate their tails. If a→b is an edge, b is the head and a is the tail. These types of edges
are called as backedg
Example:
In the above graph,
7→ 4 4 DOM7
0 →7 7 DOM10
4→ 3
8→ 3
9 →1
LOOP:
• If we use the natural loops as “the loops”, then we have the useful property that
unless two loops have the same header, they are either disjointed or one is entirely
contained in the other. Thus, neglecting loops with the same header for the
moment, we have a natural notion of inner loop: one that contains no otherloop.
• When two natural loops have the same header, but neither is nested within the
other, they are combined and treated as a singleloop.
Pre-Headers:
header pre-
header
loop L
header
loop L
Definition:
• A flow graph G is reducible if and only if we can partition the edges into
twodisjoint groups, forward edges and back edges, with the followingproperties.
• The forward edges from an acyclic graph in which every node can be reached
from initial node ofG.
• The back edges consist only of edges where heads dominate theirstails.
Example: The above flow graph isreducible.
• If we know the relation DOM for a flow graph, we can find and remove all
the back edges.
• The remaining edges are forwardedges.
• If the forward edges form an acyclic graph, then we can say the flow graphreducible.
• In the above example remove the five back edges 4→3, 7→4, 8→3, 9→1 and 10→7
whose heads dominate their tails, the remaining graph isacyclic.
• The key property of reducible flow graphs for loop analysis is that in such flow graphs
every set of nodes that we would informally regard as a loop must contain a backedge
PEEPHOLE OPTIMIZATION
This equation can be read as “ the information at the end of a statement is either generated within
the statement , or enters at the beginning and is not killed as control flows through thestatement.”
• The details of how data-flow equations are set and solved depend on threefactors.
• The notions of generating and killing depend on the desired information, i.e., on thedata
flow analysis problem to be solved. Moreover, for some problems, instead of
proceeding along with flow of control and defining out[s] in terms of in[s], we need to
proceed backwards and define in[s] in terms ofout[s].
• Since data flows along control paths, data-flow analysis is affected by the constructs in a
program. In fact, when we write out[s] we implicitly assume that there is unique end
point where control leaves the statement; in general, equations are set up at the level of
basic blocks rather than statements, because blocks do have unique endpoints.
• There are subtleties that go along with such statements as procedure calls,
assignments through pointer variables, and even assignments to arrayvariables.
d:a:=b+c
S
gen [S] = { d }
kill [S] = Da – { d }
out [S] = gen [S] U ( in[S] – kill[S] )
Observe the rules for a single assignment of variable a. Surely that assignment is a
definition of a, say d. ThusGen[S]={d}
On the other hand, d “kills” all other definitions of a, so we write Kill[S]
= Da –{d}
ii
S S 1S 2
While the details are dependent on the target language and the operating system, issues such as
memory management, instruction selection, register allocation, and evaluation order are inherent
in almost all code generation problems.
INPUT TO THE CODE GENERATOR
The input to the code generator consists of the intermediate representation of the source program
produced by the front end, together with information in the symbol table that is used to determine
the run time addresses of the data objects denoted by the names in the intermediate
representation.
There are several choices for the intermediate language, including: linear representations such as
postfix notation, three address representations such as quadruples, virtual machine representations
such as syntax trees and dags.
We assume that prior to code generation the front end has scanned, parsed, and translated the
source program into a reasonably detailed intermediate representation, so the values of names
appearing in the intermediate language can be represented by quantities that the target machine
can directly manipulate (bits, integers, reals, pointers, etc.). We also assume that the necessary
type checking has take place, so type conversion operators have been inserted wherever necessary
and obvious semantic errors (e.g., attempting to index an array by a floating point number) have
already been detected. The code generation phase can therefore proceed on the assumption that its
TARGET PROGRAMS
The output of the code generator is the target program. The output may take on a variety of
forms: absolute machine language, relocatable machine language, or assembly language.
Producing an absolute machine language program as output has the advantage that it can be
placed in a location in memory and immediately executed. A small program can be
compiled and executed quickly. A number of “student-job” compilers, such as WATFIV and
PL/C, produce absolute code.
Because producing assembly code does not duplicate the entire task of the assembler, this choice
is another reasonable alternative, especially for a machine with a small memory, where a
compiler must uses several passes.
A code-generation algorithm:
The algorithm takes as input a sequence of three-address statements constituting a basic block.
For each three-address statement of the form x : = y op z, perform the following actions:
Invoke a function getreg to determine the location L where the result of the computation y op
z should be stored.
Consult the address descriptor for y to determine y‟, the current location of y. Prefer the
register for y‟ if the value of y is currently both in memory and a register. If the value of
y is not already in L, generate the instruction MOV y‟ , L to place a copy of y in L.
If the current values of y or z have no next uses, are not live on exit from the block, and are
in registers, alter the register descriptor to indicate that, after execution of x : = y op z , those
registers will no longer contain y or z.
The assignment d : = (a-b) + (a-c) + (a-c) might be translated into the following three-
address code sequence:
t:=a–b
u:=a–c
v:=t+u
d:=v+u
with d live at the end.
Code sequence for the example is:
Statements Code Generated Register descriptor Address descriptor
Register empty
R
d:=v+u ADD R1, R0 0 contains d d in R0
d in R0 and memory
MOV R0, d
The table shows the code sequences generated for the indexed
assignment statements a : = b [ i ] and a [ i ] : = b
The table shows the code sequences generated for the pointer assignments
a : = *p and *p : = a
a : = *p MOV *Rp, a 2
*p : = a MOV a, *Rp 2
REGISTER ALLOCATION
Instructions involving register operands are usually shorter and faster than those involving operands in memory.
Therefore, efficient utilization of register is particularly important in generating good code. The use of registers is
often subdivided into two sub problems:
1. During register allocation, we select the set of variables that will reside in registers at
a point in theprogram.
2. During a subsequent register assignment phase, we pick the specific register that a
variable will residein.
Finding an optimal assignment of registers to variables is difficult, even with single register
values. Mathematically, the problem is NP-complete. The problem is further complicated because
the hardware and/or the operating system of the target machine may require that certain register
usage conventions be observed.
Certain machines require register pairs (an even and next odd numbered register) for some
operands and results. For example, in the IBM System/370 machines integer multiplication and
integer division involve register pairs. The multiplication instruction is of the form
M x,y
where the 64-bit dividend occupies an even/odd register pair whose even register is x; y represents
the divisor. After division, the even register holds the remainder and the odd register the quotient.
Now consider the two three address code sequences (a) and (b) in which the only difference is the
operator in the second statement. The shortest assembly sequence for (a) and (b) are given in(c).
Ri stands for register i. L, ST and A stand for load, store and add respectively. The optimal
choice for the register into which „a‟ is to be loaded depends on what will ultimately happen to e.
t := a+b t := a +b
t := t* c t := t +c
t := t/ d t := t / d
L R1, a L R0, a
A R1, b A R0, b
M R0, c A R0, c
(a) (b)
THE DAG REPRESENTATION FOR BASIC BLOCKS
A DAG for a basic block is a directed acyclic graph with the following labels on nodes:
Leaves are labeled by unique identifiers, either variable names or constants.
Interior nodes are labeled by an operator symbol.
Nodes are also optionally given a sequence of identifiers for labels to store
the computed values.
DAGs are useful data structures for implementing transformations on basic blocks.
It gives a picture of how the value computed by a statement is used in
subsequent statements.
It provides a good way of determining common sub - expressions
Input: A basic block
Output: A DAG for the basic block containing the following information:
A label for each node. For leaves, the label is an identifier. For interior nodes,
an operator symbol.
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 2: For the case(i), create a node(OP) whose left child is node(y) and right child is
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). Append x to the listof attached identifiers for the
t1 := 4* i
t2 := a[t1]
t3 := 4* i
t4 := b[t3]
t5 := t2*t4
t6 := prod+t5
prod := t6
t7 := i+1
i := t7
if i<=20 goto (1)
I0
tfi,prod
Final DAG
prodo
7 20
10
The advantage of generating code for a basic block from its dag representation is that, from a
dag we can easily see how to rearrange the order of the final computation sequence than we can
starting from a linear sequence of three-address statements or quadruples.