关于arm64:04ARMv8指令集运算指令集

12次阅读

共计 3765 个字符,预计需要花费 10 分钟才能阅读完成。

  • 加法指令 ADD、ADDS、ADCS
  • 减法指令 SUB、SUBS、SBC,SBCS,CMP
  • 位操作 AND, ANDS, ORR、EOR、BFI、UBFX、SBFX

1. 加法指令

加法指令有 ADD、ADDS、ADCS。ADD 一般性加法指令,ADCS 带 C 标记位运算的加法指令,ADDS 影响 C 标记位的加法运算。

1.1 ADD

a = a + b, 没有进位标记,也不会利用进位标记

  • ADD (extended register) :

    • Define: ADD <Xd|SP>, <Xn|SP>, <Wm>, {<extend> {#<amount>}}
    • Example1: add x0, x1, x2 (x0 = x1 + x2)
    • Example2: add x0, x1, x2, lsl #5(x0 = x1 + (x2 << 5) )
  • ADD (immediate):

    • Define: ADD <Xd|SP>, <Xn|SP>, #<imm>{, lsl <#shift>}, note shift supports #0 and #12 only.
    • Example1: add x1, x2, #8 (x1 = x2 + 8)
    • Example2: add x1, x2, #8, lsl #12 (x1 = x2 + (8 << 12) )
  • ADD (shifted register):

    • Define: ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>} , note #amount range 0 to 63
    • Note: LSL when shift = 0, LSR when shift = 1, ASR when shift = 2
    • Example1: add x1, x2, x3, asr #2

1.2 ADDS

(a,C) = a + b, 带进位标记的加法,用法和 ADD 一样

1.3 ADCS

(a,C) = a + b + C,带进位标记的加法,且须要加上 C 标记位,用法和 ADD 一样。留神,如果加法溢出的时候 C 标记位会置位为 1,比方,a = 0xFFFFFFFFFFFFFFFF, b = 1,此时,加法溢出,C 置位 1。

1.4 ADR

a = b + PC, 以后程序的 PC 值加上给定的地址偏移

  • ADR

    • Define: ADR <Xd>, <label>
    • Note, no 32-bit
    • Note, <label> range ±1MB, offset from the address of this instruction.
    • Example01: adr x1, #25

1.5 对于查看 C flag 的办法

办法 1:应用 MSR/MRS 指令

    msr NZCV, xzr       // clear the NZCV
    mrs x0, NZCV     // 查看 NZCV 寄存器,NZCV 在高位 28 - 32 bits

办法 2:应用 ADCS 的 + C 个性

adcs x0, zxr, xzr 让两个 0 寄存器相加 0+0+ c 就能够失去 C 标记位的值

2. 减法指令

减法指令蕴含 SBC,SBCS。请参考 ARMv8 手册,C6.2.231 C6-1299

2.1 SUB

a = a – b, 没有进位标记,也不会利用进位标记。应用办法和 ADD 统一。

2.2 SUBS

(a,N) = a – b, 会置标记位 N。应用办法和 SUBS 统一,减成正数的时候,其余地位补 1。

2.3 SBC

a = a – b – 1 + C

  • SBC (Subtract with Carry):

    • Define: SBC <Xd>, <Xn>, <Xm>
    • Example: sbc x0, xzr, xzr

2.4 SBCS

(a, N) = a – b – 1 + C,如果减出正数的话,N 会被置位

  • SBC (Subtract with Carry, setting N flag):

    • Define: SBCS <Xd>, <Xn>, <Xm>
    • Example: sbcs x0, xzr, x1

2.5 CMP

比拟指令,实际上也应用 SBC 实现的, cmp x1, x2

  • 若 x1 > x2, NCZV = 0100
  • 若 x1 = x2, NCZV = 0110
  • 若 x1 < x2, NCZV = 1000

Define 1: CMP <Xn|SP>, <R><m>{, <extend> {#<amount>}}

Define 2: CMP <Xn|SP>, #<imm>{, <shift>}

Define 3: CMP <Xn|SP>, <Xm>{, <shift> #<amount>}

Example:

* The function cmp_and_return_test:

* if a >= b return 1

* if a < b return 0

test_cmp:
    cmp x0, x1               // if x0 >= x1,  C is 1; if x0 < x1 C is 0
  adcs x0, xzr, xzr // 0 + 0 + C

3. 位操作

位操作蕴含 AND, ANDS, ORR、EOR、BFI、UBFX、SBFX,别离是与、与置位标记位、或、异或、插入、无符号提取、有符号提取。

3.1 ORR

a = a | b;

Define 1: ORR <Xd|SP>, <Xn>, #<imm>

Define 2: ORR <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}

test_orr:
    // ORR test  0xAA oor 0x55 = 0xFF
    //           0xFF oor 0x00 = 0xFF
    //           0xFF oor 0xFF = 0xFF
    //           0x00 oor 0x00 = 0x00
    mov x0, xzr
    mov x1, #0xAA
    mov x2, #0x55
    orr x1, x1, x2

    mov x1, #0xFF
    orr x1, x1, xzr

    mov x1, #0xFF
    orr x1, x1, x1

    orr x1, xzr, xzr

    ret

3.2 EOR

a = a ^ b;

Define 1: EOR <Xd|SP>, <Xn>, #<imm>

Define 2: EOR <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}

test_eor:
    // test 2 exchange the value x1 = 0x07, x2 = 0xAA
    // using the orr, just use two register.
    // x1 = x1^x2
    // x2 = x2^x1
    // x1 = x1^x2
    ldr x1, =0x07
    ldr x2, =0xAA
    eor x1, x1, x2
    eor x2, x2, x1
    eor x1, x1, x2
    ret

几个 EOR 的小技巧:

  • 翻转某些位: 比方把右数第 0 位到第 3 位翻转:1010 1001 ^ 0000 1111 = 1010 0110
  • 替换数值:a=a^b; b=b^a; a=a^b,不借助第三个变量
  • 置 0:a^a
  • 判断相等 a^b == 0

3.3 AND

3.3.1 AND

a = a & b;

Define 1: AND <Xd|SP>, <Xn>, #<imm>

Define 2: AND <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}

    msr NZCV, xzr       // clear the NZCV
    ldr x1, =0xAA
    ldr x2, =0x0
    // test AND, no Z flag. x1 = x1&x2
    and x1, x1, x2
    mrs x0, NZCV

3.3.2 ANDS

(a, z) = a & b. 如果 a 和 b 与的后果为 0,z flag 置位

    // test ANDS, z flag, if the result is 0, Z is 1
    msr NZCV, xzr       // clear the NZCV
    mov x0, xzr
    ldr x1, =0xAA
    ands x1, x1, x2
    mrs x0, NZCV

3.4 BFI

Define 1: BFI <Xd>, <Xn>, #<lsb>, #<width>

从 Xn 寄存器外面从低位开始,插入到 Xd 寄存器从 #<lsb> 开始,#<width> 长度。

读取 Xn 寄存器的低位开始计算,插入到 Xd 寄存器从 #<lsb> 开始,#<width> 长度。这个没有方法管制 Xd 的地位,只能从 Xd 的最低位开始。

test_bfi:
    // 0000 0000 0000 1010
    //                 |
    //                 V
    //           0000 0000 0000 0000  ->  0000 1010 0000 0000
    ldr x1, =0x000A
    mov x2, xzr
    bfi x2, x1, #0x8, #0x4

    // 0000 0000 0000 1010
    //                 |
    //                 V
    //           0000 0101 0000 0000  ->  0000 1010 0000 0000
    ldr x1, =0x000A
    mov x2, #0x0500
    bfi x2, x1, #0x8, #0x4

    ret

3.5 UBFX/SBFX

Define: UBFX <Xd>, <Xn>, #<lsb>, #<width>

Define: SBFX <Xd>, <Xn>, #<lsb>, #<width>

读取 Xn 寄存器的 #<lsb> 开始,#<width> 长度开始计算,替换到 Xd 寄存器低位的地位#<width> 长度。这个没有方法管制 Xd 的地位,只能从 Xd 的最低位开始。SBFX 是有符号的,替换之后 Xd 其余位 0 变为 1。

    // x1:      0000 0000 1111 0000
    //                     |
    //                     V
    // x2: 0000 0000 0000 0000  ->  0000 0000 0000 1111

    mov x1, #0x00F0
    mov x2, xzr
    ubfx x2, x1, #0x4, #0x4

    //          1000 0000 1111 0000
    //                     |
    //                     V
    // x1: 0000 0000 0000 0000  ->  1111 1111 1111 1111
    mov x1, #0x80F0
    mov x2, xzr
    sbfx x2, x1, #0x4, #0x4

Ref

[1] Arm Armv8-A Architecture Registers-NZCV, Condition Flags

[2] ARM Cortex-A Series Programmer’s Guide for ARMv8-A – Arithmetic and logical operations

[3] ARM 架构(三)ARMv8 Programm Model Overview

[4] ARMv8 官网手册学习笔记 (三):寄存器

正文完
 0