Compiler Construction: Lecture 9 - Semantic Analysis and Intermediate Code Generation
Compiler Construction: Lecture 9 - Semantic Analysis and Intermediate Code Generation
1
Quadruples
• Quadruples consist of four fields: one
operation and up to three operands.
• Quadruples also known as three address
codes.
• Quadruples can be implemented using an
enumerated type to represent the operations
and pointers to symbol table entries to
represent the operands.
2
AST for Sample
Program
Id Id Id
Call Set Write
ReadNums
ReadNums :=
Integer Real Begin Id
Id Id Read Read Id Id Id +
Id Id Id Id
arg i ReadNums:
arg y param i
call ReadNums param y
t1 := float(i) arg i
t2 := t1 + y call Read
x := t2 arg y
arg x call Read
call Write return
return
3
Generating Quadruples for Expressions
SET z := a * x + b * y
4
:=
z 1
+ 3
t1 := a * x
t2 := b * y 2
t3 := t1 + t2 * *
z := t3 a x b y
:= :=
z x z x
4
Generating Quadruples for IF-THEN -ENDIF
IF y > 0 THEN SET z := x/y
ENDIF;
if y <= 0 goto L3
IF z := x/y
L3:
> THEN
y 0
SET
:=
z /
x y
L4:
WHILE if x <= 0 goto L5
x := x/2.0
goto L4
> DO L5:
x 0
SET
:=
x /
x 2.0
5
Generating Quadruples for Call ProcId(x, y)
CALL ProcId(x, y);
Call
arg x
ProcId arg y
call ProcId
x y
ProcId:
param x
param y
ProcId … …
return
Integer Real Begin
x y
6
Semantic Actions in a Recursive-Descent Parser
void ProcHeader(void)
{
/* Header ::= program identifier ; */
if (thistoken != tokprogram)
error("Expected reserved word “
”\"program\"", linenum);
7
Semantic Actions and Recursively Parsing
Expressions
8
Changing The Parsing Algorithm TO
Accommodate Semantic Actions
Processing context -free expressions requires the use of a stack. The
Parsing algorithm uses a stack:
Place the start symbol in a node and push it onto the stack.
Fetch a token
REPEAT
Pop a node from the stack
IF it contains a terminal, match it to the current token (no match
indicates a parsing error) and fetch another token
ELSE IF it contains a nonterminal, look it up in the production table
using the nonterminal and the current token. Place the variables in
REVERSE order on the stack
ELSE IF it contains an action, call the appropriate procedure to
perform the action
UNTIL the stack is empty
9
void parse(void)
{
int i, lines = 0;
initparsestack();
parsetree = getparsenode(Nonterm, NTProgram);
parsepush(parsetree);
do {
/*
* Look up the production in the production
* table. If not there, terminate with an
* error message.
*/
thisnode = parsepop();
10
case Nonterm:
/* Expand the nonterminal and push the
* items on the right hand side in reverse
* order */
processnonterm(thisnode);
if (thisnode -> ParseItem == NTHeader)
AddProgName();
break;
case Action:
/* Perform the appropriate semantic action */
processaction(thisnode -> ParseItem);
break;
default:
error("Encountered invalid production”
“ item", linenum);
}
} while(!parseempty());
}
/*
* ProcessAction() - The driver for the
* semantic actions. It
* calls the appropriate
* function for the
* necessary semantic action.
*/
void processaction(int actiontype)
{
switch(actiontype) {
case AcAddProgName: AddProgName(); break;
case AcPushReal: PushReal(); break;
case AcPushInt: PushInt(); break;
… … … default:
error("Invalid action", linenum);
}
}
11
/*
* GenRead() - Generate the intermediate code
* for Read statements
*/
void GenRead(void)
{
struct addressrec x;
/*
* Generate two instructions: an arg
* instruction to pass the identifier as the
* one argument and a call of the Read
* procedure
*/
if (!isvalidtype(oldtabindex)) {
printlexeme(oldtabindex);
error(" is an invalid type", linenum);
}
x = setaddress(opnvar, oldtabindex);
nextop = genquad(opread, x, no_op, no_op);
}
12
/*
* CalcTerm() - Calculate a term in a
* expression being parsed. The factor and
* the rest of the term are on the semantic
* actions stack along with the operator,
* which is the middle item on the top of the
* stack.
*/
void CalcTerm(void)
{
enum tokentype tokoptor, tokopnd1, tokopnd2;
enum optype optor;
struct addressrec op1, op2, op3;
13
/*
* Convert the operand's symbol table entry
* into an address and generate the
* intermediate code
*/
op2 = getaddress(tokopnd1);
op3 = getaddress(tokopnd2);
/*
* GenArithQuad() - Generate an arithmetic
* quadruple instruction
*/
struct addressrec GenArithQuad
(struct addressrec op2,
enum optype optor, struct addressrec op3)
{
struct addressrec op1, op4;
enum optype otype;
/*
* If the two operands match, get a temporary
* variable of matching type. If not, convert
* the integer variable to real and get a real
* temporary variable.
*/
14
if (data_class(op2.opnd) == dtinteger)
if (data_class(op3.opnd) == dtinteger)
op1 = gettempvar(dtinteger);
else {
/* Convert the integer operand to real */
op4 = op2;
op2 = gettempvar(dtreal);
nextop = genquad(opfunc, op2, func,
op4);
op1 = gettempvar(dtreal);
}
15