AI 移动端框架常用指令·汇总(待续)

卷积操作常用的汇编指令(NEON)

前言

首先我们要知道,ios的芯片虽然是ARM内核的,但是后来慢慢地魔改已经跟公版的ARM有所区别了,因此其对应配套的汇编规范也就有些细微的差别了:

arm32 函数调用约定

arm64函数调用约定

ios函数调用约定

因此安卓端的汇编跟iOS端的汇编就得区别对待了!

1. Android端32bit和64bit汇编的区别

先大概看下arm下SIMD汇编指令语法的区别在哪!

数据来源:《Arm® Compiler Version 6.6 armasm User Guide》

1.1 GCC内联汇编模板

模板就是这个样子

__asm__ [__volatile__] (assembler template
    : [output operand list]                          /* optional */
    : [input operand list]                          /* optional */ 
    : [clobbered register list]                       /* optional */ 
    );

真实的例子就是这样:

    asm volatile(
    "0:                         \n"

        "vdup.16 q0, r0         \n"
        "add %0, %0, #10        \n"
        "vst1.s16   {q0}, [%0]  \n"
    :
    "=r"(src) // %0
    :
    "0"(src), //
    "w"(zxl)  //%2
    :
    "cc", "memory", "q0", "r0", "r1"
    );

1.2 armV8和armV7指令集

armV7指令集主要是针对32bit的,armV8指令集则是针对最新的64bit架构;

首先总体来说,v7指令集在操作Q寄存器时,指令喜欢带个V表示,我这是在操作NEON寄存器,而v8的ISA64指令集就把这个给取消了,我只出指令,具体的什么操作细节,你操作数去细化指明。

1.3 预取

v7这样用:从%3处预取192个byte;

  "pld        [%3, #192]          \n"

v8: pld1kepp这个参数是可以改的,改为预取到L2中,不keep,而是流式缓存,也就是不会真放进cache中,具体的可以去看芯片手册。

 "prfm   pldl1keep, [%1, #512]       \n"

1.4 内存加载

** V7: **

  • 带了前缀v的就是v7 32bit指令的标志;
  • ld1表示是顺序读取,还可以取ld2就是跳一个读取,ld3、ld4就是跳3、4个位置读取,这在RGB分解的时候贼方便;
  • 后缀是f32表示单精度浮点,还可以是s32、s16表示有符号的32、16位整型值。(对比后面的ISA64指令集就可以知道它浮点用的是4s,int16是4h)
  • 这里Q寄存器是用q表示,q5对应d10、d11可以分开单独访问(注:v8就没这么方便了。)
  • 大括号里面最多只有两个Q寄存器。
"vld1.f32   {q10}, [%3]!        \n"
"vld1.s16 {q0, q1}, [%2]!       \n" 

v8:

  • 可以看到指令就是单纯的表达我是个什么样操作而已,具体的什么数据类型啊就全部交给底下的寄存器去表达了。
  • NEON寄存器用V来表示(后缀为8B/16B/4H/8H/2S/4S/2D);
  • 大括号内最多支持4个V寄存器;
"ld1    {v0.4s, v1.4s, v2.4s, v3.4s}, [%2], #64 \n"
"ld1    {v0.8h, v1.8h}, [%2], #32     \n"
"ld1    {v0.4h, v1.4h}, [%2], #32     \n"
  • 无法直接访问V0 的上半部分的D寄存器,目前的情报是:直接访问D0就是v0.4h,要访问V0的高64bit,可以先V0.8h加载满数据,然后以V0.h[4~7]来访问单独的int16数据;再或者就是像这样来访问:把v5.4s里面的4个32bit整型值压缩进v8.8h高64bit里面的4个int16里面。
"sqshrn  v8.4h, v4.4s, %15            \n" // output int16x4_t
"sqshrn2 v8.8h, v5.4s, %15            \n" // output int16x4_t

1.5 以占位符方式访问Q、D寄存器

