PCD

166
Unit I Introduction a) Source code The form in which a computer program is written by the programmer. Source code is written in some formal programming language which can be compiled automatically into object code or machine code or executed by an interpreter . b) Machine code The representation of a computer program which is actually read and interpreted by the computer. A program in machine code consists of a sequence of machine instructions c) Object code The machine code generated by a source code language processor such as an assembler or compiler . A file of object code may be immediately executable or it may require linking with other object code files, e.g. libraries, to produce a complete executable program. d) Macro A name (possibly followed by a formal argument list) that is equated to a text or symbolic expression to which it is to be expanded (possibly with the substitution of actual arguments ) by a macro expander e) Mnemonic A word or string which is intended to be easier to remember than the thing it stands for. Most often used in "instruction mnemonic " which are so called because they are easier to remember than the binary patterns they stand for. 1

Transcript of PCD

Page 1: PCD

Unit I

Introductiona) Source code

The form in which a computer program is written by the programmer. Source code is written in some formal programming language which can be compiled automatically into object code or machine code or executed by an interpreter.

b) Machine code

The representation of a computer program which is actually read and interpreted by the computer. A program in machine code consists of a sequence of machine instructions

c) Object code

The machine code generated by a source code language processor such as an assembler or compiler. A file of object code may be immediately executable or it may require linking with other object code files, e.g. libraries, to produce a complete executable program.

d) Macro

A name (possibly followed by a formal argument list) that is equated to a text or symbolic expression to which it is to be expanded (possibly with the substitution of actual arguments) by a macro expander

e) Mnemonic

A word or string which is intended to be easier to remember than the thing it stands for. Most often used in "instruction mnemonic" which are so called because they are easier to remember than the binary patterns they stand for.

f) Operation code (Or "op code")

The part or parts of a machine language instruction which determines what kind of action the computer should take, e.g. add, jump, load, store.

g) Instruction set

The collection of machine language instructions that a particular processor understands.

1

Page 2: PCD

h) High-level language

A programming language which provides some level of abstraction above assembly language. These normally use statements consisting of English-like keywords such as "FOR", "PRINT" or "GOTO", where each statement corresponds to several machine language instructions. It is much easier to program in a high-level language than in assembly language though the efficiency of execution depends on how good the compiler or interpreter is at optimising the program

i) Assembly language

A symbolic representation of the machine language of a specific processor. Assembly language is converted to machine code by an assembler. Usually, each line of assembly code produces one machine instruction, though the use of macros is common.

j) Interpreter

A program which executes other programs.

Interpreting code is slower than running the compiled code because the interpreter must analyse each statement in the program each time it is executed and then perform the desired action whereas the compiled code just performs the action. This run-time analysis is known as "interpretive overhead". Access to variables is also slower in an interpreter because the mapping of identifiers to storage locations must be done repeatedly at run time rather than at compile time.

k) Compiler

A program that converts another program from some source language (or programming language) to machine language (object code). Some compilers output assembly language which is then converted to machine language by a separate assembler.

l) Peephole optimisation

A kind of code optimisation that considers only a few adjacent instructions at a time and looks for certain combinations which can be replaced with more efficient sequences. E.g.

1. ADD R0, #12. ADD R0, #1

(add one to register R0) could be replaced by 3. ADD R0, #2

2

Page 3: PCD

m) Register allocation

The phase of a compiler that determines which values will be placed in registers. Register allocation may be combined with register assignment

n) C preprocessor

The standard macro-expansion utility run as the first phase of the C compiler, cc. Cpp interprets lines beginning with "#" such as

1. #define BUFFER_SIZE 256

as a textual assignment giving the symbol BUFFER_SIZE a value "256".preprocessor

o) Execution

The process of carrying out the instructions in a computer program by a computer

p) Grammar

A formal definition of the syntactic structure of a language (see syntax), normally given in terms of production rules which specify the order of constituents and their sub-constituents in a sentence (a well-formed string in the language). Each rule has a left-hand side symbol naming a syntactic category (e.g. "noun-phrase" for a natural language grammar) and a right-hand side which is a sequence of zero or more symbols. Each symbol may be either a terminal symbol or a non-terminal symbol. A terminal symbol corresponds to one "lexeme" - a part of the sentence with no internal syntactic structure (e.g. an identifier or an operator in a computer language). A non-terminal symbol is the left-hand side of some rule.

q) Instruction mnemonic

A word or acronym used in assembly language to represent a binary machine instruction operation code. Different processors have different instruction set and therefore use a different set of mnemonics to represent them.

E.g. ADD, B (branch), BLT (branch if less than), SVC, MOVE, LDR (load register).

r) Lexical analysis

The first stage of processing a language. The stream of characters making up the source program or other input is read one at a time and grouped into lexemes (or "tokens") - word-like pieces such as keywords, identifiers, literals and punctutation. The lexemes are then passed to the parser

3

Page 4: PCD

s) Lexical analyser (Or "scanner")

The initial input stage of a language processor (e.g. a compiler), the part that performs lexical analysis.

t) Parser

An algorithm or program to determine the syntactic structure of a sentence or string of symbols in some language. A parser normally takes as input a sequence of tokens output by a lexical analyser. It may produce some kind of abstract syntax tree as output. One of the best known parser generators is yacc.

u) Parser generator

A program which takes a formal description of a grammar (e.g. in BNF) and outputs source code for a parser which will recognise valid strings obeying that grammar and perform associated actions. Unix's yacc is a well known example

v) Run time

1. The elapsed time to perform a computation on a particular computer. 2. The amount of time a processor actually spent on a particular process and

not on other processes or overhead (see time-sharing).

The period of time during which a program is being executed, as opposed to compile-time or load time.

w) Token

A basic, grammatically indivisible unit of a language such as a keyword, operator or identifier.

The Importance of Compilers

A compiler does not only “translate".It determines whether its input is grammatical and meaningful.Usually it also improves the input program in some signicant way.The optimising compiler is a very intricate piece of software, and its correctness is of vast importance.Compiler front-end technology is important for many diferent software tools, where structured text input has to be handled.

4

Page 5: PCD

( Normally a program written ina high-level programming language)

COMPILERSource program Target program

Error message

Normally the equivalent program in machine code – relocatable object file)

Understanding the operation of a compiler gives better insight into the structure of programming languages.The concept of syntax-directed processing is also of wider importance.

COMPILERS

A compiler is a program takes a program written in a source language and translates it into an equivalent program in a target language.

Other Applications

In addition to the development of a compiler, the techniques used in compiler design can be applicable to many problems in computer science.

Techniques used in a lexical analyzer can be used in text editors, information retrieval system, and pattern recognition programs.

Techniques used in a parser can be used in a query processing system such as SQL.

Many software having a complex front-end may need techniques used in compiler design.

A symbolic equation solver which takes an equation as input. That program should parse the given input equation.

Most of the techniques used in compiler design can be used in Natural Language Processing (NLP) systems.

Major Parts of Compilers

There are two major parts of a compiler: Analysis and Synthesis In analysis phase, an intermediate representation is created from the given source

program. Lexical Analyzer, Syntax Analyzer and Semantic Analyzer are the parts of this

phase. In synthesis phase, the equivalent target program is created from this intermediate

representation. Intermediate Code Generator, Code Generator, and Code Optimizer are the parts

of this phase.

5

Page 6: PCD

Lexical analysis

Syntax analysis

Semantic analysis

Code generation

Code optimisation

Intermediate code generation

Source program

Target program

Phases of compiler

Phases of A Compiler

6

Page 7: PCD

assigstmt

identifier := Expression

Expression newval

identifier

+

oldval 12

Each phase transforms the source program from one representation into another representation.

They communicate with error handlers. They communicate with the symbol table.

Lexical Analyzer

Lexical Analyzer reads the source program character by character and returns the tokens of the source program.

A token describes a pattern of characters having same meaning in the source program. (such as identifiers, operators, keywords, numbers, delimeters and so on)

Ex: newval := oldval + 12 => tokens: o newval identifiero := assignment operatoro oldval identifiero + add operatoro a number

Puts information about identifiers into the symbol table. Regular expressions are used to describe tokens (lexical constructs). A (Deterministic) Finite State Automaton can be used in the implementation of a

lexical analyzer.

Syntax Analyzer

A Syntax Analyzer creates the syntactic structure (generally a parse tree) of the given program.

A syntax analyzer is also called as a parser. A parse tree describes a syntactic structure

Expression

number

7

Page 8: PCD

Syntax Analyzer (CFG)

The syntax of a language is specified by a context free grammar (CFG). The rules in a CFG are mostly recursive. A syntax analyzer checks whether a given program satisfies the rules implied by a

CFG or not. If it satisfies, the syntax analyzer creates a parse tree for the given program.

•Ex: We use productions to specify a CFG

assigstmt -> identifier := expression expression -> identifier expression -> number expression -> expression + expression

Syntax Analyzer versus Lexical Analyzer

Which constructs of a program should be recognized by the lexical analyzer, and which ones by the syntax analyzer?

Both of them do similar things; But the lexical analyzer deals with simple non-recursive constructs of the language.

The syntax analyzer deals with recursive constructs of the language. The lexical analyzer simplifies the job of the syntax analyzer. The lexical analyzer recognizes the smallest meaningful units (tokens) in a source

program. The syntax analyzer works on the smallest meaningful units (tokens) in a source

program to recognize meaningful structures in our programming language.

Parsing Techniques

Depending on how the parse tree is created, there are different parsing techniques. These parsing techniques are categorized into two groups: Top-Down Parsing, Bottom-Up Parsing

Top-Down Parsing:

Construction of the parse tree starts at the root, and proceeds towards the leaves. Efficient top-down parsers can be easily constructed by hand. Recursive Predictive Parsing, Non-Recursive Predictive Parsing (LL Parsing).

Bottom-Up Parsing:

8

Page 9: PCD

Construction of the parse tree starts at the leaves, and proceeds towards the root. Normally efficient bottom-up parsers are created with the help of some software

tools. Bottom-up parsing is also known as shift-reduce parsing. Operator-Precedence Parsing – simple, restrictive, easy to implement LR Parsing – much general form of shift-reduce parsing, LR, SLR, LALR

Semantic Analyzer

A semantic analyzer checks the source program for semantic errors and collects the type information for the code generation.

Type-checking is an important part of semantic analyzer. Normally semantic information cannot be represented by a context-free language

used in syntax analyzers. Context-free grammars used in the syntax analysis are integrated with attributes

(semantic rules) the result is a syntax-directed translation, Attribute grammars Ex: newval := oldval + 12 The type of the identifier newval must match with type of the expression

(oldval+12)

Intermediate Code Generation

A compiler may produce an explicit intermediate codes representing the source program.

These intermediate codes are generally machine (architecture independent). But the level of intermediate codes is close to the level of machine codes.

Ex:newval := oldval * fact + 1id1 := id2 * id3 + 1MULT id2,id3,temp1 Intermediates Codes (Quadraples)ADD temp1,#1,temp2MOV temp2,,id1

Code Optimizer (for Intermediate Code Generator)

The code optimizer optimizes the code produced by the intermediate code generator in the terms of time and space.

Ex:MULT id2,id3,temp1ADD temp1,#1,id1

Code Generator

9

Page 10: PCD

Produces the target language in a specific architecture. The target program is normally is a relocatable object file containing the machine

codes. Ex: ( assume that we have an architecture with instructions whose at least one of its

operands is a machine register)

MOVE id2,R1MULT id3,R1ADD #1,R1MOVE R1,id1

Some software tool for analysis1. Structure Editors:

A structure editor takes as input a sequence of commands to build a source program. The structure editor not only performs the text creation and modification functions of an ordinary text editor, but it also analyzes the program text, putting an appropriate hierarchical structure on the source program. Thus the structure editor can perform additional tasks that are useful in the preparation of programs.

2. Pretty printers:

A pretty printer analyzes a program and prints it in such a way that the structure of the program becomes clearly visible.

3. Static Checkers: A static checker reads a program , analyzes it, and attempts to discover potential bugs without running the program.

4. Interpreters: Instead of producing a target program as translation an interpreter performs the operations implied by the source program for an assignment statement. Interpreter are frequently used to execute command languages , since each operator executed in a command language is usually an invocation of a complex routine such as an editor or compiler.

Analysis portion is similar to Conventional Compiler in

1. Text formatters: A text formatter takes input that is a stream of characters ,most of which is text to be typeset, but some of which includes commands to indicate paragraphs , figures, or mathematical structures like subscripts and superscripts.

2. Silicon Compilers: A Silicon compiler has a source language that is similar or identical to a conventional programming language. However, the variables of the language represent , not locations

10

Page 11: PCD

in memory, but, logical signals(0 or 1) or groups of signals in a switching circuit. The output is a circuit design in an appropriate language.

3. Query interpreters: A query interpreter translates a predicate containing relational and Boolean operators into commands to search a database for records satisfying that predicate.

Compiler construction tools

Compiler writing systems are often called as compiler- compiler, compiler generators, or translator writing systems.Lexical analyzers for all the languages are same, except for the particular keywords. Some general tools have been created for the automatic design of specific compiler components. These tools use specialized languages for specifying and implementing the component. The following are some useful compiler construction tools.

1. Parser generators2. Scanner generators3. Syntax directed translation engines4. Automatic code generators5. Data flow engines

1. Parser generator

Parser generators produce syntax analyzer from input that is based on the context free grammar. Syntax analysis consumed a large fraction of the running time of a compiler. Also syntax analysis consumed a large fraction of the intellectual effect of writing a compiler. Many parser generators utilize powerful parsing algorithms that are too complex to be carried out by hand.

2. Scanner generators

Scanner generators automatically generate lexical analyzers, normally from a specification based on the regular expression. The result of the lexical analyzer is based on the finite automata

3. Syntax directed translation engines

Syntax directed translation engines produce collections of routines that walk the parse tree and produces intermediate code. Translations are associated with each node of the parse tree. Each translation is defined in terms of translations at its neighbor nodes of the tree.

4. Automatic code generators

11

Page 12: PCD

Automatic code generators take a collection of rules that define the translation of each operation of the intermediate language into the machine language fore the target machine. The rules should have sufficient details to handle the different possible access methods for data.

E.g. variables may be in the registers, or location in memory or allocated a position on a stack.The intermediate code statement are replaced by the template that represent sequence of the machine instruction in such a way that the assumption about the storage of variable match from template to template. This technique is called template matching

5. Data flow engine

Data flow engine helps in data flow analysis. Data flow analysis is needed to perform good code optimisation. It also helps us to know how values are transmitted from one part of a program to other part. The user has to supply detail relation ship between intermediate code statements. The information is gathered and the data analysis is done.

Token specification

Alphabet : a finite set of symbols (ASCII characters)

String : Finite sequence of symbols on an alphabet Sentence and word are also used in terms of string is the empty string |s| is the length of string s.

Language: sets of strings over some fixed alphabet the empty set is a language. {} the set containing empty string is a language The set of well-wormed C programs is a language The set of all possible identifiers is a language.

Operators on Strings:

Concatenation: xy represents the concatenation of strings x and y. s = s, s =s (Exponentiation) sn = s s s .. s ( n times) s0 =

Parts of string:

prefix of s : a string abtained by removing zero or more trailing symbols of the string s; eg. Com is a prefix of Computer

12

Page 13: PCD

suffix of s : a string abtained by removing zero or more leading symbols of the string s; eg. puter is a suffix of Computer

sub string of s: a string obtained by deleting the prefix and suffix from the string s. eg put in computer

proper prefix, suffix or substring of s: any non empty string x that is, respectively , a prefix , suffix, or substring of s suct that sx.

