AArch64 does not have dedicated sign and zero-extension instructions because they can be synthesized as pseudo-instructions from the bitfield extraction instructions.
; unsigned extend byte
; Rd = (uint8_t)Rn
uxtb Rd/zr, Rn/zr ; ubfx Rd, Rn, #0, #8
; unsigned extend halfword
; Rd = (uint16_t)Rn
uxth Rd/zr, Rn/zr ; ubfx Rd, Rn, #0, #16
; signed extend byte
; Rd = (int8_t)Rn
sxtb Rd/zr, Rn/zr ; sbfx Rd, Rn, #0, #8
; signed extend halfword
; Rd = (int16_t)Rn
sxth Rd/zr, Rn/zr ; sbfx Rd, Rn, #0, #16
; unsigned extend word
mov Wd, Wn
; signed extend word
sxtw Xd/zr, Xn/zr ; sbfx Xd, Xn, #0, #32
The odd man out here is the lack of an unsigned extend word pseudo-instruction, but a MOV works just as well, because the rule for 32-bit destinations is that they are zero-extended to a 64-bit value. So just moving a 32-bit register secretly zero-extends it. In practice, you can usually get the zero-extension for free by using a 32-bit register as the destination for the original calculation.
You can avoid having using these instructions if you can merge it into a subsequent extended register operation:
; as two instructions, using r3 as scratch register
uxtb r3, r2 ; r3 = (uint8_t)r2
add r0, r1, r3 ; r0 = r1 + r3
; merged into one instruction, avoids scratch register
add r0, r1, r2, uxtb ; r0 = r1 + (uint8_t)r2
For extending a word to a doubleword, you can synthesize that easily enough:
; unsigned extend word in Xd to doubleword in Xd/X(d+1)
mov X(d+1), #0
; signed extend word in Xd to doubleword in Xd/X(d+1)
asr X(d+1), Xd, #63 ; copy sign bits to all bits
Next time, we’ll look at ways of loading constants.
0 comments