对于以下功能……
uint16_t swap(const uint16_t value) { return value << 8 | value >> 8; }
…为什么带有-O2的ARM gcc 6.3.0会产生以下程序集?
swap(unsigned short): lsr r3,r0,#8 orr r0,r3,lsl #8 lsl r0,#16 # shift left lsr r0,#16 # shift right bx lr
看起来编译器使用两个移位来屏蔽不需要的字节,而不是使用逻辑AND.编译器可以使用和r0,#4294901760吗?
解决方法
较旧的ARM程序集无法轻松创建常量.相反,它们被加载到文字池中,然后通过内存负载读入.这个和你建议只能让我相信一个带有移位的8位字面值.您的0xFFFF0000需要16位作为1条指令.
所以,我们可以从内存加载并执行和(慢),
用2条指令创建值,1到和(更长),
或者只是便宜地换两次并称之为好.
编译器选择了班次,老实说,它很快.
现在进行现实检查:
担心单一班次,除非这是100%肯定的瓶颈是浪费时间.即使编译器是次优的,你几乎也不会感觉到它.担心代码中的“热”循环而不是像这样的微操作.从好奇心看这个很棒.担心这个确切的代码在您的应用程序中的性能,而不是.
编辑:
其他人已经注意到,ARM规范的更新版本允许更有效地完成此类事情.这表明,在这个级别讨论时,重要的是指定芯片或至少指定我们正在处理的精确ARM规范.我从你的输出中缺少“更新”的指令来假设古老的ARM.如果我们正在跟踪编译器错误,那么这个假设可能不成立,并且知道规范甚至更重要.对于像这样的交换,在更高版本中确实有更简单的指令来处理它.
编辑2
可能做得更快的一件事就是使其内联.在这种情况下,编译器可以将这些操作与其他工作交错.根据cpu的不同,这可能会使吞吐量翻倍,因为许多ARM cpu都有2个整数指令流水线.尽可能地扩展说明,以便没有危险,然后就可以了.这必须权衡I-Cache的使用,但在重要的情况下,你可以看到更好的东西.