关于这部分在NCNN github的wiki页面有详细的解释,可以去那看看哟!

V7:

  • 先定义变量,表明_bias0就是一个向量格式了;
 const float* bias = _bias;
 
 float32x4_t _bias0 = bias ? vld1q_f32(bias+g*4) : vdupq_n_f32(0.f);
  • 然后在占位符那里表明我这个_bias0需要用一个浮点寄存器缓存起来;
"w"(_bias0)     // %23
  • 然后我们在f32里面用是这样用的:把%23里面加入一个q表明这个是一个Q寄存器,然后按位与,最终输出到Q12向量寄存器里面。
"vand.f32     q12, %q23, %q23   \n"
  • 因为q0是按照s16的方式导进来,因此,d0相当于有4个int16,因此能索引到d[3]
  "vld1.s16 {q0, q1}, [%2]!       \n" // input int16x4_t
  "vmull.s16   q10,  %P6, d0[3]   \n" // 
  • 首先定义的是int16x4,就是D寄存器,因此在访问的时候就是直接%P14了m,用P指明是D寄存器;
int16x4_t _k4 = vld1_s16(k0 + 16);

 "vmlal.s16   q13, %P14, d3[3]   \n" //_k4 ==> %14

V8:

  • 相对应的V8以占位符方式访问向量寄存器的时候,是直接在后面加后缀来指明立场的;如浮点乘法的时候就是%16.4s指明是单精度浮点(4个single精度浮点值),同样的v21.s[3]是访问4个中的其中一个浮点值。
  "fmla   v28.4s, %16.4s, v21.s[3]       \n"
  • v8的int16操作:以int16 的方式加载一个D寄存器的_k0出来,然后v0.8h表示加载8个int16,然后v0.h[7]跟%6以4h的方式相乘,最后拓展输出到V4寄存器,以4s的格式,也就是4个32位;
  int16x4_t _k0 = vld1_s16(k0     );
  "ld1    {v0.8h, v1.8h}, [%2], #32     \n" // input int16x4_t
  "smull   v4.4s, %6.4h, v0.h[7]        \n" 
  "w"(_k0),    // %6

1.6 小结

v7的浮点操作:
  "vmla.f32   q4, %q6, d0[0]   \n" // q0.s[0]

v8的浮点操作:
  "fmla   v4.4s, %6.4s, v0.s[0]       \n"
  
v7的int16操作:
 "vmull.s16   q10,  %P6, d0[0]   \n" // v0.h[0]
 
v8的int16操作:
 "smull   v4.4s, %6.4h, v0.h[0]        \n" 

2. ios端汇编

In general, iOS adheres to the generic ABI specified by ARM for the ARM64 architecture. However there are some choices to be made within that framework, and some divergences from it. This document describes these issues.

官网的一段申明,指明虽然继承自公版,但其与公版ARM架构仍有所区别;

具体的去这里看吧!ios函数调用约定

2.1 汇编文件

我们先看下如何写汇编文件,并在c++中如何调用它吧~

先大概了解下外围!

这个作者讲的很细致!

知乎专栏

这个作者也讲的很细!

老外总是能把很繁琐的知识(汇编)讲的很简洁!
看看人家的副标题:Learn how to read assembly in iOS – a useful skill when debugging your code or diagnosing why a crash has occurred.

解释下什么是FP(Frame Point)寄存器:
通常在C程序编译过程中,所有函数的局部变量被分配在一个连续的存储区中,一般这个存储区是在堆栈中,这个连续的存储区称为这个函数的存储“帧”,它是通过一个指针访问的。

这里要注意的是Apple所采用的ARM汇编器遵循GNU Assembler规范。

其中,我们可以看到,汇编文件里的注释可以采用C语言标准的注释方式,也可以用C++标准的//注释方式。

  • .text表示代码正文部分。

  • .align根据不同的汇编器会有不同的行为,像这里的.align4可能表示4字节对齐,也可能表示16字节对齐。

GAS规范中表示,可以用.global或.globl来标注全局函数。

