首页 > 编程语言 >10、 ARM 内联汇编学习笔记

10、 ARM 内联汇编学习笔记

时间:2022-11-14 16:02:45浏览次数:67  
标签:std 10 操作数 寄存器 列表 指令 time 内联 ARM


基本思想:随手记录一下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架构包含:

ARM

描述

8086/8088

R0

通用寄存器

AX

R1-R5

通用寄存器

BX、CX、DX、SI、DI

R6-R10

通用寄存器

-

R11

栈帧指针

BP

R12

内部程序调用

-

R13

栈指针

SP

R14

链接寄存器

-

R15

程序计数寄存器

IP

CPSR

程序状态寄存器

FLAGS

  • 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{<mod>}<op>{<shape>}{<cond>}{.<dt>}{<dest>}, src1, src2

<mod> 修饰符 (Q, H, D, R)

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 就是这种指令.

<op> - 操作运算符(如, ADD, SUB, MUL等)

<shape> - Shape (L, W or N, as described in NEON registers)

L 长指令对双字向量操作数执行运算,并生成四字向量结果。 所生成的元素通常是操作数元素宽度的两倍,并属于同一类型。通过将 L 追加到指令助记符来指定长指令。

W 宽指令对一个双字向量操作数和一个四字向量操作数执行运算。 此类指令生成四字向量结果。 所生成的元素和第一个操作数的元素是第二个操作数元素宽度的两倍。通过将 W 追加到指令助记符来指定宽指令。

N 窄指令对四字向量操作数执行运算,并生成双字向量结果。 所生成的元素通常是操作数元素宽度的一半。通过将 N 追加到指令助记符来指定窄指令

<cond> - 条件代码

<.dt> - 数据类型

<dest> - 目的操作数

<src1> - 源操作数 1

<src2> - 源操作数 2.

其中ARM架构的CSPR寄存器中的关键几位N,Z,C,V与8086架构EFLAG中的NF ,SF ,ZF ,CF,OF相对应,主要是用于汇编计算中,使用源操作数计算的目标结果的状态记录和压栈、出栈、跳转状态保存和恢复

标志位

含义

N

当两个有符号数进行运算时,N=1表示运算的结果为负数;N=0表示运算的结果为正数或零

Z

Z=1表示运算的结果为零,Z=0表示运算的结果非零。

C

可以有4种方法设置C的值:

 -加法运算(包括CMP):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。

 -减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。

 -对于包含移位操作的非加/减运算指令,C为移出值的最后一位。

 -对于其它的非加/减运算指令,C的值通常不会改变。

V

指令结果不能用32位的二进制补码存储,溢出时置一

E

小端序置0,大端序置1

T

Thumb模式置一,ARM模式置零

M

当前的权限模式(用户态和内核态)

J

允许ARM处理器去以硬件执行java字节码的状态标识

ARMV8 ​​移动端arm cpu优化学习笔记第4弹--内联汇编入门 - 知乎​

Arm v8-A AArch64架构

有31个64位通用目的寄存器,每一个通用寄存器具有64位(X0-X30)或是32位模式(W0-W30)

有32个128位寄存器,也能当作32位Sn寄存器或是64位Dn寄存器使用。

​{<prefix>}<op>{<suffix>} Vd.<T>, Vn.<T>, Vm.<T>​​​ 这里:
​<prefix>​​——前缀,如S/U/F/P 分别表示 有符号整数/无符号整数/浮点数/布尔数据类型
​<op>​​——操作符。例如ADD,AND等。
​<suffix>​​——后缀,通常是有以下几种

  • P:将向量按对操作,例如ADDP
  • V:跨所有的数据通道操作,例如FMAXV
  • 2:在宽指令/窄指令中操作数据的高位部分。例如ADDHN2,SADDL2。

ADDHN2:两个128位矢量相加,得到64位矢量结果,并将结果存到NEON寄存器的高64位部分。
SADDL2: 两个NEON寄存器的高64位部分相加,得到128-位结果。
​​​<T>​​ ——数据类型,通常是8B/16B/4H/8H/2S/4S/2D等。B代表8位数据类型;H代表16位数据宽度;S代表32位数据宽度,可以是32位整数或单精度浮点;D代表64位数据宽度,可以是64位整数或双精度浮点。

arm_neon.h 支持的操作,如果每行指令后面追加“\n\t”,只是为了将neon assembly生成汇编比较美观一些

指令

含义

指令

含义

MOV

移动数据

EOR

异或

MVN

取反码移动数据

LDR

加载数据

ADD

数据相加

STR

存储数据

SUB

数据相减

LDM

多次加载

MUL

数据相乘

STM

多次存储

LSL

逻辑左移

PUSH

压栈

LSR

逻辑右移

POP

出栈

ASR

算术右移

B

分支跳转

ROR

循环右移

BL

链接分支跳转

CMP

比较操作

BX

分支跳转切换

AND

比特位与

BLX

链接分支跳转切换

ORR

比特位或

SWI/SVC

系统调用

注)、上表指令后面可以追加一些后缀,比如"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<<computeC(a,b,c)<<std::endl;
auto end_time=std::chrono::steady_clock::now();
std::cout<<std::chrono::duration<double>(end_time-start_time).count()<<"s"<<std::endl;
LOGD("computeC %f\n",computeC(a,b,c));
LOGD("computeC time %d ms\n",std::chrono::duration<double>(end_time-start_time).count());
start_time=std::chrono::steady_clock::now();
std::cout<<computeNeon(a,b,c)<<std::endl;
end_time=std::chrono::steady_clock::now();
std::cout<<std::chrono::duration<double>(end_time-start_time).count()<<"s"<<std::endl;
LOGD("computeNeon %f\n",computeNeon(a,b,c));
LOGD("computeNeon time %d ms\n",std::chrono::duration<double>(end_time-start_time).count());
start_time=std::chrono::steady_clock::now();
std::cout<<computeAsm(a,b,c)<<std::endl;
end_time=std::chrono::steady_clock::now();
std::cout<<std::chrono::duration<double>(end_time-start_time).count()<<"s"<<std::endl;
LOGD("computeAsm %f \n",computeAsm(a,b,c));
LOGD("computeAsm time %d ms\n",std::chrono::duration<double>(end_time-start_time).count());

}

测试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弹--内联汇编入门 - 知乎​​ zz大佬

​Arm NEON programming quick reference guide - Operating Systems blog - Arm Community blogs - Arm Community​


​Armv7 Mix Assembly And Intrinsic - Ncnn - DocsForge​

​arm汇编基础 - 知否 | nop​

​ARM汇编语言 - 简介 [一]-电子工程世界​

标签:std,10,操作数,寄存器,列表,指令,time,内联,ARM
From: https://blog.51cto.com/u_12504263/5849320

相关文章

  • ERROR: Timeout after 10 minutes ERROR: Error cloning remote repo 'origin'
    jenkins构建时出现报错,日志输出:ERROR:Timeoutafter10minutesERROR:Errorcloningremoterepo'origin'hudson.plugins.git.GitException:Command"gitfetch--t......
  • Win10副屏模糊
    第一步:选择副屏并将当前副屏的缩放大小设置为自定义。第二步:将自定义的数值设置为当前屏幕缩放大小+1即可,比如我当前的缩放为100那我就设置为101。(注销才能生效) ......
  • 在安卓手机上运行arm汇编程序
      效果图手机安装gcc.datamsg:.asciz"hello,gnuasm\n"len=.-msg.text.globalmainmain:push{r0,r1,r2,lr}ldrr1,=msgmov......
  • pycharm中格式标准化代码
    点击之后,可以使代码标准化 ......
  • 巨蟒python全栈开发flask10 项目开始2
    1.websocket异常处理出现上图报错的原因是什么?原因是:websocket断开了,所以报错19行接收的msg是None值,所以报错.打开一个文件,点击发送音乐,出现上面的内容:客户端app发送......
  • pycharm如何自定义模板?
    按照上图箭头方向设置即可. ......
  • DTSE Tech Talk | 第10期:云会议带你入门音视频世界
    摘要:本期直播主题是《云会议带你入门音视频世界》,华为云媒体服务产品部资深专家金云飞,与开发者们交流华为云会议在实时音视频行业中的集成应用,帮助开发者更好的理解华为云......
  • MACM1 VM安装Centos7ARM版
    ......
  • win10查看WiFi密码
    打开网络连接后,找到WLAN连接wifi的那张网卡,双击这张WLAN网卡——无线属性(W)——安全——勾选显示字符(H),即可看到。......
  • E710芯片系列四口模块
    简介E710四口模块是一款高性能的嵌入式UHF超高频电子标签读写模块,完全自主知识产权设计,结合专有的高效碰撞处理算法,在保持高识读率的同时,实现对电子标签的快速读写处理,可广......