软流水解析

软流水解析

基本思想:将有真相关的指令分隔到不同的循环周期内执行。

软流水后,循环体会变成装入、主体循环、排空三个部分

软流水解题思路

例子:将以下代码使用软流水优化:

image-20231105140817405
1
2
3
4
5
6
L1: LDC1  F0, 0(R1)
ADD.D F2,F0,F1
SDC1 F2,0(R1)
ADDIU R1,R1,-8
BNE R1,R2,L1
NOP

首先确定循环主体:

1.执行顺序确定

暂时不需要考虑循环控制部分,分析依赖关系画出数据流图:

image-20231105133645193

软件流水前后主循环程序的运行逻辑发生了如下所示的变化:

image-20231105133645193

为什么要这样?

竖着执行的时候指令1,2,3之间存在相关性。但如果是不同循环的指令交替就没有相关性了,举个栗子:

从上面的图中可以看出来,第i次循环指令3执行,需要依赖第i次循环的指令2,如果横着执行:2,1,3,在执行3的时候2已经执行过了,而且中间隔了1指令和ADDIU R1,R1,-8 BNE R1,R2,L1是隔了足够数量的指令的,不会有延迟

1
2
3
4
5
6
7
8
9
10
11
12
# 第i次的循环
L1: LDC1 F0,0,0(R1)
ADD.D F2,F0,F1
SDC1 F2,0(R1)
# 第i+1次的循环
LDC1 F0,0,-8(R1)
ADD.D F2,F0,F1
SDC1 F2,-8(R1)
#第i+2次循环
LDC1 F0,0,-16(R1)
ADD.D F2,F0,F1
SDC1 F2,-16(R1)

因此最终执行顺序:

image-20231105143413047
1
2
3
4
5
6
SDC1  F2,0(R1)
ADD.D F2,F0,F1
LDC1 F0,0,-16(R1)
ADDIU R1,R1,-8
BNE R1,R2,L1
NOP

2.修改偏移量防止数组越界

上述代码片段存在越界的问题:

分析:判断循环是否结束的条件是:BNE R1,R2,L1,也就是判断r1是否越界,R1是递减的,也就是说条件是R1>XX,而上述代码还存在R1-16的值,也就是说当R1满足循环结束条件时,-16(R1)已经越界了,所以需要考虑一个偏移把最后一项归零。这里需要+16:

1
2
3
4
5
6
7
8
9
10
11
12
# 第i次的循环
L1: LDC1 F0,0,16(R1)
ADD.D F2,F0,F1
SDC1 F2,16(R1)
# 第i+1次的循环
LDC1 F0,0,8(R1)
ADD.D F2,F0,F1
SDC1 F2,8(R1)
#第i+2次循环
LDC1 F0,0,0(R1)
ADD.D F2,F0,F1
SDC1 F2,0(R1)
1
2
3
4
5
6
SDC1  F2,16(R1)
ADD.D F2,F0,F1
LDC1 F0,0,0(R1)
ADDIU R1,R1,-8
BNE R1,R2,L1
NOP

3.延迟槽填充

可以考虑把ADDIU R1,R1,-24指令用于填充延迟槽,这样在判断完条件后才会-8,也就是说会多-8越界,所以需要把r1的初始值再偏移8

1
2
3
4
5
SDC1  F2,24(R1) 8
ADD.D F2,F0,F1
LDC1 F0,0,8(R1)
BNE R1,R2,L1
ADDIU R1,R1,-8

4. 装入代码

装入就是预处理。

装入代码就是上面这部分代码

image-20231105192657593

1
2
3
4
5
6
7
8
9
10
11
12
# 第i次的循环
L1: LDC1 F0,0,24(R1)
ADD.D F2,F0,F1
SDC1 F2,24(R1)
# 第i+1次的循环
LDC1 F0,0,16(R1)
ADD.D F2,F0,F1
SDC1 F2,16(R1)
#第i+2次循环
LDC1 F0,0,8(R1)
ADD.D F2,F0,F1
SDC1 F2,8(R1)
1
2
3
LDC1 F0,0,24(R1) 
ADD.D F2,F0,F1
LDC1 F0,0,16(R1)

整个程序第一次取到的应该是0(R1)和0(R2)的值,而后续主循环体中的偏移量是+24,因此要对装入代码的偏移量进行修正对R1-24,这样就24-24=0了

1
2
3
4
LDC1 F0,0,0(R1) 
ADD.D F2,F0,F1
LDC1 F0,0,-8(R1)
ADDIU R1,R1,-24

5.排空代码

排空就是收尾

image-20231105193621362

image-20231223142849981
1
2
3
SDC1  F2,16(R1)
ADD.D F2,F0,F1
SDC1 F2,8(R1)

因为在循环体里面的延迟槽执行了-8操作,从循环出来还要+8

1
2
3
SDC1  F2,24(R1)
ADD.D F2,F0,F1
SDC1 F2,16(R1)

软流水后执行周期数计算

假设R1初始值8n+R2:循环n次

装入代码中:LDC1 F0,0,24(R1) ADD.D F2,F0,F1 存在数据相关,根据表知道延迟2周期,也就+1拍

同理排空代码中ADD.D F2,F0,F1 SDC1 F2,8(R1) 存在数据相关,延迟3周期+2拍

主循环体5拍

一共:(4+1) + (5*(n-2)) + (3+2) = 5n 拍

例题

首先分析相关性:

计算主循环代码:在这条关键路径(1->2->4->5)上指令的数量,就是做软件流水时需要展开循环的次数(原因:保证有相关的指令能被分隔到不同循环执行)。

指令3和指令4之间有相关,它们之间不能有间隔。因此指令3要和指令2绑定在一起。 不能和1绑定的原因:假如3和1取自同一循环,指令4要使用3(蓝色圈)的结果,但是从左往右执行时,这个结果会被另一个3(红色圈)覆盖。而如果3和2取自同一循环就不会出错。

于是我们可以得到下面的表,高亮部分是软件流水的主体循环代码,上方是装入代码,下方是排空代码。

首先得到结果如下图左边所示,需要将偏移置零,得到右边

这里我们可以看出4需要用的3的寄存器,但是3寄存器和5寄存器是同名的,而5在4之前执行,5本来需要存的是4产生的值。软流水后5变成了存3加载的值,所以需要修改寄存器名

循环主体

装入代码是一开始的,所以偏移从0开始。因为主循环有-24的偏移,为了保证进入循环有这个偏移需要+24,如下所示:

排空代码本身就有主循环的-24偏移。因为最后会比主循环多+8所以需要还需要再-8,如下所示:

其它参考博客

参考文章1

参考文章2