加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 内联汇编优点
    • 内联汇编缺点
    • 内联汇编语法
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

Linux+ARM内联汇编inline assembly学习(一)

05/07 15:03
4001
阅读需 8 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

哈喽,大家好,我是程序员秘书LittleG。

前言

​内联汇编允许开发者在C/C++代码中直接嵌入汇编指令,对于编写性能敏感的代码、访问特定硬件特性或者实现某些底层功能非常有用。

Linux+ARM平台上使用内联汇编(inline assembly),可以通过GCC(GNU Compiler Collection)提供的内联汇编语法来实现。

下面就结合内联汇编的优/劣为出发点,明白为什么要学习和掌握它,以及以一个内联汇编代码为例,熟悉一下内联汇编相关语法规则。

正文

内联汇编作为一种在高级语言代码中直接嵌入低级机器指令的技术,具有其独特的优势和潜在的局限性。以下是内联汇编的主要优点和缺点。

内联汇编优点

性能优化:

内联汇编允许开发者直接控制硬件资源,执行高度优化的指令序列,绕过编译器可能产生的低效代码。例如,在图像处理或加密算法中,某些关键循环通过手工优化的汇编代码能显著提高执行速度。

:在图像像素操作中,直接利用SIMD指令(如ARM64的NEON指令集)进行向量运算,相比依赖编译器自动矢量化,往往能实现更高效的像素处理逻辑。

硬件特性利用: 对于一些新硬件特性或特定硬件接口的访问,标准语言可能没有提供直接的支持。内联汇编允许直接与硬件交互,比如配置寄存器、执行特定指令等。

:直接操控GPIO(通用输入输出端口)来控制外设,如点亮LED灯,这通常需要对特定寄存器进行读写操作,通过内联汇编可以直接高效完成。

​兼容性和移植性:    在某些情况下,为了兼容遗留代码或特定平台的特性,内联汇编可以用来复现特定行为,尤其是在跨平台开发中,通过条件编译可以针对不同架构编写不同的汇编代码。

:在实现跨平台的系统调用时,不同的CPU架构(如x86_64和ARM64)有不同的指令序列来完成系统调用。通过内联汇编和条件编译,可以在一个代码库中实现对多种架构的支持。

内联汇编缺点

可读性和维护性差:    汇编代码对大多数人来说难以阅读,特别是当涉及到复杂的逻辑时。这增加了代码维护的成本,尤其是对那些不熟悉底层细节的开发者而言。

:尝试理解一个涉及多条分支跳转和复杂数据操作的内联汇编函数,对于不熟悉的人来说是比较困难的,从而导致后续维护和调试效率低下。

编译器优化受限:    编译器无法像处理高级语言那样有效地优化内联汇编代码。必须手动考虑循环展开、寄存器分配等问题,而这些通常由现代编译器自动处理。

:在循环中使用内联汇编,编译器可能无法识别循环不变部分并将其移出循环体,导致性能损失。

平台依赖性:    内联汇编代码通常紧密绑定到特定的处理器架构和指令集上,这意味着代码在不同平台间的移植变得复杂。

:为ARM64编写的一段内联汇编代码无法直接在x86架构上运行,需要重新编写适配新的指令集和寄存器规则。当然如上面兼容性所说,如果一开始开发代码就充分考虑了兼容性,做了兼容适配则另当别论,但这种情况是受个人编码能力差异,公司业务方向影响的,所以大多数情况,对于平台的兼容性都是不确定的,或者说比较差的,也就是依赖性比较强的。

内联汇编语法

Linux ARM的内联汇编遵循GCC的一般格式,使用asm关键字,基本结构如下:

asm("assembly_code"
    : output_operands  // 输出操作数
    : input_operands   // 输入操作数
    : list_of_clobbered_regs); // 被修改但未列出的寄存器列表

下面通过具体例子熟悉一下。

代码举例:交换两个变量的值

假设我们想要使用内联汇编直接交换两个整型变量ab的值,可按照如下实现:

#include <stdio.h>

int main() {
    int a = 10, b = 20;
    printf("Before swap: a = %d, b = %dn", a, b);

    // 使用内联汇编交换a和b的值
    asm volatile (
        "mov %w[tmp], %w[a]nt"    // 将a的值保存到tmp中
        "mov %w[a], %w[b]nt"     // 将b的值赋给a
        "mov %w[b], %w[tmp]"       // 将tmp中的值赋给b
        : [a] "=r" (a),            // 输出操作数a
          [b] "=r" (b)             // 输出操作数b
        : [tmp] "=&r" (a)          // tmp作为中间变量,需要提前声明
        : "cc"                      // clobbered寄存器,这里指条件码寄存器
    );

    printf("After swap: a = %d, b = %dn", a, b);
    return 0;
}

说明:

volatile: 关键字确保汇编代码不会被优化器删除或重排。

%w[tmp]%w[a]%w[b]:这些是操作数修饰符,%w表示32位宽的寄存器或立即数,适合处理int类型。如果是64位长的,则直接使用%
="r" 表示输出操作数,r表示任意寄存器。
=&r 表示输入操作数同时也是输出操作数(中间变量),并且必须分配一个新的寄存器。
: "cc":说明此段汇编可能会改变条件码寄存器(影响CPU的状态标志),因此编译器在安排指令时需注意。

需特别注意,实际使用内联汇编时,一定要精确控制并明确指出所有可能影响的寄存器,以免引起未预期的行为或编译错误。此外,尽量避免不必要的内联汇编使用,除非是标准库或高级语言无法有效支持的特定场景。

总结,内联汇编是把双刃剑,虽然提供了底层控制能力以追求极致性能或实现特定功能,但同时也带来了代码维护困难、编译器优化限制以及平台依赖性增强等挑战。实际开发过程中可能需要结合公司硬件平台差异,业务方向,技术栈部署等实际情况,权衡利弊,在确实必要的情况下合理使用内联汇编,并且要尽量确保代码注释清晰,便于后期理解和维护。

下期见~

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
SN74LV595ARGYR 1 Texas Instruments Eight-bit shift registers with 3-state output registers 16-VQFN -40 to 125

ECAD模型

下载ECAD模型
$0.9 查看
XLH736100.000000I 1 Integrated Device Technology Inc HCMOS Output Clock Oscillator

ECAD模型

下载ECAD模型
$2.38 查看
XLH536050.000000I 1 Integrated Device Technology Inc CLCC-6, VARR
$1.2 查看

相关推荐

电子产业图谱

记录和分享C/C++、Linux、ARM、Android、IoT相关知识。技术相伴于生活和成长,愿你我永为少年,心中有火,眼中有光,始保热情。