x86 Assembly/Shift and Rotate
Logical Shift Instructions
[edit | edit source]In a logical shift instruction (also referred to as unsigned shift), the bits that slide off the end disappear (except for the last, which goes into the carry flag), and the spaces are always filled with zeros. Logical shifts are best used with unsigned numbers.
shr cnt, dest | GAS Syntax |
shr dest, cnt | Intel Syntax |
Logical shift dest
to the right by cnt
bits.
shl cnt, dest | GAS Syntax |
shl dest, cnt | Intel Syntax |
Logical shift dest
to the left by cnt
bits.
Examples (GAS Syntax):
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)
Arithmetic Shift Instructions
[edit | edit source]In an arithmetic shift (also referred to as signed shift), like a logical shift, the bits that slide off the end disappear (except for the last, which goes into the carry flag). But in an arithmetic shift, the spaces are filled in such a way to preserve the sign of the number being slid. For this reason, arithmetic shifts are better suited for signed numbers in two's complement format.
sar cnt, dest | GAS Syntax |
sar dest, cnt | Intel Syntax |
Arithmetic shift dest
to the right by cnt
bits. Spaces are filled with sign bit (to maintain sign of original value), which is the original highest bit.
sal cnt, dest | GAS Syntax |
sal dest, cnt | Intel Syntax |
Arithmetic shift dest
to the left by cnt
bits. The bottom bits do not affect the sign, so the bottom bits are filled with zeros. This instruction is synonymous with SHL.
Examples (GAS Syntax):
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)
Extended Shift Instructions
[edit | edit source]The names of the double precision shift operations are somewhat misleading, hence they are listed as extended shift instructions on this page.
They are available for use with 16- and 32-bit data entities (registers/memory locations). The src
operand is always a register, the dest
operand can be a register or memory location, the cnt
operand is an immediate byte value or the CL register. In 64-bit mode it is possible to address 64-bit data as well.
shld cnt, src, dest | GAS Syntax |
shld dest, src, cnt | Intel Syntax |
The operation performed by shld
is to shift the most significant cnt
bits out of dest
, but instead of filling up the least significant bits with zeros, they are filled with the most significant cnt
bits of src
.
shrd cnt, src, dest | GAS Syntax |
shrd dest, src, cnt | Intel Syntax |
Likewise, the shrd
operation shifts the least significant cnt
bits out of dest
, and fills up the most significant cnt
bits with the least significant bits of the src
operand.
Intel's nomenclature is misleading, in that the shift does not operate on double the basic operand size (i.e. specifying 32-bit operands doesn't make it a 64-bit shift): the src
operand always remains unchanged.
Also, Intel's manual[1] states that the results are undefined when cnt
is greater than the operand size, but at least for 32- and 64-bit data sizes it has been observed that shift operations are performed by (cnt mod n
), with n being the data size.
Examples (GAS Syntax):
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
Other examples (decimal numbers are used instead of binary number to explain the concept)
# 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
Rotate Instructions
[edit | edit source]Rotate Right
[edit | edit source]In a rotate instruction, the bits that slide off the end of the register are fed back into the spaces.
ror offset, variable | GAS Syntax |
ror variable, offset | Intel Syntax |
Rotate variable
to the right by offset
bits.
Here is a graphical representation how this looks like:
╭─────────────────╮ %al old │ 0 0 1 0'0 1 1 1 │ ror 1, %al ╰─╮╲ ╲ ╲ ╲ ╲ ╲ ╲╰─╯ %al new 1 0 0 1'0 0 1 1
The number of bits to rotate offset
is masked to the lower 5 bits (or 6 bits in 64-bit mode).
This is equivalent to a operation, i. e. the remainder of integer division (note: ).
This means, you can never do one or more “complete” rotations.
Operands
[edit | edit source]Variable
has to be a register or memory location.Offset
can be either- an immediate value (where the value
1
has a dedicated opcode), - or the
cl
register (that is the lowest byte ofecx
).
- an immediate value (where the value
Modified Flags
[edit | edit source]ror
only alters flags if the masked offset
is non-zero.
The CF becomes the most recently rotated bit, so in the case of ror
the result’s MSB (the “sign”).
Furthermore, if the masked offset
= 1, OF ≔ result[MSB] ⊻ result[MSB−1], so the OF tells us, whether “the sign” has changed.
Rotate Left
[edit | edit source]rol offset, variable | GAS Syntax |
rol variable, offset | Intel Syntax |
Rotate variable
to the left by offset
bits.
Operands and modified flags are pretty much the same as for ror
.
However, in the case that the masked offset
= 1, the OF is defined differently, although it has effectively same meaning.
For rol 1, x
the OF ≔ result[MSB] ⊻ result[LSB].
Note that the CF contains the LSB in the case of rol
.
Rotate With Carry Instructions
[edit | edit source]Like with shifts, the rotate can use the carry bit as the "extra" bit that it shifts through.
rcr cnt, dest | GAS Syntax |
rcr dest, cnt | Intel Syntax |
Rotate dest
to the right by cnt
bits with carry.
rcl cnt, dest | GAS Syntax |
rcl dest, cnt | Intel Syntax |
Rotate dest
to the left by cnt
bits with carry.
Number of arguments
[edit | edit source]Unless stated, these instructions can take either one or two arguments. If only one is supplied, it is assumed to be a register or memory location and the number of bits to shift/rotate is one (this may be dependent on the assembler in use, however).
shrl $1, %eax
is equivalent to shrl %eax
(GAS syntax).