文章目录
在过去的四天里,我弄清楚了flex和bison的用法,知道了他们如何协作并联合编译,做到了不关注c代码只看.l和.y就能知道生成程序做了什么
同时掌握了makefile的编写和使用
现在是2025/3/29 Sat 12:19
我们需要搞定目标代码生成
32位MIPS汇编
- 固定长度指令:每条指令都用固定的32位长度,每条指令都是四个字节,所以内存数据的访问必须以32位严格对齐
- 寻址方式支持基址寻址、立即数寻址、寄存器寻址和伪立即数
- 跳转指令只有26位目标地址,加上2位对齐位,寻址空间256M
- 栈空间走向从高地址到低地址
- 包含32个寄存器
? 临时寄存器(临时寄存器池)
接下来需要:
-
熟悉pascal语言
-
熟悉32位MIPS汇编
-
熟悉生成目标代码的具体原理过程
先进行第一步:
SNL:程序头 声明部分 程序体
我看了编译课设参考书,大致有了个了解
然后进行第二部
复习计组大黑书第二章
3.31
我今天复习完了计组大黑书第二章,同时还复习了编译原理课本,看了参考书。
但是源码有问题,我直接跑跑不起来,环境有问题,VSCode和Visual Studio的环境配置明天得搞一下
4.1
参考程序能在实验室电脑上完美运行,命令行程序还是图形化程序都能,但是我的电脑上不行,原因未知
编译代码失败,在哪里都不行,就把命令行SNLC那个项目用CodeBlocks打开,编译直接报错
现在就能知道参考程序是用visual C++ 6.0写的
我安装了Visual C++ 6.0 ,然后打开了它的项目,事实证明它源码就是有错,有几个函数声明和定义不一样,我改了声明然后build成功了,13:26,终于取得一点进展。
13:47
试了一下,现在在VS里只要关掉SDL检查也能跑起来了,
现在只要改这个示例代码就可以了,
我做的好多项目都是这样,需要去看别人的代码,我不知道这样是否正确
我回宿舍以后在自己电脑上克隆了项目运行,报错和之前一样,这次通过用Visual Studio一步一步debug才知道原来是没有C盘权限导致的,我以管理员身份运行exe后果然没问题了
想自己写,却又不知道怎么写,
想看别人代码改,却又怕比自己写更麻烦
想来想去,还是改吧!改的过程中参透原理,然后还有时间的话可以自己写一个新的设计。
现在我打算先:
1.改参考代码,使得生成MIPS汇编,其余不动,作为版本T0
需要改code.cpp和cgen.cpp,预计工作量大概1000-1500行
2.能够生成MIPS汇编之后,将其余部分改头换面并优化
- 将词法和语法使用flex和bison生成
我现在对SNL语言足够熟悉了
需要看语义分析和目标代码生成的源码,搞明白原理
目标代码生成可以一点一点做,可以先能生成最简单的程序的目标代码,然后再做复杂的,走增量模型,一点点扩充
涉及到的汇编语言:
- 数据加载与存储指令
LDC (Load Constant): 将常数加载到寄存器。
LDA (Load Address): 加载地址到寄存器(基址+偏移)。
LD (Load): 从内存地址加载值到寄存器。
ST (Store): 将寄存器值存储到内存地址。
2. 算术运算指令
ADD: 加法运算。
SUB: 减法运算。
MUL: 乘法运算。
DIV: 除法运算。
3. 输入输出指令
IN: 从外部读取输入到寄存器。
OUT: 将寄存器值输出到外部。
4. 跳转与控制指令
JLT (Jump if Less Than): 若结果小于0则跳转。
JEQ (Jump if Equal): 若结果等于0则跳转。
JNE (Jump if Not Equal): 若结果不等于0则跳转。
HALT: 停止程序执行。
5. 特殊用途指令
LDA + pc 的组合用于绝对地址跳转(例如 LDC pc, address)。
目标代码生成的要点:
TM架构与MIPS架构的不同:
指令集不同:
寄存器-寄存器指令=emitRO=MIPS中的R型指令,寄存器内存&寄存器立即数=emitRM=MIPS中的I型指令
#寄存器-寄存器型=emitRO=R型
HALT 0,0,0
IN 0,0,0
OUT 0,0,0
ADD 1,1,6 # 1号寄存器和6号寄存器相加放回1里
SUB
MUL
DIV
#寄存器-内存&寄存器-立即数=emitRM=I型 都是两个寄存器操作数一个立即数
LD 2,1(6)
ST
LDA 7,0(2)
LDC 0,0(0) #括号里永远是0,这个参数被忽略的
JLT 0,2(7)
对应MIPS中我们要使用的指令集:
存储器不同:
TM架构的存储器:
typedef struct {
int iop ;
int iarg1 ;
int iarg2 ;
int iarg3 ;
} INSTRUCTION;
指令存储器:
INSTRUCTON imem[1024];
数据存储器:
int dmem[1024];
MIPS存储器: 内存对齐
- .align伪指令
- sw/lw偏移需要为4的倍数
寄存器不同:
TM寄存器:
/* 程序指令指示器pc为7,指向当前指令存储位置 *
* 程序指示器将使用寄存器数组中的第8个寄存器 */
#define pc 7
/* 过程活动记录头指针指示器sp指向过程活动记录的头地址指针 */
#define sp 6
/* 过程活动记录尾指针指示器top指向过程活动记录的尾地址指针 */
#define top 5
/* 过程活动记录sp到display表距离指示器displayOff */
#define displayOff 4
/* 存储指示器mp指向用于临时变量存储的数据存储器顶端 */
#define mp 3
#define ac2 2 /* 第三累加器 */
#define ac1 1 /* 第二累加器 */
#define ac 0 /* 第一累加器 */
尤其pc,MIPS需要jal/j
代码生成函数接口需要改:
/****************** 代码生成函数修改 *******************/
// 注释生成适配 MIPS 语法
void emitComment(const char * c) {
if (TraceCode) fprintf(code, "# %s\n", c); // 使用 # 代替原注释格式
}
// R 型指令生成(对应 MIPS 三操作数指令)
void emitRO(const char *op, int r, int s, int t, const char *c) {
fprintf(code, "%-6s%%%d, %%%d, %%%d", op, r, s, t);
if (TraceCode) fprintf(code, "\t# %s", c);
fprintf(code, "\n");
}
// I 型指令生成(对应 load/store 等指令)
void emitRM(const char * op, int r, int d, int s, const char *c) {
fprintf(code, "%-6s%%%d, %d(%%%d)", op, r, d, s);
if (TraceCode) fprintf(code, "\t# %s", c);
fprintf(code, "\n");
}
// 绝对地址处理适配 MIPS
void emitRM_Abs(const char *op, int r, int a, const char * c) {
fprintf(code, "%-6s%%%d, %d", op, r, a); // 实际可能需要 la 伪指令
if (TraceCode) fprintf(code, "\t# (abs) %s", c);
fprintf(code, "\n");
}
特殊寄存器宏
// MIPS 特殊寄存器访问宏
#define HI $hi
#define LO $lo
快捷操作宏
// 常用伪指令支持
#define li(reg, imm) emitRO("li", reg, 0, 0, "Load immediate " #imm)
#define la(reg, label) emitRO("la", reg, 0, 0, "Load address " #label)
// 栈操作宏
#define PUSH(reg) emitRM("sw", reg, 0, sp); \
emitRO("addiu", sp, sp, -4, "Push " #reg)
#define POP(reg) emitRO("addiu", sp, sp, 4, "Pop " #reg); \
emitRM("lw", reg, 0, sp)
#define SAVE_REG(reg) emitRM("sw", reg, 0, $sp); \
emitRO("addiu", $sp, $sp, -4, "Push $"#reg)
#define RESTORE_REG(reg) emitRO("addiu", $sp, $sp, 4, "Pop $"#reg); \
emitRM("lw", reg, 0, $sp)
#define LOAD_IMMEDIATE(reg, val) emitRM("li", reg, val, 0, "Load "#val)