BCSE307L Lab 5 21BAI1743
BCSE307L Lab 5 21BAI1743
BCSE307L Lab 5 21BAI1743
Prepared by:
Slot: L15+L16
LAB -5
ALGORITHM :
Define tokens: Use LEX to define tokens like identifiers,
constants, operators, etc.
Define grammar: In YACC, define grammar rules for
expressions, statements, and control structures.
Generate three-address codes: Within YACC actions, generate
three-address codes for each grammar rule. Use temporary
variables to store intermediate results.
Handle control flow: Implement control flow statements (if,
else, while) using labels and jumps in three-address codes.
Optimize codes: Apply optimizations like constant folding, dead
code elimination, and common subexpression elimination.
Generate final output: Output the optimized three-address
codes for the given program.
CODE :
Lex file
%{
#include <string.h>
#include "y.tab.h"
%}
%%
[a-z] { yylval.val = strdup(yytext); return VAR; }
[0-9]+ { yylval.val = strdup(yytext); return NUM; }
[\n] { return *yytext; }
. { return yytext[0]; }
%%
int yywrap() {}
Yacc file
%{
#include <stdio.h>
#include <string.h>
int yylex(void);
int yyerror(char *s);
void codegen(char);
void push(char *);
%}
%union
{
char *val;
}
%token<val> VAR
%token<val> NUM
%type<val> E
%type<val> T
%left '='
%left '+' '-'
%left '*' '/'
%%
S: E { return 0; };
E: E '=' T { codegen('='); }
|T
;
T: T '+' F { codegen('+'); }
| T '-' F { codegen('-'); }
|F
;
F: F '*' G { codegen('*'); }
| F '/' G { codegen('/'); }
|G
;
int main()
{
printf("Enter the infix expression:\n");
yyparse();
return 0;
}
OUTPUT :
AIM : Implement simple code optimization techniques
(Constant folding, Strength reduction and Algebraic
transformation).
ALGORITHM :
Tokenize the input code and build a parse tree using lexical
analysis (LEX) and syntax analysis (YACC).
Traverse the parse tree and evaluate constant expressions.
Replace nodes with constant expressions with their computed
values.
Identify opportunities to replace expensive operations with
cheaper ones.
Replace multiplications by powers of 2 with bit shifts, and
simplify other arithmetic expressions.
Apply algebraic identities to simplify expressions wherever
possible.
Examples include x * 0 to 0, x * 1 to x, x + 0 to x, x - 0 to x, etc.
Generate optimized code based on the modified parse tree.
Emit efficient instructions or three-address codes reflecting the
optimized logic.
Test the optimized code to ensure correctness and validate
against the original code's behavior.
CODE :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_VALUES 10
#define MAX_EXPR_LENGTH 20
struct op {
char l[MAX_EXPR_LENGTH];
char r[MAX_EXPR_LENGTH];
} op[MAX_VALUES], pr[MAX_VALUES];
int main() {
int i, j, n, z = 0;
printf("Enter the Number of Values: ");
scanf("%d", &n);
int result_d =
evaluateArithmeticExpressionWithVariables(pr[z - 1].r, n);
sprintf(pr[z - 1].r, "%d", result_d);
return 0;
}
OUTPUT :
AIM : Implement Back-End of the compiler for which three
address code is given as input and the 8086 assembly language
is produced as output.
ALGORITHM :
Read the three-address code instructions, which typically
involve operations with temporary variables and memory
locations.
Maintain a symbol table to keep track of variables, their types,
and memory locations.
Allocate memory for variables and temporary results based on
their data types.
Traverse each three-address code instruction.
Convert each instruction into equivalent 8086 assembly
language instructions.
Handle arithmetic operations, conditional jumps, loops,
function calls, and memory accesses according to the 8086
architecture.
Apply optimization techniques such as constant folding,
strength reduction, and algebraic transformations to optimize
the generated assembly code.
Optimize memory accesses and register usage for efficiency.
CODE :
#include <stdio.h>
#include <string.h>
int main() {
char icode[10][30], str[20], opr[10], var[5], op1[5], op2[5];
int i = 0;
return 0;
}
OUTPUT :