1 / 41

Mini C 到 Unicore 32 的编译和模拟

Mini C 到 Unicore 32 的编译和模拟. 徐泽骅,刘智猷,寻云波. 成果. Mini C 到 Unicore 32 平台的编译器 生成 Unicore 32 汇编 比 gcc 不开优化的结果快 20% Unicore 32 模拟器 行为级模拟 Cache 周期数估算. 任务分工. 徐泽骅:Mini C 编译器前端 刘智猷:Mini C 编译器后端 寻云波:Unicore 32 模拟器. Mini C 编译器前端. 徐泽骅. Mini C 编译器前端结构. 类型系统. 对象类型 标量类型 基本类型 一级指针 集合类型:数组 函数类型

casta
Télécharger la présentation

Mini C 到 Unicore 32 的编译和模拟

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Mini C 到 Unicore 32 的编译和模拟 徐泽骅,刘智猷,寻云波

  2. 成果 • Mini C 到 Unicore 32 平台的编译器 • 生成 Unicore 32 汇编 • 比 gcc 不开优化的结果快 20% • Unicore 32 模拟器 • 行为级模拟 • Cache • 周期数估算

  3. 任务分工 • 徐泽骅:Mini C 编译器前端 • 刘智猷:Mini C 编译器后端 • 寻云波:Unicore 32 模拟器

  4. Mini C 编译器前端 徐泽骅

  5. Mini C 编译器前端结构

  6. 类型系统 • 对象类型 • 标量类型 • 基本类型 • 一级指针 • 集合类型:数组 • 函数类型 • <返回值, 参数列表>:都是标量类型 • 没有指针数组和高阶指针 • 没有数组指针和函数指针

  7. 语言支持 • 声明管理 • 符号表 • 参数列表管理 • 局部状态管理 • ID 分配及导出 • 变量 ID • 标号

  8. AST 的构造和求值 • Bison 解释器自底向上构造 AST • 组合表达式和语句 • 类型推导 • 规约到最顶端的语句列表时,自顶向下求值 • 语句(表达式)递归调用其子句(子表达式),并生成控制逻辑 • 自顶向下求值避免了回填操作

  9. 前端优化 • 没有流图? • 实际上 AST 就是流图 • 没有 goto、break 和 continue • 逻辑短路相当于复合 if

  10. 前端优化 • 在特定的执行状态中求值 • 定义一个特殊的结构,称为“执行状态” • 每个求值过程都接受一个执行状态作为参数 • 执行状态影响求值的行为 • 求值的过程会改变执行状态

  11. 前端优化

  12. 前端优化 • 在执行状态中记录可用表达式,就实现了公共表达式优化 • 在公共表达式的基础上通过三次迭代,可以实现循环常量外提 • 第一次迭代:可用表达式积累 • 第二次迭代:可变表达式消除 • 消除的结果就是循环常量 • 第三次迭代:生成最终代码

  13. int func(int *a, int b, int c){ int i; for(i = 0; i < 10; ++i) a[b * c] = 0;} LD_WORD TEMP_2 [AUTO_2 + 0*0] LD_WORD TEMP_3 [AUTO_1 + 0*0] MUL TEMP_4 TEMP_3 TEMP_2 LD_WORD TEMP_5 [AUTO_0 + 0*0] LABEL_1: MOV_ALL TEMP_12 0 ST_WORD TEMP_12 [TEMP_5 + TEMP_4*4] ADD TEMP_8 TEMP_1 1 ST_WORD TEMP_8 [AUTO_3 + 0*0] LD_WORD TEMP_1 [AUTO_3 + 0*0] CSUB TEMP_0 TEMP_1 10 JMP_SLT LABEL_1 前端优化

  14. IR to Assembly 刘智猷

  15. 要解决的问题 • 需要保证中间代码中Call/Return语句的语义 • 维护不同过程的栈帧 • 按调用约定保存和恢复寄存器 • 将数量无限的临时变量映射到有限个寄存器上 • 基于图染色算法的寄存器分配

  16. Interference Graph t2 t1 = 1 t2 = 2 t3 = t1 + t2 t4 = t1 + 3 t5 = t1 + t2 t1 t3 t5 t4 t1-t5 are not live Slides make by Wei Li ,in Standford CS243

  17. Register Assignment t2/r2 t1/r1 t3/r3 t5/r1 t4/r3 Slides make by Wei Li ,in Standford CS243

  18. 另外一些小问题 • 32位定长指令系统,32位地址空间 • 不能给寄存器赋值一个静态变量的指针 • 在代码段中预先保存需要用的静态变量指针 • 不能直接将一个寄存器赋值为大整数 • 用Mov和位移凑 • 对指令中的立即数大小有限制 • 为不能处理的立即数分配寄存器

  19. MiniC vs MiniJava:优化视角 • 优点 • 没有继承 • 新问题:存在指针,潜在地修改其他变量的值。 int *p; int a, b, c, d; if ( some_cond) p = &a; else p = &b; *p = a + b;

  20. MiniC vs MiniJava:优化视角 • 另一个问题:中间表示中使用保守的Load/Store策略:每个MiniC中的变量存储在一块地址空间中,更新变量时Load到Temp变量上,计算,再存回。 • 产生大量的Load/Store指令

  21. MiniC vs MiniJava:优化视角 void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp; } ST_WORD TEMP_-1 [AUTO_0 + 0*0] ST_WORD TEMP_-2 [AUTO_1 + 0*0] LD_WORD TEMP_1 [AUTO_0 + 0*0] LD_WORD TEMP_0 [TEMP_1 + 0*0] ST_WORD TEMP_0 [AUTO_2 + 0*0] LD_WORD TEMP_3 [AUTO_1 + 0*0] LD_WORD TEMP_2 [TEMP_3 + 0*0] ST_WORD TEMP_2 [TEMP_1 + 0*0] LD_WORD TEMP_4 [AUTO_2 + 0*0] ST_WORD TEMP_4 [TEMP_3 + 0*0] RETURN

  22. 别名分析 • 对C语言中的指针: 分析每个Temp变量在每个点上做为指针可能指向的变量 • 对于多余的Load/Store: 分析每个Temp变量在每个点上一定存有的变量. • 还应考虑数组:指针和数组有同样的表示。例如:int*

  23. 别名分析 • 为每个变量定义三个属性 • hold:该变量在当前点一定存储着的变量 • ref:该变量做为指针可能指向的变量 • offseted:该变量做为指针是否做过偏移(即是否可能指向数组变量的非0元素) • 使用数据流分析方法计算这三个变量

  24. 别名分析-例子 • Mov temp1, temp2 • temp1.hold = temp2.hold • temp1.ref = temp2.ref • temp1.offseted = temp2.offseted • Add temp1, var2, var3 • temp1.hold = { } • temp1.ref = temp1.ref • temp1.offseted = true;

  25. 别名分析-例子 • Load temp1, auto1 • temp1.hold = {auto1} • temp1.ref = auto1.ref • temp1.offseted = auto.offseted • Load temp1, [temp2 + offset] • temp1.hold = {} • 由于MiniC中不存在指针数组,temp1的ref和offseted已经没有意义了

  26. 别名分析-例子 • Call function, arg temp1 • clear temp1.ref in all holds (and static var) • all offseted of temp1.ref set as true • Store temp1, auto1 • clear all {auto1} in holds • temp1.hold = {auto1} + temp1.hold • auto1.ref = temp1.ref • auto.offseted = temp1.offseted

  27. 别名分析的使用 • 所有的数据流分析都基于正确的别名分析信息 • 在分析指令行为时要充分考虑到别名信息 • 例子:到达定值 • Store可能对所有的引用定值

  28. 别名分析的使用:Load消除 • 对一个Load指令, 如果能确定其Load的目标,同时有一个临时变量hold了该目标,可以用Mov代替这个Load,后继的复写传播可以进一步消除Mov

  29. ST_WORD TEMP_-1 [AUTO_0 + 0*0] ST_WORD TEMP_-2 [AUTO_1 + 0*0] LD_WORD TEMP_1 [AUTO_0 + 0*0] LD_WORD TEMP_0 [TEMP_1 + 0*0] ST_WORD TEMP_0 [AUTO_2 + 0*0] LD_WORD TEMP_3 [AUTO_1 + 0*0] LD_WORD TEMP_2 [TEMP_3 + 0*0] ST_WORD TEMP_2 [TEMP_1 + 0*0] LD_WORD TEMP_4 [AUTO_2 + 0*0] ST_WORD TEMP_4 [TEMP_3 + 0*0] RETURN ST_WORD TEMP_-1 [AUTO_0 + 0*0] ST_WORD TEMP_-2 [AUTO_1 + 0*0] MOV_ALL TEMP_1 TEMP_-1 LD_WORD TEMP_0 [TEMP_1 + 0*0] ST_WORD TEMP_0 [AUTO_2 + 0*0] MOV_ALL TEMP_3 TEMP_-2 LD_WORD TEMP_2 [TEMP_3 + 0*0] ST_WORD TEMP_2 [TEMP_1 + 0*0] MOV_ALL TEMP_4 TEMP_0 ST_WORD TEMP_4 [TEMP_3 + 0*0] RETURN

  30. Store消除 • 对一个Store指令,将其看成一次define。如果该define的所有可达使用都存在别的变量hold其define的变量,可以删除该Store

  31. ST_WORD TEMP_-1 [AUTO_0 + 0*0] ST_WORD TEMP_-2 [AUTO_1 + 0*0] MOV_ALL TEMP_1 TEMP_-1 LD_WORD TEMP_0 [TEMP_1 + 0*0] ST_WORD TEMP_0 [AUTO_2 + 0*0] MOV_ALL TEMP_3 TEMP_-2 LD_WORD TEMP_2 [TEMP_3 + 0*0] ST_WORD TEMP_2 [TEMP_1 + 0*0] MOV_ALL TEMP_4 TEMP_0 ST_WORD TEMP_4 [TEMP_3 + 0*0] RETURN MOV_ALL TEMP_1 TEMP_-1 LD_WORD TEMP_0 [TEMP_1 + 0*0] MOV_ALL TEMP_3 TEMP_-2 LD_WORD TEMP_2 [TEMP_3 + 0*0] ST_WORD TEMP_2 [TEMP_1 + 0*0] MOV_ALL TEMP_4 TEMP_0 ST_WORD TEMP_4 [TEMP_3 + 0*0] RETURN

  32. 复写传播之后 void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp; } LD_WORD TEMP_0 [TEMP_-1 + 0*0] LD_WORD TEMP_2 [TEMP_-2 + 0*0] ST_WORD TEMP_2 [TEMP_-1 + 0*0] ST_WORD TEMP_0 [TEMP_-2 + 0*0] RETURN

  33. Unicore32体系实习模拟器部分 寻云波

  34. 完成内容 • 因为初期的目标是完成一个供编译实习使用的Unicore32模拟器,所以重点是保证Unicore32模拟器的功能集,没有采用流水线结构,实现了下面几个功能: • Unicore32功能集; • 简单的Debug模式; • Unicore32运行周期的统计; • Cache;

  35. 流程 • 通过运行参数获得运行的文件以及模式; • 初始化内存寄存器,读入运行文件(ELF)加载到内存中; • 通过读入ELF文件的信息,运行代码,其中栈段从0x0c000000开始;同时进行系统性能和Cache的统计。 • 程序运行结束之后,输出运行时统计的信息

  36. Unicore32功能集 • 这部分主要完成了Unicore32手册上所给的所有指令: • 其中ALU计算、跳转、读取等6个操作已经在中期报告中有详细说明。 • 处理LDM操作,即把多项寄存器的内容压入栈中:因为手册中没有给运行参数,所以在编译时进行了处理优化

  37. Unicore32功能集 • 系统调用处理:完成了基本的I/O处理,及输入输出整型,字符型,字符串6个功能。 • 因为在指令手册中只告诉了系统调用的进入参数,具体功能参数并没有告诉。 • 具体参数是从0x50开始,一直到0x55:分别是:读入整型,读入字符、读入字符串,输出整型,输出字符,输出字符串。 • 其中r0保存的参数为:读入操作是需要保存的内存地址,输出整型、字符是输出的值,输出字符串是输出字符串在内存中的地址,以\0结束。

  38. 简单Debug模式 • 主要是进行调试,用来处理检查模拟器和编译出的汇编代码可能出现的错误 • 通过./SIM <> D来开启Debug模式。 • 每次运行完一条语句都会输出寄存器当前的信息。 • N:运行下一句。 • R:输出寄存器信息。 • C:输出状态寄存器信息4个值NZCV的信息。 • E:退出模拟器。 • S:关闭Debug模式

  39. Unicore32运行的周期统计 • 根据5级流水,进行周期的粗略估计: • 处理的情况有:数据冒险和跳转冒险。 • 数据冒险:只有在出现前一条指令从内存中读入一个数,后一条指令就使用时; • 跳转冒险:只出现在条件跳转下; • 对于这两个冒险处理是加入一个气泡(总周期数加1)。

  40. Cache • Cache基本信息:大小1024byte,高速缓存块大小32btye,高速缓存组数16组,采用两路组相联高速缓存,以及用最不常使用策略进行替换。 • 考虑到代码段和数据段:用I-Cache和D-Cache来进行分别的统计。 • Cache的加入主要是进行Cache命中率的分析,用来测试前面翻译成汇编代码的运行优化效率。 • CacheHit 和Miss以及Cache命中率统计情况。

  41. 最后总结 • 完成了Unicore32模拟器,并且能够跑出所有对所有的指令。 • 加入Debug模式,用来调试正确性。 • 加入了Cache和周期的统计,用来测试编译器优化的效率。 • 不足:运行时间过长,一旦运行指令过多时,所运行时间会很长。

More Related