You are on page 1of 16

CS1352 Principles of Compiler Design Unit-III Question and answers ----------------------------------------------------------------------------------------------------1) What is intermediate code?

In Intermediate code generation we use syntax directed methods to translate the source program into an intermediate form programming language constructs such as declarations, assignments and flowof-control statements.

intermediate code is:
• • •

the output of the Parser and the input to the Code Generator. relatively machine-independent: allows the compiler to be retargeted. relatively easy to manipulate (optimize).

2) What are the advantages of using an intermediate language? Advantages of Using an Intermediate Language Retargeting - Build a compiler for a new machine by attaching a new code generator to an existing front-end. 2. Optimization - reuse intermediate code optimizers in compilers for different languages and different machines. Note: the terms “intermediate code”, “intermediate language”, and “intermediate representation” are all used interchangeably. 3) What are the types of intermediate representations? There are three types of intermediate representation:1. Syntax Trees 2. Postfix notation 3. Three Address Code

2 Postfix notation is a linearized representation of a syntax tree. A DAG (Directed Acyclic Graph) gives the same information but in a more compact way because common subexpressions are identified. 4) Construct a syntax directed definition for constructing a syntax tree for assignment statements. using a staff. The postfix notation for the syntax tree in the fig is a b c uminus + b c uminus * + assign The edges in a syntax tree do not appear explicitly in postfix notation. . . A syntax tree for the assignment statement a:=b*-c+b*-c appear in the figure.Semantic rules for generating three-address code from common programming language constructs are similar to those for constructing syntax trees of for generating postfix notation. fig8. Syntax tree for assignment statements are produced by the syntax directed definition in fig. of an expression in postfix notation. it is a list of the nodes of the in which a node appears immediately after its children. They can be recovered in the order in which the nodes appear and the no. Graphical Representations A syntax tree depicts the natural hierarchical structure of a source program. The recovery of edges is similar to the evaluation. of operands that the operator at a node expects.

