C语言ASM汇编内嵌语法详解

2020-01-18 19:38:27于丽

都是合法的写法。如果你将指令放在多对引号中,则除了最后一对引号之外,前面的所有引号里的最后一条指令之后都要有一个分号(;)或(n)或(nt)。比如:


__asm__("movl %eax, %ebx 
stin" 
"popl %edi;" 
"subl %ecx, %ebx"); 

__asm__("movl %eax, %ebx; stint" 
"popl %edi; subl %ecx, %ebx");

__asm__("movl %eax, %ebx; stint popl %edin"
"subl %ecx, %ebx");

__asm__("movl %eax, %ebx; stint popl %edi;" "subl %ecx, %ebx");

都是合法的。

上述原则可以归结为:

任意两个指令间要么被分号(;)分开,要么被放在两行; 放在两行的方法既可以从通过n的方法来实现,也可以真正的放在两行; 可以使用1对或多对引号,每1对引号里可以放任一多条指令,所有的指令都要被放到引号中。在基本内联汇编中,“Instruction List”的书写的格式和你直接在汇编文件中写非内联汇编没有什么不同,你可以在其中定义Label,定义对齐(.align n ),定义段(.section name )。例如:


__asm__(".align 2nt" 
"movl %eax, %ebxnt" 
"test %ebx, %ecxnt" 
"jne errornt" 
"stint" 
"error: popl %edint" 
"subl %ecx, %ebx");

上面例子的格式是Linux内联代码常用的格式,非常整齐。也建议大家都使用这种格式来写内联汇编代码。

3、__volatile__

__volatile__是GCC关键字volatile的宏定义:

#define __volatile__ volatile

__volatile__ 或volatile是可选的,你可以用它也可以不用它。如果你用了它,则是向GCC声明“不要动我所写的Instruction List,我需要原封不动的保留每一条指令”,否则当你使用了优化选项(-O)进行编译时,GCC将会根据自己的判断决定是否将这个内联汇编表达式中的指 令优化掉。

那么GCC判断的原则是什么?我不知道(如果有哪位朋友清楚的话,请告诉我)。我试验了一下,发现一条内联汇编语句如果是基本 内联汇编的话(即只有“Instruction List”,没有Input/Output/Clobber的内联汇编,我们后面将会讨论这一点),无论你是否使用__volatile__来修饰, GCC 2.96在优化编译时,都会原封不动的保留内联汇编中的“Instruction List”。但或许我的试验的例子并不充分,所以这一点并不能够得到保证。

为了保险起见,如果你不想让GCC的优化影响你的内联汇编代码,你最好在前面都加上__volatile__,而不要依赖于编译器的原则,因为即使你非常了解当前编译器的优化原则,你也无法保证这种原则将来不会发生变化。而__volatile__的含义却是恒定的。

2、带有C/C++表达式的内联汇编

GCC允许你通过C/C++表达式指定内联汇编中"Instrcuction List"中指令的输入和输出,你甚至可以不关心到底使用哪个寄存器被使用,完全靠GCC来安排和指定。这一点可以让程序员避免去考虑有限的寄存器的使用,也可以提高目标代码的效率。