c语⾔⾥⾯加汇编指令,(转载)在C语⾔中加⼊汇编指令的⽅
__asm__ __volatile__内嵌汇编⽤法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下⾯简要介绍下__asm__ __volatile__内嵌汇编⽤法。因为我们华清远见教学平台是ARM体系结构的,所以下⾯的⽰例都是⽤ARM汇编。
带有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系统结构上关闭中断的操作
1 int disable_interrupts (void)
2 {
3 unsigned longold,temp;
4 __asm__ __volatile__("mrs %0, cpsr/n"
5 "orr %1, %0, #0x80/n"
6 "msr cpsr_c, %1"
7 : "=r" (old), "=r"(temp)8 :9 : "memory");10 return (old & 0x80) == 0;11 }
3、 __volatile__
__volatile__是GCC 关键字volatile 的宏定义
#define __volatile__ volatile
__volatile__或volatile 是可选的。如果⽤了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使⽤了优化选项(-O)进⾏编译时,GCC 将会根据⾃⼰的判断决定是否将这个内联汇编表达式中的指令优化掉。
4、Output
Output ⽤来指定当前内联汇编语句的输出
汇编指令有多少个
例如:从arm协处理器p15中读出C1值
1 static unsigned long read_p15_c1 (void)
2 {
3   unsigned longvalue;
4   __asm__ __volatile__( "mrc p15, 0, %0, c1, c0, 0 @ read control reg/n"
5                : "=r" (value) @编译器选择⼀个R*寄存器
6                :
7                : "memory");
8   #ifdef MMU_DEBUG
9   printf ("p15/c1 is = %08lx/n", value);10   #endif
11   returnvalue;12 }
5、 Input
Input 域的内容⽤来指定当前内联汇编语句的输⼊Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)
例如:向arm协处理器p15中写⼊C1值
1 static void write_p15_c1 (unsigned longvalue)
2 {
3   #ifdef MMU_DEBUG
4   printf ("write %08lx to p15/c1/n", value);
5   #endif
6   __asm__ __volatile__( "mcr p15, 0, %0, c1, c0, 0 @ write it back/n"
7                :8                : "r" (value) @编译器选择⼀个R*寄存器9                : "memory");10   read_p15_c1 ();11 }
6、Clobber/Modify
有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进⾏修改,希望GCC在编译时能够将这⼀点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况⼀般发⽣在⼀个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在⼀些Input/Output操作表达式使⽤"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,⽽这个寄存器只是供当前内联汇编临时使⽤的情况。
例如:
1 __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声明内存发⽣了变化,⽽内存发⽣变化带来的影响并不⽌这⼀点。
例如:
1 int main(int __argc, char*__argv[])
2 {
3   int* __p = (int*)__argc;
4   (*__p) = 9999;
5   __asm__("":::"memory");
6
if((*__p) == 9999)7     return 5;8   return (*__p);9 }
本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是⼀句废话。GCC在优化时会意识到这⼀点,⽽直接只⽣成return 5的汇编代码,⽽不会再⽣成if语句的相关代码,⽽不会⽣成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)⼀定与9999相等,它只有⽼⽼实实⽣成这条if语句的汇编代码,⼀起相关的两个return语句相关代码。
另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中
1 # define barrier() _asm__volatile_("": : :"memory")
主要是保证程序的执⾏遵循顺序⼀致性。呵呵,有的时候你写代码的顺序,不⼀定是最终执⾏的顺序,这个是处理器有关的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。