2 appear in Fig. starting from the root at position IO. right) return a pointer to an existing node whenever possible.nptr) E.nptr := mknode(‘+’. representing the lexeme associated with that occurrence of S → id := E E → E1 + E2 E → E1 * E2 E → .4(a) .nptr := mkleaf(id.nptr) E.8. child) and mknode(op. E.nptr .nptr := E1. instead of constructing new nodes.nptr . then attribute name might be the index of the first character of the lexeme. mkleaf(id. E1. The token id has an attribute place that points to the symbol-table entry for the identifier id.nptr) E. nodes are allocated from an array of records and the index or position of the node serves as the pointer to the node. id. fig8. E1.nptr := mknode( ‘assign’.4(b). lf the lexical analyzer holds all lexemes in a single array of characters.E2.E2. All the nodes in the syntax tree can be visited by following 5) How a syntax tree is represented? This same syntax-directed definition will produce the dag if the functions mkunode(op. Two representations of the syntax tree in Fig8.nptr := mkunode(‘uminus’. id.nptr := mknode(‘* ’. Each node is represented as a record with a field for its operator and additional fields for pointers to its E → ( E1 ) E → id Semantic Rule S.nptr E. left.4. In Fig 8. E1.nptr) E.

so Fig.5. op stands for any operator.0 id b 1 id c 2 uminus 1 3 * 0 2 4 id b 5 id c 6 uminus 5 7 * 4 6 8 + 3 7 9 id a 1 assign 9 8 1 …… into a sequence t1 := y * z t2 : = x + t1 fig8. three-address code is a linearized representation of a syntax tree or a dag in which explicit names correspond to the interior nodes of the graph. 8. as there is only one operator on the right side of a statement. Note that no built-up arithmetic expressions are permitted. Variable names can appear directly in three-address statements.4. such as a fixed. 8. This unraveling of complicated arithmetic expressions and of nested flow-of-control statements makes three-address code desirable for target code generation and optimization. or compiler-generated temporaries. y. or a logical operator on Boolean-valued data.three-address code to be easily rearranged – unlike postfix notation. 8. Code for syntax tree t1 := -c t2 := b * t1 t3 := -c t4 := b * t3 t5 := t2 + t4 a := t5 Code for DAG . 8. The use of names for the intermediate values computed by a program allow. constants.5(a) has no statements corresponding to the leaves in Fig. Thus a source language expression like x+y*z might be translated where t1 and t2 are compiler-generated temporary names. The syntax tree and dag in Fig.or floating-point arithmetic operator.2 are represented by the three-address code sequences in Fig. and z are names.4(b) 6) What is three address code? Three-address code is a sequence of statements of the general form X:= Y Op Z where x.

two for the operands and one for the result. In the implementations of three-address code given later in this section. etc.6. Conditional jumps such as if x relop y goto L. The three-address statement with label L is the next to be executed. Copy statements of the form x: = y where the value of y is assigned to x. where op is a binary operation. The unconditional jump goto L. logical negation. Actual indices can be substituted for the labels either by making a separate pass. If not. A symbolic label represents the index of a three-address statement in the array holding inter.mediate code. 7) What are the types of three address statements? Types Of Three-Address Statements Three-address statements are akin to assembly code.) to x and y. 6. or by using ”back patching. Statements can have symbolic labels and there are statements for flow of control. =. arithmetic or logical 2. and conversion operators that. Assignment instructions of the form x:= op y. param x and call p. n for procedure calls and return y. Assignment statements of the form x: = y op z.t1 := -c t2 := b * t1 t5 := t2 + t2 a := t5 The reason for the term ”three-address code” is that each statement usually contains three addresses. the three-address statement following if x relop y goto L is executed next. where y representing a returned value is optional.” discussed in Section 8. 3. for example. 4. Here are the common three-address statements used in the remainder of this book: 1. Their typical use is as the sequence of three-address statements param x1 param x2 . >=. This instruction applies a relational operator (<. where op is a unary operation. shift operators. and executes the statement with label L next if x stands in relation relop to y. a programmer-defined name is replaced by a pointer tc a symbol-table entry for that name. Essential unary operations include unary minus. 5. as in the usual sequence. convert a fixed-point number to a floating-point number.

and x is a pointer name or temporary. x~. and i refer to data objects. techniques for reusing temporaries are given in Section S. The first of these sets x to the value in the location i memory units beyond location y.. say y. In both these instructions. that denotes an expression with an I-value such as A[i. x:= *y and *x: = y. The r-value of x is made equal to the contents of that location. That is. temporary names are made up for the interior nodes of a syntax tree.code represents the threeaddress code for the assignment S. and 2. Syntax-Directed Translation into Three-Address Code When three-address code is generated. . 8. 8. it produces the code in Fig. In the statement x: = ~y.7. In general.. Finally. +x: = y sets the r-value of the object pointed to by x to the r-value of y.. The optimizer and code generator may then have to work harder if good code is to be generated.. followed by the assignment id. The statement x[i]:=y sets the contents of the location i units beyond x to the value of y.value is a location. language operations. x”). The value of non-terminal E on the left side of E  E1 + E will be computed into a new temporary t. n” is not redundant because calls can be nested. For the moment. presumably y is a pointer or a temporary whose r. perhaps a temporary. A small operator set is easier to implement on a new target E.5(a). E. If an expression is a single identifier. then y itself holds the value of the expression. The first of these sets the value of x to be the location of y. 7.code. 8. Indexed assignments of the form x: = y[ i ] and x [ i ]: = y. y. we create a new name every time a temporary is needed. The choice of allowable operators is an important issue in the design of an intermediate form.address code for id: = E consists of code to evaluate E into some temporary t.6 generates three-address code for assignment statements. The S-attributed definition in Fig. the name that will hold the value of E. x. The operator set must clearly be rich enough to implement the operations in the source language. The integer n indicating the number of actual parameters in ”call p. Presumably y is a name.param xn call p. Given input a: = b+ – c + b+ – c.3. j]. The non-terminal E has two attributes: = t. the sequence of three-address statements evaluating E. the three. the r-value of x is the l-value (location) of some object!. a restricted instruction set may force the front end to generate long sequences of statements for some source.. The synthesized attribute S. The implementation of procedure calls is outline d in Section 8. 8) Explain the process of syntax directed translation of three address code. However. Address and pointer assignments of the form x:= &y. n generated as part of a call of the procedure p(x.

8. and z are evaluated when passed to gen. are taken literally.. 8. Flowof-control statements can be added to the language of assignments in Fig. respectively. is generated using’ new attributes S.6 to represent the three-address statement x: = y + z. we use the notation gen(x ’: =’ y ’+’ z) in Fig. and quoted operators or operands. For convenience. . y. threeaddress statements might be sent to an output file. In practice.7.. like ’+’.The function newtemp returns a sequence of distinct names t1.. Expressions appearing instead of variables like x. the code for S .while E do S. t2.6 by productions and semantic rules )like the ones for while statements in Fig. In the figure.begin and S. in response to successive calls. 8. rather than built up into the code attributes.after to mark the first statement in the code for E and the statement following the code for S.

