软流水解析
软流水解析
基本思想:将有真相关的指令分隔到不同的循环周期内执行。
软流水后,循环体会变成装入、主体循环、排空三个部分
软流水解题思路
例子:将以下代码使用软流水优化:

1 | L1: LDC1 F0, 0(R1) |
首先确定循环主体:
1.执行顺序确定
暂时不需要考虑循环控制部分,分析依赖关系画出数据流图:

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

为什么要这样?
竖着执行的时候指令1,2,3之间存在相关性。但如果是不同循环的指令交替就没有相关性了,举个栗子:
从上面的图中可以看出来,第i次循环指令3执行,需要依赖第i次循环的指令2,如果横着执行:2,1,3,在执行3的时候2已经执行过了,而且中间隔了1指令和ADDIU R1,R1,-8 BNE R1,R2,L1
是隔了足够数量的指令的,不会有延迟
1 | # 第i次的循环 |
因此最终执行顺序:

1 | SDC1 F2,0(R1) |
2.修改偏移量防止数组越界
上述代码片段存在越界的问题:
分析:判断循环是否结束的条件是:BNE R1,R2,L1
,也就是判断r1是否越界,R1是递减的,也就是说条件是R1>XX,而上述代码还存在R1-16的值,也就是说当R1满足循环结束条件时,-16(R1)已经越界了,所以需要考虑一个偏移把最后一项归零。这里需要+16:
1 | # 第i次的循环 |
1 | SDC1 F2,16(R1) |
3.延迟槽填充
可以考虑把ADDIU R1,R1,-24
指令用于填充延迟槽,这样在判断完条件后才会-8,也就是说会多-8越界,所以需要把r1的初始值再偏移8
1 | SDC1 F2,24(R1) 8 |
4. 装入代码
装入就是预处理。
装入代码就是上面这部分代码
1 | # 第i次的循环 |
1 | LDC1 F0,0,24(R1) |
整个程序第一次取到的应该是0(R1)和0(R2)的值,而后续主循环体中的偏移量是+24,因此要对装入代码的偏移量进行修正对R1-24,这样就24-24=0了
1 | LDC1 F0,0,0(R1) |
5.排空代码
排空就是收尾

1 | SDC1 F2,16(R1) |
因为在循环体里面的延迟槽执行了-8操作,从循环出来还要+8
1 | SDC1 F2,24(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,如下所示: