简介
- 是一种SIMD协处理器,可选配置成向量浮点VFPv3
- 16个128位寄存器Q0-Q15,32个64位寄存器D0-D31
- 128位与64位寄存器是共用的,小新覆盖
armcc编译器
- 开启优化等级-O2或者-O3, 开启–vectorize选项来使能向量化编译
- 指定–cpu 7-A或–cpu Cortex-A8指定指令集架构和CPU类型
- 指定-mfpu=vfpv3-fp16作为vfp协处理、
- 指定-mfpu=neon-vfpv4NEON+VFP
- 指定-mfloat-abi=soft使用软件浮点库不是用VFP或者NEON指令
- 指定-mfloat-abi=softfp使用软件浮点的调用规则来使用VFP和NEON指令
- 指定-mfloat-abi=hard使用VFP和NEON指令,改变ABI调用规则提供效率,如用vfp寄存器进行浮点数参数传递减少NEON寄存器和ARM寄存器的拷贝
- 指定-ffast-math使用浮点数加速
gcc编译器
- 开启-O3,开启-ftree-vectorize来使能向量化
- 指定-mfpu=neon和-mcpu=cortex-a5
- 指定-mfpu=vfpv3-fp16作为vfp协处理、
- 指定-mfpu=neon-vfpv4 NEON+VFP
- 指定-mfloat-abi=soft使用软件浮点库不是用VFP或者NEON指令
- 指定-mfloat-abi=softfp使用软件浮点的调用规则来使用VFP和NEON指令
- 指定-mfloat-abi=hard使用VFP和NEON指令,改变ABI调用规则提供效率,如用vfp寄存器进行浮点数参数传递减少NEON寄存器和ARM寄存器的拷贝
- 指定-ffast-math使用浮点数加速
#cpu选择 CPU类型|CPU类型选项|FP选项|FP + SIMD选项|备注 :-|:-|:-|:-|:-| Cortex-A5|-mcpu=cortex-a5|-mfpu=vfpv3-fp16 -mfpu=vfpv3-d16-fp16|-mfpu=neon-fp16|-d16表明只有前16个浮点寄存器可用 Cortex-A7|-mcpu=cortex-a7|-mfpu=vfpv4 -mfpu=vfpv4-d16|-mfpu=neon-vfpv4|-fp16表明支持16bit半精度浮点操作 Cortex-A8|-mcpu=cortex-a8|-mfpu=vfpv3|-mfpu=neon| Cortex-A9|-mcpu=cortex-a9|-mfpu=vfpv3-fp16 -mfpu=vfpv3-d16-fp16|-mfpu=neon-fp16| Cortex-A15|-mcpu=cortex-a15|-mfpu=vfpv4|-mfpu=neon-vfpv4|
NEON 数据类型
- 无符号整数 U8 U16 U32 U64
- 有符号整数 S8 S16 S32 S64
- 未指定类型的整数 I8 I16 I32 I64
- 浮点数 F32
- 多项式 P8 P16
NEON 指令
- 分为正常指令、宽指令、窄指令、饱和指令、长指令
- 正常指令:返回值与操作数类型及位数相同
- 宽指令:操作数位数不同,返回值与最长的一样,以W结尾如VADDW指令
- 窄指令:返回值是操作数的一半,以N结尾如果VMOVN
- 饱和指令:操作数超过数据长度自动限制到范围,以Q为第二字母如VQSHRUN
- 长指令:返回值是操作数的一倍,以L结尾如VADDL
- VMOV 复制指令 ``` //正常指令 VMOV d0, r0, r1 //d0=(r0 « 32) | r1 VMOV r0, r1, d0 //r0=d0 & 0x00000000FFFFFFFF r1=(d0 » 32) & 0x00000000FFFFFFFF VMOV.U32 d0[0], r0 //d0=r0 VMOV.U32 r0, d0[0] //0=d0 & 0x00000000FFFFFFFF VMOV.U16 d0, #1 //d0=0x0001000100010001 VMOV.U32 q0, #1 //d0=0x00000001000000010000000100000001
//长指令 VMOVL.U16 q0, d0 //q0=(d0 & 0x0000000000FFFF)|((d0 & 0x000000FFFF0000) « 32)|… 16->32
//窄指令 VMOVN.I32 d0, q0 //d0=(q0 & 0x000000000000000000FFFFFFFF)|((q0 & 0x0000000000FFFFFFFF00000000) » 32)|.. 32->16
//饱和指令 VQMOVN.S32 d0, q0 //32->16,每部分取值0-65535
## arm-v7
- 32位寄存器, 0-15通用
- R0-R3 指令操作数、函数参数、返回值
- R4-R11 局部变量、函数执行前保留现场返回时恢复
- R12 FP栈帧基地址
- R13 SP栈指针
- R14 LR 链接寄存器 子程序调用返回地址,即执行BL(Branch and Link)时保存当前PC地址
- R15 PC 程序计数器
- R16 CPSR Current Program Status Register
- 条件标志: 零标志(Z)、负标志(N)、进位标志(C)、溢出标志(V)
- 控制标志: 中断禁用(I)、快速中断禁用(F)
- 模式标志: 用户模式、系统模式、中断模式
- R17-R? SPSR Saved Program Status Register
- 中断发生 保存CPSR到SPSR
- 每种中断有自己对应的寄存器
## arm-v8
- 64位寄存器, 0-30 通用
- X0 返回值或传参
- X1-X7 传参
- X8 返回值、间接结果地址
- X9-X28 一般寄存器
- X18 平台寄存器 ABI Thumb
- X29 FP栈帧基地址(栈底地址)
- X30 LR返回地址
- X31 SP栈顶地址、ZR零寄存器===0
- X32 PC 程序计数器指令地址
- W0-W30 做为32位寄存器时使用的名字,只使用低32位
- WSP 32位栈帧名字, 64位用SP
- WZR 32位零寄存器、ZR64位零寄存器
## arm 执行模式
- User Mode 用户模式, 正常程序执行的默认模式
- System Mode 系统模式, 用于运行操作系统的内核代码,与用户模式共享相同的寄存器
- FIQ Mode 快速中断模式, 用于处理快速中断请求,有额外的寄存器以减少中断服务例程的执行时间
- IRQ Mode 普通中断模式, 用于处理普通的中断请求
- Supervisor Mode 管理模式, 用于操作系统的内核级别操作,通常在系统启动或系统调用时进入
- Undefined Mode 未定义模式, 当执行了未定义的指令时进入此模式
- Abort Mode 中止模式, 当发生数据或指令预取中止时进入此模式
## arm 函数调用约定
- AAPCS ARM Architecture Procedure Call Standard 默认函数调用约定
- R0-R3 参数传递、剩余采用栈传递; 参数从左->右
- 栈 保存局部变量、临时变量、函数调用状态; 地址从高->低;
- 管理 由被调用方管理参数入栈及寄存器状态保存及恢复
- AAPCS-VFP ARM Architecture Procedure Call Standard with the Vector Floating-Point extension 浮点扩展
- S0-S3 参数传递、剩余采用栈传递;
- FPSCR 浮点状态寄存器
## 汇编指令
- 寄存器寻址 mov r1, r2 ;R2中的值移动到R1中
- 立即寻址 mov r0, #0xFF00 ;将0xFF00立即数移动到R0中
- 寄存器移位寻址 mov r0, r1, lsl #3 ;将R1左移3位的结果移动到R0中
- 寄存器间接寻址 ldr r1, [r2] ;将R2指向的地址中的值加载到R1中r1=*r2
- 基址变址寻址 ldr r1, [r2, #4] ;将R2加上4所得的地址中的值加载到R1中r1=*(r2+4)
- 多寄存器寻址 ldmia r1!, {r2-r7, r12} ;将R1中的值加载到R2-R7和R12中,然后将R1加上32
- 堆栈寻址 stmfd sp!, {r2-r7, lr} ;将R2-R7、LR的值存储到堆栈中,然后将SP减去24
- 相对寻址 beq flag '\n' flag: ;如果条件码为EQ,则跳转到标签flag处
- 强制跳转 B label ;无条件跳转到标签label处
- 带返回的跳转 BL label ;跳转到标签label处,并将返回地址存储在LR中
- 带返回和带状态的切换 BLX label ;跳转到标签label处,并将返回地址存储在LR中;ARM和Thumb之间的切换
- 带状态的跳转 BX R1 ;跳转到地址存储在R1中的位置,并切换到相应的指令集模式
- 赋值 MOV R0, R1 ;将R1中的值赋值到R0中
- 加 ADD R0, R1, R2 ;将R1和R2中的值相加,结果存储到R0中
- 减 SUB R0, R1, R2 ;将R1-R2,结果存储到R0中
- 位与 AND R0, R1, R2 ;将R1和R2中的值进行按位与操作,结果存储到R0中
- 位异或 EOR R0, R1, R2 ;将R1和R2中的值进行按位异或操作,结果存储到R0中
- 位或 ORR R0, R1, R2 ;将R1和R2中的值进行按位或操作,结果存储到R0中
- 位与非 BIC R0, R1, #0xf ;将R1和立即数#0xf进行按位与非操作,将结果存储到R0中;实现的BitClear
- 乘法 MUL r0 r1,r2 ;r0 = r1 * r2
- 带加法的乘法 MLA r0,r1,r2,r3 ;r0 = r1 * r2 + r3
- 64位乘法 SMULL r0,r1 ,r2 ,r3 ;r0 = (r2 * r3)的低32位 r1 = (r2 * r3)的高32位
- 64位带加法的乘法 SMLAL r0,r1 ,r2 ,r3 ;r0 = (r2 * r3)的低32位 + r0 r1 = (r2 * r3)的高32位 + r1
- 64位无符号乘法 UMULL r0,r1 ,r2 ,r3 ;r0 = (r2 * r3)的低32位 r1 = (r2 * r3)的高32位
- 64位无符号带加法的乘法 UMLAL r0,r1 ,r2 ,r3 ;r0 = (r2 * r3)的低32位 + r0 r1 = (r2 * r3)的高32位 + r1
- 逻辑左移 LSL R0, R1, #2 ;将R1中的值左移2位,结果存储到R0中,右侧空出的位补零
- 逻辑右移 LSR R0, R1, #3 ;将R1中的值右移3位,结果存储到R0中,左侧空出的位补零
- 循环右移 ROR R0, R1, #4 ;将R1中的值循环右移4位,结果存储到R0中,左侧空出的位补上右侧移出的位
- 算术右移 ASR R0, R1, #5 ;将R1中的值算术右移5位,结果存储到R0中,左侧空出的位补上符号位
- 扩展的循环右移指令 RRX R0, R1 ;将R1中的值扩展的循环右移1位,结果存储到R0,C标志位的值插入到左侧
- 比较 CMP r1, #10 ;比较寄存器r1的值和立即数10;相等N = V && Z = 0
- 比较补码 CMN r2, #5 ;比较寄存器r2的值的补码和立即数5
- 小于跳转 BLE label ;如果r2的补码值小于或等于5,则跳转到label
- 掩码位是否全为1 TST r3, #0xFF ;检查寄存器r3的低八位是否全部为1
- 等于跳转 BEQ label ;如果r3的低八位全为1,则跳转到label;(即Z = 1)
- 按位异或是否为0 TEQ r4, #0x80 ;检查寄存器r4的值和立即数0x80的异或结果不为0
- 不等于跳转 BNE label ;如果r4的值与0x80异或后不为零,则跳转到label;(即Z = 0)
- 非负数 BPL label ;结果非负,则跳转到label;(即N = 0)
- 负数 BMI label ;结果为负,则跳转到label;(即N = 1)
- 无进位 BCC label ;结果无进位,则跳转到label;(即C = 0)
- 有进位 BCS label ;结果有进位,则跳转到label;(即C = 1)
- 小于无符号数 BLO label ;即C = 1
- 大于等于无符号数 BHS label ;即C = 0
- 大于无符号数 BHI label ;即C = 0 && Z = 0
- 小于等于无符号数 BLS label ;即C = 1 && Z = 1
- 无溢出有符号数 BVC label ;即V = 0
- 有溢出有符号数 BVS label ;即V = 1
- 大于有符号数 BGT label ;即N = V && Z = 0,则跳转到label
- 大于等于有符号数 BGE label ;即N = V,则跳转到label
- 小于有符号数 BLT label ;即N != V,则跳转到label
- 小于等于有符号数 BLE label ;即N != V || Z = 1,则跳转到label
- 内存访问指令 ARM汇编采用RISC架构,CPU不能直接的读取内存,先将内存加载到通用寄存器再由CPU进行计算
- 读取 32位中会读4个字节,在64位中会读8个字节
```arm
ldr r0,=0x12 ;将0x12赋值给r0
ldr r0, .lable1 ;获得.lable1的地址,存储在r0之中
ldr r0,[r3] ;r0 = *r3
ldr r0,[r3,#4] ;r0 = *(r3 + 4)
ldr r0,[r3,r2,LSL #2] ;r0 = *(r3+(r2 << 2))
- 存储 32位中会读4个字节,在64位中会读8个字节
str R0, [R1], #8 ;将R0中的数据写入以R1为地址的存储器中,并将新地址R1+8写入R1 str R0, [R1, #8] ;将R0中的数据写入以R1+8为地址的存储器中 str R1, [r0] ;将r1寄存器的值,传送到地址值为r0的内存中 - ldr与 str 指令的后缀与变种在32位中会读4个字节 在64位中会读8个字节
- ldrb 读一个字节
- ldrh 读两个字节
- ldm 批量读
- strb 写入一个字节
- strh 写入两个字节
- stm 批量写
堆栈的指令组合后缀 四种栈的类型:空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出。满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针。增栈:栈指针移动时向地址增加的方向移动的栈。减栈:栈指针移动时向地址减小的方向移动的栈。 针对此衍生出了8种后缀:ia(increase after:先传输,再地址+4ib(increase before: 先地址+4,再传输da(decrease after: 先传输,再地址-4db(decrease befor: 先地址-4,再传输fd(full decrease):满递减堆栈ed(empty decrease):空递减堆栈fa:满递增堆栈ea:空递增堆栈 在理解的时候我们就可以将其拆开理解,比如LDMIA 就是LDM IA 两部分理解一些例子: 从地址 r0 开始读取 4 个字(word),分别存储到 r1-r4 寄存器中,然后将地址 r0 增加 4。LDMIA r0!, {r1-r4}
将地址 r0 增加 4,然后从新地址开始读取 3 个字,分别存储到 r1-r3 寄存器中。LDMIB r0, {r1-r3}
从地址 r0 开始读取 2 个字,分别存储到 r1-r2 寄存器中,然后将地址 r0 减少 4,并将读取的最后一个字存储到 lr 寄存器中。STMFD sp!, {r1-r3, lr}
将 r1-r3 寄存器中的数据存储到地址 sp 指向的存储器中,然后将 sp 减少 16(4 个字),并将 lr 寄存器中的数据存储到新的地址 sp 指向的存储器中。LDMDB r0!, {r1-r2, lr}
指令中!的作用 一般的,当感叹号 “!” 出现在寄存器名称后面时,就表示在执行指令后会更新该寄存器的值。如果没有!,表示执行指令前不更新该寄存器的值。ldmia r0, {r2 - r3} ldmia r0!, {r2 - r3}
这里感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,第二句的ldm时会改变r0的值,而第一句,没有!,运行之后不会更新 r0 的值。 指令中^的作用 ^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。ldmfd sp!, {r0 - r6, pc} ldmfd sp!, {r0 - r6, pc}^
汇编不同写法,对于寄存器的改变(先算括号) 偏移量方法LDR R0,[R1, #4]
实现的操作:r0= *(r1 + 4) 事先更新LDR R0,[R1, #4]!
实现的操作:r0 = *(r1 + 4)r1 = r1 +4 事后更新:LDR R0,[R1], #4
实现的操作:r0=*r1 r1=r1+4