转自:(72条消息) 10、 ARM 内联汇编学习笔记_天昼AI实验室的博客-CSDN博客_armv8 内联汇编
基本思想:随手记录一下ARM的内联汇编的基础语法,以便更深入的学习NCNN源码~
ARM GCC Inline Assembler Cookbook 参考官网
(1)、基本的汇编语法结构为
asm volatile (
code 代码列表
: output operand list 输出运算符列表
: input operand list 输入运算符列表
: clobber list 被更改资源列表
);
或者也可以写成这样,因为头文件做了宏定义asm volatile
__asm__ __volatile__ (
code 代码列表
: output operand list 输出运算符列表
: input operand list 输入运算符列表
: clobber list 被更改资源列表
);
(2-1)volatile 表示关键字表示不做任何优化处理;
(2-2) 代码列表,可以写多条指令指令格式如下:
ARMV7 :Documentation – Arm Developer
ARMV7架构包含:
16个通用寄存器(32bit),R0-R15
16个NEON寄存器(128bit),Q0-Q15(同时也可以被视为32个64bit的寄存器,D0-D31)
16个VFP寄存器(32bit),S0-S15
NEON和VFP的区别在于VFP是加速浮点计算的硬件不具备数据并行能力,同时VFP更尽兴双精度浮点数(double)的计算,NEON只有单精度浮点计算能力
V{
Q 该指令常用在饱和算法中,如果运算结果发生饱和(超出数据类型,导致溢出),这些状态将被FPSCR寄存器记录,则将结果自动截断,使其避免溢出。VQADD 就是这种指令的一个例子(可通过符号寄存器看出)
H 该指令将使结果减半。它通过向右移动一个位置(实际上是被截断的二分之一)来做到这一点。 VHADD 就是这种指令的一个例子——它可以用来计算两个输入的平均值。
D The instruction doubles the result and saturates. This is commonly required when multiplying numbers in Q15 format, where an additional doubling is required to get the result into the correct form.
R 该指令结果进行四舍五入处理,也就是向上取整操作,否则就将结果数据截断. VRHADD 就是这种指令.
L 长指令对双字向量操作数执行运算,并生成四字向量结果。 所生成的元素通常是操作数元素宽度的两倍,并属于同一类型。通过将 L 追加到指令助记符来指定长指令。
W 宽指令对一个双字向量操作数和一个四字向量操作数执行运算。 此类指令生成四字向量结果。 所生成的元素和第一个操作数的元素是第二个操作数元素宽度的两倍。通过将 W 追加到指令助记符来指定宽指令。
N 窄指令对四字向量操作数执行运算,并生成双字向量结果。 所生成的元素通常是操作数元素宽度的一半。通过将 N 追加到指令助记符来指定窄指令
<.dt> - 数据类型
其中ARM架构的CSPR寄存器中的关键几位N,Z,C,V与8086架构EFLAG中的NF ,SF ,ZF ,CF,OF相对应,主要是用于汇编计算中,使用源操作数计算的目标结果的状态记录和压栈、出栈、跳转状态保存和恢复
ARMV8 移动端arm cpu优化学习笔记第4弹--内联汇编入门 - 知乎
Arm v8-A AArch64架构
有31个64位通用目的寄存器,每一个通用寄存器具有64位(X0-X30)或是32位模式(W0-W30)
有32个128位寄存器,也能当作32位Sn寄存器或是64位Dn寄存器使用。
{
这里:
P:将向量按对操作,例如ADDP
V:跨所有的数据通道操作,例如FMAXV
2:在宽指令/窄指令中操作数据的高位部分。例如ADDHN2,SADDL2。
ADDHN2:两个128位矢量相加,得到64位矢量结果,并将结果存到NEON寄存器的高64位部分。
SADDL2: 两个NEON寄存器的高64位部分相加,得到128-位结果。
arm_neon.h 支持的操作,如果每行指令后面追加“\n\t”,只是为了将neon assembly生成汇编比较美观一些
注)、上表指令后面可以追加一些后缀,比如"B", "H"和"W"分别表示从给定的内存地址依次取1个字节(8位),2个字节和4个字节
<高位>32 16 8 <低位>
4) )约束字段格式 详细见下表 ARM GCC Inline Assembler Cookbook
(2-3)输出运算列表
以逗号分隔,可以写多条指令格式 [助记符名] “约束条件”(变量名)
(2-4)输入运算列表 ARM GCC Inline Assembler Cookbook
1) %0 表示输入运算符列表和输出运算符列表中的第一个值,如果没有输出列表,只有输入列表,那就代表输入列表的值。反之依然如此。如果都有,则依次排之 %0 %1 %2...
2) [{,:}] 指定特定的寄存器,取寄存器里面的内容 指令的寄存器内部存放的是地址 ==>[地址]=内容
3) [{,:}]! 指定特定的寄存器,取寄存器里面的下一个位置内容 ==>[地址]!=下一个内容
4) {}表示待传送的寄存器列表
5)"!"是表示寄存器自增/自减的
例如:vld1.8 {q1},[r1]! @v 从r1里面取出第二个参数(v)放到q1寄存器
Constraint Usage in ARM state Usage in Thumb state
f 浮点寄存器 f0 .. f7 Not available
h Not available Registers r8..r15
G 立即数(浮点数形式) Not available
H Same a G, but negated Not available
I 数据处理指令中的立即数, #operand Constant in the range 0 .. 255
e.g. SWI operand
J Indexing constants -4095 .. 4095
e.g. LDR R1, [PC, #operand] Constant in the range -255 .. -1
e.g. SUB R0, R0, #operand
K Same as I, but inverted Same as I, but shifted
L Same as I, but negated Constant in the range -7 .. 7
e.g. SUB R0, R1, #operand
l Same as r Registers r0..r7
e.g. PUSH operand
M 使用一个内存操作数,内存地址可以是机器支持的范围内 Constant that is a multiple of 4 in the range of 0 .. 1020
e.g. ADD R0, SP, #operand
m Any valid memory address
N Not available 一个确定值的立即数,范围一般限制在 0 .. 31
e.g. LSL R0, R1, #operand
O Not available 使用一个内存操作数,但是要求内存地址范围在在同一段内。例如,加上一个小的偏移量来形成一个可用的地址
r 通用寄存器R0~R15 ,使用r字段可以任意选择 Not available
w 向量寄存器 s0 .. s31 Not available
X 被修饰的操作符只能作为输出
5) 约束字段的修饰符
修饰符 说明
无 被修饰的操作符是只读的
= 被修饰的操作符只写
+ 被修饰的操作符具有可读写的属性
& 被修饰的操作符只能作为输出
0 被修饰的操作符既可以作为输入也可以作为输出
6) # 表示立即数
例如:: [temp] "=r" (tmp) //输出列表
(2-5)约束列表
:一般是"cc", "memory"开头,然后接着填内联汇编中用到的通用寄存器和向量寄存器
1) "cc"表示内联汇编代码运算过程中,会产生符号变化、数据溢出等问题,这些操作最终会修改了标志寄存器,;
2) "memory"表示汇编代码对输入和输出操作数涉及内存操作,ncnn代码使用arm neon预先将数据从内存拷贝到了寄存器中,这样写汇编指令就不涉及内存操作;
(2)、使用android studio测试ncnn-demo Aarch64 Mix Assembly And Intrinsic - Ncnn - DocsForge
ncnndemo-1
float computeC(float a,float b,float c){
return a+=b*c;
}
float computeNeon(float32_t a,float32_t b,float32_t c){
float32x4_t Aregister;
float32x4_t Bregister;
float32x4_t Cregister;
Aregister = vld1q_f32(&a);
Bregister = vld1q_f32(&b);
Cregister = vld1q_f32(&c);
Aregister = vmlaq_f32(Aregister,Bregister, Cregister);
float32_t result=0;
vst1q_f32(&result, Aregister);
return result;
}
float computeAsm(float32_t const a,float32_t const b,float32_t const c){
float32x4_t Aregister;
float32x4_t Bregister;
float32x4_t Cregister;
Aregister = vld1q_f32(&a);
Bregister = vld1q_f32(&b);
Cregister = vld1q_f32(&c);
asm volatile(
"fmla %0.4s, %2.4s, %3.4s" //这个地方为啥不能写成v0.4s v2.4s v3.4s 还不是太明白
:[Aregister0] "=w"(Aregister) // %0
:[Aregister1] "0"(Aregister),
[Bregister2] "w"(Bregister), // %2
[Cregister3] "w"(Cregister) // %3
:"cc","v0","v1","v2","v3"
);
float32_t result=0;
vst1q_f32(&result, Aregister);
return result;
}
void test() {
float a=10;
float b=20;
float c=30;
auto start_time=std::chrono::steady_clock::now();
std::cout<
std::cout<
LOGD("computeC time %d ms\n",std::chrono::duration
start_time=std::chrono::steady_clock::now();
std::cout<
std::cout<
LOGD("computeNeon time %d ms\n",std::chrono::duration
start_time=std::chrono::steady_clock::now();
std::cout<
std::cout<
LOGD("computeAsm time %d ms\n",std::chrono::duration
}
测试demo结果
$ adb shell am start -n "com.example.neon/com.example.neon.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Connected to process 3247 on device 'rockchip-rk3399-8BPEH3RXVX'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
W/om.example.neo: Accessing hidden method Landroid/graphics/drawable/Drawable;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
W/om.example.neo: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
W/om.example.neo: Accessing hidden method Landroid/widget/TextView;->getTextDirectionHeuristic()Landroid/text/TextDirectionHeuristic; (light greylist, linking)
D/TAG: NO
D/TEST_NEON: computeC 610.000000
computeC time 3 s
computeNeon 610.000000
computeNeon time 3 s
computeAsm 610.000000
computeAsm time 3 s
D/OpenGLRenderer: Skia GL Pipeline
算基本入门了~ 开始继续刷ncnn源码 谢谢 @zz大佬、@up、@白学家 解惑
参考:
移动端arm cpu优化学习笔记第4弹--内联汇编入门 - 知乎
Arm NEON programming quick reference guide - Operating Systems blog - Arm Community blogs - Arm Community
ARM NEON指令集总结_JabamiLight的博客-CSDN博客_neon指令集
ARM GCC Inline Assembler Cookbook
ARM NEON 编程系列2 - 基本指令集 - *神气* - 博客园
ARM GCC中内联汇编语法_旭旭旭旭旭的博客-CSDN博客
Armv7 Mix Assembly And Intrinsic - Ncnn - DocsForge
arm汇编基础 - 知否 | nop
ARM汇编语言 - 简介 [一]-电子工程世界