问题来源
举个小栗子
市面上有很多种芯片,其中对应的指令集也很丰富,可是不同的指令集对于各种基本功能的支持力度是不同的,以PIC16芯片和X86芯片相比较,举出一个很简单的例子,就是乘法,比如实现c = a * b
:
在X86中,实现如下:
movl b, %eax
mul b
这里不敢说乘法指令的全部操作只需要一条指令,但是乘法的本身还是只需要一条指令的,即mul b
,但是在PIC16中会变成什么样子呢,代码如下:
movf a, w
movf mul.atmp, f
movf b, w
movf mul.btmp, f
call mul.i8
movf mul.result, w
movf c, 1
这里可能写的不规范,但是总体的意思是,将变量a
和 b
分别放到乘法库函数定义的符号mul.atmp
和mul.btmp
的符号里面,然后调用乘法库函数mul.i8
,最后从库函数预先设定的符号里面取值,放到符号c
中,如果觉得不好理解,可以先看成下面的高级函数形式的声明:
char mul.i8(char mul.atmp, char mul.btmp);
然后用户调用这个函数:
mul.result = mul.i8(a, b);
c = mul.result;
小栗子的结论
从上面的小栗子可以看出,不同的芯片对于每种基本基本操作的支持力度真的是不一样的,所以本文题目中的操作—有符号扩展,在两个芯片中的支持力度也不同,下面开始分析
分析过程
所需要的指令集
指令名称 | 指令作用 |
---|---|
rlf mem, w |
将符号mem 的值循环左移一位,最高位进入进位位,最终的值写入寄存器,不写入f 中 |
rlf mem, f |
将符号mem 的值循环左移一位,最高位进入进位位,原本符号位进入mem 的最低位,写回到f 中 |
clrf mem |
将mem 处的值清零 |
decf mem, f |
将mem 的值减一,并进行回写 |
comf mem, f |
将mem 的值取反,并进行回写 |
bcf mem, b |
将mem 的第b 为清零 |
本芯片中如何用两条指令完成一次算数右移
先告诉你们一个不幸的消息,PIC16芯片中关于移位只有循环右移和循环左移,没有算数右移算数左移和逻辑右移。而且PIC16里面也没有相应的标志位寄存器,对于标志位,PIC16的策略是保存在一个内存符号里面,好在PIC16有一条指令bcf
,这条指令可以将某个内存符号中的特定位置0,对应的也有bsf
指令,可以将特定的内存位置的特定位置1,那么如何使用两条指令进行模拟呢。下面直接说结论吧,。
算数右移:
rlf mem, w
rrf mem, f
算数左移/逻辑左移:
bcf flags, 3 // 这里不一定是3,这里假设状态位的第三位是进位位,即这里是把进位位清零
rlf mem, f
逻辑右移:
bcf flags, 3 // 这里不一定是3,这里假设状态位的第三位是进位位,即这里是把进位位清零
rrf mem, f
两种没有经过优化的方式
使用库函数的方式
就像第一部分所说的,如果使用库函数,那么是8位扩展为16位,需要7条指令,因为可以指定移位次数是7,代码如下:
movf a, w
movf srashift.atmp, f
movl 7
movf srashift.btmp, f
call srashift.i8
movf srashift.result, w
movf c, f
同样可以看成高级语言函数声明:
srashift.result = srashift(char srashift.atmp, char srashift.btmp);
用户可以调用这个函数:
srashift.result = srashift(a, 7);
c = srashift.result;
而PIC16中的最高位宽是32位,最低位宽是8位,所以这里存在三种有符号扩展,分别为i8->i16
, i8 -> i32
, i16 -> i32
,这三种有符号扩展分别所需要的指令条数为7条,至少11条,和13条。这是统计数据,相信我,没骗你们。
重复生成移位指令
这个小题目看起来神秘莫测的,但是其实非常2,所谓的重复生成移位指令就是一条一条的移位指令的写,这里以8位扩展为16位为例,可以看出位宽只差为8位,可是本平台每移动一次就需要两条指令,所以如果是8位扩展为16位需要16条指令。同理8位扩展32位需要48条指令,16位扩展32位需要32条指令。
本博客提出的方法
直接说出方法吧,这里以8位扩展16为例,设低位保存在mem + 0
,值为00000000
,高位保存在mem + 1
中,由于申请这个符号的时候值是未知的,所以设置为xxxxxxxx
,完成有符号移位只需要五条指令:
clrf mem + 1, f
rlf mem + 0, w
rlf mem + 1, f
decf mem + 1, f
comf mem + 1, f
让我们来看看这五条指令的执行效果:
指令名称 | 执行效果 |
---|---|
高位初始值 | xxxxxxxx |
clrf mem + 1, f |
00000000 |
rlf mem + 0, w |
00000000 |
rlf mem + 1, f |
00000001 |
decf mem + 1, f |
00000000 |
comf mem + 1, f |
11111111 |
可以看出,最后的高位是11111111
,与mem + 0
的最高位相等。容易证明,对于正数来说也是如此,最后的结果是00000000
结语
从这篇博客里面也可以得知,不管一条指令有多么简单,都不要把这条指令的实现当成理所当然的,就比如乘法,在PIC16里面是没有对应的指令来实现的,再退一步,加法,也是通过各种位运算实现的呀。对于不同的芯片的相同指令,有不同的代码生成方法,这正是编译的乐趣所在,嘻嘻~