41
Mini C 到 Unicore 32 到到到到到到 到到到 到到到到 到到到 ,,

Mini C 到 Unicore 32 的编译和模拟

  • Upload
    casta

  • View
    243

  • Download
    0

Embed Size (px)

DESCRIPTION

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 编译器前端结构. 类型系统. 对象类型 标量类型 基本类型 一级指针 集合类型:数组 函数类型 - PowerPoint PPT Presentation

Citation preview

Page 1: Mini C 到 Unicore 32 的编译和模拟

Mini C 到 Unicore 32 的编译和模拟

徐泽骅,刘智猷,寻云波

Page 2: Mini C 到 Unicore 32 的编译和模拟

成果• Mini C 到 Unicore 32 平台的编译器

– 生成 Unicore 32 汇编– 比 gcc 不开优化的结果快 20%

• Unicore 32 模拟器– 行为级模拟– Cache– 周期数估算

Page 3: Mini C 到 Unicore 32 的编译和模拟

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

Page 4: Mini C 到 Unicore 32 的编译和模拟

Mini C 编译器前端

徐泽骅

Page 5: Mini C 到 Unicore 32 的编译和模拟

Mini C 编译器前端结构

Page 6: Mini C 到 Unicore 32 的编译和模拟

类型系统• 对象类型

– 标量类型• 基本类型• 一级指针

– 集合类型:数组• 函数类型

– < 返回值 , 参数列表 > :都是标量类型• 没有指针数组和高阶指针• 没有数组指针和函数指针

Page 7: Mini C 到 Unicore 32 的编译和模拟

语言支持• 声明管理

– 符号表– 参数列表管理– 局部状态管理

• ID 分配及导出– 变量 ID– 标号

Page 8: Mini C 到 Unicore 32 的编译和模拟

AST 的构造和求值• Bison 解释器自底向上构造 AST

– 组合表达式和语句– 类型推导

• 规约到最顶端的语句列表时,自顶向下求值– 语句(表达式)递归调用其子句(子表达式),并生成控制逻辑– 自顶向下求值避免了回填操作

Page 9: Mini C 到 Unicore 32 的编译和模拟

前端优化• 没有流图?• 实际上 AST 就是流图• 没有 goto 、 break 和

continue• 逻辑短路相当于复合 if

Page 10: Mini C 到 Unicore 32 的编译和模拟

前端优化• 在特定的执行状态中求值

– 定义一个特殊的结构,称为“执行状态”– 每个求值过程都接受一个执行状态作为参数– 执行状态影响求值的行为– 求值的过程会改变执行状态

Page 11: Mini C 到 Unicore 32 的编译和模拟

前端优化

Page 12: Mini C 到 Unicore 32 的编译和模拟

前端优化• 在执行状态中记录可用表达式,就实现了公共表达式优化• 在公共表达式的基础上通过三次迭代,可以实现循环常量

外提– 第一次迭代:可用表达式积累– 第二次迭代:可变表达式消除

• 消除的结果就是循环常量– 第三次迭代:生成最终代码

Page 13: Mini C 到 Unicore 32 的编译和模拟

前端优化• 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

Page 14: Mini C 到 Unicore 32 的编译和模拟

IR to Assembly

刘智猷

Page 15: Mini C 到 Unicore 32 的编译和模拟

要解决的问题• 需要保证中间代码中 Call/Return 语句的语义

– 维护不同过程的栈帧– 按调用约定保存和恢复寄存器

• 将数量无限的临时变量映射到有限个寄存器上– 基于图染色算法的寄存器分配

Page 16: Mini C 到 Unicore 32 的编译和模拟

Interference Graph

t1 = 1t2 = 2t3 = t1 + t2t4 = t1 + 3t5 = t1 + t2

t1 t2 t3

t4 t5

t1-t5 are not live

Slides make by Wei Li ,in Standford CS243

Page 17: Mini C 到 Unicore 32 的编译和模拟

Register Assignment

t1/r1 t2/r2 t3/r3

t4/r3 t5/r1

Slides make by Wei Li ,in Standford CS243

Page 18: Mini C 到 Unicore 32 的编译和模拟

另外一些小问题• 32 位定长指令系统, 32 位地址空间• 不能给寄存器赋值一个静态变量的指针

– 在代码段中预先保存需要用的静态变量指针• 不能直接将一个寄存器赋值为大整数

– 用 Mov 和位移凑• 对指令中的立即数大小有限制

– 为不能处理的立即数分配寄存器

Page 19: Mini C 到 Unicore 32 的编译和模拟

MiniC vs MiniJava :优化视角• 优点

– 没有继承• 新问题:存在指针,潜在地修改其他变量的值。

int *p; int a, b, c, d;if ( some_cond)

p = &a;else

p = &b;*p = a + b;

Page 20: Mini C 到 Unicore 32 的编译和模拟

MiniC vs MiniJava :优化视角• 另一个问题:中间表示中使用保守的 Load/Store策略 : 每

个 MiniC 中的变量存储在一块地址空间中 ,更新变量时 Load 到 Temp 变量上 ,计算 ,再存回。

• 产生大量的 Load/Store 指令

Page 21: Mini C 到 Unicore 32 的编译和模拟

MiniC vs MiniJava :优化视角

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

void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp;}

Page 22: Mini C 到 Unicore 32 的编译和模拟

别名分析• 对 C 语言中的指针 : 分析每个 Temp 变量在每个点上做为

指针可能指向的变量• 对于多余的 Load/Store: 分析每个 Temp 变量在每个点上

一定存有的变量 .

• 还应考虑数组:指针和数组有同样的表示。例如: int*

