ARM汇编1:如何在C语言中使用汇编

2023-05-16

如何在C语言中使用汇编语言

我最近对ARM的NEON编程有兴趣,主要是为了想学习一些矩阵计算加速相关的知识。但是我又不想写纯粹的汇编语言,我想在C语言中嵌入汇编来使用。

经过检索学习,我找到两种可行的方式。我在阅读ncnn代码的时候,发现下面这两种方式都有用到。为了后续能愉快的阅读ncnn代码,将相关知识做个简单的整理。

方式1: Neon Intrinsics(Neon内联)

NEON intrinsics可以视作在NEON指令上面封装了一层c语言接口。
Neon的汇编指令和寄存器不熟的话,可以借助arm_neon.h这个头文件,以近似C语言编程的方式调用Neon的功能。
下面是一个简单的例子:

#include "arm_neon.h"

void rgb_deinterleave_neon(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *rgb, int len_color) {
    /*
     * Take the elements of "rgb" and store the individual colors "r", "g", and "b"
     */
    int num8x16 = len_color / 16;
    uint8x16x3_t intlv_rgb;
    for (int i=0; i < num8x16; i++) {
        intlv_rgb = vld3q_u8(rgb+3*16*i);
        vst1q_u8(r+16*i, intlv_rgb.val[0]);
        vst1q_u8(g+16*i, intlv_rgb.val[1]);
        vst1q_u8(b+16*i, intlv_rgb.val[2]);
    }
}

arm_neon.h定义了一些特定的数据类型,这些数据类型可以被映射到Neon的专用寄存器,arm_neon.h里定义的一些C语言函数可以对这些特殊类型进行操作。基本上所有的Neon指令都有对应的函数。
这种方式仅限于使用Neon的功能,那些普通的汇编指令是没办法使用的。
使用GCC编译Neon Intrinsics代的时候,需要加上一些特殊选项。本文着重介绍第二种方法,这里就不详细介绍了。

方式2: 内联汇编

这种方式就是用GCC的内联汇编的机制来在C语言中嵌入一段汇编代码。这个机制不是针对ARM的,GCC支持的所有硬件平台(如X86)都可以使用。
下面是我从libyuv中复制过来的一个完整函数:

void ScaleARGBRowDownEven_NEON(const uint8_t* src_argb,
                               ptrdiff_t src_stride,
                               int src_stepx,
                               uint8_t* dst_argb,
                               int dst_width) {
  (void)src_stride;
  asm volatile(
      "1:                                        \n"
      "ld1         {v0.s}[0], [%0], %3           \n"
      "ld1         {v0.s}[1], [%0], %3           \n"
      "ld1         {v0.s}[2], [%0], %3           \n"
      "ld1         {v0.s}[3], [%0], %3           \n"
      "subs        %w2, %w2, #4                  \n"  // 4 pixels per loop.
      "prfm        pldl1keep, [%0, 448]          \n"  // prefetch 7 lines ahead
      "st1         {v0.16b}, [%1], #16           \n"
      "b.gt        1b                            \n"
      : "+r"(src_argb),                // %0
        "+r"(dst_argb),                // %1
        "+r"(dst_width)                // %2
      : "r"((int64_t)(src_stepx * 4))  // %3
      : "memory", "cc", "v0");
}

可以看到这种方式可以将c语言的变量传入汇编代码,并用汇编语言修改C语言变量。基本上这段代码可以认为是一个内联函数,有入参,有出参。
至于汇编代码部分,可以是普通的汇编,也可以是neon这类SIMD汇编。无论是ARM、x86还是RISC-V,都可以使用这样的方式来嵌入汇编。
本文后面主要介绍下第二种方式的主要相关知识。

学习资料

上面说到,第二种在C语言嵌入汇编的方式不是某一硬件平台的,而是GCC的一种机制,无论是ARM、x86还是RISC-V,都可以使用这样的方式来嵌入汇编。

所以最权威的资料在GCC的手册中,《gcc10.4.0手册在线版》的6.47小节“How to Use Inline Assembly Language in C Code”就专门介绍了相关知识,但是主要是用x86的例子来举例的。

另外,国外有个博文也是很好的学习材料:GCC-Inline-Assembly-HOWTO
国内有人对它进行了翻译,翻译质量还可以:最牛X的GCC 内联汇编

内联汇编的详细介绍

基本汇编内联

如果汇编代码无需使用C的变量作为输出和输入操作数,则可以使用这种方式。