Operations on Languages

•Concatenation:o L1L2 = { s1s2 | s1 L1 and s2 L2 }

•Uniono L1 L2 = { s | s L1 or s L2 }

•Exponentiation:o L0 = {} L1 = L L2 = LL

•Kleene Closureo –L* = zero or more occurance

•Positive Closure

o L+ = one or more occurance

Example

L1 = {a,b,c,d} L2 = {1,2} L1L2 = {a1,a2,b1,b2,c1,c2,d1,d2} L1 È L2 = {a,b,c,d,1,2} L1

3 = all strings with length three (using a,b,c,d}

L1* = all strings using letters a,b,c,d and empty string

L1+ = doesn’t include the empty string

Regular Expressions

We use regular expressions to describe tokens of a programming language. A regular expression is built up of simpler regular expressions (using defining

rules) Each regular expression denotes a language. A language denoted by a regular expression is called as a regular set.

Regular Expressions (Rules)

Regular expressions over alphabet S

Reg. Expr Language it denotes

13

Page 14: PCD

{}a S {a}(r1) | (r2) L(r1) L(r2)(r1) (r2) L(r1) L(r2)(r)* (L(r))*

(r) L(r)

(r)+ = (r)(r)*

(r)? = (r) |

We may remove parentheses by using precedence rules.

o * highesto concatenation nexto | lowest

ab*|c means (a(b)*)|(c)

Examples:

S = {0,1} 0|1 => {0,1} (0|1)(0|1) => {00,01,10,11} 0* => { ,0,00,000,0000,....} (0|1)* => all strings with 0 and 1, including the empty string

Regular Definitions

To write regular expression for some languages can be difficult, because their regular expressions can be quite complex. In those cases, we may use regular definitions.

We can give names to regular expressions, and we can use these names as symbols to define other regular expressions.

A regular definition is a sequence of the definitions of the form:d1 r1 where di is a distinct name andd2 r2 ri is a regular expression over symbols in

. S{d1,d2,...,di-1}

14

Page 15: PCD

dn rn basic symbols previously defined names

Ex:Identifiers in Pascal

letter A | B | ... | Z | a | b | ... | z digit 0 | 1 | ... | 9 id letter (letter | digit ) *

If we try to write the regular expression representing identifiers without using regular definitions, that regular expression will be complex.

o (A|...|Z|a|...|z) ( (A|...|Z|a|...|z) | (0|...|9) ) *•

Ex: Unsigned numbers in Pascal

digit 0 | 1 | ... | 9 digits digit +opt-fraction ( . digits ) ? opt-exponent ( E (+|-)? digits ) ? unsigned-num digits opt-fraction opt-exponent

Recognition machine

Finite Automata

A recognizer for a language is a program that takes a string x, and answers “yes” if x is a sentence of that language, and “no” otherwise.

•We call the recognizer of the tokens as a finite automaton. A finite automaton can be: deterministic(DFA) or non-deterministic (NFA) This means that we may use a deterministic or non-deterministic automaton as a

lexical analyzer. Both deterministic and non-deterministic finite automaton recognize regular sets.

Which one? (deterministic or non-deterministic finite automaton) deterministic – faster recognizer, but it may take more space non-deterministic – slower, but it may take less space Deterministic automatons are widely used lexical analyzers. First, we define regular expressions for tokens; Then we convert them into a DFA

to get a lexical analyzer for our tokens.

Recogniserx Yes or no

15

Page 16: PCD

0 1

a

b

a b

start

–Algorithm1: Regular Expression NFA DFA (two steps: first to NFA, then to DFA)

–Algorithm2: Regular Expression DFA (directly convert a regular expression into a DFA)

Non-Deterministic Finite Automaton (NFA)

A non-deterministic finite automaton (NFA) is a mathematical model that consists of:

o Q - a set of states o - a set of input symbols (alphabet)o move – a transition function move to map state-symbol pairs to sets of

states.o q0 - a start (initial) stateo F – a set of accepting states (final states)

- transitions are allowed in NFAs. In other words, we can move from one state to another one without consuming any symbol.

A NFA accepts a string x, if and only if there is a path from the starting state to one of accepting states such that edge labels along this path spell out x.

NFA (Example)

Five tuples

0 is the start state q0

{2} is the set of final states F = {a,b}

Q = {0,1,2}Transition Function :

a b0 {0,1} {0}1 _ {2}2 _ _

16

Page 17: PCD

0 1

ab

a bstart

a

The language recognized by this NFA is (a|b) * a b

Deterministic Finite Automaton (DFA)