Page 23: Mini C 到 Unicore 32 的编译和模拟

别名分析• 为每个变量定义三个属性• hold :该变量在当前点一定存储着的变量• ref :该变量做为指针可能指向的变量• offseted :该变量做为指针是否做过偏移(即是否可能指

向数组变量的非 0元素)• 使用数据流分析方法计算这三个变量

Page 24: Mini C 到 Unicore 32 的编译和模拟

别名分析 -例子• 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;

Page 25: Mini C 到 Unicore 32 的编译和模拟

别名分析 -例子• Load temp1, auto1

– temp1.hold = {auto1}– temp1.ref = auto1.ref– temp1.offseted = auto.offseted

• Load temp1 , [temp2 + offset]

– temp1.hold = {}– 由于 MiniC 中不存在指针数组, temp1 的 ref 和 offseted已经没

有意义了

Page 26: Mini C 到 Unicore 32 的编译和模拟

别名分析 -例子• 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

Page 27: Mini C 到 Unicore 32 的编译和模拟

别名分析的使用• 所有的数据流分析都基于正确的别名分析信息

• 在分析指令行为时要充分考虑到别名信息– 例子:到达定值

• Store 可能对所有的引用定值

Page 28: Mini C 到 Unicore 32 的编译和模拟

别名分析的使用: Load 消除• 对一个 Load 指令, 如果能确定其 Load 的目标,同时有

一个临时变量 hold 了该目标,可以用 Mov 代替这个 Load ,后继的复写传播可以进一步消除 Mov

Page 29: Mini C 到 Unicore 32 的编译和模拟

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

Page 30: Mini C 到 Unicore 32 的编译和模拟

Store 消除

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

Page 31: Mini C 到 Unicore 32 的编译和模拟

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

Page 32: Mini C 到 Unicore 32 的编译和模拟

复写传播之后

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

void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp;}

Page 33: Mini C 到 Unicore 32 的编译和模拟

Unicore32体系实习模拟器部分

寻云波

Page 34: Mini C 到 Unicore 32 的编译和模拟

完成内容• 因为初期的目标是完成一个供编译实习使用的 Unicore32

模拟器,所以重点是保证 Unicore32 模拟器的功能集,没有采用流水线结构,实现了下面几个功能:

• Unicore32功能集;• 简单的 Debug 模式;• Unicore32运行周期的统计;• Cache;

Page 35: Mini C 到 Unicore 32 的编译和模拟

流程• 通过运行参数获得运行的文件以及模式;• 初始化内存寄存器,读入运行文件( ELF )加载到内存

中;• 通过读入 ELF文件的信息,运行代码,其中栈段从

0x0c000000 开始;同时进行系统性能和 Cache 的统计。• 程序运行结束之后,输出运行时统计的信息

Page 36: Mini C 到 Unicore 32 的编译和模拟

Unicore32功能集• 这部分主要完成了 Unicore32手册上所给的所有指令:• 其中 ALU计算、跳转、读取等 6 个操作已经在中期报告

中有详细说明。• 处理 LDM 操作,即把多项寄存器的内容压入栈中:因为手册中没有给运行参数,所以在编译时进行了处理优化

Page 37: Mini C 到 Unicore 32 的编译和模拟

Unicore32功能集• 系统调用处理:完成了基本的 I/O 处理,及输入输出整型,字符型,字符串 6 个功能。

• 因为在指令手册中只告诉了系统调用的进入参数,具体功能参数并没有告诉。

• 具体参数是从 0x50 开始,一直到 0x55 :分别是:读入整型,读入字符、读入字符串,输出整型,输出字符,输出字符串。

• 其中 r0 保存的参数为:读入操作是需要保存的内存地址,输出整型、字符是输出的值,输出字符串是输出字符串在内存中的地址,以 \0 结束。

Page 38: Mini C 到 Unicore 32 的编译和模拟

简单 Debug 模式• 主要是进行调试,用来处理检查模拟器和编译出的汇编代

码可能出现的错误• 通过 ./SIM <> D来开启 Debug 模式。• 每次运行完一条语句都会输出寄存器当前的信息。• N:运行下一句。• R :输出寄存器信息。• C:输出状态寄存器信息 4 个值 NZCV 的信息。• E :退出模拟器。• S :关闭 Debug 模式

Page 39: Mini C 到 Unicore 32 的编译和模拟

Unicore32运行的周期统计• 根据 5 级流水,进行周期的粗略估计:• 处理的情况有:数据冒险和跳转冒险。• 数据冒险:只有在出现前一条指令从内存中读入一个数,

后一条指令就使用时;• 跳转冒险:只出现在条件跳转下;• 对于这两个冒险处理是加入一个气泡(总周期数加 1 )。

Page 40: Mini C 到 Unicore 32 的编译和模拟

Cache• Cache 基本信息:大小 1024byte ,高速缓存块大小

32btye ,高速缓存组数 16 组,采用两路组相联高速缓存,以及用最不常使用策略进行替换。

• 考虑到代码段和数据段:用 I-Cache 和 D-Cache来进行分别的统计。

• Cache 的加入主要是进行 Cache命中率的分析,用来测试前面翻译成汇编代码的运行优化效率。

• Cache Hit 和 Miss 以及 Cache命中率统计情况。

Page 41: Mini C 到 Unicore 32 的编译和模拟

最后总结• 完成了 Unicore32 模拟器,并且能够跑出所有对所有的指

令。• 加入 Debug 模式,用来调试正确性。• 加入了 Cache 和周期的统计,用来测试编译器优化的效率。

• 不足:运行时间过长,一旦运行指令过多时,所运行时间会很长。