the intermediate form produced by the syntax-directed translations in this chapter can he changed by making similar modifications to the semantic rules. .code:= E1. these statements can be implemented as records with fields for the operator and the operands. 1he postfix notation for an identifier is the identifier itself.5). that is. is the semantic rule E. 2.6 (or see Fig. Note that S. Three such representations are quadruples. For example. associated with the production E – E. f:expressions that govern the flow of control may in general be Boolean expressions containing relational and logical operators. control leaves the while statement. and indirect triples.These attributes represent labels created by a function new label that returns a new label every time it is called. The semantic rules for while statements in Section 8.after becomes the label of the statement that comes after the code for the while statement. The rules for the other productions concatenate only the operator after the code for the operands. Postfix notation -an be obtained by adapting the semantic rules in Fig. Implementations of three-Address Statements A three-address statement is an abstract form of intermediate code. 9) Explain in detail the implementation of three address statements.code || ’uminus’ 1n general.6 differ from those in Fig. when the value of F becomes zero.7 to allow for flow of contro1 within Boolean expressions. 8. In a compiler. We assume that a non-zero expression represents true. 8. triples.

Note that the copy statement a:= t5 is encoded in the triple representation by placing a in the arg 1 field and using the operator assign. for the arguments of op. as shown in Fig. H. 8. Triples To avoid entering temporary names into the symbol table. as in Fig.defined names or constants) or pointers into the triple structure (for temporary values). 8.8(b) correspond to the quadruples in Fig. The op field contains an internal code for the operator. z in arg 2. op Arg1 Arg2 Since three fields are used. which we call op. In practice. 8. 8. ) (2 uminus c ) op Arg1 Arg2 Result (3 * b (2) (0 uminus c t1 ) ) (4 + (1) (3) (1 * b t1 t2 ) ) (5 := a (4) (2 uminus c t3 fig8. and result are normally pointers to the symbol-table entries for the names represented by these fields.8(a) ) ) (3 * b t3 t4 ) (4 + t2 t4 t5 ) (5 := t5 a ) Qudraples fig8. arg 2. are either pointers to the symbol table (for programmer. we might refer to a temporary value bi the position of the statement that computes it.8(b) Triples Parenthesized numbers represent pointers into the triple structure.’ Except for the treatment of programmer-defined names. and x in result. 8. Statements with unary operators like x: = – y or x: = y do not use arg 2. They are obtained from the three-address code in Fig. The three-address statement x:= y op z is represented by placing y in arg 1. The triples in Fig. temporary names must be entered into the symbol table as they are created.S(a) are for the assignment a: = b+ – c + b i – c. triples (0 uminus c correspond to the representation of a syntax tree or dag by an array of ) (1 * b (0) nodes. The contents of fields arg 1. If so. the information needed to interpret the different kinds of entries in the arg 1 and arg2 fields can be encoded into the op field or some additional fields. A ternary operation like x[ i ]: = y requires two entries in the triple structure. and result. while x: = y[i] is naturally represented as two operations in Fig. . arg l.5(a). as in Fig. this intermediate code format is known as triples. The fields arg l and arg2. arg 1 and arg2. arg 2.8(a). Conditional and unconditional jumps put the target label in result. Operators like param use neither arg2 nor result. If we do so.8(b). 8.9(b). 8. while symbol-table pointers are represented by the names themselves.9(a). The quadruples in Fig.4. three-address statements can be represented by records with only three fields: op.Quadruples A quadruple is a record structure with four fields.

Indirect Triples Another implementation of three-address code that has been considered is that of listing pointers to triples. Suppose that addresses of consecutive integers differ by 4 on a byte. This implementation is naturally called indirect triples. rather than listing the triples themselves.addressable machine.10. and hence their addresses. 8. We . The instruction set of the target machine may also favor certain layouts of data objects. let us use an array statement to list pointers to triples in the desired order. The relative address consists of an offset from the base of the static data area or the field for local data in an activation record. 10) How declarations are translated into intermediate code? DECLARATIONS As the sequence of declarations in a procedure or block is examined. The address calculations generated by the front end may therefore include multiplications by 4. Then the triples in Fig. When the front end generates addresses. For example. we can lay out storage for names local to the procedure. For each local name. we create a symbol-table entry with information like the type and the relative address of the storage for the name. 8. it may have a target machine in mind.8(b) might be represented as in Fig.

width := 8} T  array [num ] of T1 {T.he first declaration is considered.width :=num. We use synthesized attributes type and width for non-terminal T to indicate the type and width.. The procedure enter(name.width} T  ^T1 {T.val X T1.width } T  integer {T. As each new name is seen. Offset:= offset + T.width :=4} T  real {T. In the translation scheme of Fig.D D  id : T {enter (id. Example 7. as in Section 6. offset) creates a symbol-table entry for name. gives it type and relative address offset in its data area. Before ’. The width of an array is obtained by multiplying the width of each element by the number of elements in the array.width:=4} . offset is set to 0.type := real. type. T. T1. Declarations in a Procedure The syntax of languages such as C.type). then attribute type might be a pointer to the node representing a type expression. 8.val. S. Pascal. and offset is incremented by the width of the data object denoted by that name. allows all the declarations in a single procedure to be processed as a group. T. In Fig. and Fortran. In this case. T. offset).type).type :=pointer (T.3 shows how data objects are aligned by two compilers.l. If type expressions are represented by :=array(num. T. I . integers have width 4 and real have width 8. say offset.type. can keep track of the next avai1able relative address. Attribute type represents a type expression constructed from the basic types integer and real by applying the type constructors pointer and array.ignore alignment of data objects here. or number of memory units taken by objects of that type.I1 non-terminal P generates a sequence of declarations of the form id: T. that name is entered in the symbol table with offset equal to the current value of offset. a global variable.The width of each pointer is assumed to be 4. PD DD.type :=integer. T.

2) can be restated as: PMD M ε (offset:= 0} Keeping Track of Scope Information In a language with nested procedures.In Pascal and C.11 . processing of declarations in the enclosing procedure is temporarily suspended. 8. The nesting structure of the procedures can be deduced from the links between the symbol tables. The symbol tables for procedures readarray.11 is that the procedure enter is told which symbol table to make an entry in. Using a marker non-terminal M. 8. the name represented by id itself is local to the enclosing procedure. exchange. The only change from the treatment of variable declarations in Fig. l2. A new symbol table is created when a procedure declaration D proc id D~. This approach will he illustrated by adding semantic rules to the following language. The non-terminal T has synthesized attributes type and width. 7. Clever implementations can be substituted if desired. When a nested procedure is seen. PD D  D. a pointer may be seen before we learn the type of the object pointed to Storage allocation for such types is simpler if all pointers have the same width. and quicksort point back to that for the containing procedure sort.S The production for non-terminals S for statements and T for types are not shown because we focus on declarations. as in the translation scheme of Fig. symbol tables for five procedures are shown in Fig. 8. 8. The new table points back to the symbol table of the enclosing procedure.22. (8. suppose that there is a separate symbol table for each procedure in the language (8. The initialization of offset in the translation scheme of Fig. For simplicity. the program is in Fig. For example.3). . Since partition is declared within quicksort. can be used to rewrite productions so that all actions appear at the ends of right sides.D | id: T proc id. One possible implementation of a symbol table is a linked list of entries for names. 5 is seen. consisting of the entire program. D. names local to each procedure can be assigned relative addresses using the approach of Fig. and entries for the declarations in D~ are created in the new table.1 is more evident if the first production appears on one line as: P {offset:= 0 } D Non-terminals generating a. its table points to that of quick sort. called marker non-terminals in Section 5.6.

mktable(previous) creates a new symbol table and returns a pointer to the new table. 2.The semantic rules are defined in terms of the following operations: I. enter places type and relative address offset in fields within the entry. enter(table. 3. presumably that for the enclosing procedure. Again. We can also number the procedures in the order they are declared and keep this number in the header. width) records the cumulative width of all the entries table in the header associated with this symbol table. The pointer previous is placed in a header for the new symbol table. addwidth(table. name. offset) creates a new entry for name name in the symbol table pointed to by table. The argument previous points to a previously created symbol table. type. . along with additional information such as the nesting depth of a procedure.

pop(offset)} Mε { t := mktable(nil). and we revert to examining the declarations in the closing procedure. tblptr will contain pointers to the tables for -ort. the top of stack offset is incremented by T. The pointer to the current symbol table is on top. S. and partition when the declarations in partition are considered. 8. Push(t. 8.width. l3 shows how data can be laid out in one pass.S { t := top(tblptr). created by operation mktable(nil). The argument newtable points to the symbol table for this procedure name.tblptr).12. pop(offset).top(offset)). Hence. S occurs.l3 is the first to be done. quicksort. the width of all declarations generated by D1 is on top of stack offset. it is recorded using addwidth. Pop(tblptr).. top(offset)). t)} . For each variable declaration id: T. All semantic actions in the sub-trees for B and C in A B C { actionA} are done before actionA the end of the production occurs.D2 D  proc id . With the symbol tables ot’ The other stack offset is the natural generalization to nested procedures of attribute offset in Fig. N D1 . The top element of offset is the next available relative address for a local of the current procedure. Here the argument top(tblptr) gives the enclosing scope of the new table. push(0. PMD {addwidth(top(tblptr).offset)} D  D1 . the action associated with the marker M in Fig. Again. name. an entry is created for id in the current symbol table. newtable) creates a new entry for procedure name in the symbol table pointed to by table. enterproc (table.4. id. when the action on the right side of D proc id: N D. 0 is pushed onto offset. Its action uses the operation mktable(top(tblptr)) to create a new symbol table. The non-terminal V plays a similar role when a procedure declaration appears. enterproc(top(tblptr). The action for non-terminal M initializes stack tblptr with a symbol table for the outermost scope. This declaration leaves the stack pointer unchanged. A pointer to the new table is pushed above that for the enclosing scope.’. the name of the enclosed procedure is entered into the symbol table of its enclosing procedure. At this point. The translation scheme in Fig. 8. pop(tblptr). The action also pushes relative address 0 onto stack offset. l I. and offset are then popped. using a stack tblptr to hold pointers to symbol tables of the enclosing procedures. addwidth(t.

type := record(top(tblptr)).width := top(offset). 8. push( we overlook the fact that the above production also allows procedure definitions to appear within records.T. T  record L D end {T.type.width } Nε { t := mktable(top(tblptr)) and arrays: T  record D end The actions in the translation scheme of Fig. push (0.13. tblptr). Pop(tblptr). offset)} Field Names in Records The following production allows non-terminal T to generate records in addition to basic types. pop(offset) } L ε { t:= mktable(nil). tblptr). Push(t. Push(t. offset) } .D  id : T {enter(top(tblptr). pointers. Since procedure definitions do not affect the width computations in Fig.I4 emphasize the similarity between the layout of records as a language construct and activation records. S. top(offset) := top(offset) +T. T.

The action following end in Fig. 8. the acting associated with the marker creates a new symbol table for the field names. Furthermore. 14 returns the width as synthesized attribute T. 11) 12) . The type T. 8.type is obtained by applying the constructor record to the pointer to the symbol table for this record.13 therefore enters information about the field name id into the symbol table for the record.width. the top of stack will hold the width of all the data objects within the record after the fields have been examined.After the keyword record is seen. A pointer to this symbol table is pushed onto stack tblptr and relative address 0 is pushed onto stack . The action for D  id: T in Fig.