arm下的gcc内联汇编

转载 2012年03月28日 20:07:17
arm下的gcc内联汇编

__asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。

带有C/C++表达式的内联汇编格式为:

__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

其中每项的概念及功能用法描述如下:

1、 __asm__

__asm__是GCC 关键字asm 的宏定义:

#define __asm__ asm

__asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。

2、Instruction List

Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory");

就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。 当在"Instruction List"中有多条指令的时候,可以在一对引号中列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符 (/n)将它们分开. 综上述:(1)每条指令都必须被双引号括起来 (2)两条指令必须用换行或分号分开。

例如: 在ARM系统结构上关闭中断的操作

int disable_interrupts (void)
        {
                unsigned long old,temp;
                __asm__ __volatile__("mrs %0, cpsr/n"
                                "orr %1, %0, #0x80/n"
                                "msr cpsr_c, %1"
                                : "=r" (old), "=r" (temp)
                                :
                                : "memory");
                return (old & 0x80) == 0;
        }

3. __volatile__

__volatile__是GCC 关键字volatile 的宏定义

#define __volatile__ volatile

__volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

4、 Output

Output 用来指定当前内联汇编语句的输出

例如:从arm协处理器p15中读出C1值

static unsigned long read_p15_c1 (void)
        {
                unsigned long value;
                __asm__ __volatile__(
                                "mrc p15, 0, %0, c1, c0, 0 @ read control reg/n"
                                : "=r" (value) @编译器选择一个R*寄存器
                                :
                                : "memory");
        #ifdef MMU_DEBUG
                printf ("p15/c1 is = %08lx/n", value);
        #endif
                return value;
        }

5、 Input

Input 域的内容用来指定当前内联汇编语句的输入Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)

例如:向arm协处理器p15中写入C1值

static void write_p15_c1 (unsigned long value)
        {
        #ifdef MMU_DEBUG
                printf ("write %08lx to p15/c1/n", value);
        #endif
                __asm__ __volatile__(
                                "mcr p15, 0, %0, c1, c0, 0 @ write it back/n"
                                :
                                : "r" (value) @编译器选择一个R*寄存器
                                : "memory");
                read_p15_c1 ();
        } 

6.、Clobber/Modify

有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这 一点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。

例如:

__asm__ ("mov R0, #0x34" : : : "R0");

寄存器R0出现在"Instruction List中",并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定"R0",以让GCC知道这一点。

因为你在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作 表达式使用"r"约束,让GCC为你选择一个寄存器时,GCC对这些寄存器是非常清楚的——它知道这些寄存器是被修改的,你根本不需要在 Clobber/Modify域再声明它们。但除此之外, GCC对剩下的寄存器中哪些会被当前的内联汇编修改一无所知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在Clobber/Modify 中声明它们,让GCC针对这些寄存器做相应的处理。否则有可能会造成寄存器的不一致,从而造成程序执行错误。

如果一个内联汇编语句的Clobber/Modify域存在"memory",那么GCC会保证在此内联 汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放 在寄存器中的拷贝。因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。

这只是使用"memory"时,GCC会保证做到的一点,但这并不是全部。因为使用"memory"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。

例如:

int main(int __argc, char* __argv[])
        {
        int* __p = (int*)__argc;
        (*__p) = 9999;
        __asm__("":::"memory");
        if((*__p) == 9999)
        return 5;
        return (*__p);
        }

本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这 一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。

另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中

# define barrier() _asm__volatile_("": : :"memory")

主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是最终执行的顺序,这个是处理器有关的。

Arm汇编学习笔记(三)——GCC内联汇编

之前每次看内联汇编都有一些地方看不明白,"=r","r","%0","%1"这些符号看不明白,本次总结一下这些内容吧,虽然很简单,但是手不能懒! 首先,C语言中为什么要内联汇编以及其带来的好...
  • beyond702
  • beyond702
  • 2016年08月12日 17:34
  • 2041

ARM GCC汇编伪指令

转载,原文地址:http://blog.chinaunix.net/uid-20626696-id-199009.html word expression就是在当前位置放一个word型的...
  • LouisScola
  • LouisScola
  • 2014年12月17日 10:28
  • 2657

ARM GCC 内嵌(inline)汇编手册

ARM GCC 内嵌(inline)汇编手册      关于这篇文档这篇文章是本人为方便各位业界同仁而翻译,方便大家开发底层代码使用,转载请注明出处,谢谢。要是你E文功底好,本人还是建议阅读E文版的...
  • shanzhizi
  • shanzhizi
  • 2014年08月15日 13:39
  • 796

arm汇编指令集整理

b, bl  (相对跳转指令)
  • gemdream1
  • gemdream1
  • 2014年08月17日 13:51
  • 922

GCC中内嵌arm汇编

首先内嵌汇编的形式是 asm {      “要执行的语句;”      :输入参数      :输出参数      :被改变的寄存器 }...
  • groundhappy
  • groundhappy
  • 2016年09月06日 17:12
  • 347

GNU ARM 汇编指令(注意是GNU,区别与armasm)

GNU ARM 汇编指令(注意是GNU)
  • gx19862005
  • gx19862005
  • 2014年06月23日 17:25
  • 2017

ATPCS和内嵌汇编:arm处理器上函数调用寄存器的使用规则

为了优化 arm cpu做deinterlace,学习arm的汇编,对于arm汇编的传参规则不了解,特此记录。 原文链接: http://lli_njupt.0fees.net/ar01s05.ht...
  • yypony
  • yypony
  • 2013年12月28日 16:03
  • 3191

gcc编译c语言中内嵌汇编

--AT&T and Intel 汇编语法对照 寄存器命名: AT&T:  %eax Intel: eax AT&T 语法源地址在左侧,目的地址在右侧与Intel 方式语法相反 将e...
  • xjbclz
  • xjbclz
  • 2016年07月04日 22:36
  • 890

ARM中C和汇编混合编程及示例

参数的传递规则.      根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序.这两种子程序的参数传递规则是不同的.      1.参数个数可变的子程序参数传...
  • Ropai
  • Ropai
  • 2013年05月20日 15:07
  • 2328

ARM / Thumb 指令混合编程之代码交织 ( interworking )

本文翻译整理自 http://stuff.mit.edu/afs/sipb/project/egcs/src/egcs/gcc/config/arm/README-interworking 针对AR...
  • cfy_phonex
  • cfy_phonex
  • 2014年01月23日 09:48
  • 3340
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:arm下的gcc内联汇编
举报原因:
原因补充:

(最多只允许输入30个字)