编译原理课设工作日志


在过去的四天里,我弄清楚了flex和bison的用法,知道了他们如何协作并联合编译,做到了不关注c代码只看.l和.y就能知道生成程序做了什么

同时掌握了makefile的编写和使用

现在是2025/3/29 Sat 12:19
我们需要搞定目标代码生成

32位MIPS汇编

  • 固定长度指令:每条指令都用固定的32位长度,每条指令都是四个字节,所以内存数据的访问必须以32位严格对齐
  • 寻址方式支持基址寻址、立即数寻址、寄存器寻址和伪立即数
  • 跳转指令只有26位目标地址,加上2位对齐位,寻址空间256M
  • 栈空间走向从高地址到低地址
  • 包含32个寄存器

? 临时寄存器(临时寄存器池)

接下来需要:

  1. 熟悉pascal语言

  2. 熟悉32位MIPS汇编

  3. 熟悉生成目标代码的具体原理过程

先进行第一步:

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语言足够熟悉了
需要看语义分析和目标代码生成的源码,搞明白原理

目标代码生成可以一点一点做,可以先能生成最简单的程序的目标代码,然后再做复杂的,走增量模型,一点点扩充

涉及到的汇编语言:

  1. 数据加载与存储指令​​
    ​​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)

函数实现框架:

genProc:

在这里插入图片描述

genExp:

在这里插入图片描述

genStmt:

在这里插入图片描述

genStmt的call语句部分:

在这里插入图片描述

FindAdd:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值