在Apple的Assembler中仅支持.globl函数名前要加下划线。

.arm表示后面的函数中的指令都是arm指令。而.thumb表示后面函数中的指令都是thumb或thumb-2指令。其中,如果一个函数是用thumb写的,那么必须用.thumb_func修饰,否则连接器在连接符号时会有问题。

另外,Apple LLVM汇编器中的条件预处理与C语言用的也几乎一样。可以使用#if、#else、#endif、#ifdef、#ifndef、#elif等等。另外,在架构标识上也统一使用了标准的架构标识符,比如:i386表示x86处理器架构;x86_64表示64位的x86处理器;arm表示ARM架构的处理器;arm64表示64位ARM架构处理器。

iOS中ARMv7以及ARM64下的ABI:

ARMv7中,对于通用寄存器,自己写的过程中需要保护R4、R5、R6、R7、R8、R9、R10、R11以及R14寄存器;NEON寄存器需要保存Q4、Q5、Q6、Q7寄存器。

ARM64模式下,通用寄存器X18、X30不能被使用。而需要被自己写的过程所保护的是:X19、X20、X21、X22、X23、X24、X25、X26、X27、X28、X29寄存器;而SIMD寄存器需要保护的是V8、V9、V10、V11、V12、V13、V14、V15。

堆栈的意义在于保存状态。

讲一个最简单的例子吧!如何在工程内添加及使用汇编文件:

  • 新建一个汇编文件asmTest.s
.text   //申明是代码区
.align 4 //4字节对齐
.globl _asmTest //申明全局函数名(注:要下划线开头,在外面调用的时候就不需要了)


_asmTest:
// 单纯测试trn转置指令的;

    mov w9, #1
    mov v6.s[0], w9
    mov w9, #2
    mov v6.s[1], w9
    mov w9, #3
    mov v6.s[2], w9
    mov w9, #4
    mov v6.s[3], w9

    mov w9, #4
    mov v7.s[0], w9
    mov w9, #5
    mov v7.s[1], w9
    mov w9, #6
    mov v7.s[2], w9
    mov w9, #7
    mov v7.s[3], w9

    trn1 v10.4s, v6.4s, v7.4s
    trn2 v11.4s, v6.4s, v7.4s

RET
  • 在调用时,先extern int asmTest(void);,然后直接像正常函数那样使用就行了!
  • 还有一种实现方式就是内联汇编了!直接可以在函数内部使用,很方便且性能不受影响。
    __asm volatile (
                    "mov w9, #1               \n"
                    "mov v6.s[0], w9          \n"
                    "mov w9, #2               \n"
                    "mov v6.s[1],  w9         \n"
                    "mov w9, #3               \n"
                    "mov v6.s[2],  w9         \n"
                    "mov w9, #4               \n"
                    "mov v6.s[3],  w9         \n"

                    "mov w9, #5               \n"
                    "mov v7.s[0], w9          \n"
                    "mov w9, #6               \n"
                    "mov v7.s[1],  w9         \n"
                    "mov w9, #7               \n"
                    "mov v7.s[2],  w9         \n"
                    "mov w9, #8               \n"
                    "mov v7.s[3],  w9         \n"

                    "trn1 v10.4s, v6.4s, v7.4s\n"
                    "trn2 v11.4s, v6.4s, v7.4s\n"
                    :
                    :
                    : "cc", "memory"
    );

2.2 翻译下老外的篇吧!讲的挺好的!

(汇编)讲的挺简洁!

Calling Conventions讲的是汇编函数调用的规范,用来弄出一个统一的标准。

······
······
······

image

哈哈~发现讲的很基础了!还是不翻译了!你们自己去看吧!

三、 实例

下面是pooling汇编实现的kernel部分,里面有arch32跟arch64的对应版本,对比着看就很明显了:

  • 32bit里面喜欢在指令里面表明数据格式,而64bit则把这个任务交给寄存器去申明了!
  • 。。。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容