ARM GCC的内联汇编详解 (ARM GCC Inline Assembler Cookbook)

本文详细介绍了如何在 ARM 平台上使用 GCC 的内联汇编,包括 GCC 的 `asm` 语句、C 代码优化、输入输出操作数、常见问题和陷阱。内容涵盖基本和扩展内联汇编的使用,以及与 C 语言表达式的交互。通过实例展示了如何在内联汇编中处理变量和函数,以及如何避免常见问题,适合熟悉 ARM 汇编的开发者参考。
摘要由CSDN通过智能技术生成

 

说明:

 在linux内核源码中,定义了一个__asm__的宏,其定义为:

       #define __asm__ asm

 另外有一个经常和 __asm__一起被用到的宏 __volatile__ 定义如下:

      #define __volatile__ volatile

   下面的文章中是直接用 asm ,但是,我们在linux的源码中看到的大部分都是 __asm__ 。其实他们是等价的。只是视觉上的效果不一样而已。

 

本文前面一大段都是英文,中间有中文注解。对英文不感兴趣的,请直接跳到本文中间部分

 

ARM GCC Inline Assembler Cookbook

About this document

The GNU C compiler for ARM RISC processors offers, to embed assembly language code into C programs. This cool feature may be used for manually optimizing time critical parts of the software or to use specific processor instruction, which are not available in the C language.

It's assumed, that you are familiar with writing ARM assembler programs, because this is not an ARM assembler programming tutorial. It's not a C language tutorial either.

All samples had been tested with GCC version 4, but most of them should work with earlier versions too.

GCC asm statement

Let's start with a simple example. The following statement may be included in your code like any other C statement.

/* NOP example */
asm("mov r0,r0");

It moves the contents of register r0 to register r0. In other words, it doesn't do much more than nothing. It is also known as a NOP (no operation) statement and is typically used for very short delays.

Stop! Before adding this example right away to your C code, keep on reading and learn, why this may not work as expected.

With inline assembly you can use the same assembler instruction mnemonics as you'd use for writing pure ARM assembly code. And you can write more than one assembler instruction in a single inline asm statement. To make it more readable, you can put each instruction on a separate line.

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);

The special sequence of linefeed and tab characters will keep the assembler listing looking nice. It may seem a bit odd for the first time, but that's the way the compiler creates its own assembler code while compiling C statements.

So far, the assembler instructions are much the same as they'd appear in pure assembly language programs. However, registers and constants are specified in a different way, if they refer to C expressions. The general form of an inline assembler statement is

asm(code : output operand list : input operand list : clobber list);

The connection between assembly language and C operands is provided by an optional second and third part of theasm statement, the list of output and input operands. We will explain the third optional part, the list of clobbers, later.

The next example of rotating bits passes C variables to assembly language. It takes the value of one integer variable, right rotates the bits by one and stores the result in a second integer variable.

/* Rotating bits example */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));

Each asm statement is divided by colons into up to four parts:

  1. The assembler instructions, defined in a single string literal:
    "mov %[result], %[value], ror #1"
  2. An optional list of output operands, separated by commas. Each entry consists of a symbolic name enclosed in square brackets, followed by a constraint string, followed by a C expression enclosed in parentheses. Our example uses just one entry:
    [result] "=r" (y)
  3. A comma separated list of input operands, which uses the same syntax as the list of output operands. Again, this is optional and our example uses one operand only:
    [value] "r" (x)
  4. Optional list of clobbered registers, omitted in our example.

As shown in the initial NOP example, trailing parts of the asm statement may be omitted, if unused. Inline asm statements containing assembler instruction only are also known as basic inline assembly, while statements containing optional parts are called extended inline assembly. If an unused part is followed by one which is used, it must be left empty. The following example sets the current program status register of the ARM CPU. It uses an input, but no output operand.

asm("msr cpsr,%[ps]" : : [ps]"r"(status));

Even the code part may be left empty, though an empty string is required. The next statement creates a special clobber to tell the compiler, that memory contents may have changed. Again, the clobber list will be explained later, when we take a look to code optimization.

asm("":::"memory");

You can insert spaces, newlines and even C comments to increase readability.

asm("mov    %[result], %[value], ror #1"

           : [result]"=r" (y) /* Rotation result. */
           : [value]"r"   (x) /* Rotated value. */
           : /* No clobbers */
    );

In the code section, operands are referenced by a percent sign followed by the related symbolic name enclosed in square brackets. It refers to the entry in one of the operand lists that contains the same symbolic name. From the rotating bits example:

%[result] refers to output operand, the C variable y, and

             %[result]  对应输出操作数,对应C变量 y
%[value] refers to the input operand, the C variable x.

               %[value]  对应输入操作数,对应C变量 x

Symbolic operand names use a separate name space. That means, that there is no relation to any other symbol table. To put it simple: You can choose a name without taking care whether the same name already exists in your C code. However, unique symbols must be used within each asm statement.