A deterministic finite automaton (DFA) is a mathematical model that consists of:o Q - a set of states o - a set of input symbols (alphabet)o move – a transition function is from pair of state-symbol to state (not set

of states).o q0 - a start (initial) stateo F – a set of accepting states (final states

A Deterministic Finite Automaton (DFA) is a special form of a NFA.

no state has - transition for each symbol a and state s, there is at most one labeled edge a leaving s.

The language recognized by this DFA is also (a|b) * a b

Implementing a DFA

Let us assume that the end of a string is marked with a special symbol (say eos). The algorithm for recognition will be as follows: (an efficient implementation)

0 is the start state q0

{2} is the set of final states F = {a,b}

Q = {0,1,2}Transition Function :

a b0 {0,1} {0}1 _ {2}2 _ _

b

17

Page 18: PCD

•s q0 { start from the initial state }c nextchar { get the next character from the input string }while (c != eos) do { do until the en dof the string }

begin s move(s,c) { transition function } c nextchar

endif (s in F) then { if s is an accepting state }

return “yes”else

return “no”

Implementing a NFA

S e-closure({q0}) { set all of states can be accessible from q0 by -transitions }c nextcharwhile (c != eos) {

begin s e-closure(move(Q,c)){ set of all states can be accessible from a state in Q c nextchar by a transition on c }end

if (QF != ) then { if Q contains an accepting state }return “yes”

elsereturn “no”

This algorithm is not efficient.

Converting A Regular Expression into A NFA (Thomson’s Construction)

This is one way to convert a regular expression into a NFA. There can be other ways (much efficient) for the conversion. Thomson’s Construction is simple and systematic method. It guarantees that the resulting NFA will have exactly one final state, and one start

state. Construction starts from simplest parts (alphabet symbols). To create a NFA for a complex regular expression, NFAs of its sub-expressions

are combined to create its NFA,

To recognize an empty string

18

Page 19: PCD

fi

N(r2)

N(r1)

fi

To recognize a symbol a in the alphabet

If N(r1) and N(r2) are NFAs for regular expressions r1 and r2

For regular expression r1 | r2

NFA for r1 | r2

For regular expression r1 r2

Final state of N(r2) become final state of N(r1r2)

For regular expression r*

19

Page 20: PCD

bb:

(a | b)

a

b

b

a

b

a

a(a|b) * a

NFA for r*

(Example - (a|b) * a )

(a|b) *

20

Page 21: PCD

b

a

a0 1

3

4 5

2

7 86

Converting a NFA into a DFA (subset construction)

put -closure({q0}) as an unmarked state into the set of DFA (DS)while (there is one unmarked Q1 in DS) do

begin mark S1 for each input symbol a do begin Q2 -closure(move(Q1,a)) if (Q2 is not in DS) then

add Q2 into DS as an unmarked state transfunc[Q1,a] Q2

endend

•a state Q in DS is an accepting state of DFA if a state in S is an accepting state of NFA•the start state of DFA is -closure({q0})

Converting a NFA into a DFA (Example)

.

Q0 = -closure({0}) = {0,1,2,4,7} Q0 into DS as an unmarked state mark Q0

-closure(move(Q0,a)) = -closure({3,8}) = {1,2,3,4,6,7,8} = Q1 Q1 into DS-closure(move(S0,b)) = -closure({5}) = {1,2,4,5,6,7} = Q2 Q2 into DS

transfunc[Q0,a] Q1 transfunc[Q0,b] S2 mark Q1

-closure(move(Q1,a)) = -closure({3,8}) = {1,2,3,4,6,7,8} = Q1 -closure(move(S1,b)) = -closure({5}) = {1,2,4,5,6,7} = Q2

set of states to which there is a transition on a from a state s in Q1

-closure({q0}) is the set of all states can be accessible from q0 by -transition

21

Page 22: PCD

b

a

b

b

a

S1

S2

S0

*

|

a

#4

3

transfunc[Q1,a] S1 transfunc[Q1,b] S2

mark Q2-closure(move(Q2,a)) = -closure({3,8}) = {1,2,3,4,6,7,8} = Q1 -closure(move(Q2,b)) = -closure({5}) = {1,2,4,5,6,7} = Q2

transfunc[Q2,a] Q1 transfunc[Q2,b] Q2

Q0 is the start state of DFA since 0 is a member of Q0={0,1,2,4,7}Q1 is an accepting state of DFA since 8 is a member of Q1 = {1,2,3,4,6,7,8}

Converting Regular Expressions Directly to DFAs

We may convert a regular expression into a DFA (without creating a NFA first). First we augment the given regular expression by concatenating it with a special

symbol #. r (r)# augmented regular expression

Then, we create a syntax tree for this augmented regular expression. In this syntax tree, all alphabet symbols (plus # and the empty string) in the

augmented regular expression will be on the leaves, and all inner nodes will be the operators in that augmented regular expression.

Then each alphabet symbol (plus #) will be numbered (position numbers).

(a|b) * a (a|b) * a # augmented regular expression

Syntax tree of (a|b) * a #

• each symbol is numbered (positions)• each symbol is at a leave

• inner nodes are operators22

Page 23: PCD

followpos

Then we define the function followpos for the positions (positions assigned to leaves).

followpos(i) -- is the set of positions which can follow the position i in the strings generated by the augmented regular expression.

For example, ( a | b) * a # 1 2 3 4

followpos(1) = {1,2,3}followpos(2) = {1,2,3}followpos(3) = {4}followpos(4) = {}

firstpos, lastpos, nullable

•o evaluate followpos, we need three more functions to be defined for the nodes (not just for leaves) of the syntax tree.

•firstpos(n)

the set of the positions of the first symbols of strings generated by the sub-expression rooted by n.

•lastpos(n)

the set of the positions of the last symbols of strings generated by the sub-expression rooted by n.

•nullable(n)

true if the empty string is a member of strings generated by the sub-expression rooted by n false otherwise

Syntax tree of (a|b) * a #

• each symbol is numbered (positions)• each symbol is at a leave

• inner nodes are operators

23

Page 24: PCD

*

|

a

#4

3

{1,2,3}

{3}

{1,2,3}

{1,2}

{1,2}

{4}

{4}

{4}{3}

{3}{1,2}

{1,2}

How to evaluate firstpos, lastpos, nullable

n nullable(n) firstpos(n) lastpos(n)leaf labeled true leaf labeled with position i

false {i} {i}

|

c1 c2

nullable(c1) or nullable(c2)

firstpos(c1) firstpos(c2)

lastpos(c1) lastpos(c2)

c1 c2

nullable(c1) and nullable(c2)

if (nullable(c1)) firstpos(c1) firstpos(c2)else firstpos(c1)

if (nullable(c2)) lastpos(c1) lastpos(c2)else lastpos(c2)

* c1

true firstpos(c1) lastpos(c1)

How to evaluate followpos

•Two-rules define the function followpos:

If n is concatenation-node with left child c1 and right child c2, and i is a position in lastpos(c1), then all positions in firstpos(c2) are in followpos(i).

If n is a star-node, and i is a position in lastpos(n), then all positions in firstpos(n) are in followpos(i).

If firstpos and lastpos have been computed for each node, followpos of each position can be computed by making one depth-first traversal of the syntax tree.

Example -- ( a | b) * a #

green – firstposblue – lastpos

Then we can calculate followpos

followpos(1) = {1,2,3}followpos(2) = {1,2,3}followpos(3) = {4}followpos(4) = {}

24

Page 25: PCD

After we calculate follow positions, we are ready to create DFA for the regular expression

Algorithm (RE DFA)

Create the syntax tree of (r) #Calculate the functions: followpos, firstpos, lastpos, nullablePut firstpos(root) into the states of DFA as an unmarked state.while (there is an unmarked state S in the states of DFA) do

mark Sfor each input symbol a do

let s1,...,sn are positions in S and symbols in those positions are a

S’ followpos(s1) ... followpos(sn)move(S,a) S’ if (S’ is not empty and not in the states of DFA)

put S’ into the states of DFA as an unmarked state.

the start state of DFA is firstpos(root)the accepting states of DFA are all states containing the position of #

Example -- ( a | b) * a # 1 2 3 4

followpos(1)={1,2,3} followpos(2)={1,2,3} followpos(3)={4} followpos(4)={}

S1=firstpos(root)={1,2,3} mark S1

a: followpos(1) followpos(3)={1,2,3,4}=S2 move(S1,a)=S2b: followpos(2)={1,2,3}=S1 move(S1,b)=S1

mark S2a: followpos(1) followpos(3)={1,2,3,4}=S2 move(S2,a)=S2

Then we can calculate followpos

followpos(1) = {1,2,3}followpos(2) = {1,2,3}followpos(3) = {4}followpos(4) = {}

25

Page 26: PCD

S1 S2a

b

b

a

S3

S2

S1

c

ab

b

b: followpos(2)={1,2,3}=S1 move(S2,b)=S1

start state: S1accepting states: {S2}

Example -- ( a | ) b c* #1 2 3 4

followpos(1)={2} followpos(2)={3,4} followpos(3)={3,4} followpos(4)={}S1=firstpos(root)={1,2}

mark S1a: followpos(1)={2}=S2 move(S1,a)=S2b: followpos(2)={3,4}=S3 move(S1,b)=S3

mark S2b: followpos(2)={3,4}=S3 move(S2,b)=S3

mark S3c: followpos(3)={3,4}=S3 move(S3,c)=S3

start state: S1accepting states: {S3}

Minimizing Number of States of a DFA

partition the set of states into two groups: G1 : set of accepting states G2 : set of non-accepting states

•For each new group G

26

Page 27: PCD

b a

a

a

b

b

3

2

1

{1,3}

a

a

b

b

{2}

– partition G into subgroups such that states s1 and s2 are in the same group iff

– for all input symbols a, states s1 and s2 have transitions to states in the same group.

Start state of the minimized DFA is the group containing the start state of the original DFA.

Accepting states of the minimized DFA are the groups containing the accepting states of the original DFA.

Minimizing DFA – Example

So, the minimized DFA (with minimum states)

Some Other Issues in Lexical Analyzer

The lexical analyzer has to recognize the longest possible string. Ex: identifier newval -- n ne new newv newva newval

•What is the end of a token? Is there any character which marks the end of a token?

G1 = {2}G2 = {1,3}

G2 cannot be partitioned becausemove(1,a)=2 move(1,b)=3move(3,a)=2 move(2,b)=3

27

Page 28: PCD

It is normally not defined. If the number of characters in a token is fixed, in that case no problem: + - But < < or <> (in Pascal) The end of an identifier : the characters cannot be in an identifier can mark the

end of token. We may need a lookhead In Prolog: p :- X is 1. p :- X is 1.5. The dot followed by a white space character can mark the end of a number.

But if that is not the case, the dot must be treated as a part of the number

Skipping comments Normally we don’t return a comment as a token. We skip a comment, and return the next token (which is not a comment) to the

parser. So, the comments are only processed by the lexical analyzer, and the don’t

complicate the syntax of the language.

Symbol table interface symbol table holds information about tokens (at least lexeme of identifiers) how to implement the symbol table, and what kind of operations.

•hash table – open addressing, chaining •putting into the hash table, finding the position of a token from its

lexeme.

Positions of the tokens in the file (for the error handling).

Error Recovery Techniques

Panic-Mode Error Recovery Skipping the input symbols until a synchronizing token is found.

Phrase-Level Error Recovery

Each empty entry in the parsing table is filled with a pointer to a specific error routine to take care that error case.

Error-Productions

If we have a good idea of the common errors that might be encountered, we can augment the grammar with productions that generate erroneous constructs.

When an error production is used by the parser, we can generate appropriate error diagnostics.

28

Page 29: PCD

LEX COMPILER

Lex source pgm lex.lLex.yy.c

Since it is almost impossible to know all the errors that can be made by the programmers, this method is not practical.

Global-Correction

Ideally, we we would like a compiler to make as few change as possible in processing incorrect inputs.

We have to globally analyze the input to find the error. This is an expensive method, and it is not in practice.

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 that unmatched terminal is inserted.

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.

A typical lexical analyzer generator

A language for specifying lexical analyzers

Lex is a tool widely used to specify lexical analyzers.

29

Page 30: PCD

C COMPILERlex.yy.c

a.out

a.outInput stream

Sequence of token

Lex specification

A Lex program consists of three partsDeclarations%%translation%%auxicillary procedures

Declaration

Variables Manifest constants: an identifier that is declared to represent a constant Regular definition:

To write regular expression for some languages can be difficult, because their regular expressions can be quite complex. In those cases, we may use regular definitions.

We can give names to regular expressions, and we can use these names as symbols to define other regular expressions.

•A regular definition is a sequence of the definitions of the form: d1 r1 where di is a distinct name and

30

Page 31: PCD

d2 r2 where ri is a regular expression over symbols in {d1,d2,...,di-1}

dn rn

Translation rules

The translation rules of the Lex program are statements of the form

p1 {action1}p2 {action2}.. …pn {actionn}

Where pi is a regular expression and Each actioni} is program fragment describing what action the lexical analyzer should take when pattern matches the lexeme

Auxicillary procedures:

Needed by the actionCompiled separately and loaded with the lexical analyzer.

Example

%{/* Definition of manifest constants LT, LE, EQ,NE,GT,GE,IF,THEN, ELSE, ID, NUMBER, RELOP*/}%/*regular definition*/delim { \t\n}ws {delim}+letter {A-Za-z}digit {0-9}id {letter}({letter}|{digit})*number {digit}+(\.{digit}+)?(E[+\-]?{digit}+)?%%{ws} {/*no action and noreturn*/}if {return (IF);}then {return (THEN);}else {return (ELSE);}{id} {yyval=install_id(); return (ID);}{number} {yyval=install_num (); return (NUMBER);}“<” {yyval = LT; return (RELOP);}“<=” {yyval = LE; return (RELOP);}“=” {yyval = EQ; return (RELOP);}“<>” {yyval = NE; return (RELOP);}

31

Page 32: PCD

LEX COMPILERLex specification

Transition table

Lexeme

FASimulator

Transition table

Input buffer

“>” {yyval = GT; return (RELOP);}“>=” {yyval = GE; return (RELOP);}%%INSTALL_ID(){/* Procedure to install the lexemes, whose first character is pointed to by yytext and whose length is yyleng, into the symbol table and return a pointer there to */}install_num(){Similar procedure to install a lexeme that is a number */}

Design of a lexical analyzer generator

The specification of the lexical analyzer of the form

p1 {action1}p2 {action2}.. …pn {actionn}

where pi ia a regular expression and Each actioni} is program fragment describing what action the lexical analyzer should take when pattern matches the lexeme

Our problem is to construct a recognizer that looks for the lexemes in the input buffer. If more than one matches, the recognizer is to choose the longest lexeme matched.

32

Page 33: PCD

S0

N(p1)

N(pn)

N(p2)

A finite automaton is natural model around which to build a lexical analyzer There are two pointer to input buffer (beginning and forward pointer)The output of Lex compiler is transition tableThe lexical analyzer consists of a finite automaton simulator that uses this tranition table to look for the regular expression patterns in the input buffer.

Pattern matching based on NFA’s

Construct the transitition table of a NFA N for the composite pattern p1|p2|..|pn

create NFA N(pi) for each pi .Add a new state S0.Link S0 to the start state of each pattern N(pi) with a transitition.

……….

NFA recognizes the longest prefix of the input string that is matched by a pattern Simulate the NFA using the algorithm Construct the sequence of sets of state that the combined NFA can be in after

seeing each input symbol We must continue to simulate NFA until it reaches termination.

Example

a {}

33

Page 34: PCD

1 2

3 5 6 4

7 8

a

a b b

b

ba

1 2

3 5 6 4

a

a b b

0

abb {}a*b+ {}

NFA for a, abb, a*b+

34

Page 35: PCD

Nfa recognizing three different patterns

Processing string aab a a b

0137 247 7 8

p1 p3

Matched pattern: a, , a*b+

Longest matched pattern: , a*b+

DFA simulator for lexical analyzer can do this.

Syntax Analyzer

Syntax Analyzer creates the syntactic structure of the given source program. This syntactic structure is mostly a parse tree. Syntax Analyzer is also known as parser. The syntax of a programming is described by a context-free grammar (CFG). We

will use BNF (Backus-Naur Form) notation in the description of CFGs.

35

Page 36: PCD

ParserLexical Analyzer

Source

program

token

Get next token

The syntax analyzer (parser) checks whether a given source program satisfies the rules implied by a context-free grammar or not.

If it satisfies, the parser creates the parse tree of that program. Otherwise the parser gives the error messages.

A context-free grammar

gives a precise syntactic specification of a programming language.the design of the grammar is an initial phase of the design of a compiler.–a grammar can be directly converted into a parser by some tools.

Parser

Parser works on a stream of tokens.

The smallest item is a token.

We categorize the parsers into two groups:

1.Top-Down Parser–the parse tree is created top to bottom, starting from the root.

2.Bottom-Up Parser–the parse is created bottom to top; starting from the leaves

•Both top-down and bottom-up parsers scan the input from left to right (one symbol at a time). •Efficient top-down and bottom-up parsers can be implemented only for sub-classes of context-free grammars.

LL for top-down parsing LR for bottom-up parsing

Context-Free Grammars

Inherently recursive structures of a programming language are defined by a context-free grammar.

In a context-free grammar, we have:

A finite set of terminals (in our case, this will be the set of tokens)

parse

tree

36

Page 37: PCD

A finite set of non-terminals (syntactic-variables) A finite set of productions rules in the following form A a where A is a non-terminal and a is a string of terminals and non-

terminals (including the empty string) A start symbol (one of the non-terminal symbol)

••Example:E E + E | E – E | E * E | E / E | - EE ( E )E id

Derivations

E E+E•E+E derives from E

–we can replace E by E+E–to able to do this, we have to have a production rule EE+E in our grammar.

•E E+E id+E id+id•A sequence of replacements of non-terminal symbols is called a derivation of id+id from E.

•In general a derivation step isA

if there is a production rule A in our grammar where and are arbitrary strings of terminal and non-terminal symbols

1 2 ... n (n derives from 1 or 1 derives n )

: derives in one step*

: derives in zero or more steps+

: derives in one or more steps

CFG – Terminology

L(G) is the language of G (the language generated by G) which is a set of sentences.

A sentence of L(G) is a string of terminal symbols of G.

37

Page 38: PCD

If S is the start symbol of G then w is a sentence of L(G) iff S where is a string of terminals of G.

If G is a context-free grammar, L(G) is a context-free language. Two grammars are equivalent if they produce the same language.

S - If contains non-terminals, it is called as a sentential form of G.- If does not contain non-terminals, it is called as a sentence of G.

Derivation Example

E -E -(E) -(E+E) -(id+E) -(id+id)OR

E -E -(E) -(E+E) -(E+id) -(id+id)

•At each derivation step, we can choose any of the non-terminal in the sentential form of G for the replacement.

If we always choose the left-most non-terminal in each derivation step, this derivation is called as left-most derivation.

If we always choose the right-most non-terminal in each derivation step, this derivation is called as right-most derivation.

Left-Most and Right-Most Derivations

Left-Most DerivationE -E -(E) -(E+E) -(id+E) -(id+id) lm lm lm lm lm

Right-Most Derivation

E -E -(E) -(E+E) -(E+id) -(id+id) rm rm rm rm rm

•We will see that the top-down parsers try to find the left-most derivation of the given source program.•We will see that the bottom-up parsers try to find the right-most derivation of the given source program in the reverse order.

Parse Tree

Inner nodes of a parse tree are non-terminal symbols. The leaves of a parse tree are terminal symbols.

38

Page 39: PCD

E

E

E-

( )

-(E)E -E E

E-

E

E

E

EE +

-

( )

id

-(id+E)

E

E

EE

E

+

-

( )

-(E+E)

E

E

id

E

E

E +

-

( )

id

-(id+id)

A parse tree can be seen as a graphical representation of a derivation

Ambiguity

A grammar produces more than one parse tree for a sentence is called as an ambiguous grammar.

id + E

id + id

39

Page 40: PCD

E

E +

id E

E

* E

id id

E

id

E +

id

id

E

E

* E

E E+E id+E id+E*E id+id*E id+id*id

E E*E E+E*E id+E*E id+id*E id+id*id

For the most parsers, the grammar must be unambiguous.

Unambiguous grammar unique selection of the parse tree for a sentence

We should eliminate the ambiguity in the grammar during the design phase of the compiler.An ambiguous grammar should be written to eliminate the ambiguity.We have to prefer one of the parse trees of a sentence (generated by an ambiguous grammar) to disambiguate that grammar to restrict to this choice.

stmt if expr then stmt | if expr then stmt else stmt | otherstmts

if E1 then if E2 then S1 else S2

40

Page 41: PCD

stmt

if expr then stmt else stmt

E1 if expr then stmt S2

E2 S1

stmt

if expr then stmt

E1 if expr then stmt else stmt

E2 S1 S2

2

We prefer the second parse tree (else matches with closest if). So, we have to disambiguate our grammar to reflect this choice.

The unambiguous grammar will be:

stmt matchedstmt | unmatchedstmtmatchedstmt if expr then matchedstmt else matchedstmt | otherstmtsunmatchedstmt if expr then stmt | if expr then matchedstmt else unmatchedstmt

Ambiguity – Operator Precedence

1

41

Page 42: PCD

Ambiguous grammars (because of ambiguous operators) can be disambiguated according to the precedence and associativity rules.

E E+E | E*E | E^E | id | (E) disambiguate the grammar

precedence: ^ (right to left)* (left to right)+ (left to right)

E E+T | TT T*F | FF G^F | GG id | (E)

Left Recursion

A grammar is left recursive if it has a non-terminal A such that there is a derivation.

o +

o A A for some string

Top-down parsing techniques cannot handle left-recursive grammars. So, we have to convert our left-recursive grammar into an equivalent grammar

which is not left-recursive. The left-recursion may appear in a single step of the derivation (immediate left-

recursion), or may appear in more than one step of the derivation.

Immediate Left-Recursion

A A | where does not start with A eliminate immediate left recursion

A A’

A’ A’ | an equivalent grammar

In general,

A A 1 | ... | A m | 1 | ... | n where 1 ... n do not start with A

eliminate immediate left recursionA 1 A’ | ... | n A’

A’ 1 A’ | ... | m A’ | an equivalent grammar

Immediate Left-Recursion – Example

42

Page 43: PCD

E E+T | TT T*F | FF id | (E)

eliminate immediate left recursion

E T E’

E’ +T E’ | T F T’

T’ *F T’ | F id | (E)

Left-Recursion – Problem

A grammar cannot be immediately left-recursive, but it still can be left-recursive. By just eliminating the immediate left-recursion, we may not get a grammar which is not left-recursive.

S Aa | bA Sc | d This grammar is not immediately left-recursive,

but it is still left-recursive.

S Aa Sca orA Sc Aac causes to a left-recursion

So, we have to eliminate all left-recursions from our grammar

Eliminate Left-Recursion – Algorithm

Arrange non-terminals in some order: A1 ... An

- for i from 1 to n do { - for j from 1 to i-1 do {

replace each production Ai Aj by Ai 1 | ... | k where Aj 1 | ... | k

}- eliminate immediate left-recursions among Ai productions

}

Eliminate Left-Recursion – Example

43

Page 44: PCD

S Aa | bA Ac | Sd | f- Order of non-terminals: S, Afor S:

- we do not enter the inner loop.- there is no immediate left recursion in S.

for A:- Replace A Sd with A Aad | bd So, we will have A Ac | Aad | bd | f- Eliminate the immediate left-recursion in A

A bdA’ | fA’ A’ cA’ | adA’ |

So, the resulting equivalent grammar which is not left-recursive is:S Aa | bA bdA’ | fA’

A’ cA’ | adA’ |

Eliminate Left-Recursion – Example2

S Aa | bA Ac | Sd | f- Order of non-terminals: A, Sfor A:

- we do not enter the inner loop.- Eliminate the immediate left-recursion in A

A SdA’ | fA’ A’ cA’ |

for S:- Replace S Aa with S SdA’a | fA’a So, we will have S SdA’a | fA’a | b - Eliminate the immediate left-recursion in S

S fA’aS’ | bS’ S’ dA’aS’ |

So, the resulting equivalent grammar which is not left-recursive is:S fA’aS’ | bS’

S’ dA’aS’ | A SdA’ | fA’

A’ cA’ |

Left-Factoring

44

Page 45: PCD

A predictive parser (a top-down parser without backtracking) insists that the grammar must be left-factored.

grammar a new equivalent grammar suitable for predictive parsing

stmt if expr then stmt else stmt | if expr then stmt

•when we see if, we cannot now which production rule to choose to re-write stmt in the derivation.

In general, •A 1 | 2 where is non-empty and the first symbols of 1 and 2 (if they

have one)are different. when processing a we cannot know whether expand

A to 1 or A to 2

But, if we re-write the grammar as follows

A A’

A’ 1 | 2 so, we can immediately expand A to A’

Left-Factoring -- Algorithm

For each non-terminal A with two or more alternatives (production rules) with a common non-empty prefix, let say

•A 1 | ... | n | 1 | ... | m

convert it into

A A’ | 1 | ... | m A’ 1 | ... | n

Left-Factoring – Example1

45

Page 46: PCD

A abB | aB | cdg | cdeB | cdfB

A aA’ | cdg | cdeB | cdfBA’bB | B

A aA’ | cdA’’

A’ bB | BA’’ g | eB | fB

Left-Factoring – Example2

A ad | a | ab | abc | b

A aA’ | bA’ d | e | b | bc

A aA’ | bA’ d | e | bA’’A’’ e | c

Non-Context Free Language Constructs

There are some language constructions in the programming languages which are not context-free. This means that, we cannot write a context-free grammar for these constructions.

L1 = { c | is in (a|b)*} is not context-free declaring an identifier and checking whether it is declared or not later.

We cannot do this with a context-free language. We need semantic analyzer (which is not context-free).

L2 = {anbmcndm | n1 and m1 } is not context-free declaring two functions (one with n parameters, the other one with

m parameters), and then calling them with actual parameters.•

Top-Down Parsing

46

Page 47: PCD

The parse tree is created top to bottom.Top-down parser

–Recursive-Descent Parsing

Backtracking is needed (If a choice of a production rule does not work, we backtrack to try other alternatives.)

It is a general parsing technique, but not widely used. Not efficient

–Predictive Parsing no backtracking efficient needs a special form of grammars (LL(1) grammars). Recursive Predictive Parsing is a special form of Recursive Descent parsing

without backtracking. Non-Recursive (Table Driven) Predictive Parser is also known as LL(1) parser.

Recursive-Descent Parsing (uses Backtracking)

S aBcB bc | binput: abc

Predictive Parser

a grammar a grammar suitable for predictive eliminate left parsing (a LL(1) grammar) left recursion factor no %100 guarantee.

When re-writing a non-terminal in a derivation step, a predictive parser can uniquely choose a production rule by just looking the current symbol in the input string.

47

Page 48: PCD

A 1 | ... | n input: ... a .......

current token

stmt if ...... |while ...... |begin ...... |for .....

When we are trying to write the non-terminal stmt, if the current token is if we have to choose first production rule.

When we are trying to write the non-terminal stmt, we can uniquely choose the production rule by just looking the current token.

We eliminate the left recursion in the grammar, and left factor it. But it may not be suitable for predictive parsing (not LL(1) grammar).

Recursive Predictive Parsing

Each non-terminal corresponds to a procedure.Ex: A aBb (This is only the production rule for A)

proc A { - match the current token with a, and move to the next token; - call ‘B’; - match the current token with b, and move to the next token;}

A aBb | bAB

proc A {case of the current token {

‘a’: - match the current token with a, and move to the next token; - call ‘B’; - match the current token with b, and move to the next token;‘b’: - match the current token with b, and move to the next token; - call ‘A’;

- call ‘B’;}

}

When to apply -productions.

A aA | bB | e

48

Page 49: PCD

If all other productions fail, we should apply an -production. For example, if the current token is not a or b, we may apply the -production.

Most correct choice: We should apply an -production for a non-terminal A when the current token is in the follow set of A (which terminals can follow A in the sentential forms).

Recursive Predictive Parsing (Example)

A aBe | cBd | CB bB | eC f

proc C {

match the current token with f, and move to the next token; }

proc A {

case of the current token {

a: - match the current token with a, and move to the next token; - call B;

- match the current token with e, and move to the next token; c: - match the current token with c, and move to the next token;

- call B;- match the current token with d, and move to the next token;

f: - call C}

}

proc B {

case of the current token {

b: - match the current token with b, and move to the next token;- call B;

e,d: do nothing }

f- first set of Ce,d – follow set of B

Non-Recursive Predictive Parsing -- LL(1) Parser

Non-Recursive predictive parsing is a table-driven parser.

49

Page 50: PCD

It is a top-down parser. It is also known as LL(1) Parser.

input buffer

stack Non-recursive outputPredictive Parser

Parsing Table

LL(1) Parserinput buffer

our string to be parsed. We will assume that its end is marked with a special symbol $.

output a production rule representing a step of the derivation sequence (left-most

derivation) of the string in the input buffer.stack–contains the grammar symbols

at the bottom of the stack, there is a special end marker symbol $. initially the stack contains only the symbol $ and the starting symbol S. $S initial stack when the stack is emptied (ie. only $ left in the stack), the parsing is completed.

•parsing table a two-dimensional array M[A,a] each row is a non-terminal symbol each column is a terminal symbol or the special symbol $ each entry holds a production rule.

LL(1) Parser – Parser Actions

The symbol at the top of the stack (say X) and the current symbol in the input string (say a) determine the parser action.

There are four possible parser actions. 1. If X and a are $

parser halts (successful completion) 2. If X and a are the same terminal symbol (different from $)

parser pops X from the stack, and moves the next symbol in the input buffer.

3. If X is a non-terminal parser looks at the parsing table entry M[X,a]. If M[X,a] holds a

production rule XY1Y2...Yk, it pops X from the stack and pushes Yk,Yk-1,...,Y1 into the stack. The parser also outputs the production rule XY1Y2...Yk to represent a step of the derivation.

4. none of the above

50

Page 51: PCD

S

Ba a

B

Bb

b

parse tree

error o all empty entries in the parsing table are errors. o If X is a terminal symbol different from a, this is also an error case.

LL(1) Parser – Example1

S aBa B bB |

stack input output$S abba$ S aBa$aBa abba$$aB bba$ B bB $aBb bba$$aB ba$ B bB $aBb ba$$aB a$ B e$a a$$ $ accept, successful completion

a b $S S aBaB B B bB

LL(1) Parsing Table

Outputs: S aBa B bB B bB B

Derivation(left-most): SaBaabBaabbBaabba

LL(1) Parser – Example2

E TE’

51

Page 52: PCD

F (E)F idF

T’ T’ T’ *FT’

T’ T

T FT’T FT’T

E’ E’ E’ +TE’

E’

E TE’E TE’E

$)(*+id

E’ +TE’ |

T FT’

T’ *FT’ |

F (E) | id

stack input output$E id+id$ E TE’$E’T id+id$ T FT’

$E’ T’F id+id$ F id$ E’ T’id id+id$$ E’ T’ +id$ T’ $ E’ +id$ E’ +TE’

$ E’ T+ +id$$ E’ T id$ T FT’

$ E’ T’ F id$ F id$ E’ T’id id$$ E’ T’ $ T’ $ E’ $ E’ $ $ accept

Constructing LL(1) Parsing Tables

•Two functions are used in the construction of LL(1) parsing tables:

52

Page 53: PCD

–FIRST FOLLOW

FIRST() is a set of the terminal symbols which occur as first symbols in strings derived from where is any string of grammar symbols.

if derives to , then is also in FIRST() .

FOLLOW(A) is the set of the terminals which occur immediately after (follow) the non-terminal A in the strings derived from the starting symbol.

a terminal a is in FOLLOW(A) if S Aa $ is in FOLLOW(A) if S A

Compute FIRST for Any String X

If X is a terminal symbol FIRST(X)={X}

If X is a non-terminal symbol and X is a production rule is in FIRST(X).

If X is a non-terminal symbol and X Y1Y2..Yn is a production rule if a terminal a in FIRST(Yi) and is in all FIRST(Yj) for j=1,...,i-1

then a is in FIRST(X). if is in all FIRST(Yj) for j=1,...,n then is in FIRST(X). If X is

FIRST(X)={ }

If X is Y1Y2..Yn if a terminal a in FIRST(Yi) and is in all FIRST(Yj) for j=1,...,i-1

then a is in FIRST(X). if is in all FIRST(Yj) for j=1,...,n then is in FIRST(X).

FIRST Example

E TE’

E’ +TE’ |

53

Page 54: PCD

T FT’

T’ *FT’ |

F (E) | id

FIRST(F) = {(,id} FIRST(TE’) = {(,id}FIRST(T’) = {*, } FIRST(+TE’ ) = {+}FIRST(T) = {(,id} FIRST() = {}FIRST(E’) = {+, } FIRST(FT’) = {(,id}FIRST(E) = {(,id} FIRST(*FT’) = {*}

FIRST() = {}FIRST((E)) = {(}FIRST(id) = {id}

Compute FOLLOW (for non-terminals)

If S is the start symbol $ is in FOLLOW(S)

if A B is a production rule everything in FIRST() is FOLLOW(B) except

If ( A B is a production rule )or ( A B is a production rule and is in FIRST(b) )

everything in FOLLOW(A) is in FOLLOW(B).

We apply these rules until nothing more can be added to any follow set.

FOLLOW Example

E TE’

E’ +TE’ |

T FT’

T’ *FT’ |

F (E) | id

FOLLOW(E) = { $, ) }FOLLOW(E’) = { $, ) }FOLLOW(T) = { +, ), $ }FOLLOW(T’) = { +, ), $ }FOLLOW(F) = {+, *, ), $ }

Constructing LL(1) Parsing Table -- Algorithm

for each production rule A of a grammar G

54

Page 55: PCD

–for each terminal a in FIRST() add A to M[A,a]

–If in FIRST() for each terminal a in FOLLOW(A) add A to M[A,a]

–If in FIRST() and $ in FOLLOW(A) add A to M[A,$]

All other undefined entries of the parsing table are error entries.

Constructing LL(1) Parsing Table – Example

E TE’ FIRST(TE’)={(,id} E TE’ into M[E,(] and M[E,id]

E’ +TE’ FIRST(+TE’ )={+} E’ +TE’ into M[E’,+]

E’ FIRST()={} nonebut since in FIRST() and FOLLOW(E’)={$,)} E’ into M[E’,$] and M[E’,)]

T FT’ FIRST(FT’)={(,id} T FT’ into M[T,(] and M[T,id]

T’ *FT’ FIRST(*FT’ )={*} T’ *FT’ into M[T’,*] T’ FIRST()={} none

but since in FIRST() and FOLLOW(T’)={$,),+} T’ e into M[T’,$], M[T’,)]

and M[T’,+] F (E) FIRST((E) )={(} F (E) into M[F,(]F id FIRST(id)={id} F id into M[F,id]

LL(1) Grammars

A grammar whose parsing table has no multiply-defined entries is said to be LL(1) grammar.

(1) one input symbol used as a look-head symbol do determine parser action L left most derivation L input scanned from left to right

The parsing table of a grammar may contain more than one production rule. In this case, we say that it is not a LL(1) grammar.

A Grammar which is not LL(1)

S i C t S E | a FOLLOW(S) = { $,e }E e S | FOLLOW(E) = { $,e }C b FOLLOW(C) = { t }

55

Page 56: PCD

FIRST(iCtSE) = {i}FIRST(a) = {a}FIRST(eS) = {e}FIRST() = {}FIRST(b) = {b}

two production rules for M[E,e]Problem ambiguity

What do we have to do it if the resulting parsing table contains multiply defined entries?

If we didn’t eliminate left recursion, eliminate the left recursion in the grammar. If the grammar is not left factored, we have to left factor the grammar. If its (new grammar’s) parsing table still contains multiply defined entries, that

grammar is ambiguous or it is inherently not a LL(1) grammar.

A left recursive grammar cannot be a LL(1) grammar.

A A | any terminal that appears in FIRST() also appears FIRST(A) because

A . If is , any terminal that appears in FIRST() also appears in

FIRST(A) and FOLLOW(A).

A grammar is not left factored, it cannot be a LL(1) grammar A 1 | 2

any terminal that appears in FIRST(1) also appears in FIRST(2).

56

Page 57: PCD

An ambiguous grammar cannot be a LL(1) grammar.

Properties of LL(1) Grammars

A grammar G is LL(1) if and only if the following conditions hold for two distinctive production rules A and A

Both and cannot derive strings starting with same terminals.

At most one of and can derive to .

3.If can derive to , then cannot derive to any string starting with a terminal in FOLLOW(A).

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.

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.

S ... w (the right-most derivation of w) (the bottom-up parser finds the right-most derivation in the

reverse order)•Bottom-up parsing is also known as shift-reduce parsing because its two main actions are shift and reduce.–At each shift action, the current symbol in the input string is pushed to a stack.

57

Page 58: PCD

–At each reduction step, the symbols at the top of the stack (this symbol sequence is the right side of a production) will replaced by the non-terminal at the left side of that production.–There are also two more actions: accept and error.

Shift-Reduce Parsing

A shift-reduce parser tries to reduce the given input string into the starting symbol.

a string the starting symbolreduced to

At each reduction step, a substring of the input matching to the right side of a production rule is replaced by the non-terminal at the left side of that production rule.

If the substring is chosen correctly, the right most derivation of that string is created in the reverse order.

Rightmost Derivation: S Shift-Reduce Parser finds: ... S

Shift-Reduce Parsing -- Example

S aABb input string: aaabbA aA | a aaAbbB bB | b aAbb reduction

aABbS

S aABb aAbb aaAbb aaabb rm rm rm rm

Right Sentential Forms

How do we know which substring to be replaced at each reduction step?

Handle

Informally, a handle of a string is a substring that matches the right side of a production rule.–But not every substring matches the right side of a production rule is handle

A handle of a right sentential form ( ) is a production rule A and a position of

where the string may be found and replaced by A to produce the previous right-sentential form in a rightmost derivation of .

58

Page 59: PCD

S A

If the grammar is unambiguous, then every right-sentential form of the grammar has exactly one handle.We will see that is a string of terminals.

Handle Pruning

A right-most derivation in reverse can be obtained by handle-pruning.

S=0 1 2 ... n-1 n= input string)

Start from n, find a handle Ann in n, and replace n in by An to get n-1. Then find a handle An-1n-1 in n-1, and replace n-1 in by An-1 to get n-2. Repeat this, until we reach S.

A Shift-Reduce Parser

E E+T | T Right-Most Derivation of id+id*idT T*F | F E E+T E+T*F E+T*id E+F*idF (E) | id E+id*id T+id*id F+id*id id+id*id

Right-Most Sentential Form Reducing Production

id+id*id F idF+id*id T FT+id*id E TE+id*id F idE+F*id T FE+T*id F idE+T*F T T*F E+T E E+T E

Handles are red and underlined in the right-sentential forms.

A Stack Implementation of A Shift-Reduce Parser

There are four possible actions of a shift-parser action:

1.Shift : The next input symbol is shifted onto the top of the stack.2.Reduce: Replace the handle on the top of the stack by the non-terminal.3.Accept: Successful completion of parsing.4.Error: Parser discovers a syntax error, and calls an error recovery routine.

Initial stack just contains only the end-marker $.

59

Page 60: PCD

The end of the input string is marked by the end-marker $.

A Stack Implementation of A Shift-Reduce Parser

Stack Input Action$ id+id*id$ shift$id +id*id$ reduce by F id $F +id*id$ reduce by T F$T +id*id$ reduce by E T $E +id*id$ shift$E+ id*id$ shift $E+id *id$ reduce by F id$E+F *id$ reduce by T F $E+T *id$ shift$E+T* id$ shift $E+T*id $ reduce by F id$E+T*F $ reduce by T T*F$E+T $ reduce by E E+T$E $ accept

Parse Tree

60

Page 61: PCD

SLR

CFGGLR

LALR

Conflicts during Shift-Reduce Parsing

There are context-free grammars for which shift-reduce parsers cannot be used.

Stack contents and the next input symbol may not decide action:

–shift/reduce conflict: Whether make a shift operation or a reduction.–reduce/reduce conflict: The parser cannot decide which of several reductions to make.

If a shift-reduce parser cannot be used for a grammar, that grammar is called as non-LR(k) grammar.•

left to right right-most k lookheadscanning derivation

•An ambiguous grammar can never be a LR grammar

Shift-Reduce Parsers

1.Operator-Precedence Parser– simple, but only a small class of grammars.–––2.LR-Parsers–covers wide range of grammars.SLR – simple LR parser LR – most general LR parserLALR – intermediate LR parser (lookhead LR parser)–SLR, LR and LALR work same, only their parsing tables are different.

61

Page 62: PCD

Operator-Precedence Parser

Operator grammar –small, but an important class of grammars–we may have an efficient operator precedence parser (a shift-reduce parser) for an operator grammar.In an operator grammar, no production rule can have:–e at the right side–two adjacent non-terminals at the right side.

•Ex:EAB EEOE EE+E |Aa Eid E*E |Bb O®+|*|/ E/E | id

not operator grammar not operator grammar operator grammar–

Precedence Relations

In operator-precedence parsing, we define three disjoint precedence relations between certain pairs of terminals.• a <. b b has higher precedence than a

a =· b b has same precedence as aa .> b b has lower precedence than a

•The determination of correct precedence relations between terminals are based on the traditional notions of associativity and precedence of operators. (Unary minus causes a problem).

Using Operator-Precedence Relations

•The intention of the precedence relations is to find the handle of a right-sentential form,

<. with marking the left end, =· appearing in the interior of the handle, and .> marking the right hand.

62

Page 63: PCD

•In our input string $a1a2...an$, we insert the precedence relation between the pairs of terminals (the precedence relation holds between the terminals in that pair).

E E+E | E-E | E*E | E/E | E^E | (E) | -E | id

The partial operator-precedence table for this grammar

•Then the input string id+id*id with the precedence relations inserted will be:• $ <. id .> + <. id .> * <. id .> $

id + * $id .

>.> .>

+ <. .

><. .>

* <. .

>.> .>

$ <. <.

<.

To Find The Handles

1.Scan the string from left end until the first .> is encountered. 2.Then scan backwards (to the left) over any =· until a <. is encountered. 3.The handle contains everything to left of the first .> and to the right of the <. is encountered.

$ <. id .> + <. id .> * <. id .> $ E id $ id + id * id $$ <. + <. id .> * <. id .> $ E id $ E + id * id $ $ <. + <. * <. id .> $ E id $ E + E * id $ $ <. + <. * .> $ E E*E $ E + E * .E $$ <. + .> $ E E+E $ E + E $$ $ $ E $

Operator-Precedence Parsing Algorithm

63

Page 64: PCD

The input string is w$, the initial stack is $ and a table holds precedence relations between certain terminalsAlgorithm:

set p to point to the first symbol of w$ ;repeat forever if ( $ is on top of the stack and p points to $ ) then return else { let a be the topmost terminal symbol on the stack and let b be the symbol

pointed to by p; if ( a <. b or a =· b ) then { /* SHIFT */ push b onto the stack; advance p to the next input symbol; } else if ( a .> b ) then /* REDUCE */ repeat pop stack until ( the top of stack terminal is related by <. to the terminal most

recently popped ); else error(); }

Operator-Precedence Parsing Algorithm – Example

stack input action$ id+id*id$ $ <. id shift$id +id*id$ id .> + reduce E id$ +id*id$ shift$+ id*id$ shift$+id *id$ id .> * reduce E id$+ *id$ shift$+* id$ shift$+*id $ id .> $ reduce E id $+* $ * .> $ reduce E E*E $+ $ + .> $ reduce E E+E $ $ accept

64

Page 65: PCD

<.<.<.<.<.<.<.$.>.>.>.>.>.>.>)

=·<.<.<.<.<.<.<.(.>.>.>.>.>.>.>id.>.><.<.<..>.>.>.>^.>.><.<.<..>.>.>.>/.>.><.<.<..>.>.>.>*.>.><.<.<.<.<..>.>-.>.><.<.<.<.<..>.>+

$)(id^/*-+

How to Create Operator-Precedence Relations

We use associativity and precedence relations among operators. .If operator O1 has higher precedence than operator O2,

o O1 .> O2 and O2 <. O1

.If operator O1 and operator O2 have equal precedence,

o they are left-associative O1 .> O2 and O2 .> O1 o they are right-associative O1 <. O2 and O2 <. O1

.For all operators o O, O <. id, id .> O, O <. (, (<. O, O .> ), ) .> O, O .> $, and $

<. O

.Also, leto (=·) $ <. ( id .> ) ) .> $

o ( <. ( $ <. id id .> $ ) .> )

o ( <. id

Operator-Precedence Relations

65

Page 66: PCD

Handling Unary Minus

Operator-Precedence parsing cannot handle the unary minus when we also the binary minus in our grammar.

The best approach to solve this problem, let the lexical analyzer handle this problem.

The lexical analyzer will return two different operators for the unary minus and the binary minus.

The lexical analyzer will need a lookhead to distinguish the binary minus from the unary minus.

•Then, we make

O <. unary-minus for any operator

unary-minus .> O if unary-minus has higher precedence than O

unary-minus <. O if unary-minus has lower (or equal) precedence than O

Precedence Functions

Compilers using operator precedence parsers do not need to store the table of precedence relations.

The table can be encoded by two precedence functions f and g that map terminal symbols to integers.

For symbols a and b. f(a) < g(b) whenever a <. b f(a) = g(b) whenever a =· b f(a) > g(b) whenever a .> b

Disadvantages of Operator Precedence Parsing

Disadvantages:

It cannot handle the unary minus (the lexical analyzer should handle the unary minus).

Small class of grammars.Advantages:

simple powerful enough for expressions in programming languages

66

Page 67: PCD

Error Recovery in Operator-Precedence Parsing

Error Cases: No relation holds between the terminal on the top of stack and the next input

symbol. A handle is found (reduction step), but there is no production with this handle as a

right side

Error Recovery: 1.Each empty entry is filled with a pointer to an error routine. .Decides the popped handle “looks like” which right hand side. And tries to

recover from that situation

LR Parsers

The most powerful shift-reduce parsing (yet efficient) is: LR(k) parsing.

L left to right Scanning R right-most Derivation (k) k lookhead (k is omitted it is 1)

LR parsing is attractive because: LR parsing is most general non-backtracking shift-reduce parsing, yet it is still

efficient. The class of grammars that can be parsed using LR methods is a proper superset

of the class of grammars that can be parsed with predictive parsers. LL(1)-Grammars LR(1)-Grammars

An LR-parser can detect a syntactic error as soon as it is possible to do so a left-to-right scan of the input.

LR-Parsers

covers wide range of grammars. SLR – simple LR parser LR – most general LR parser LALR – intermediate LR parser (look-head LR parser) SLR, LR and LALR work same (they used the same algorithm), only their parsing

tables are different.

67

Page 68: PCD

LR Parsing Algorithm

A Configuration of LR Parsing Algorithm

A configuration of a LR parsing is:( So X1 S1 ... Xm Sm, ai ai+1 ... an $ )

Stack Rest of Input

Sm and ai decides the parser action by consulting the parsing action table. (Initial Stack contains just So )

A configuration of a LR parsing represents the right sentential form:X1 ... Xm ai ai+1 ... an $

68

Page 69: PCD

Actions of A LR-Parser

1.shift s -- shifts the next input symbol and the state s onto the stack( So X1 S1 ... Xm Sm, ai ai+1 ... an $ ) ( So X1 S1 ... Xm Sm ai s, ai+1 ... an $ )

2.reduce A (or rn where n is a production number)–pop 2|| (=r) items from the stack; –then push A and s where s=goto[sm-r,A]

( So X1 S1 ... Xm Sm, ai ai+1 ... an $ ) è ( So X1 S1 ... Xm-r Sm-r A s, ai ... an $ )

–Output is the reducing production reduce A.3.Accept – Parsing successfully completed

4.Error -- Parser detected an error (an empty entry in the action table)

Reduce Action

pop 2|| (=r) items from the stack; let us assume that = Y1Y2...Yr then push A and s where s=goto[sm-r,A]

( So X1 S1 ... Xm-r Sm-r Y1 Sm-r ...Yr Sm, ai ai+1 ... an $ ) ( So X1 S1 ... Xm-r Sm-r A s, ai ... an $ )

In fact, Y1Y2...Yr is a handle.• X1 ... Xm-r A ai ... an $ X1 ... Xm Y1...Yr ai ai+1 ... an $

(SLR) Parsing Tables for Expression Grammar

1) E E+T2) E T3) T T*F4) T F5) F (E)6) F id

69

Page 70: PCD

r3r3r3r310

r5r5r5r511

r1r1s7r19

s11

s68

10s4s57

39s4s56

r6r6r6r65

328s4s54

r4r4r4r43

r2r2s7r22

accs61

321s4s50

FT E$)(*+idstate

ACTION TABLE GOTO TABLE

Actions of A (S)LR-Parser -- Example

stack input action output0 id*id+id$ shift 50id5 *id+id$ reduce by Fid Fid0F3 *id+id$ reduce by TF TF0T2 *id+id$ shift 70T2*7 id+id$ shift 50T2*7id5 +id$ reduce by Fid Fid0T2*7F10 +id$ reduce by TT*F TT*F0T2 +id$ reduce by ET ET0E1 +id$ shift 60E1+6 id$ shift 50E1+6id5 $ reduce by Fid Fid0E1+6F3 $ reduce by TF TF0E1+6T9 $ reduce by EE+T EE+T0E1 $ accept

70

Page 71: PCD

Constructing SLR Parsing Tables – 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.Ex: A aBb Possible LR(0) Items: A .aBb (four different possibility) A a.Bb

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.Augmented Grammar:

G’ is G with a new production rule S’S where S’ is the new starting symbol.

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:

Initially, every LR(0) item in I is added to closure(I). 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).

The Closure Operation -- Example

E’ E closure({E’ .E}) =

E E+T { E’ .E kernel items

E T E .E+T

T T*F E .T

T F T .T*F

F (E) T .F

F id F .(E)

71

Page 72: PCD

F .id }

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).

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 }

goto(I,T) = { E T., T T.*F }

goto(I,F) = {T F. }

goto(I,() = { F (.E), E .E+T, E .T, T .T*F, T .F,

F .(E), F .id }

goto(I,id) = { F id. }

Construction of The Canonical LR(0) Collection

To create the SLR parsing tables for a grammar G, we will create the canonical LR(0) collection of the grammar G’.

72

Page 73: PCD

Algorithm:C is { closure({S’.S}) }repeat the followings until no more set of LR(0) items can be added to C.

for each I in C and each grammar symbol Xif goto(I,X) is not empty and not in C

add goto(I,X) to C

goto function is a DFA on the sets in C.

The Canonical LR(0) Collection – Example

I0: E’ .E I1: E’ E. I6: E ® E+.T I9: E E+T. E .E+T E E.+T T .T*F T T.*F E .T T .F T .T*F I2: E T. F .(E) I10: T T*F. T .F T T.*F F .id F .(E) F .id I3: T F. I7: T T*.F I11: F (E).

F .(E) I4: F (.E) F .id E .E+T E .T I8: F (E.)

T .T*F E E.+T T .F F .(E) F .id

I5: F id.

73

Page 74: PCD

I0 I1

I2

I3

I4

I5

I6

I7

I8to I2to I3

to I4

I9

to I3to I4to I5

I10to I4

to I5

I11

to I6

to I7

id

(

F

*

E

E

+T

T

T

)

F

FF

(

id id

(

*

(

id

Transition Diagram (DFA) of Goto Function

Constructing SLR Parsing Table (of an augumented grammar G’)

Construct the canonical collection of sets of LR(0) items for G’. o C{I0,...,In}

.Create the parsing action table as followso If a is a terminal, A.a in Ii and goto(Ii,a)=Ij then action[i,a] is shift

j.o •If A. is in Ii , then action[i,a] is reduce A for all a in

FOLLOW(A) where AS’.o •If S’S. is in Ii , then action[i,$] is accept.o •If any conflicting actions generated by these rules, the grammar is not

SLR(1). .Create the parsing goto table

o for all non-terminals A, if goto(Ii,A)=Ij then goto[i,A]=j All entries not defined by (2) and (3) are errors. .Initial state of the parser contains S’.S

74

Page 75: PCD

r3r3r3r310r5r5r5r511

r1r1s7r19s11

s6810

s4s5739s4s56

r6r6r6r65328s4s54

r4r4r4r43r2r2s7r22acc

s61321s4s50

FT E$)(*+idstate

Parsing Tables of Expression Grammar

Action table Goto table

SLR(1) Grammar

An LR parser using SLR(1) parsing tables for a grammar G is called as the SLR(1) parser for G.If a grammar G has an SLR(1) parsing table, it is called SLR(1) grammar (or SLR grammar in short).Every SLR grammar is unambiguous, but every unambiguous grammar is not a SLR grammar.

shift/reduce and reduce/reduce conflicts

If a state does not know whether it will make a shift operation or reduction for a terminal, we say that there is a shift/reduce conflict.

If a state does not know whether it will make a reduction operation using the production rule i or j for a terminal, we say that there is a reduce/reduce conflict.

75

Page 76: PCD

If the SLR parsing table of a grammar G has a conflict, we say that that grammar is not SLR grammar

Conflict Example

S L=R I0: S’ .S I1:S’ S. I6:S L=.R I9: S L=R.S R S .L=R R .LL *R S .R I2:S L.=R L .*RL id L .*R R L. L .idR L L .id

R .L I3:S R.

I4:L *.R I7:L *R. Problem R .L

FOLLOW(R)={=,$} L .*R I8:R L.= shift 6 L .id

reduce by R Lshift/reduce conflict I5: L id.

Conflict Example2

76

Page 77: PCD

S AaAb I0: S’ .S S BbBa S .AaAb A S .BbBaB A .

B .

ProblemFOLLOW(A)={a,b}FOLLOW(B)={a,b}

a reduce by A b reduce by A reduce by B reduce by B reduce/reduce conflict reduce/reduce conflict

Constructing Canonical LR(1) Parsing Tables

In SLR method, the state i makes a reduction by A when the current token is a:–if the A. in the Ii and a is FOLLOW(A)

–In some situations, A cannot be followed by the terminal a in a right-sentential form when and the state i are on the top stack. This means that making reduction in this case is not correct. S AaAb SAaAbAabab SBbBaBbabaS BbBaA Aab ab Bba baB AaAb Aa b BbBa Bb a

LR(1) Item

To avoid some of invalid reductions, the states need to carry more information.Extra information is put into a state by including a terminal symbol as a second component in an item.

A LR(1) item is:

A .,a where a is the look-head of the LR(1) item(a is a terminal or end-marker.)

When ( in the LR(1) item A .,a ) is not empty, the look-head does not have any affect.

77

Page 78: PCD

When is empty (A .,a ), we do the reduction by A only if the next input symbol is a (not for any terminal in FOLLOW(A)).

A state will contain A .,a1 where {a1,...,an} Í FOLLOW(A)...

A .,an

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.

closure(I) is: ( where I is a set of LR(1) items)–every LR(1) item in I is in closure(I)

–if A.B,a in closure(I) and B is a production rule of G; then B.,b will be in the closure(I) for each terminal b in FIRST(a) .

goto operation

•If I is a set of LR(1) items and X is a grammar symbol (terminal or non-terminal), then goto(I,X) is defined as follows:

–If A .X,a in I then every item in closure({A X.,a}) will be in goto(I,X).

Construction of The Canonical LR(1) Collection

Algorithm:C is { closure({S’.S,$}) }repeat the followings until no more set of LR(1) items can be added to C.

for each I in C and each grammar symbol Xif goto(I,X) is not empty and not in C

add goto(I,X) to C

goto function is a DFA on the sets in C.

A Short Notation for The Sets of LR(1) Items

78

Page 79: PCD

A set of LR(1) items containing the following items A .,a1 ... A .,an

can be written as

A .,a1/a2/.../an

Canonical LR(1) Collection -- Example

S AaAb I0: S’ .S ,$ s I1: S’ S. ,$ S BbBa S .AaAb ,$ AA S .BbBa ,$ I2: S A.aAb ,$ a to I4B A . ,a B

B . ,b I3: S B.bBa ,$ b to I5 I4: S Aa.Ab ,$ A I6: S AaA.b ,$ a I8: S AaAb. ,$ A . ,bI5: S Bb.Ba ,$ B I7: S BbB.a ,$ b I9: S BbBa. ,$ B . ,a

Construction of LR(1) Parsing Tables

79

Page 80: PCD

1.Construct the canonical collection of sets of LR(1) items for G’. C{I0,...,In}

2.Create the parsing action table as followso If a is a terminal, A.a,b in Ii and goto(Ii,a)=Ij then action[i,a] is

shift j.o If A.,a is in Ii , then action[i,a] is reduce Aa® where AS’.o If S’S.,$ is in Ii , then action[i,$] is accept.o If any conflicting actions generated by these rules, the grammar is not

LR(1). 3.Create the parsing goto table

o for all non-terminals A, if goto(Ii,A)=Ij then goto[i,A]=j 4.All entries not defined by (2) and (3) are errors. 5.Initial state of the parser contains S’.S,$

LALR Parsing Tables

LALR stands for LookAhead LR.

LALR parsers are often used in practice because LALR parsing tables are smaller than LR(1) parsing tables.The number of states in SLR and LALR parsing tables for a grammar G are equal. But LALR parsers recognize more grammars than SLR parsers.yacc creates a LALR parser for the given grammar. A state of LALR parser will be again a set of LR(1) items.

Creating LALR Parsing Tables

Canonical LR(1) Parser LALR Parser shrink # of states

This shrink process may introduce a reduce/reduce conflict in the resulting LALR parser (so the grammar is NOT LALR)But, this shrik process does not produce a shift/reduce conflict.

The Core of A Set of LR(1) Items

The core of a set of LR(1) items is the set of its first component.Ex: S L.=R,$ S L.=R Core

R L.,$ R L.

80

Page 81: PCD

We will find the states (sets of LR(1) items) in a canonical LR(1) parser with same cores. Then we will merge them as a single state.

I1:L id.,= A new state: I12: L id.,= L id.,$

I2:L id.,$ have same core, merge themWe will do this for all states of a canonical LR(1) parser to get the states of the LALR parser.In fact, the number of the states of the LALR parser for a grammar will be equal to the number of states of the SLR parser for that grammar

Creation of LALR Parsing Tables

Create the canonical LR(1) collection of the sets of LR(1) items for the given grammar.Find each core; find all sets having that same core; replace those sets having same cores with a single set which is their union.

C={I0,...,In} C’={J1,...,Jm} where m nCreate the parsing tables (action and goto tables) same as the construction of the parsing tables of LR(1) parser.–Note that: If J=I1 ... Ik since I1,...,Ik have same cores

cores of goto(I1,X),...,goto(I2,X) must be same. –So, goto(J,X)=K where K is the union of all sets of items having same cores as goto(I1,X).If no conflict is introduced, the grammar is LALR(1) grammar. (We may only introduce reduce/reduce conflicts; we cannot introduce a shift/reduce conflict)

Shift/Reduce Conflict

We say that we cannot introduce a shift/reduce conflict during the shrink process for the creation of the states of a LALR parser.Assume that we can introduce a shift/reduce conflict. In this case, a state of LALR parser must have:

A .,a and B .a,b

This means that a state of the canonical LR(1) parser must have:

A .,a and B .a,c

But, this state has also a shift/reduce conflict. i.e. The original canonical LR(1) parser has a conflict.

(Reason for this, the shift operation does not depend on lookaheads)

81

Page 82: PCD

Reduce/Reduce Conflict

But, we may introduce a reduce/reduce conflict during the shrink process for the creation of the states of a LALR parser.

I1 : A .,a I2: A .,b

B .,b B .,c

I12: A .,a/b reduce/reduce conflict

B .,b/c

Using Ambiguous GrammarsAll grammars used in the construction of LR-parsing tables must be un-ambiguous.Can we create LR-parsing tables for ambiguous grammars ?

–Yes, but they will have conflicts. –We can resolve these conflicts in favor of one of them to disambiguate the

grammar. –At the end, we will have again an unambiguous grammar.

Why we want to use an ambiguous grammar? –Some of the ambiguous grammars are much natural, and a

corresponding unambiguous grammar can be very complex. –Usage of an ambiguous grammar may eliminate unnecessary

reductions.Ex.

E E+T | TE E+E | E*E | (E) | id T T*F | F

F (E) | id•

Error recovery

Error Recovery in LR Parsing

82

Page 83: PCD

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. An LR parser will announce error as soon as there is no valid continuation for the

scanned portion of the input. 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.

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. (Get rid of everything from the stack before this state s).

Discard zero or more input symbols until a symbol a is found that can legitimately follow A.

o –The symbol a is simply in FOLLOW(A), but this may not work for all situations.

The parser stacks the nonterminal A and the state goto[s,A], and it resumes the normal parsing.

This nonterminal A is normally is a basic programming block (there can be more than one choice for A).

o –stmt, expr, block, ...–

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).o –missing operando –unbalanced right parenthesis

Intermediate languages

83

Page 84: PCD

Parser Code generator

Intermediate code generator

Static checker

Intermediate code

Position of intermediate code

The front end translates a source program into an intermediate represntation from which the back end generates the target code.

Advantage of machine independent intermediate form

Retarget is facilitated. [a compiler for a different machine can be created by attaching a back end for the new machine to an existing front end]

A machineindepedent code optimiser can be applied to the intermediate represntation

Intermediate languages

types of intermediate represntation

Syntax trees Postfix notation Three address codes: [the semantic rules for generating three address code

from common programming languages]

Graphical representation

A syntax tree shows the hierarchical structure of a source programA DAG gives the same information but in compact way because common sub expression are identified.Statements a:= b*-c +b*-c

84

Page 85: PCD

postfix notation

linear represntation of a syntax tree

postfix for the Statements a:= b*-c +b*-c

a b c uminus * b c unminus * + assign

syntax tree for the assignment statement is produced by the syntax directed translation

85

Page 86: PCD

Nonterminal S generates an assignment statement+ and – are operators in the typical languagesoperator associates and precedence are usual

syntax directed definition for the Statements a:= b*-c +b*-c

Production Semantic ruleS id:=E S.nptr := mknode (‘assign’, mkleaf(id, id.place),E.nptr)EE1 + E2 E.nptr := mknode (‘+’ E1.nptr, E2.nptr)EE1 * E2 E.nptr := mknode (‘*’ E1.nptr, E2.nptr)

E-E1 E.nptr := mknode (‘uminus’ E1.nptr)E ( E1 ) E.nptr :=E1.nptrEid E.nptr := mkleaf(id,id.place)

Representation of syntax tree

Each node as As a recordo Field – operator, pointers to children

Node are allocated from an array

Each node as As a record

86

Page 87: PCD

Node are allocated from an array

Three address codes

Each statement has three addresso Two for operands

87

Page 88: PCD

o One for result General form

o x:= y op zo where x, y,z – name, constant , compiler generated temporarieso op – operators

Examplesx+y*zt1:= y * zt2:=x + t1

Code for the syntax tree (Statements a:= b*-c +b*-c)

1. t1:= -c 2. t2:= b * t1

3. t3:= -c4. t4:= b * t3

5. t5 := t2 + t4

6. a:= t5

Code for the DAG (Statements a:= b*-c +b*-c)1. t1:= -c 2. t2:= b * t1

3. t5 := t2 + t2

4. a:= t5

Types of three address codes

o similar to assembly codeo statement can have symbolic labelsand there are statement for flow of

conrol.o A symbolic label represnts the index of a three address statement in the

array holding the intermediate code.

Common three address statements

Assignment statement of form x: = y op zo Where op is binary or logical operation

Assignment statement of form x:= op yo op is a unary operationo unary minus, logical negation, shift operators and conversion operators

Copy statement of form x:=yo The value y is assigned to x.

The unconditional statement go to L Conditional jumps such as if x relop y goto L

o relop – relational operator

88

Page 89: PCD

o {<,>.<>,=……} param x and call p, n for procedure calls and return y. y is optional

o param x1o param x2o param x3o ……o param xno call p, no call of the procedure p (x1,x2, x3..xn)

Indexed statement of form x:=y[i] and x[i] := yo the statement x[i] := y sets the contents of the location i units beyond x to

the value y.o the statement x:=y[i]sets x to the value in the location I memory units

beyond location. Address and ponters assignments of the form x:=&y, x:=*y and *x=y

o x:=&y sets the value of x to be the location of y

Declarations

as new name is seen, the name is entered in the symbol tablewith the offset equal to the current value of offset.

For each local name entry is created in symbol table along with other information(type and the realive address of the storage for the name)

declaration in a procedure

the procedure enter (name, type, offset) creates an symbol table entry for name, gives it type type and relative address offset in its data area.attribute type represnts a type expression constructed from the basic types integer and real

P D {offset :=0}

D D ; D

D id : T { enter(id.name, T.type, offset); Offset:=offset + T.width}

Tinteger { T.type := integer;T.width:=4 }

Treal { T.type := real;T.width:=8 }

Tarray [num] of T1 { T.type := array (num.val T1.type);T.width:=num.val x T1.width }

89

Page 90: PCD

To readarray

To exchange

TT1 { T.type := pointer (T1.type)T.width:=4 }

Computing the types and relative address of the declared names

Keep track ofscope information

Local names of ecah procedure can be assigned relative address using the above approachFor nested procedures the processing of declaration is temperorily suspended. P DD D ; D | id : T | proc id; D ; S

S for statement T for ypes are not shown

A new symbol table is created when a procedure declaration D proc id D ; S is seen and the entries for the declaration in D1 are created in the new table

The new table points back to the symbol table of the enclosing procedure.The name represnted by id is localSymbol tables of procedures are shown in fig

Procedure : sort, readarray, exchange, quicksort, partition

The symbol tables of the procedures readarray, exchange, quicksort points back to the containing procedure sort.Since partition is declared with in quicksort, its table points to that of quick sort

nil headerax

Readarray exchangequicksort

quicksortexchange readarray

partition

90

nil headerkv

partitionnil headeri

nil header

1. nil 2. header3. i 4.5. j 6.

Page 91: PCD

The semantic rules are defined in terms of the following operation

1. mktable(previous) creates a new symbol table and returns a pointer to the new table

a. The argument previous points to the previously created symbol table

2. Enter (table, name, type, offset) creates an entry for name name in the symbol table pointed by the table

3. Enter places type type and the relative address address offset in fields within the entry.

4. Addwidth (table, width ) records the cumulative width of all the entries in table in the header assiciated with this table.

5. Enterproc (table, name, newname) creates a new entry for the procedure name in the symbol table pointed to the table. The argument newtable points to the symbol table for the procedure name.

Fields name in record

The following production allows nonterminal T to generate records in addition to basic types , pointers and arrays

T record D end

The action to the translation scheme

T record D end {T.type:=record(top(tblptr)); T.width:=top(offset); Pop(tblptr);pop(offset)}

L {t:=mktable(nil);Push(t, tblptr); push(0,offset)}

Setting up a symbol table for field name in a record

Flow control statement

Case statement

Our switch state syntax

Switch expressionBegin

91

Page 92: PCD

Case value : statementCase value : statement Case value : statement …………..Case value : statementdefault : statement

end

The translation of the switch is code to

1. evaluate the expression2. find which value in the list of case is the same as the value of the expression.

a. If non of the value matches, then default3. execute the statement associated with the value

syntax directed translation of the case statement

Switch expressionBegin

Case v1 : s1Case v2 : s2Case v3 : s3 …………..Case vn-1 : sn-1

default : sn

end

Translation of case statement

Code to evaluate E into tL1: code for s1

goto nextL2: code for s2

goto next

Ln-1: code for n-1

goto nextL n: code for s n

goto nexttest: if t = V1 goto L1

if t = V2 goto L2……if t = V n-1 goto L n-1

goto L n

next:

92

Page 93: PCD

Another Translation of case statementCode to evaluate E into tIf t V1 goto L1Code for S1Goto next

L1: If t V2 goto L2Code for S2Goto next

L2: If t V3 goto L3Code for S3Goto next

Ln-2: If t V n-1 goto L n-1

Code for S n-1

Goto nextLn-1: code for S n

Next:

Procedure calls

Calling sequence

The procedure is a programming construct The procedure is imperative for a compiler to generate good code for procedure call and returnsThe run time routines handles procedure argument passing, calls, and returns

Grammar for simple procedure call.

1. S call id (Elist)2. Elist Elist, E3. Elist E

Calling sequence

A sequence of action taken on entry to and exit from each procedure

93

Page 94: PCD

When a procedure call occurs

1. When a procedure call occurs, space must be allocated for the activation record of the called procedure.

2. The argument of the called procedure must be evaluated and made available to the called procedure in a known place

3. Environment pointers must be established to enable the called procedure to access data in the enclosing blocks

4. The state of the calling procedure must be Saved so it can resume execution after the call.

5. Also the return address of the calling procedure is saved in a known place

6. The return address is the location of the the instruction that follows the call in the calling procedure

7. Finally a jump to the beginning of the code for the called procedure must be generated

When a procedure returns

When a function returns,1. if the procedure is a function , the result must be stored in a known place .2. The activatation record of the calling procedure must be restored.3. A jump to the beginningof the code for the called procedure must be

generated

There is no exact division of the run-time tasks between the calling called procedureSyntax directed translation

1. S call id (Elist)2. Elist Elist, E3. Elist E

S call id (Elist){ for each item p on queue doemit (‘param’ p)emit (‘call’ id.place)}

Elist Elist, E{ append E.place to the end of queue}

Elist E

94

Page 95: PCD

{ initilize the queue to contain only E.place}

1. We use param statements as place holders for the arguments2. The called procedure is passed a pointer in the register to the first param

statements.3. Pointers to any other arguments by using the proper offset from the base pointer.4. If we do not want to mix the argument evaluating statement with the param

statement, we have to save the value of E.place, for each expression E in id (E,E,E,..E)

5. If the parameters are passed to the called procedure by putting them on a stack, as would normally be the case for the dynamically allocated data, there is no reason not to mix evaluation and param statement

6. The data used to save this values is a queue

Code Generation

A code generator takes an intermediate representation of a source program, and produces an equivalent program as an output.

The requirements on a code generator:o –The output code must be correct.o –The output code must be high quality.o –It should make effective use of the resources of the target machine.o –It should run efficiently.

In theory, the problem of generating optimal code is undecidable. In practice, we use heuristic techniques to generate sub-optimal (good, but not

optimal) target code. The choice of the heuristic is important since a carefully designed code generation algorithm can produce much better code than a naive code generation algorithm.

Input to Code Generator

The input of a code generator is the intermediate representation of a source program (together with the information in the symbol table to figure out the addresses of the symbols).

The intermediate representation can be:o –Three-address codes (quadraples).o –Trees o –Dags (Directed Acyclic Graphs)o –or, other representations

Code generator assumes that codes in the intermediate representation are free of semantic errors and we have all the type conversion instructions in these codes.

95

Page 96: PCD

Target Programs (Output of Code Generation)

The output of the code generation is the target program. The target program can be in one of the following form:

o –absolute machine languageo –relocatable machine languageo –assembly languageo –virtual machine codes(Java..)

If absolute machine language is used, the target program can be placed in a fixed location, and immediately executed (WATFIV, PL/C).

If relocatable machine language is used, we need a linker and loader to combine the relocatable object files and load them. It is a flexible approach (C language)

If assembly language is used, we need an assembler is need

Memory Management

Implementation of static and stack allocation of data objects? How the names in the intermediate codes are converted into addresses in the

target code? The labels in the intermediate codes must be converted into the addresses of the

target machine instructions. A quadraple will match to more than one machine instruction. If that quadraple

has a label, this label will be the address of the first machine instruction corresponding to that quadraple.

Instruction Selection

The structure of the instruction set of the target machine determines the difficulty of the instruction selection.

–The uniformity and completeness of the instruction set are an important factors. Instruction speeds are also important. –If we do not care speed, the code generation is a straight forward job. We can

map each quadraple into a set of machine instructions. Naive code generation:ADD y,z,x MOV y, R0

ADD z, R0MOV R0,x

The quality of the generated code is determined by its speed and size. Instruction speeds are needed to design good code sequences.

96

Page 97: PCD

Register Allocation

Instructions involving register operands are usually shorter and faster than those involving operands in memory.

The efficient utilization of registers is important in generating good code sequence.

The use of registers is divided into two sub-problems:o –Register Allocation – we select the set of registers that will be reside in

registers at a point in the program.o –Register Assignment – we pick the specific register that a variable will

reside in. Finding an optimal assignment of registers is difficult.

o –In theory, the problem is NP-complete.o –The problem is further complicated because some architectures may

require certain register-usage conventions such as address vs data registers, even vs odd registers for certain instructions.

Choice of Evaluation Order

The order of computations affect the efficiency of the target code. Some computation orders require less registers to hold intermediate results. Picking the best computation order is also another NP-complete problem. We will try to use the order used in the intermediate codes. But, the most important criterion for a code generator is that it should

produce correct code. We may use a less efficient code generator as long as it produces correct codes.

But we cannot use a code generator which is efficient but it does not produce correct codes.

Target Machine

To design a code generator, we should be familiar with the structure of the target machine and its instruction set.

nstead of a specific architecture, we will design our own simple target machine for the code generation.

– We will decide the instruction set, but it will be closer actually machine instructions.

– We will decide size and speeds of the instructions, and we will use them in the creation of good code generators.

– Although we do not use an actual target machine, our discussions are also applicable to actual target machines.

97

Page 98: PCD

Our Target Machine

Our target machine is a byte-addressable machine (each word is four-bytes). Our target machine has n general purpose registers – R0, R1,...,Rn-1 Our target machine has two-address instructions of the form:

op source,destination where op is an op-code, and source and destination are data fields.

ADD add source to destinationSUB subtract source from destination

MOV move source to destination

Our Target Machine – Address Modes

•The source and destination fields are not long enough to hold memory addresses. Certain bit-patterns in these fields specify that words following the instruction (the instruction is also one word) contain operand addresses (or constants).

•Of course, there will be cost for having memory addresses and constants in instructions.

•We will use different addressing modes to get addresses of source and destination.

MODE FORM ADDRESS ADDED COSTabsolute M M 1register R R 0indexed c(R) c+contents(R) 1indirect register *R contents(R) 0indirect indexed *c(R) contents(c+contents(R)) 1literal #c c 1

MOV R0,M move contents of R0 into memory location MMOV M,R0 move contents of memory location M into R0.MOV 4(R0),M move contents of 4+contents(R0) into

memory location M.MOV *R0,R1 move contents of contents(R0) into R1. MOV *4(R0),R1 move contents of 4+contents(R0) into R1. MOV #13,R1 move the constant 13 into R1.

MOV R1,*4(R0) move contents of R1 into the memory location 4+contents(R0) .

98

Page 99: PCD

Temporaries

Local Variables

Other Stuff

Actual Parameters

Return Value

Return address

Our Target Machine – Instruction Costs

The cost of an instruction is one plus the costs associated with the source and destination address modes.

This cost corresponds to the length (in words) of the instruction.o –In real architectures, there are similar costs.o –When we try to minimize the cost in time, we also minimize the cost in

space in our architecture (this may not be true in a real architecture).

MOV R0,a cost is 2MOV R0,R1 cost is 1MOV 4(R0),b cost is 3 MOV *R0,R1 cost is 1MOV *4(R0),R1 cost is 2 MOV #13,R1 cost is 2

Run-Time Storage Organization

Static Allocation -- the static allocation can be performed by just reserving enough memory space for static data objects.

o –Static variables can be accessible by just using absolute memory address. Stack Allocation – the code generator should produce machine codes to allocate

the activation records (corresponding to intermediate codes).o –Normally we will use a specific register to point (the beginning of) the

activation record, and we will use this register to access variables residing in that activation record.

o We cannot know actual address of these stack variables until run-time.

Stack Allocation – Activation Record

SP

99

Page 100: PCD

All values in the activation record can be accessible from SP by a positive offset.

And all these offsets are calculated at compile-time.

Possible Procedure Invocation

ADD #caller.recordsize,SPMOV PARAM1,*8(SP) // save parametersMOV PARAM2,*12(SP)

.MOV PARAMn,*4+4n(SP)

. // saving other stuffMOV #here+16,*SP // save return addressGOTO callee.codearea // jump to procedureSUB #caller.recordsize,SP // return address

Possible Return from A Procedure Call

MOV RETVAL,*4(SP) // save the return valueGOTO *SP // return to caller

Run-Time Addresses

Static Variables:o static[12] staticaddressblock+12

if the beginning of the static address block is 100, o MOV #0,,static[12] MOV #0,112

So, the static variables are absolute addresses and these absolute addresses are evaluated at compile time (or load time).

Run-Time Addresses

Stack Variableso Stack variables are accesses using offsets from the beginning of the

activation records.

local variable *OFFSET(SP)

non-local variable o access linkso displays

100

Page 101: PCD

Basic Blocks

A basic block is a sequence of consecutive statements (of intermediate codes – quadraples) in which flow of control enters at the beginning and leaves at the end without halt or possibility of branch (except at the end).

A basic block:t1 := a * at2 := a * bt3 := t1 – t2

Partition into Basic Blocks

Input: A sequence of three-address codesOutput: A list of basic blocks with each three-address statement in exactly one block.Algorithm:

1.Determine the list of leaders. The first statement of each basic block will be a leader.

o The first statement is a leadero Any statement that is the target of a jump instruction (conditional or

unconditional) is a leader.o Any statement immediately following a jump instruction (conditional or

unconditional) is a leader. 2.For each leader, its basic block consists of the leader and all statements up to

but not including the next leader or the end of the program.

Example Pascal Program

beginprod := 0;i := 1;do begin

prod := prod + a[i] * b[i];i := i + 1;

endwhile i <= 20

end

101

Page 102: PCD

Corresponding Quadraples

1: prod := 0 2: i := 1 3: t1 := 4*i 4: t2 := a[t1] 5: t3 := 4*i 6: t4 := b[t3] 7: t5 := t2*t4 8: t6 := prod+t5 9: prod := t610: t7 := i+111: i := t712: if i<=20 goto 3

Basic Blocks (Another Example)

x:=1; 01: mov 1,,x y:=x+10; 02: add x,10,t1 leaderwhile (x<y) { 03: mov t1,,yx:=x+1; 04: lt x,y,t2if (x%2==1) then y:=y+1; 05: jmpf t2,,17else y:=y-2; 06: add x,1,t3

} 07: mov t3,,x08: mod x,2,t4

09: eq t4,1,t510: jmpf t5,,1411: add y,1,t612: mov t6,,y13: jmp ,,1614: sub y,2,t715: mov t7,,y16: jmp ,,417: •

Live Variables

A three-address statement x := y op z is said to define x and

102

Page 103: PCD

to use (or reference) y and z

A name in a basic block is said to be live at a given point if its value is used after that point in the program (in that basic block or in another basic block).

A basic block computes a set of expressions. These expressions are values of the

some names live on exit from the block. Two blocks are said to be equivalent if they compute the same set of expressions.

Transformations on Basic Blocks

A number of transformations can be applied to a basic block without changing the set of expressions computed by the block.

Many of these transformations are useful for improving the quality of code that will be generated from that block.

These transformations are categorized into two groups:o –Structure-Preserving Transformationso –Algebraic Transformations.

Structure-Preserving Transformations

The primary structure-preserving transformations are:o –common sub-expression eliminationo –dead-code eliminationo –renaming of temporary variableso –interchange of two independent adjacent statements.

Common Sub-expression Elimination

a := b+c a := b+cb := a-d b := a-dc := b+c c := b+cd := a-d d := b

a-d in 2 and 4 statements are common sub-expressions.

But, b+c in 1 and 3 are not common sub-expressions because the values of b in those statements are different.

103

Page 104: PCD

Dead-Code Elimination

We say that x is dead at a certain point, if it is not used after that point in the block (or in the following blocks).

If x is dead at the point of the statement x := y op z, this statement can be safely eliminated without changing the meaning of the block

Renaming Temporary Variables

Without changing the meaning of a block, we may rename temporary variables in that block. The new block with renamed variables is equivalent to the original block.

t1 := a+b t2 := a+bt2 := t1*c t1 := t2*c

Interchange of Statements

If two adjacent statements are independent they can be interchanged without affecting the value of the basic block.

t1 := a+b t2 := x*yt2 := x*y t1 := a+b

Algebraic Transformations

x := x+0 eliminate this statementx := y+0 x := yx := x+1 INC ,,Xx := y**2 x := y*y

Flow Graphs

We can add the flow-of-control information to the set of basic blocks making up a program by constructing a directed graph called a flow graph.

There is a directed edge from block B1 to block B2 if B2 immediately follows B1 in some sequence; that is if:

104

Page 105: PCD

o –there is a conditional or unconditional jump from the last statement of B1 to the first statement of B2, or

o –B2 immediately follow B1 in the order of the program, and B1 does not end with an unconditional jump.

We say that B2 is successor of B1, and B1 is predecessor of B2.

Flow Graphs - Example

prod := 0i := 1

t1 := 4*it2 := a[t1]t3 := 4*it4 := b[t3]t5 := t2*t4t6 := prod*t5prod := t6t7 := i+1i := t7if i<=20 goto 3

Representation of Basic Blocks

We may different data structures to represent basic blocks.o –We may use linked lists of quadraples.o –Instead of jumping to the location of the first quadraple of a basic block

(leader), we may assume that jump instructions jumps to the block. Thus, if the structure of that basic block is changed because of the optimization, we will not be affected.

if i<10 goto B2

105

Page 106: PCD

An edge of the flow graph from block B1 to B2 does not specify the conditions under which control flows from B1 to B2.

o –This information can be recovered from the jump instruction at the end of the block.

Loops

What is a loop in a flow graph? A loop in a flow graph is:

o 1.All nodes in a a loop is strongly connected. In other words, from any node in a loop to any other node there is a path of length one or more, and all nodes in that path are in that loop.

o 2.The collection of nodes in a loop has a unique entry. In other words, the only way to reach a node in a loop from an outside node is to first go through that unique entry.

A loop that contain no other loop is called inner loop. We will assume that we can find all loops in a given flow graph.

Next Uses

We say that a quadraple (x := y op z) uses names y and z. For each name in a quadraple, we would like to know the next use of that name. We also would like to know which names are live after the block; ie, they will be

used after this block.o –Without a global live-analysis, we cannot determine which names will be

live after a block. o –For simplicity, we may assume that all variables and some of temporaries

are live after each block. We use next use and live information about names to determine which names will

be in registers.

Computation of Next Uses

We scan a block backward to collect next use information about names. For each quadraple, i: x := y op z

o 1.Attach i to the next use and liveness lists of x, y and z.o 2.Mark x as “not live” and “no next use”o 3.Mark y and z as “live” and “next use”

Thus, we collect the usage information about all the names in a basic block.

106

Page 107: PCD

Storage for Temporaries

If two temporaries are not live at the some time, we can pack these temporaries into a same location.We can use the next use information to pack temporaries.

t1 := a*a t1 := a*at2 := a*b t2 := a*bt3 := 2*t2 t2 := 2*t2t4 := t1+t3 t1 := t1+t2t5 := b*b t2 := b*bt6 := t4+t5 t1 := t1+t2

Simple Code Generator

For simplicity, we will assume that for each intermediate code operator we have a corresponding target code operator.

We will also assume that computed results can be left in registers as long as possible.

o –If the register is needed for another computation, the value in the register must be stored.

o –Before we leave a basic block, everything must be stored in memory locations.

We will try to produce reasonable code for a given basic block. The code-generation algorithm will use descriptors keep track of register contents

and addresses for names.

Register Descriptors

A register descriptor keeps track of what is currently in each register. It will be consulted when a new register is needed by code-generation algorithm. We assume that all registers are initially empty before we enter into a basic block.

This is not true if the registers are assigned across blocks. At a certain time, each register descriptor will hold zero or more names.

R1 is emptyMOV a,R1

R1 holds aMOV R1,b

R1 holds both a and b

107

Page 108: PCD

Address Descriptors

An address descriptor keeps track of the locations where the current value of a name can be found at run-time.

The location can be a register, a stack location or a memory location (in static area). The location can be a set of these.

This information can be stored in the symbol table.

a is in the memoryMOV a,R1

a is in R1 and in the memoryMOV R1,b

b is in R1 and in the memory

A Code Generation Algorithm

This code generation algorithm takes a basic block of three-address codes, and produces machines codes in our target architecture.

For each three-address code we perform certain actions. We assume that we have getreg routine to determine the location of the result of

the operation.

Code Generation Actions for x := y op z

1.Invoke getreg function determine the location Lx of the result (x). 2.Consult the address descriptor of y to determine the location Ly for y. Prefer a

register for Ly if y is in both in a register and in a memory location. If y is not already in Lx, generate the instruction MOV Ly,Lx

3.Generate the instruction OP Lz,Lx where Lz is the location of z. If z is in both in a register and in memory, prefer the register. Update the address descriptor of x to indicate that x is in Lx. If Lx is a register update its descriptor to indicate that it contains x. Remove x from all other register descriptors.

4.If y and z has no next uses, are not live on the exit from block, and they are in registers, change the register descriptors so that they do not contain y or z respectively.

•At the end of block, if a name is live after block and it is not in memory, we generate a move instruction for this name.

108

Page 109: PCD

Function getreg for Lx (x := y op z)

1.If y is in a register that does not hold no other names, and y is not live and has no next use after x:=y op z, then return the register of y as Lx. Update the address descriptor of y to indicate that y is no longer in Lx.

2.If (1) fails, return an empty register for Lx if there is one. 3.If (2) fails if x has a next use in the block (or OP is a special instruction

needs a register), find a suitable occupied register and empty that register. 4.If x is not used in the block, or no suitable register can be found, select the

memory location of x as Lx. 5.A more sophisticated getreg function can be designed.

Code Generation Example

REG. DESC. ADDR. DESC.Registers are empty

t:=a-b MOV a,R0 R0 contains a a in R0SUB b,R0 R0 contains t t in R0

u:=a-c MOV a,R1 R1 contains a a in R0SUB c,R1 R1 contains u u in R1

v:=t+u ADD R1,R0 R0 contains v v in R0d:=v+u ADD R1,R0 R0 contains d d in R0

MOV R0,d d in R0 and memory

Handling Indexing Operations in Quadraples

a := b[i] We have to move i into a register first (if it is not in a register).

i in a register MOV b(Ri),a

i in static data area MOV Mi,R MOV b(R),a

i in the stack MOV Si(ARReg),R MOV b(R),a

109

Page 110: PCD

Handling Pointer Operations in Quadraples

a := *p We have to move p into a register first (if it is not in a register).

p in a register MOV *Mp,a

p in static data area MOV Mp,Ro MOV *R,a

p in the stack MOV Si(ARReg),Ro MOV *R,a

Handling Conditional Statements

Most of the machines uses a set of condition codes to indicate whether the last quantity computed or loaded into a register is negative,zero, or positive.

A compare CMP can be used to set these condition codes without evaluating the value.

Then a conditional jump instruction (based on these conditional codes) can be used for designated condition: < = > <= >=

if a<b goto l1 CMP a,b a:=b+c MOV b,R0 CJ< l1 if a<0 goto l2 ADD c,R0

MOV R0,aCJ< l2

Register Allocation

Just holding values in registers during a single block may not produce good results.

We need some other techniques for register allocation:o –Putting specific values into fix registers – activation record pointer, stack

pointer, ...o –global analysis (across the blocks) of the variables to decide which

variables will be in registers. Frequently used variables are kept in the registers. A frequently used variable in an inner loop should be in a register.

o –Or, in some programming languages (such as C), programmer can use register declarations to keep certain variables in registers.

110

Page 111: PCD

a:=b+cd:=d-be:=a+f

f:=a-d

b:=d+fe:=a-c

b:=d+c

B1

B2B3

B4

bcdf

acdef

acde

cdef cdef

bcdef

acdf

bcdef

bcdef - live

Usage Counts

Keeping a variable x in a register for the duration of a loop L è save one unit of cost for each reference to x if x is in a register.

If a variable x has subsequent usages in next blocks, it should stay in a register. So, if x stays in the register:

o –we save one unit every time x is referenced prior to any definition of x.o –we save two units because we avoid a store of x at the end of a block.

Benefit (approximate) of keeping x in the register is:

where use(x,B) is the number of times x is used in B prior to any definition of x. live(x,B) is 1 if x is live on exit from B, and is assigned a value in B; otherwise

live(x,B) is 0

Usage Counts – Example

111

∑bocksBinL

(use (x ,B )+2∗live ( x , B ))

Page 112: PCD

MOV R1,R0ADD c,R0SUB R1,R2MOV R0,R3ADD f,R3MOV R3,e

MOV R0,R3SUB R2,R3MOV R3,f

MOV R2,R2ADD f,R1MOV R0,R3SUB c,R3MOV R3,e

MOV R2,R1ADD c,R1

MOV b,R1MOV d,R2

MOV R1,bMOV R2,d

MOV R1,bMOV R2,d

Usage Counts – Example

benefit-a-inreg = 0+2*1 + 1+2*0 + 1+2*0 + 0+2*0 = 4o use(a,B1)+2*(live,B1)+ use(a,B2)+2*(live,B2)+ use(a,B3)+2*(live,B3)+

use(a,B4)+2*(live,B4) benefit-b-inreg = 2+2*0 + 0+2*0 + 0+2*1 + 0+2*1 = 6

So, benefits:a: 4 Thus, b and d must be in registers. If we can only useb: 6 two registers.c: 3 If we have a third one we can put one of a, e, f into register.d: 6 For example, a into a register.e: 4f: 4

Code Sequence – if a,b,d in registers (globally)

112

Page 113: PCD

Register Allocation by Graph Coloring

•First, for each symbol, we assign a symbolic register (we assume that we have symbolic registers as much as we need).

•A register-interference graph is created in which the nodes are symbolic registers, and there is an edge between two nodes if the names in both nodes are live at the same time.

•Then, we have N machine registerso –we apply N-coloring problem to this register-inference graph to find a

solution. If two nodes have an edge, they cannot have a same color (i.e. they cannot be assigned to same machine registers because they are live at the same time).

o –Since graph-coloring problem is NP-complete, we use certain heuristics to get approximations to find the solutions

Register Allocation by Graph Coloring –Example

DAG Representation of Basic Blocks

•Directed Acyclic Graphs (dags) can be useful data structures for implementing transformations on basic blocks.

•Using dags

x:=1y:=2w:=x+yz:=x+1u:=x*yt:=z*2

s1:=1s2:=2s3:=s1+s2s4:=s1+1s5:=s1*s2s6:=s4*2

r1:=1r2:=2r3:=r1+r2r3:=r1+1r1:=r1*r2r2:=r3*2

113

Page 114: PCD

o –we can easily determine common sub-expressionso –We can determine which names are evaluated outside of the block, but

used in the block. •First, we will construct a dag for a basic block. •Then, we apply transformations on this dag. •Later, we will produce target code from a dag.

A dag for A Basic Block

A dag for a basic block is:o –Leaves are labeled with unique identifiers(names, constants). If the value

of a variable is changed in a basic block we use subscripts to distinguish two different value of that name.

o –Interior nodes are labeled by an operator symbolo –Interior nodes optionally may also have a sequence of names as labels.

So, for each basic block we can create a dag for that basic block.

Three-Address Codes for A Basic Block

1: t1 := 4*i 2: t2 := a[t1] 3: t3 := 4*i 4: t4 := b[t3] 5: t5 := t2*t4 6: t6 := prod+t5 7: prod := t6 8: t7 := i+1 9: i := t710: if i<=20 goto 1

114

Page 115: PCD

Corresponding DAG

Construction of DAGs

•We can systematically create a corresponding dag for a given basic block. •Each name is associated with a node of the dag. Initially, all names are undefined

(i.e. they are not associated with nodes of the dag). •For each three-address code x := y op z

o –Find node(y). If node(y) is undefined, create a leaf node labeled y and let node(y) to be this node.

o –Find node(z). If node(z) is undefined, create a leaf node labeled y and let node(z) to be this node.

o –If there is a node with op, node(y) as its left child, and node(z) as its right child this is node is also treated as node(x).

o –Otherwise, create node(x) with op, node(y) as its left child, and node(z) as its right child.

Applications of DAGs

We automatically detect common sub-expressions. We can determine which identifiers whose values are used in the block. (the

identifier at leaves). We can create simplified quadraples for a block using its dag.

o –taking advantage of common sub-expressionso –without performing unnecessary move instructions.

115

Page 116: PCD

In general, the interior nodes of a the dag can be evaluated in any order that is a topological sort of the dag.

o –In topological sort, a node is not evaluated until its all children are evaluated.

o –So, a different evaluation order may correspond to a better code sequence.

Simplified Basic Block

t1 := 4 * it2 := a [t1]t4: = b[t1]

t5 := t2 * t4 prod := prod + t5i := i + 1if i<=20 goto (1)

Some Problems

we have to careful when using arrays, pointers.

x := a[i] x:=a[i] unsafe optimization whena[j]:=y z:=x i is equal to j.z:=a[i] a[j]:= y

In these cases, we should not treat a[i] as common sub-expression.

Peephole Optimization

Peephole Optimization is a method to improve performance of the target program by examining a short sequence of target instructions (called peephole), and replacing these instructions shorter and faster instructions.

o –peephole optimization can be applicable to both intermediate codes and target codes.

o –the peephole can be in a basic block (sometimes can be across blocks).o –we may need multiple passes to get best improvement in the target code.o –we will look at certain program transformations which can be seen as

peephole optimization.

1: t1 := 4*i 2: t2 := a[t1] 3: t3 := 4*i 4: t4 := b[t3] 5: t5 := t2*t4 6: t6 := prod+t5 7: prod := t6 8: t7 := i+1 9: i := t710: if i<=20 goto 1

116

Page 117: PCD

Redundant Instruction Elimination

MOV R0,a MOV R0,aMOV a,R0•We can eliminate the second instruction, if there is no jump instruction jumping to that instruction.

Unreachable Code

We may remove unreachable codes.#define debug 0..if (debug==1) { print debugging info }

This is an unreachable code sequence. So we can eliminate it.

Flow-of-Control Optimizations

goto L1 goto L2.

L1: goto L2 L1: goto L2---------------------------------------------- if a<b goto L1 if a<b goto L2

. L1: goto L2 L1: goto L2-----------------------------------------------goto L1 if a<b goto L2

. goto L3L1: if a<b goto L2 .L3: L3:

Other Peephole Optimizations

Algebraic Simplifications:o –x := x+0o –x := x*1o –... more

Reduction in Strengtho –x := y**2 x := y*yo –x := y*2 x := lshift(y,1)

Specific Machine Instructions

117

Page 118: PCD

o –The target machine may specific instructions to implement specific operations.

o –auto increment, auto decrement, ...

Generating Code From DAGs

We can directly generate target code from a dag of a basic block. In fact, if the dag of the basic block is a tree, we can generate the optimal code

(optimal size in our target machine) We will look at an algorithm which produces optimal codes from trees. We will use the fewest number of temporaries. We may change the structure of the tree to get optimal code. The structure of the tree effects the generated code

Rearranging the Order

(a+b)-(e-(c+d))

t1 := a+b - t4t2 := c+dt3 := e-t2 + t1 - t3t4 := t1-t3

a b e + t2

c d

Target Codes

only t4 is live on exit from the block, and we have only registers R0 and R1t1:=a+b t2:=c+d t3:=e-t2 t4:=t1-t3 t2:=c+d t3:=e-t2 t1:=a+b t4:=t1-t3

MOV a,R0 MOV c,R0ADD b,R0 ADD d,R0MOV c,R1 MOV e,R1ADD d,R1 SUB R0,R1MOV R0,t1 MOV a,R0MOV e,R0 ADD b,R0SUB R1,R0 SUB R1,R0MOV t1,R1 MOV R0,t4SUB R0,R1MOV R1,t4 Revised code sequence

118

Page 119: PCD

Heuristic Ordering of DAGs

The reordering may improve the generated target code.o –In the previous example, we got an improvement because t4 is

immediately evaluated after t1 which is the left operand of t4. In selecting an ordering for the nodes of a dag, we are only constrained to

preserve the edge relationships of the dag. We can use a heuristic ordering algorithm, which attempts as far as possible to

make the evaluation of a node immediately follow the evaluation of its leftmost argument.

Heuristic Ordering of DAGs – Algorithm

while (unlisted interior nodes remain) {

select an unlisted node n, all of whose parents have been listed;list n;while (the leftmost child m of n has no unlisted parents and is not leaf) {

// since n was just listed, m is not listed yet

list m;n ;= m;

}}

Heuristic Ordering of DAGs – Example

* 1

+ 2 - 3

* 4

- 5 + 6

+ 7 c 8 d 11 e 12

a 9 b 10

119

Page 120: PCD

The resulting list: 1234576 6754321 is evaluation order

t6:=d+et7:=a+bt5:=t7-ct4:=t5*t6t3:=t4-et2:=t6+t4t1:=t2*t3

Optimal Ordering for Trees

If the dag representation of a basic block is a tree, we can have an algorithm which finds the optimal order (shortest instruction sequence) for the evaluation of the instructions in that basic block.

The algorithm has two parts: 1.Labeling – Label each node of the tree (bottom-up) with an integer that denotes

the fewest number of registers required to evaluate the tree with no stores of intermediate results.

2.Code Generation from The Labeled Tree – We traverse the tree by looking the computed label of the tree, and emit the target code during that traversal.

–For a binary operator, we evaluate the hardest operand first, then we evaluate the other operand.

–The hardest operand requires more registers

Labeling – Algorithm

if (n is a leaf) {if (n is the leftmost child of its parent)

label(n) := 1;else

label(n) := 0;else { // interior node (assume that a binary operator)

if (label(child1)=label(child2))label(n) := label(child1)+1

elselabel(n) := max(label(child1),label(child2));

}In general, if c1,c2,...,ck are children of n ordered by label label(c1)>=label(c2)>=...>=label(ck)label(n) := max(label(ci)+i-1) where i from 1 to k.

120

Page 121: PCD

Labeling – Example

t4 2

t1 1 t3 2

a 1 b 0 e 1 t2 1

c 1 d 0

Code generation From Labeled Tree This algorithm takes a labeled tree as input, and generates the target code and

leaves the the result of the tree in the register R0. The algorithm is a recursive procedure. We have two stacks:

o –rstack – register stack. Initially R0,R1,...,R(r-1)o –tstack – temporaries stack. Initially T0,T1,...

We have operationso –swap(rstack) – to swap two top registers in rstacko –pop(stack) – pop an item from the stack, and return ito –push(stack,item) – push the given item into the stack

–For a binary operator, we evaluate the hardest operand first, then we evaluate the other operand.

–The hardest operand requires more registers

Code generation From Labeled Tree – Algorithm

procedure gencode (n) {// case 0if (n is a left leaf representing operand x and n is the leftmost child of its parent)

emit(‘MOV’ x ‘,’ top(rstack));else if (n is an interior node with operator op, left child n1, and

right child n2) {// other cases

// case 1 – n2 is just simple operandif (label(n2)=0) {

let x be the operand represented by n2;gencode(n1);emit(op x ‘,’ top(rstack));

}

121

Page 122: PCD

// case 2 – n2 is harderelse if (1<=label(n1)<label(n2) and label(n1)<r) {

swap(rstack);gencode(n2);R := pop(rstack); // R holds the result of n2gencode(n1);emit(op R ‘,’ top(rstack));push(rstack,R);swap(rstack);

}// case 3 – n1 is harderelse if (1<=label(n2)<=label(n1) and label(n2)<r) {

gencode(n1);R := pop(rstack); // the result of n1 in Rgencode(n2);emit (op top(rstack) ‘,’ R);push(rstack,R);

}

// case 4, both labels >= relse {

gencode(n2);T := pop(tstack);emit(‘MOV’ top(rstack) ‘,’ T);gencode(n1);push(tstack,T);emit(op T ‘,’ top(rstack))

}

Codegen From Labeled Tree – Example

gencode(t4) case2 R1 R0gencode(t3) case3 R0 R1

gencode(e) case0 R0,R1MOV e,R1

gencode(t2) case1 R0gencode(c) case0 R0

MOV c,R0ADD d,R0

SUB R0,R1gencode(t1) case1 R0

gencode(a) case0 R0MOV a,R0

ADD b,R0SUB R1,R0

122

Page 123: PCD

Optimal Code

codegen produce optimal code for our target machine if we assume thato –there are no common sub-expressiono –there are no algebraic properties of operators which effect the timing.

Algebraic properties of operators (such as commutative, associative) may effect the generated code.

When there are common sub-expressions, the dag will no longer be a tree. In this case, we cannot apply the algorithm directly.

GENCODE for general DAGs

If the dag of a basic block is not tree, we cannot apply gencode procedure directly to that dag.

We partition the dag into trees, and apply gencode to these trees. Then we combine the solutions. This may not be optimal solution, but it will be very good solution.

Each shared node will be the root of a tree. Then, we can put these trees into an evaluation order.

Partitioning a dag into trees

1 1

2 3 2 3

4 7 4 4 9

5 6

7 8 9 4

5 6

7 8 8 9

7 8 9

123

Page 124: PCD

A Dynamic Programming Alg. for Code Generation

We can have a general purpose algorithm (using dynamic programming) for different target machines.

This algorithm can be applicable to the broad class of register machines with complex instructions.

We will assume that we have a similar target machine:o –r machine registerso –load instructiono –store instruction.o –although each instruction can have different cost, for simplicity we will

assume that the cost of each of them is one unit.

Principle of Dynamic Programming

OP

E1 E2

Optimal solution of this node can be found:o –finding the optimal solution E1 and E2o –and combining these optimal solutions

What is the optimal solution for E1 with i registers (where i<=r)? First, we evaluate optimal solutions (with different number registers) for the

children, then we find the optimal solution for the node

Contiguous Evaluation

The contiguous evaluation of a tree is:o –first evaluate left sub-tree , then evaluate right sub-tree, and the root.o –Or, first evaluate the right sub-tree, then evaluate the left sub-tree, and

finally the root. In non-contiguous evaluations, we may mix the evaluations of the sub-trees. For any given machine-language program P (for register machines) to evaluate an

expression tree T, we can find an equivalent program Q such that: 1.Q does not have higher cost than P 2.Q uses no more registers than 3.Q evaluates the tree in a contiguous fashion. –This means that every expression tree can be evaluated optimally by a

contiguous program.

124

Page 125: PCD

Example

Assume that we have the following machine codes, and the cost of each of them is one unit.

o –mov M,Rio –mov Ri,Mo –mov Ri,Rjo –OP M,Rio –OP Rj,Ri

Assume that we have only two registers R0 and R1. First, we have to evaluate cost arrays for the tree.

125