编译原理---简单的代码生成程序
题目:
通过三地址代码序列生成计算机的目标代码,在生成算法中,对寄存器的使用顺序为:寄存器中存有 > 空寄存器 > 内存中存有 > 以后不再使用 > 最远距离使用
Input
单组输入,给定输出的三地址代码的个数和寄存器的个数.所有的变量为大写字母,寄存器的数量不超过9
Output
参照示例格式输出,不需要将最后的寄存器中的值写回内存
不再使用变量不用写回内存
Sample Input
[code]4 2 T:=A-B U:=A-C V:=T+U W:=V+U
Sample Output
[code]LD R0, A SUB R0, B LD R1, A SUB R1, C ADD R0, R1 ADD R0, R1
方法一:(PTA部分正确,一个寄存器过不去)
[code]#include <cstdio> using namespace std; int n,m;//n,m分别是三地址代码的个数和寄存器的个数 char s[110][10]; char R[10]; int cnt; int toR(char c)//判断c是否在某一寄存器中 { for(int i=0;i<m;i++) { if(R[i]==c)//若在哪一寄存器中将该寄存器号返回 return i; } return -1; } int getLast(int pos,char c)//pos是toR()返回的寄存器号 { for(int i=pos;i<n;i++) { if(s[i][3]==c||s[i][5]==c) { return i; } } return n; } int getPos(int pos) { if(cnt<m) { return cnt++; } int ans=-1; int m2=-1; for(int i=0;i<m;i++) { int n2=getLast(pos,R[i]); if(n2>m2) { m2=n2; ans=i; } } return ans; } void PrintOp(char c) { if(c=='+') printf("ADD"); else if(c=='-') printf("SUB"); else if(c=='*') printf("MUL"); else if(c=='\\') printf("DIV"); } void printRight(char c) { int pos=toR(c); if(pos!=-1) printf("R%d\n",pos); else printf("%c\n",c); } int main() { scanf("%d%d",&n,&m); cnt=0;//实时记录寄存器的使用个数 for(int i=0;i<n;i++)//输入三地址代码 scanf("%s",s[i]); for(int i=0;i<n;i++)//遍历每个三地址代码 { int pos=toR(s[i][3]); if(pos==-1)//三地址代码左边的第一个符号不在寄存器中 { pos=getPos(i); if(R[pos]&&getLast(i,R[pos])<n) { printf("ST R%d, %c\n",pos,R[pos]); } printf("LD R%d, %c\n",pos,s[i][3]); } PrintOp(s[i][4]); printf(" R%d, ",pos); printRight(s[i][5]); R[pos]=s[i][0]; } return 0; }
方法二:
[code]#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; char s[100][100]; char p[100]; int a, b, to = 0; int get(char ch)//寄存器中是否存在变量 { for (int i = 0; i < b; i++) { if (ch == p[i]) { return i; } } return -1; } int use(int x, char ch)//查找后面是否使用 { for (int i = x; i < a; i++) { if (ch == s[i][3] || ch == s[i][5]) return i; } return a; } int find(int x) { if (to < b) return to++; int w = -1; int ans = -1; for (int i = 0; i < b; i++) { int v = use(x, p[i]); if (v > ans) { ans = v; w = i; } } return w; } void print1(char ch) { if (ch == '+') printf("ADD "); else if (ch == '-') printf("SUB "); else if (ch == '*') printf("MUL "); else if (ch == '\\') printf("DIV "); } void print2(char ch) { int x = get(ch); if (x != -1) { printf("R%d\n", x); } else printf("%c\n", ch); } int main() { cin >> a >> b; for (int i = 0; i < a; i++) { cin >> s[i]; } for (int i = 0; i < a; i++) { int x = get(s[i][3]); if (x == -1) { x = find(i); if (p[x] != '\0' && use(i, p[x]) < a) { printf("ST R%d, %c\n", x, p[x]); p[x] = NULL; } printf("LD R%d, %c\n", x, s[i][3]); } print1(s[i][4]); printf("R%d, ", x); print2(s[i][5]); p[x] = s[i][0]; } return 0; }
简单代码生成算法:
假设只有A:=B op C的四元式序列策略
A)对每个四元式i: A:=B op C,依次执行下述步骤:
(1)以A:=B op C为参数,调用函数getreg(i:A:=B op C) 从getreg返回时,得到一寄存器R,用它作存放A现行 值的寄存器;(寻找存放A的寄存器)
(2)利用AVALUE[B]和AVALUE[C],确定出B和C现行值存 放位置B’和C’,如在寄存器,则把寄存器取作B’和C’;(尽量取寄存器中的A和C)
(3)如B’≠R,则生成目标代码 LD R,B’ op R,C’ (结果A存在R中) 否则B’=R,生成目标代码 op R,C’,如B’或C’为R,则删除AVALUE[B]或AVALUE[C]中的R(修改B和C的地址描述符)
(4)令AVALUE[A]={R},并令RVALUE[R]={A},以表变量A的现行值只在R中并且R中的值只代表A的现行值(修改A的寄存器和地址描述符)
(5)如B或C的现行值在基本块中不再被引用,它们也不是基本块出口之后的活跃变量,并且其现行值在某个寄存器Rk中,则删除RVALUE[Rk]中的B或C以及AVALUE[B]或AVALUE[C]中的Rk,使该寄存器不再为B或C所占用。(释放B、C不再用的寄存器,修改相应描述符)
B)处理完基本块中所有三地址代码之后,对现行值只 在寄存器R中的每个变量M,若它在出口之后是活跃的,则要存入内存,生成目标代码 ST R, M(活跃变量存入内存)
- 点赞
- 收藏
- 分享
- 文章举报
- 设计有穷自动机DFA实现C++简单程序的词法分析、扫描(编译原理实验) 推荐
- 编译原理结构框架11代码生成
- 在Linux下如何使用GCC编译程序、简单生成静态库及动态库
- 语义分析和中间代码生成——哈工大编译原理课程(三)
- GCC Coverage代码分析-GCC如何编译生成gcov/gcov-dump程序及其bug分析
- 编译原理 词法分析器简单实现 java代码
- [置顶] [编译原理读书笔记][第2章 一个简单的语法制导程序]
- 收了100元辛苦费,写了一个最简单的C#ASP.NET的3层架构例子代码,源码是通过代码生成器生成的【写程序的效率神奇的高】
- 一步步学习汇编系列(6)-从一个简单的程序谈编译,连接,执行,跟踪的原理
- 如何在windows编译生成嵌入式linux程序的代码
- 简单的代码生成程序
- 通过简单的Linux内核启动程序代码窥探操作系统的启动原理
- C#基础--.net平台的重要组成部分以及.net程序简单的编译原理
- 编译原理实验,实现一个代码解析程序
- 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库。
- 关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码
- 编译原理:运行时空间组织管理&优化&目标代码生成
- 在Linux下如何使用GCC编译程序、简单生成静态库及动态库。
- 在Linux下如何使用GCC编译程序、简单生成静态库及动态库
- VC编译下对一个超简单的缓冲区溢出程序的原理解析以及c程序的汇编分析