1、MIPS 代码生成,MIPS 代码是一种汇编代码主要汇编信息: .text /代码段(指令段) .data /数据段.globl /全局符号声明 .align 0 /关闭所有的自动对齐 .asciiz /字符串(带终止符),MIPS的寄存器约定,a0 - a3:存放向子函数传递的参数 v0、v1:存放子函数调用返回结果 还可用于表达式求值 s0 - s7:存放局部变量 在发生函数调用时一般要保存它们的内容 t0 t8:存放临时运算结果 在发生函数调用时不必保存它们的内容 sp: 栈(stack)指针 fp: 帧(frame)指针 ra: 返回地址(用于过程调用),MIPS指令,运算 addu,
2、 add subu, sub mul and neg 常数操作 li,数据传输 la lw sw move 比较 seq slt,MIPS指令,控制指令 b label: 分支到 label beqz Rsrc, label:如果 Rsrc 为 0 就分支到 label bgeu Rsrc1, Src2, label:如果 Rsrc1 大于等于 Src2就分支到 label j label: 无条件转移到 label jal label:(jump and link) 无条件转移到 label,并将下一指令的地址写入 $ra jalr Rsrc: 使用寄存器的跳转指令指令的跳转地址在 Rsrc
3、 寄存器中并将下一指令的地址写入 $ra,更多细节见:http:/www.cs.purdue.edu/homes/hosking/352/spim/raw.html体系结构课程的相关资料,系统调用指令:syscall$v0 中包含调用号(共12个):1:打印整数,数字在 $a0 中4:打印字符串,字符串在 $a0 中9:申请内存块,申请长度在 $a0 中,所获得内存的地址在$v0中,MIPS指令,堆栈操作,对每个子例程堆栈空间排列可以参考: * 高地址 - 低地址 * - *| ra | fp | 溢出TEMP | 保存用到的S寄存器|参数X| 参数4 | 下一帧 * -fp指向上个sp,0(
4、$fp)可以取到入参数4,依次类推。参数的个数是当前子例程调用其他例程时需传参数的最大数减去4. 子例程开始时: 1. 操作堆栈,分配空间。 2. 保存所有用到的S寄存器。 3. 将所有用到的入参拷贝到S寄存器或T寄存器或溢出空间里。 结束时: 恢复S寄存器和堆栈。,对出现在子例程中的所有TEMP: 如果它从来不活跃,例如(只写未读或未写未读),该TEMP只可能是MOVE或HLOAD目标,可以转化该语句为nop。如果它的活性区间跨调用(出现在至少一个CALL语句的LiveOut中),分配s寄存器,或溢出。 如果它的活性区间不跨调用,优先分配t寄存器,其次s寄存器,或溢出。可以用v0和v1存取溢
5、出的TEMP。,MAIN 002MOVE t0 HALLOCATE 4MOVE t1 HALLOCATE 4MOVE t2 Fac_ComputeFacHSTORE t0 0 t2HSTORE t1 0 t0MOVE t0 t1HLOAD t1 t0 0HLOAD t2 t1 0MOVE t1 10MOVE a0 t0MOVE a1 t1CALL t2MOVE t3 v0PRINT t3 END,.text /指令段.globl main main: /主函数move $fp, $sp /记录上帧首址subu $sp, $sp, 4 /设置本帧栈长sw $ra, -4($fp) /保存返回地址
6、li $a0 4 /将数值4设为入参jal _halloc /调用子过程获取内存move $t0 $v0 /保存所获内存首址 li $a0 4 /将数值4设为入参jal _halloc /调用子过程获取内存move $t1 $v0 /保存所获内存首址la $t2 Fac_ComputeFac/获取子过程地址sw $t2, 0($t0) /将直接地址存起来 sw $t0, 0($t1) /将间接过程存起来move $t0 $t1 /获取间接过程地址lw $t1 0($t0) /获取间接过程lw $t2 0($t1) /获取直接地址li $t1 10 /加载整数10move $a0 $t0 /将间
7、接地址设为入参move $a1 $t1 /将10设为入参jalr $t2 /调用子过程move $t3 $v0move $a0 $t3 jal _print /打印结果lw $ra, -4($fp) /获取返回值地址 addu $sp, $sp, 4 /弹出栈内容j $ra / 返回,Fac_ComputeFac 232ASTORE SPILLEDARG 0 s0ASTORE SPILLEDARG 1 s1ASTORE SPILLEDARG 2 s2MOVE s0 a0MOVE s1 a1MOVE t0 1MOVE t1 LT s1 t0CJUMP t1 L2MOVE s2 1JUMP L3
8、L2 NOOPMOVE t0 s0HLOAD t1 t0 0HLOAD t2 t1 0MOVE t1 1MOVE t3 MINUS s1 t1MOVE a0 t0MOVE a1 t3CALL t2MOVE t1 v0MOVE t0 TIMES s1 t1MOVE s2 t0 L3 NOOPMOVE v0 s2ALOAD s0 SPILLEDARG 0ALOAD s1 SPILLEDARG 1ALOAD s2 SPILLEDARG 2 END,.text Fac_ComputeFac:sw $fp, -8($sp) /保存上上帧首址 move $fp, $sp /设置上帧首址subu $sp,
9、$sp, 20/设置本帧栈长sw $ra, -4($fp) /保存返回地址sw $s0, 0($sp) /保存老入参1sw $s1, 4($sp) /保存老入参2sw $s2, 8($sp) /保存返回值move $s0 $a0 /取新入参1move $s1 $a1 /取新阶乘因子li $t0 1 / 加载数值1slt $t1, $s1, $t0 /比较阶乘因子与1beqz $t1 L2 /如果大于1转L2li $s2 1 /加载1给计算结果b L3 /跳转 L3 L2: nopmove $t0 $s0 /取入参(间接地址)lw $t1 0($t0) /加载间接地址lw $t2 0($t1)
10、/加载直接地址li $t1 1 /加载数值1sub $t3, $s1, $t1 /阶乘因子减1move $a0 $t0 /将间接地址设为入参move $a1 $t3 /将阶乘因子设为入参jalr $t2 /递归调用move $t1 $v0 /获取返回的计算结果mul $t0, $s1, $t1 /阶乘因子乘结果move $s2 $t0 /得到新结果 L3: nop move $v0 $s2 /准备返回新结果lw $s0, 0($sp) /恢复局部变量内容 lw $s1, 4($sp) /恢复局部变量内容lw $s2, 8($sp) /恢复局部变量内容lw $ra, -4($fp) /获取返回地
11、址lw $fp, 12($sp) /获取上帧首址addu $sp, $sp, 20 /弹出栈j $ra /返回,.text.globl _halloc _halloc: li $v0, 9 syscall j $ra .text.globl _print _print: li $v0, 1syscallla $a0, newlli $v0, 4syscallj $ra.data.align 0 newl: .asciiz “n“ .data.align 0 str_er: .asciiz “ ERROR: abnormal terminationn“,内存分配基本约定指令段:0x0040000
12、0 数据段:0x10000000 运行栈:0x7fffffff,帧与栈的运行效果?请见下页,move $fp, $sp subu $sp, $sp,4 sw $ra, -4($fp) . jalr $t2 Fac_ComputeFac: sw $fp, -8($sp) move $fp, $sp subu $sp,$sp,20 sw $ra, -4($fp) sw $s0, 0($sp) sw $s1, 4($sp) sw $s2, 8($sp) jalr $t2 move $t1 $v0 mul $t0,$s1,$t1 move $s2 $t0 move $v0, $s2 lw $s0,0(
13、$sp) lw $s1,4($sp) lw $s2,8($sp) lw $ra,-4($fp) lw $fp,12($sp) addu $sp,$sp,20 j $ra,sp,fp,0x7ffffef78,f74,f70,f6c,f68,f64,f60,f5c,f58,f54,f50,f4c,f48,f44,f40,f3c,0x00400018,0x00400074,返回地址,返回地址,返回地址,上上帧首址,上上帧首址,0x7fffef78,0x00000000,0x00000000,0x00000000,0x004000fc,0x7fffef74,0x10040004,0x0000000a,
14、0x00000000,0x004000fc,0x7fffef60,0x10040004,0x00000009,0x00000000,0x7ffffef78,0x7ffffef78,0x7ffffef74,0x7ffffef74,0x7ffffef60,0x00400018,ra,0x00400024,0x00400028,0x00400070,0x00400074,0x004000f8,0x00400074,0x004000fc,0x004000fc,0x7ffffef60,0x7ffffef4c,间接地址,阶乘因子,0x004000a0,0x10040004,0x10040000,0x100
15、40000,0x000400a0,返回地址,上上帧首址,间接地址,阶乘因子,0x7ffffef4c,0x7ffffef38,f38,数据段,指令段,执行栈,main,返回值用,返回值用,运行目标码,在操作系统(虚拟机?)上运行 将汇编代码转换为二进制代码 初始化 联接 在模拟器(更高层次的虚拟机)上运行 便于调试 移植性好,MIPS运行模拟器:SPIM,可以直接读取和解释MIPS 汇编指令 SPIM显示了指令运行过程中各个寄存器的值,以及各指令、数据的值和地址 多种版本以运行于不同的操作系统 Windows, Linux 开源 可以自己修改,以满足特殊需求,寄存器,指令,数据,消息,SPIM
16、与 simplescalar,SPIM 功能简单些,主要用于指令模拟 调试界面好! 主要用于教学 体系结构课程以前用它作第一个实习作业 simplescalar 功能更强,可以进行性能方面的模拟 界面比较简单 多数用于研究,本阶段任务,将任意给定的符合 MiniJava 规范的源码正确地翻译为 “符合 MIPS 规范的代码” 主要挑战:执行堆栈的安排 本帧的运行栈应该多大(空间优化)? 如何多用寄存器、减少内存访问(时间优化)? 选择哪个指令?,“我的编译器我做主”在保证运行结果正确的前提下实现上有充分的自由例如:运行栈中每帧的内容可以自己确定不一定完全按照例子的模式“高效”与“易维”每个人有
17、不同的体会最后都可以写到实习报告,1)彻底理解MIPS指令系统及运行机理 运用体系结构课程讲的 MIPS 知识 2)进一步扩展自己的程序,使其能将已生成的中间代码进一步转换为 MIPS 代码 回顾上学期的“目标代码生成”内容 参考现代编译器的Java实现第11、10章 3)合并前面所有步骤,成为“一个完整的编译器”不是“多个分步骤的编译器”!总入口函数名统一为 MiniJava,实现本次任务的基本途径,什么时候结束?,如何判断自己的编译器是否正确? 运行自己的编译器,将一个正确的 MiniJava 程序翻译为 MIPS 代码 利用 SPIM 执行生成的代码 看运行结果是否与 MiniJava
18、程序的运行结果相同,有精力的同学可以进一步 做活性分析、优化代码提高执行速度降低内存消耗提高编译器的实用性,词法 语法 分析,类型 检查,Piglet 代码 生成,MIPS 代码 生成,MiniJava Grammer,Piglet 解释器,SPIM (MIPS模拟器),源 代 码,Piglet Grammar,MIPS Instruction Spec,语法树,JavaCC (JJTree),自动生成,符号表,Piglet代码,Spiglet Grammar,Spiglet 代码 生成,Spiglet代码,Kanga 代码 生成,Spiglet 解释器,Kanga 解释器,Kanga代码,Mips 代码,Kanga Grammar,寄存器分配,调用堆栈,最后的冲刺!,0,20,40,60,80,100,鼓励在教学网上讨论、交流、互相帮助! 尤其是帮助其他同学解决问题的回复!,