asm volatile("movl %ecx %eax"); /* 这个好像是x86的汇编,将 ecx 寄存器的内容移至 eax  */

这里汇编可以是一行,也可以是多行。

扩展内联汇编

如果汇编代码需要使用C语言的变量和内存,则需要使用扩展内联汇编的方式。

这种方式的格式为:

asm asm-qualifiers ( AssemblerTemplate
	: OutputOperands
	[ : InputOperands
	[ : Clobbers ] ])

翻译一些下就是:

asm 限定符( 汇编程序模板
	: 输出操作数 /* 可选 */
	: 输入操作数 /* 可选 */
	: 修饰寄存器列表 /* 可选 */
);

其中限定符有三个:volatile、inline、goto。
其中常用的就是volatile,我在libyuv中看到的都是volatile,所以就不考虑其它的了。
如果GCC的优化器确定不需要输出变量,有时会丢弃asm语句。此外,如果优化器认为代码总是返回相同的结果(即在调用之间没有任何输入值改变),则可能会将代码移出循环。使用volatile限定符将禁用这些优化。

汇编程序模板

汇编程序模板是一个包含汇编程序指令的字符串。字符串可以包含汇编程序识别的任何指令。GCC不解析汇编程序指令本身,不知道它们的含义,甚至不知道它们是否是有效的汇编程序输入。

每条指令应以分界符结尾。对于ARM汇编,一般用"\n"或者"\n\t"来分割各行代码。

输出和输入操作数

内联汇编语句可以有零个或多个输出操作数,指示被汇编代码修改的C变量的名称。
输入操作数也类似,可以为空。有多个操作数的时候,需要以逗号分割,为空的时候冒号也不能省。输出操作数表达式必须是左值。

下面的例子有三个输出操作数,没有输入操作数。

void CopyRow_NEON(const uint8_t* src, uint8_t* dst, int width) {
  asm volatile(
      "1:                                        \n"
      "ldp         q0, q1, [%0], #32             \n"
      "prfm        pldl1keep, [%0, 448]          \n"
      "subs        %w2, %w2, #32                 \n"  // 32 processed per loop
      "stp         q0, q1, [%1], #32             \n"
      "b.gt        1b                            \n"
      : "+r"(src),                  // %0
        "+r"(dst),                  // %1
        "+r"(width)                 // %2  // Output registers
      :                             // Input registers
      : "cc", "memory", "v0", "v1"  // Clobber List
  );
}

在汇编代码中使用操作数有两种方式,一种就是类似上面的代码,"%num"这种占位符的方式来使用。其中num是从0 开始的偏移量,第一个操作数就是%0, 第三个就是%2。
输出和输入操作数共用这个数字的范围。如果上面的代码增加一个输入操作数的话,则汇编代码可以用%3来指代它。

操作数前面的字符串里属于约束修饰符,常用的有"r"和"m"这两种来指定存储方式:

"r" : 寄存器操作数约束
"m" : 内存操作数约束,该操作数不会通过寄存器中转

其中"r"修饰的操作数会被gcc分配一个寄存器来存储,能加快访问速度。
另外常用的约束修饰符还有(指定读写操作):

"+":表示该操作数同时被指令读写
"=" : 意味着对于这条指令,操作数为只写的;旧值会被忽略并被输出数据所替换。
"&" : 在所有不能与输入重叠的输出操作数上使用' & '约束修饰符。否则,GCC可能会将输出操作数分配到与不相关的输入操作数相同的寄存器中,假设汇编代码在产生输出之前消耗其输入。

约束修饰符可以组合,比如: “+r”、“=m”、“+&r” 。
当您列出多个可能的位置(例如,“=rm”)时,编译器将根据当前上下文选择最有效的位置。

我个人理解:下为啥要写清楚读写操作,因为处理器的存储是分层的,寄存器最快,后面是cache、内存和磁盘。对于只写的,就不用在运行汇编程序前从内存同步到cache或寄存器了,如果是只读的,程序运行结束就不用同步到内存了。总之可以节省点拷贝和同步的时间,并确保内容的正确性。)

总共的操作数数量上限是30。如果使用’ + '约束修饰符的操作数作为两个操作数计数(即同时作为输入和输出)。

修饰寄存器列表 (Clobbers)

