GNU C内嵌汇编语言

大部分的移动端深度学习框架都会使用到neon优化对深度算法的算子进行优化,有些使用了Neon Instruction C进行优化,而更多的可能是使用嵌入汇编进行优化,本文就介绍一下如何在C中嵌入汇编。当然C嵌入汇编还有很多场景,很多操作系统开发场景中,C是无法完全代替汇编语言的。例如操作某些特殊的CPU寄存器、操作主板上的某些IO端口或者对性能要求极其苛刻的场景等,我们都需要在C中嵌入汇编来满足要求。
GUN C语言提供了关键词asm来声明代码是内嵌的汇编语句,如下(摘自NCNN前向推理框架中的arm优化代码):

 if (nn > 0)
        {
        asm volatile(
            "0:                             \n"
            "pld        [%1, #128]          \n"
            "vld1.f32   {d0-d1}, [%1: 128]  \n"

            "vmax.f32   q0, q0, %q4         \n"
            "vmin.f32   q0, q0, %q5         \n"

            "subs       %0, #1              \n"
            "vst1.f32   {d0-d1}, [%1: 128]! \n"

            "bne        0b                  \n"

            : "=r"(nn),     // %0
              "=r"(ptr)     // %1
            : "0"(nn),
              "1"(ptr),
              "w"(_min),    // %q4
              "w"(_max)     // %q5
            : "cc", "memory", "q0"
        );
        }
  1. asm关键字:用于声明这行代码是一个内嵌汇编表达式,它是关键词asm的宏定义(#define asm asm)。它是内嵌汇编语言必不可少的关键字,任何内嵌的 汇编表达式都以次关键字作为开头;如果希望编写符合ANSI C标准的程序(即与ANSI C标准兼容),那么建议使用关键字asm
  2. volatile关键字:其作用是告诉编译器此行代码不能被编译器优化,编译时保持代码原状。由此看来,它也是内嵌汇编语言不可或缺的关键字,否则经过编译器优化后,汇编语句很可能被修改以至于无法达到预期的执行效果。如果期望编写符合ANSI C标准的程序(即与ANSI C标准兼容),那么建议使用关键字volatile

内嵌汇编表达式

C语言中嵌入汇编需要显式的标明寄存器的分配情况、与C程序的融合情况等。主要靠内嵌汇编表达式说明。表达式主要由4部分构成,它们分别由“:”号分隔,如上ncnn代码所示,其完整的格式如下:
指令部分:输出部分:输入部分:损坏部分
如果将内嵌汇编表达式当作函数,指令部分时函数中的代码,输入部分用于向函数传入参数,而输出部分则可以理解为函数的返回值。损坏部分描述了在指令部分执行过程中,将被修改的寄存器、内存空间或标志寄存器,并且这些修改部分并未在输出部分和输入部分出现过,格式为:
“损坏描述”,“损坏描述” ...
如果需要声明多个寄存器,则必须使用逗号‘,’将它们分隔开,这点与输入/输出部分一致。

操作约束和修饰符

每个输入/输出表达式都必须指定自身的操作约束。操作约束的类型可以细分为寄存器约束、内存约束和立即数约束。在输出表达式中,还有限定寄存器操作的修饰符。

内嵌汇编的语法及最常用的arm汇编指令

1. 在c里内嵌汇编
         __asm__ __volatile__(
         "汇编代码 \n"              
         "汇编代码 \n"
    :"=r"(c变量名)    //第一个冒号表示从汇编里输出到c语言的变量, =号表示在汇编里只能改变C变量的值,而不能取它的值. +号表示可以取变量值,也可改变变量的值. r表示在汇编里用一个寄存器代替c变量
 
    :"r"(c变量名) //第二个冒号表示汇编里只能取c变量的值, 不能再有"=","+"号
        //输入的变量的寄存器只能使用一次, 如果多次使用此输入的值,则应放到一个固定的寄存器上面(R0-R12)
 
    :"r0", "r1" //第三个冒号表示告诉编译器不要把r0, r1寄存器分配给%0, %1等
        );  
    // __volatile__ 告诉编译器不要优化下面的汇编代码, 可用可不用
2. (mov rd, #立即数或寄存器), 把立即数或寄存器的值给rd寄存器
//立即数受限制: 4位表示移位, 8位表示数字:  0x56000000   == 0x56 << 24;
//注意,只有MOV指令才可以把一个寄存器上的值搬移到另一个寄存器
 
3. ldr  rd, =立即数, 立即数没限制, //把立即数放入rd寄存器 ,  ldr不能用于寄存器给寄存器赋值
 
4. add  rd, r0, #立即数或寄存器  //r0+#立即数或另一寄存的值放入rd寄存器   
                  //add  r0, #4  --> add   r0, r0, #4
 
5. sub  rd, 被减数, 减数 //被减数必须是寄存器, 减数可以是立即数或寄存器
 
6. mul  rd, rm, rn //rd和rm不能同一个寄存器(旧版本编译器)
 
//////////////////////////////////////////
CPSR / SPSR  (架构手册P49)
31   30    29     28    7   6     4--0  
N    Z     C      V     I   F   M[4:0]
                    工作模式位(架构手册P52)
 
I bit: Disables IRQ interrupts when it is set.
F bit: Disables FIQ interrupts when it is set.
 
N bit: 表示指令里目标寄存器的结果为负数时, N=1, 如为正数或零时N=0
Z bit: 如指令运算的结果为0, 则Z=1, 如果结果不是0, 则Z=0
C bit: 如果指令运算的结果产生进位或借位时, C=1, 如果没有, C=0
V bit: 如果指令运算的结果溢出时, V=1, 如果没有V=0
 
 
因cpsr是个特殊的寄存器, 需用特定的汇编指令来读取出来
    mrs r0, cpsr  //把cpsr的值读出放入通用寄存器r0里
 
默认情况下, 一般的指令都不会根据目标寄存器里的结果来改变cpsr里的值, 需要特定的汇编指令
才会改变cpsr的值
 
cmp   // cmp  r0, #1 根据r0减去1的结果来改变cpsr的条件状态位, r0本身的值不变
movs //根据目标寄存器的结果来改变cpsr的条件状态位
adds
subs
...
///////////////////////////////////////////
 
7. condition flasg : N(负数)  Z(是否零)  C(进位/借位)  V(溢出)
    conditon: 架构手册/p112
    eq(0000)  :  当cpsr里的Z位的值为1时, 此条件成立, 否则条件不成立, 使用此条件的
           汇编指令不会执行   
    ne(0001)  :  当cpsr里的Z位的值为0时, 此条件成立, 使用此条件的汇编指令会执行
//如果指令里没有指定条件, 则为条件码所占的位为e
 
8. cmp Rn, #立即数/寄存器 //根据Rn减去第二个参数的值的结果更新cpsr的条件标志位
 
9. movs Rd, #5 //先把5放入Rd, 再根据Rd里的结果更新cpsr的条件标志位
 
10. 移位lsl, lsr 逻辑左移, 右移
    "lsl    r0, r0, #4 \n"
     asr 算术右移(C语言里>>是算术右移)
 
11. and rd, rn, #立即数或者寄存器 //把两个数位与上后,把结果放入rd寄存器    add rd, #4 == add  rd,  rd, #4
    orr rd, rn, #立即数或者寄存器 //把两个数位或上后,把结果放入rd寄存器
 
    mvn   r0, #4 //把4的反码存入r0寄存器
 
12. bic rd, rn, op(#立即数或寄存器) //清除rn对应op里的相应位是1的值(可指定多位), 把结果放入rd
 
13. ldr rd, [rn] //把rn里的值作为地址,然后把地址上面的值放入rd
14. str r0, [rn] //把rn里的值作为地址, 然后把r0上的值写到rn里的地址上
    注意: ldr/str的"[]"符号里不能写立即数的地址, 必须要把地址保存到寄存器后才可以操作
 
 
15. ldr/str 操作4个字节   ldrb/strb操作1个字节  ldrh/strh操作2个字节
    在内嵌汇编里, 输入输出的%寄存器要用固定的寄存器来操作,尢其是输入的,一进去应用一个固定的寄存器代替它
 
16, ldr rd, [rm, rn/#立即数] //把rm寄存器上值加上rn值的结果作为地址,再把地址上值放入rd     
 
17.  mydata:
     .word 0x789, 0x123 //相当于申请8个字节空间, 前4个字节放的值为0x789, 后4个字节放的值为0x123. mydata就是首地址, mydata本身不会占指令空间
     .word func    //func是个函数,  分配4个字节空间, 装func函数的地址
 
18  mystr:
    .string "hello"
    .align 2        //.string定义一字符串hello, .align 2按2的2次方字节来对齐, mystr为字符串的首地址
 
 
   r13寄存器就是sp寄存器, 用于记录当前栈顶所在的内存地址
 
    //stm/ldm是批量处理指令, fd为full descend, 意思为sp指向的地址是有数据的, 操作时先下降, 再保存
19. stmfd  sp!, {r0-r3} //把寄存器r0-r3里的值保存到栈里去,   push {r0-r3}, 压栈顺序r3, r2, r1, r0
    ldmfd  sp!, {r0-r3} //把保存到栈的值恢复到r0-r3寄存器去,  pop {r0-r3}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容