x86 汇编/移位和旋转
在逻辑移位指令(也称为无符号移位)中,从末尾移出的位会消失(除了最后一个位进入进位标志),并且空格始终填充零。逻辑移位最适合用于无符号数。
| shr cnt, dest | GAS 语法 | 
| shr dest, cnt | Intel 语法 | 
将dest逻辑右移cnt位。
| shl cnt, dest | GAS 语法 | 
| shl dest, cnt | Intel 语法 | 
将dest逻辑左移cnt位。
示例 (GAS 语法)
movw   $ff00,%ax        # ax=1111.1111.0000.0000 (0xff00, unsigned 65280, signed -256) 
shrw   $3,%ax           # ax=0001.1111.1110.0000 (0x1fe0, signed and unsigned 8160)
                        # (logical shifting unsigned numbers right by 3
                        #   is like integer division by 8)
shlw   $1,%ax           # ax=0011.1111.1100.0000 (0x3fc0, signed and unsigned 16320) 
                        # (logical shifting unsigned numbers left by 1
                        #   is like multiplication by 2)
在算术移位(也称为有符号移位)中,与逻辑移位类似,从末尾移出的位会消失(除了最后一个位进入进位标志)。但在算术移位中,空格的填充方式会保留被移位数字的符号。因此,算术移位更适合于二进制补码格式的有符号数。
| sar cnt, dest | GAS 语法 | 
| sar dest, cnt | Intel 语法 | 
将dest算术右移cnt位。空格用符号位(以保持原始值的符号)填充,即原始最高位。
| sal cnt, dest | GAS 语法 | 
| sal dest, cnt | Intel 语法 | 
将dest算术左移cnt位。底部的位不会影响符号,因此底部位用零填充。此指令等同于 SHL。
示例 (GAS 语法)
movw   $ff00,%ax        # ax=1111.1111.0000.0000 (0xff00, unsigned 65280, signed -256)
salw   $2,%ax           # ax=1111.1100.0000.0000 (0xfc00, unsigned 64512, signed -1024)
                        # (arithmetic shifting left by 2 is like multiplication by 4 for
                        #   negative numbers, but has an impact on positives with most
                        #   significant bit set (i.e. set bits shifted out))
sarw   $5,%ax           # ax=1111.1111.1110.0000 (0xffe0, unsigned 65504, signed -32)
                        # (arithmetic shifting right by 5 is like integer division by 32
                        #   for negative numbers)
双精度移位操作的名称有些误导,因此在本页中它们被列为扩展移位指令。
它们可用于 16 位和 32 位数据实体(寄存器/内存位置)。src 操作数始终是寄存器,dest 操作数可以是寄存器或内存位置,cnt 操作数是立即字节值或 CL 寄存器。在 64 位模式下,也可以寻址 64 位数据。
| shld cnt, src, dest | GAS 语法 | 
| shld dest, src, cnt | Intel 语法 | 
shld 执行的操作是将 dest 中最重要的 cnt 位移出,但不是用零填充最低有效位,而是用 src 的最重要的 cnt 位填充。
| shrd cnt, src, dest | GAS 语法 | 
| shrd dest, src, cnt | Intel 语法 | 
类似地,shrd 操作将 dest 中最低有效的 cnt 位移出,并用 src 操作数的最低有效位填充最重要的 cnt 位。
英特尔的命名法具有误导性,因为移位不作用于双倍的基本操作数大小(即指定 32 位操作数不会使其成为 64 位移位):src 操作数始终保持不变。
此外,英特尔的参考手册[1] 指出,当 cnt 大于操作数大小时,结果是未定义的,但至少对于 32 位和 64 位数据大小,已观察到移位操作是通过 (cnt mod n) 执行的,其中 n 是数据大小。
示例 (GAS 语法)
xorw   %ax,%ax          # ax=0000.0000.0000.0000 (0x0000)
notw   %ax              # ax=1111.1111.1111.1111 (0xffff)
movw   $0x5500,%bx      # bx=0101.0101.0000.0000
shrdw  $4,%ax,%bx       # bx=1111.0101.0101.0000 (0xf550), ax is still 0xffff
shldw  $8,%bx,%ax       # ax=1111.1111.1111.0101 (0xfff5), bx is still 0xf550
其他示例(十进制数用于解释概念,而不是二进制数)
# ax = 1234 5678
# bx = 8765 4321
shrd   $3, %ax, %bx     # ax = 1234 5678 bx = 6788 7654
# ax = 1234 5678
# bx = 8765 4321
shld   $3, %ax, %bx     # bx = 5432 1123 ax = 1234 5678
在旋转指令中,从寄存器末尾移出的位会反馈到空格中。
| ror offset, variable | GAS 语法 | 
| ror variable, offset | Intel 语法 | 
将variable右移offset位。以下是这种操作的图形表示
╭──────────────────╮ %al old │ 0 0 1 0'0 1 1 1 │ ror 1, %al ╰─╮╲ ╲ ╲ ╲ ╲ ╲╰─╯ %al new 1 0 0 1'0 0 1 1
旋转位数offset被掩码到最低 5 位(或在 64 位模式下为 6 位)。这等同于  操作,即整数除法的余数(注意:)。这意味着,你永远无法进行一次或多次“完整”旋转。
- Variable必须是寄存器或内存位置。
- Offset可以是- 立即值(其中值 1有一个专门的操作码),
- 或者 cl寄存器(即ecx的最低字节)。
 
- 立即值(其中值 
ror 仅在掩码后的 offset 为非零时才会改变标志。 CF 变成最近旋转的位,因此在 ror 的情况下,result 的 MSB(“符号”。
此外,如果掩码后的 offset = 1,OF ≔ result[MSB] ⊻ result[MSB−1],因此 OF 告诉我们“符号”是否发生了变化。
| rol offset, variable | GAS 语法 | 
| rol variable, offset | Intel 语法 | 
将variable左移offset位。
操作数和修改的标志与 ror 相差无几。但是,在掩码后的 offset = 1 的情况下,OF 的定义不同,尽管实际上意义相同。对于 rol 1, x,OF ≔ result[MSB] ⊻ result[LSB]。
请注意,在 rol 的情况下,CF 包含 LSB。
类似于移位操作,旋转操作可以使用进位位作为它移位的“额外”位。
| rcr cnt, dest | GAS 语法 | 
| rcr dest, cnt | Intel 语法 | 
将dest向右旋转cnt位,并使用进位位。
| rcl cnt, dest | GAS 语法 | 
| rcl dest, cnt | Intel 语法 | 
将dest向左旋转cnt位,并使用进位位。
除非另有说明,这些指令可以接受一个或两个参数。如果只提供一个参数,则假定它是寄存器或内存地址,并且要移位/旋转的位数为1(但是,这可能取决于使用的汇编器)。 shrl $1, %eax  等效于  shrl %eax (GAS 语法)。
- ↑ 英特尔® 64 和 IA-32 架构软件开发人员手册 第 2 卷 (PDF, 6.2 MB)