每个clobber列表项都是一个用双引号括起来并用逗号分隔的字符串常量。通常都是汇编代码用到的寄存器的名字。
当编译器选择使用哪个寄存器来表示输入和输出操作数时,它会避开clobered寄存器。

上面clobber列表项有两个比较奇怪的项:“memory"和"cc”。

"cc" :表示汇编程序代码修改了标志寄存器。

"memory" :告诉编译器,程序集代码对输入和输出操作数中列出的项以外的项执行内存读写(例如,访问输入参数之一所指向的内存)。为了确保内存包含正确的值,GCC可能需要在执行asm之前将特定的寄存器值刷新到内存中。

总之记住,这两个是最常用的,理解不了也没关系,先这么用。

参考资料

NEON Programmer’s Guide

Learn the architecture - Optimizing C code with Neon intrinsics

ARM底层汇编优化之NEON优化 - 概述(基础入门 )

GCC-Inline-Assembly-HOWTO

最牛X的GCC 内联汇编

ARM汇编语言入门

gcc10.4.0手册在线版v

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARM汇编1:如何在C语言中使用汇编 的相关文章

  • c语言 inline函数的总结

    1 inline只是个编译器建议 xff0c 编译器不一定非得展开Inline函数 例如 xff1a Inline函数地址引用 inline在递归函数中使用 2 inline必须用于函数定义 xff0c 对于函数声明 xff0c inlin
  • Linux线程挂掉是否影响进程

    严格的说没有 线程崩溃 xff0c 只是触发了SIGSEGV Segmentation Violation Fault 如果没有设置对应的Signal Handler操作系统就自动终止进程 xff08 或者说默认的Signal Handle
  • python matplotlib.subplot绘制子图

    版权声明 xff1a 本文为博主原创文章 amp amp 转载请著名出处 64 http blog csdn net gatieme 目录 43 问题描述subplot函数介绍示例程序 1 规则划分成33的2 不规则划分 CSDNGitHu
  • SIFT 三线性差值原理与代码分析

    参考了文章 http blog csdn net fzthao article details 62424271 Jie Pro 在进行特征描述时 xff0c 讲的非常详细 但未对三线性插值进行阐述 我也是花了好久的时间才慢慢搞懂 有错之处
  • Cortex-M3的PendSV中断以及uCOS系统一点思考

    uCOS中 OSStart函数 OSStartHighRdy函数 会重新设置PendSV中断的优先级 把该中断优先级设置为最低 每次时钟中断时 一般为最高优先级 xff0c 查看是否要进程切换 如果此时有中断嵌套则不进行进程切换 xff0c
  • opencv3.2安装opencv_contrib

    opencv3 2 增加opencv contrib组件 之前在ubuntu16 04下安装caffe和opencv3 2 xff0c 由于需要需要使用opencv contrib组件 xff0c 在安装中遇到一些问题 在已安装好openc
  • 最小二乘法

    最小二乘法 xff08 又称最小平方法 xff09 是一种数学优化技术 它通过最小化误差的平方和寻找数据的最佳函数匹配 利用最小二乘法可以简便地求得未知的数据 xff0c 并使得这些求得的数据与实际数据之间误差的平方和为最小 最小二乘法还可
  • python vstack

    Python numpy函数hstack vstack stack dstack vsplit concatenate 感觉numpy hstack 和numpy column stack 函数略有相似 xff0c numpy vstack
  • Linux下VNC Server的配置

    1 xff09 安装vnc server xff1a rpm ivh tigervnc server 1 1 0 5 el6 x86 64 rpm 2 修改配置文件 xff0c 1 表示第1号桌面 xff0c 对应端口号5901 2 表示2
  • WIN10_GTX1650_深度学习环境搭建

    这篇博客总结的非常好 xff0c 但安装过程中可能会碰到一些问题 在这记录 xff0c 分享一下解决方案 https blog csdn net weixin 45755980 article details 105397874 Tenso
  • Linux面试必备20个常用命令

    文章目录 第一章 什么是linux第二章 linux的基础命令1 pwd 命令2 ls 命令3 cd 命令4 man 命令5 grep 命令6 find 命令7 chmod 命令8 ps 命令9 kill 命令10 tail 命令11 ne
  • Python爬虫实战(一):翻页爬取数据存入SqlServer

    目录 前言爬取目标准备工作代码分析1 设置翻页2 获取代理ip3 发送请求4 获取详情页地址5 提取详情信息6 存入数据库7 循环实现翻页8 启动 前言 x1f525 x1f525 本文已收录于Python爬虫实战100例专栏 xff1a
  • 已解决error: subprocess-exited-with-error

    已解决 xff08 pip安装第三方模块lxml模块报错 xff09 Building wheels for collected packages lxml Building wheel for lxml setup py error er
  • 已解决此处缺少‘,‘, ‘]‘字符, 实际上是一个 ‘EOF‘

    已解决Python解析JSON xff0c 抛出此处缺少 39 39 39 字符 实际上是一个 39 EOF 异常的解决方法 xff0c 亲测有效 文章目录 报错问题报错原因解决方法千人全栈VIP答疑群联系博主帮忙解决报错 报错问题 粉丝群
  • 已解决E: Unable to locate package ros-kinetic-desktop-full

    已解决Ubuntu安装ros xff0c 抛出异常E Unable to locate package ros kinetic desktop full的正确解决方法 xff0c 亲测有效 xff0c 文末附上Ubuntu系统对应ros系统
  • 数组元素交叉排列的算法题(a1 a2 a3 .. an b1 b2 b3 .. bn -->a 1 b1, a2 b2, a3 b3, .. an bn ) 概论思想(perfect shuffle 算法)

    perfect shuffle 算法 今天又发现一个关于完美洗牌的算法 这个比较简单一些 xff0c 由 microsoft的Peiyush Jain提出 原论文 xff1a A Simple In Place Algorithm for
  • Linux操作系统之命令

    Linux操作系统指令有很多 xff0c 这里就先介绍一些最最基础的吧 首先就是将操作界面显示 xff1a Ctrl 43 alt 43 t 显示当前目录内容 xff1a ls ls l xff1a 将目录内容使用列表显示 ls a xff
  • [操作系统]学习操作系统的经典书籍

    http blog chinaunix net u1 43966 showart 396940 html 介绍了一些操作系统学习的经典书籍 xff0c 包括理论上的 具体操作系统的 Abraham Silberschatz的两本书 xff1
  • 原创:史上最全最通俗易懂的,索引最左前缀匹配原则(认真脸)

    索引最左前缀匹配原则 对于最左前缀匹配原则居然没有百度百科 xff0c 实在是让我感觉不可思议 最左前缀匹配原则 xff0c 用几句话来概述就是 xff1a 顾名思义 xff0c 就是最左优先 xff0c 在创建多列索引时 xff0c 要根
  • MATLAB从文件读取数据

    一 从filename文件读取数据 1 readtable函数 语法 xff1a t 61 readtable xff08 filename xff09 支持的扩展名 xff1a txt csv xls xlsm xlsx xlsm xlt