符号名(Symbolic operand names)使用一个独立的命名空间(name space.)。这意味着,它与任何其他的符号表(symbol table)没有关系。可以让他更简单:你选择一个名字,而完全不用在意这个名字是否与c代码中的其他名字重名。然而,每个内联汇编段中的名字必须是唯一的。

If you already looked to some working inline assembler statements written by other authors, you may have noticed a significant difference. In fact, the GCC compiler supports symbolic names since version 3.1. For earlier releases the rotating bit example must be written as

如果你已经看到了一些其他作者写的汇编语句,你可能已经注意到 输入输出代号的不同。即 %[result] --> %0   %[value] --> %1 这样的差别。 实际上,gcc 编译器自从3.1版本就开始支持符号名(symbolic names),即%[result]这样的写法。对于早期发布的版本,对于这个移位操作的例子,必须按照如下的方式书写(即使用%0这样的风格):

asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value));

Operands are referenced by a percent sign followed by

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GCC内联汇编asm格式是一种将汇编代码嵌入到C或C++源代码中的方法。它允许开发人员直接使用汇编语言来访问底层硬件或执行高性能算法。以下是GCC内联汇编asm格式的详细说明。 基本格式 GCC内联汇编asm格式基本格式如下: ```c asm (assembly code : output operands : input operands : clobbered registers); ``` - assembly code:汇编代码,可以是单行或多行代码。 - output operands:用于存储计算结果的变量,可以有多个,用逗号分隔。输出操作数是可选的,可以省略。 - input operands:用于传递参数的变量,可以有多个,用逗号分隔。输入操作数是必需的。 - clobbered registers:代码执行期间会被修改的寄存器,用于通知编译器。可以有多个,用逗号分隔。clobbered registers是可选的,可以省略。 示例 以下是一个简单的GCC内联汇编asm格式示例,将eax寄存器中的值加1,并将结果存储在eax中。 ```c int value = 10; asm ("addl $1, %%eax" : "=a" (value) : "a" (value)); ``` - "addl $1, %%eax":汇编代码,将eax加1。 - "=a" (value):输出操作数,将eax中的值存储在value变量中。 - "a" (value):输入操作数,将value的值传递给eax。 - 没有clobbered registers。 输出操作数 输出操作数用于将汇编代码的结果存储在变量中。输出操作数有两种类型:普通输出(通道约束)和跨约束输出。 普通输出 普通输出使用“=约束”语法表示,其中约束指定了输出操作数应存储在哪个寄存器或内存位置中。约束可以是以下之一: - "=r"(任意寄存器) - "=m"(任意内存位置) - "=a"(eax寄存器) - "=d"(edx寄存器) - "=q"(eax或edx寄存器) 示例 以下是一个使用普通输出的示例,将eax寄存器中的值加1,并将结果存储在value变量中。 ```c int value; asm ("addl $1, %%eax" : "=a" (value) : "a" (value)); ``` 跨约束输出 跨约束输出是一种将结果存储在多个输出变量中的方法。它使用“+约束”语法表示,其中约束指定了输出操作数应存储在哪个寄存器或内存位置中。多个约束可以用逗号分隔。 示例 以下是一个使用跨约束输出的示例,将eax寄存器中的值加1,并将结果存储在value1和value2变量中。 ```c int value1, value2; asm ("addl $1, %%eax" : "+a" (value1), "=r" (value2)); ``` 输入操作数 输入操作数用于将变量的值传递给汇编代码。输入操作数使用“约束”语法表示,其中约束指定了变量应该存储在哪个寄存器或内存位置中。约束可以是以下之一: - "r"(任意寄存器) - "m"(任意内存位置) - "a"(eax寄存器) - "d"(edx寄存器) - "q"(eax或edx寄存器) 示例 以下是一个使用输入操作数的示例,将value变量的值传递给eax寄存器中。 ```c int value = 10; asm ("movl %0, %%eax" : : "r" (value)); ``` clobbered registers clobbered registers是在汇编代码执行期间会被修改的寄存器列表。它用于通知编译器哪些寄存器应该被保存和恢复。clobbered registers使用“%约束”语法表示,其中约束指定了被修改的寄存器名称。多个寄存器可以用逗号分隔。 示例 以下是一个使用clobbered registers的示例,将eax寄存器中的值加1,并告诉编译器edx寄存器也被修改了。 ```c asm ("addl $1, %%eax" : : "a" (value) : "%edx"); ``` 总结 GCC内联汇编asm格式是一种将汇编代码嵌入到C或C++源代码中的方法。它允许开发人员直接使用汇编语言来访问底层硬件或执行高性能算法。通过输出操作数、输入操作数和clobbered registers,开发人员可以管理汇编代码与C或C++代码之间的数据流和寄存器使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值