TL;DR: 实际上,如您所想,多步转换通常会通过多次转换来完成。但是可以使用一些技巧来避免移动太多次。例如,一些算法被设计成只需要移位 1,或者如果需要更大的移位,则可以使用 ISA 中的一些特殊的按位指令进行优化
嵌入式系统编程需要对该架构有更深入的了解,才能获得良好的性能和 RAM/ROM 使用率。例如,选择变量以使其适合机器字。除非绝对必要,否则没有人会像在 16 位或 8 位 MCU 上那样使用 uint64_t。对多字变量(包括位移)的操作需要更多的指令,因此它们通常不会被内联。一般来说,对于字长的倍数的移位与移位数组元素类似,因此它们会非常快
移动任意变量需要barrel shifter,它比简单的shift register 占用更多的裸片空间,因此大多数嵌入式架构一次只能移动一位。它们中的大多数还包括某种 swap halfword 指令来克服限制并允许足够快的大距离移位。例如 8 位微控制器(如 8051、PIC 或 AVR)具有交换半字节指令。见:
MSP430 是一个 16 位 MCU,因此它有一个 SWPB 指令来交换 字节,它可以类似地用于快速移位 8。这里是 some examples generated by Clang(我用 cmets ,注意 8 的移位和大于 8 的移位是如何完成的):
shift_left_15(unsigned short): ; @shift_left_15(unsigned short)
mov.b r12, r12
swpb r12 ; swap bytes then shift left 7 times
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
ret
shift_left_12(unsigned short): ; @shift_left_12(unsigned short)
mov.b r12, r12
swpb r12 ; swap bytes then shift left 4 times
add r12, r12
add r12, r12
add r12, r12
add r12, r12
ret
shift_left_10(unsigned short): ; @shift_left_10(unsigned short)
mov.b r12, r12
swpb r12 ; swap bytes then shift left 2 times
add r12, r12
add r12, r12
ret
shift_left_9(unsigned short): ; @shift_left_9(unsigned short)
mov.b r12, r12
swpb r12
add r12, r12
ret
shift_left_8(unsigned short): ; @shift_left_8(unsigned short)
mov.b r12, r12
swpb r12 ; just swap bytes
ret
shift_left_7(unsigned short): ; @shift_left_7(unsigned short)
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
add r12, r12
ret
shift_left_3(unsigned short): ; @shift_left_3(unsigned short)
add r12, r12
add r12, r12
add r12, r12
ret
您可以打开上面的 Godbolt 链接查看完整输出
如果您使用的是 MSP430X,那么它能够从 1 位转换到 4 位,greatly simplifies the shifting procedure
shift_left_15(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #15 { rlax.w R12
POPM.W #1, r4
RET
shift_left_12(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #12 { rlax.w R12
POPM.W #1, r4
RET
shift_left_10(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #10 { rlax.w R12
POPM.W #1, r4
RET
shift_left_9(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #9 { rlax.w R12
POPM.W #1, r4
RET
shift_left_8(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #8 { rlax.w R12
POPM.W #1, r4
RET
shift_left_7(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #7 { rlax.w R12
POPM.W #1, r4
RET
shift_left_3(unsigned short):
PUSHM.W #1, R4
MOV.W R1, R4
rpt #3 { rlax.w R12
POPM.W #1, r4
RET
右移可以以相同的方式完成,但将add 替换为rrc/rra 并将rlax 替换为rrax。见demo on Godbolt