随机推荐

  • 前端进阶之TS总结

    知识点 高频面试题TS装饰器axios二次封装 1 高频面试题 1 1 类型推论 amp 可赋值性 什么是类型推论 xff1f TypeScript 会在没有明确的指定类型的时候推测出一个类型 xff0c 这就是类型推论如果定义的时候没有赋
  • 岁月清浅,邀你入梦

    这世间本应美好 xff0c 怎无奈痛苦缠身 xff0c 卿心亦真 xff0c 免世人之苦 xff0c 乐自身之本 卿之容 xff0c 多沉醉 xff0c 于心赞 xff0c 日夜思 淡若微风的陪伴 xff0c 奈何情深缘浅 只相识 xff0
  • 记一次解BUG的心得感受

    今天遇到 了 一个 STP的问题 xff0c 从测试 现象 来看与之前一个FR的验证过程中表现出来的特征很相似 这种相似性将我引入了一种歧途 xff1a 怀疑原来的修改有问题 假设你知道第N次修改有潜在的case无法验证 那么这种潜在的风险
  • 02_Keil5报错 error: #5: cannot open source input file “XXX.h”: No such file or directory解决方法

    Keil5 error 5 cannot open source input file led h No such file or directory 是找不到包含文件 解决办法1 包含文件可以解决 解决办法2 如果包含了还是报 5找不到文
  • 05_FreeRTOS中断管理

    目录 什么是中断 中断相关寄存器 源码实验 什么是中断 简介 让CPU打断正常运行的程序 转而去处理紧急的事件 程序 就叫中断 举例 上课可以比做CPU正常运行的程序 上厕所可以比做中断程序 中断执行机制 可简单概括为三步 中断请求 外设产
  • 07_FreeRTOS任务调度器的挂起和恢复

    任务调度器的挂起和恢复 挂起任务调度器 调用此函数不需要关闭中断 使用格式示例 1 与临界区不一样的是 挂起任务调度器 未关闭中断 2 它仅仅是防止 xff1b 任务之间的资源争夺 中断照样可以直接响应 3 挂起调度器的方式 适合于临界区位
  • 09_FreeRTOS任务调度器

    目录 开启任务调度器vTaskStartScheduler函数 xPortStartScheduler开启任务调度器函数 启动第一个任务 prvStartFirstTask开启第一个任务函数 vPortSVCHandler SVC中断服务函
  • 13_FreeRTOS消息队列

    目录 队列简介 FreeRTOS队列特点 队列操作基本过程 队列结构体介绍 队列结构体整体示意图 队列相关API函数介绍 创建队列相关API函数介绍 往队列写入消息API函数 往队列写入消息函数入口参数解析 从队列读取消息API函数 实验源
  • golang XML解析

    使用微信支付的时候遇到这样一种情况 xff1a 支付成功之后微信会发送一个通知过来 xff0c 这个通知包含xml格式的数据 xff0c 其中有一个字段是这样的 xff1a coupon id n 代 金 券
  • FreeRTOS系列-- heap_4.c内存管理分析

    FreeRTOS系列 heap 4 c内存管理分析 heap 4 c简介理解heap 4 c的关键点图示heap 4 c内存申请过程图示heap 4 c内存合并过程内存初始化源码分析内存申请源码分析内存释放分析空闲块内存合并源码分析 hea
  • Cordova概述

    Cordova Apache Cordova is an open source mobile development framework It allows you to use standard web technologies HTM
  • Openstack学习(增加卷迁移限速)

    声明 xff1a 本博客欢迎转发 xff0c 但请保留原作者信息 博客地址 xff1a http blog csdn net halcyonbaby 内容系本人学习 研究和总结 xff0c 如有雷同 xff0c 实属荣幸 xff01 Ope
  • dht11 新手原理详解(附代码)

    dht11详解 dht11原理 简介 DHT11作为一款低价 入门级的温湿度传感器 xff0c 常用于我们的单片机设计实例中 它应用专用的数字模块采集技术和温湿度传感技术 xff0c 确保产品具有极高的可靠性与卓越的长期稳定性 传感器包括一
  • git推送报错 Your branch is ahead of 'origin/master' by 1 commit

    当出现no changes added to commit use git add and or git commit a git commit之后 xff0c 用git status xff0c 打印信息为 xff1a Your bran
  • ubuntu如何降级gcc

    ubuntu版本 xff1a ubuntu 18 04 安装指定版本的gcc g 43 43 xff0c 然后做如下链接 sudo apt get install gcc 4 5 g 43 43 4 5 cpp 4 5 gcc 4 5 mu
  • arm-linux开发环境之(busybox-ls命令)终端显示颜色

    在开发板终端中输入ls命令后终端文件夹和文件显示颜色 linux主机 xff1a ubuntu 12 04 交叉编译器 xff1a gcc version 4 6 2 20110630 prerelease 开发板kernel xff1a
  • 活体识别5:论文笔记之FeatherNets

    说明 这篇文章是这次比赛的第三名 xff1a ChaLearn Face Anti spoofing Attack Detection Challenge 64 CVPR2019 xff0c 此次比赛项目是人脸防欺诈攻击检测 论文标题 xf
  • c++中使用dlopen加载动态库中带类参数的函数

    说明 我一直都知道dlopen的大概用法 但是dlopen毕竟是c语言的函数 xff0c 能否加载带c 43 43 类型传参的函数 xff0c 我有点不确定 今天有空验证了下 xff0c 是可以的 extern C 只影响了函数在动态库中的
  • 将pytorch的pth文件固化为pt文件

    说明 我参考了一个开源的人像语义分割项目mobile phone human matting xff0c 这个项目提供了预训练模型 xff0c 我想要将该模型固化 xff0c 然后转换格式后在嵌入式端使用 该项目保存模型的代码如下 xff1
  • ARM汇编1:如何在C语言中使用汇编

    如何在C语言中使用汇编语言 我最近对ARM的NEON编程有兴趣 xff0c 主要是为了想学习一些矩阵计算加速相关的知识 但是我又不想写纯粹的汇编语言 xff0c 我想在C语言中嵌入汇编来使用 经过检索学习 xff0c 我找到两种可行的方式