Exploring AArch64 assembler – Chapter 3
In the last chapter we saw that instructions may have register operands and immediate operands. We also mentioned that mixing 32-bit and 64-bit register was not allowed. Today we will talk a bit more about register operands.
Operators for register operands
Many instructions that take a register as the second source operand of an instruction can also apply some extra operation to the value of that source register. This can be used as a way to increase density of computation by requiring less instructions and also to allow some common operations, e.g. conversions, in one of the operands.
We can distinguish two kinds of operators here: shifting operators and extending operators.
Shifting operators
There are three shifting operators in AArch64: LSL, LSR, ASR and ROR. Their syntax is as follows:
reg, LSL, #amount reg, LSR, #amount reg, ASR, #amount reg, ROR, #amount |
where reg
can be a 64-bit register Xn
or a 32-bit register Wn
and amount
is a number whose range depends on the register used and ranges from 0 to 31 for 32-bit registers and from 0 to 63 for 64-bit registers.
Operator LSL
performs a logical shift left to the value in reg
(it does not change the contents of reg
though). Shifting n bits to the left means introducing n
zeros as the least significant bits and discarding n
most significant bits from the original value. Shifting left n-bits is equivalent to multiply to 2n.
add r1, r2, r3, LSL #4 /* r1 ← r2 + (r3 << 4) this is the same as r1 ← r2 + r3 * 16 */ add r0, r0, r0, LSL #2 /* r0 ← r0 + r0 << 2 this is the same as r0 ← r0 + r0 * 4 which happens to be the same as r0 ← r0 * 5 assuming no overflow happens */ |
Operator LSR performs a logical shift right. This operation is the dual of LSL, but zeros are introduced in the n most significant bits and the n least significant bits are discarded. For unsigned arithmetic numbers, this operation is equivalent to division by 2n.
Operator ASR performs an arithmetic shift right. This is like LSL but instead of introducing zeros in the n most significant bits the most significant bit is replicated n times in the n most significant bits. As in LSL, the n least significant bits are discarded. If the most significant bit of the register is zero, ASR is equivalent to LSR. This shift operator is useful for two’s complement numbers as it propagates the sign bit (which would be the most significant bit in the register if interpreted as a binary number) and it can be used for dividing by 2n negative numbers as well. A LSR on a two’s complement negative number does not make sense for the purpose of a division.
Operator ROR performs a rotate right of the register. This is commonly used for cryptography and its usage is less usual than the other shifting operands. A rotation is similar to LSR but rather than dropping bits and introducing zeros, the least signficant bits that would be dropped are introduced as the most significant bits. There is no rotate left because a rotate right can be used for this: just rotate all bits minus the number of steps we want to rotate to the left.
In AArch64 only a few instructions (mainly logical ones) can use the ROR shifting operator.
mov w2, #0x1234 // w2 ← 0x1234 mov w1, wzr // w1 ← 0 orr w0, w1, w2, ROR #4 // w0 ← BitwiseOr(w1, RotateRight(w2, 4)) // this sets w0 to 0x40000123 orr w0, w1, w2, ROR #28 // w0 ← BitwiseOr(w1, RotateRight(w2, 32-4)) // this is in practice like RotateLeft(w2, 4) // so this sets w0 to 0x12340 |
Extending operators
Extending operators main purpose is to widen a narrower value found in a register to match the number of bits for the operation. An extending operator is of the form kxt
w, where k is the kind of integer we want to widen and w is the width of the narrow value. For the former, the kind of integer can be U
(unsigned) or S
(signed, i.e. two’s complement). For the latter the width can be B
, H
or W
which means respectively byte (least 8 significant bits of the register), half-word (least 16 significant bits of the register) or word (least significant 32 bits of the register).
This means that the extending operators are uxtb
, sxtb
, uxth
, sxth
, uxtw
, sxtw
.
These operators exist because sometimes we have to lift the range of the source value from a smaller bit width to a bigger one. In later chapters we will see many cases where this happens. For instance, it may happen that we need to add a 32-bit register to a 64-bit register. If both registers represent two’s complement integers then
add x0, x1, w2, sxtw // x0 ← x1 + ExtendSigned32To64(w2) |
There is some kind of context that has to be taken into account when using these extension operators. For instance, the two instructions below have slight different meanings:
add x0, x1, w2, sxtb // x0 ← x1 + ExtendSigned8To64(w2) add w0, w1, w2, sxtb // w0 ← w1 + ExtendSigned8To32(w2) |
In both cases the least significant 8 bits of w2
are extended but in the first case they are extended to 64 bit and in the second case to 32-bit.
Extension and shift
It is possible to extend a value and then shift it left 1, 2, 3 or 4 bits by specifying an amount after the extension operator. For instance
mov x0, #0 // x0 ← 0 mov x1, #0x1234 // x0 ← 0x1234 add x2, x0, x1, sxtw #1 // x2 ← x0 + (ExtendSigned16To64(x1) << 1) // this sets x2 to 0x2468 add x2, x0, x1, sxtw #2 // x2 ← x0 + (ExtendSigned16To64(x1) << 2) // this sets x2 to 0x48d0 add x2, x0, x1, sxtw #3 // x2 ← x0 + (ExtendSigned16To64(x1) << 3) // this sets x2 to 0x91a0 add x2, x0, x1, sxtw #4 // x2 ← x0 + (ExtendSigned16To64(x1) << 4) // this sets x2 to 0x12340 |
This may seem a bit odd and arbitrary at this point but in later chapters we will see that this is actually useful in many cases.
This is all for today.
Exploring AArch64 assembler – Chapter 2 Exploring AArch64 assembler – Chapter 4
l2: mov x1, #0x1234 // x1 ← 0x1234
l3: add x2, xzr, x1, sxtw #1 // x2 ← 0 + (ExtendSigned16To64(x1) << 1)
That is, use the xzr register (always reads 0) as the first source operand. But the gcc compiler [(SUSE Linux) 6.2.1 20161209 ] gives an error message "extst2.s:6: Error: extending shift is not permitted at operand 3 — `add x2,xzr,x1,sxtw#1'" for each of those four lines.
I can't think of any reason why that shouldn't be an allowed operation. Is that a compiler (assembler) error, or is there a reason why using "xzr" as the first source prevents use of extending shift of the second source operand?
if you check the documentation of ADD you will see that it reads as
Xn|SP
orWn|WSP
. This syntax means that XZR or WZR cannot be used in these contexts.In fact, attempting to do so, like you do in the other comment actually means using the WSP, XSP which are other registers (whose meaning I have not explained but I can advance you that they mean the stack pointer ).
Kind regards,
Roger
If I code the instruction manually into a 32-bit .word,
“add x2,xzr,x1,sxtw #1” executes as expected, storing into x2 the value in x1 shifted by the requested number of bits.
Anyone wanting to try it, the instruction is “.word 0x8b21c7e2 // add x2,xzr,x1,sxtw #1”
I would hope this would be fixed in a future release of the assembler.
thanks for your comment. As I mentioned above, not all instructions allow the usage of XZR or WZR. If the syntax says Xn|XSP or Wn|WSP it means that XZR, WZR cannot be used in that operand. The instruction you have encoded is effectively the following one:
This is clearly not what you wanted!
Kind regards,
Roger
And again, for those following this, my hand-assembled instruction “add x2,xzr,x1,sxtw #1” is, as Roger analyzed, actually the encoding for “add x2, sp, w1, sxtw #1”. As the reference manual points out (ARM DDI 0487A.k
Copyright © 2013-2016 ARM Limited or its affiliates. All rights reserved. pg C6-437), the “0b1111” encoding of the register field, which I had assumed would reference X31 (the XZR register), references XSP in this context. What I thought was a correctly executing instruction when I ran it though gdb was actually an instruction that left the correct shifted value from an earlier instruction in X2, the destination register — and I thought it had executed correctly.
Thanks again, Roger, for the tutorials and for the clarifications!