分类: C/C++
2013-01-25 17:44:11
使用c全局变量, 不能在内联汇编中使用局部变量, 注意在汇编语言代码中值被用做内存位置, 而不是立即数值
如果不希望优化内联汇编, 则可以volatile修饰符如:__asm__ volatile("code");
2,GCC内联汇编的扩展语法
__asm__("assembly code":output locations:input operands:changed registers);
第一部分是汇编代码
第二部分是输出位置, 包含内联汇编代码的输出值的寄存器和内存位置列表
第三部分是输入操作数,包含内联汇编代码输入值的寄存器和内存位置的列表
第四部分是改动的寄存器, 内联汇编改变的任何其他寄存器的列表
这几个部分可以不全有, 但是没有的还必须使用:分隔
1, 指定输入值和输出值, 输入值和输出值的列表格式为:
"constraint"(variable), 其中variable是程序中声明的c变量, 在扩展asm格式中, 局部和全局变量都可以使用,
使用constrant(约束)定义把变量存放到哪(输入)或从哪里传送变量(输出)
约束使用单一的字符, 如下:
约束 描述
a 使用%eax, %ax, %al寄存器
b 使用%ebx, %bx, %bl寄存器
c 使用%ecx, %cx, %cl寄存器
d 使用%edx, %dx, %dl寄存器
S 使用%esi, %si寄存器
D 使用%edi, %di寄存器
r 使用任何可用的通用寄存器
q 使用%eax, %ebx, %ecx,%edx之一
A 对于64位值使用%eax, %edx寄存器
f 使用浮点寄存器
t 使用第一个(顶部)的浮点寄存器
u 使用第二个浮点寄存器
m 使用变量的内存位置
o 使用偏移内存位置
V 只使用直接内存位置
i 使用立即整数值
n 使用值已知的立即整数值
g 使用任何可用的寄存器和内存位置
除了这些约束之外, 输出值还包含一个约束修饰符:
输出修饰符 描述
+ 可以读取和写入操作数
= 只能写入操作数
% 如果有必要操作数可以和下一个操作数切换
& 在内联函数完成之前, 可以删除和重新使用操作数
如:
__asm__("assembly code": "=a"(result):"d"(data1),"c"(data2));
把c变量data1存放在edx寄存器中, 把c变量data2存放到ecx寄存器中, 内联汇编的结果
将存放在eax寄存器中, 然后传送给变量result
在扩展的asm语句块中如果要使用寄存器必须使用两个百分号符号
不一定总要在内联汇编代码中指定输出值, 一些汇编指令假定输入值包含输出值, 如movs指令
其他扩展内联汇编知识:
1, 使用占位符
输入值存放在内联汇编段中声明的特定寄存器中, 并且在汇编指令中专门使用这些寄存器.
虽然这种方式能够很好的处理只有几个输入值的情况, 但对于需要很多输入值的情况, 这
中方式显的有点繁琐. 为了帮助解决这个问题, 扩展asm格式提供了占位符, 可以在内联
汇编代码中使用它引用输入和输出值.
占位符是前面加上百分号的数字, 按照内联汇编中列出的每个输入和输出值在列表中的位置,
每个值被赋予从0开始的地方. 然后就可以在汇编代码中引用占位符来表示值。
如果内联汇编代码中的输入和输出值共享程序中相同的c变量, 则可以指定使用占位符作为
约束值, 如:
__asm__("imull %1, %0"
: "=r"(data2)
: "r"(data1), "0"(data2));
如输入输出值中共享相同的变量data2, 而在输入变量中则可以使用标记0作为输入参数的约束
2, 替换占位符
如果处理很多输入和输出值, 数字型的占位符很快就会变的很混乱, 为了使条理清晰 ,GNU汇编
器(从版本3.1开始)允许声明替换的名称作为占位符.替换的名称在声明输入值和输出值的段中
定义, 格式如下:
%[name]"constraint"(variable)
定义的值name成为内联汇编代码中变量的新的占位符号标识, 如下面的例子:
__asm__("imull %[value1], %[value2]"
: [value2] "=r"(data2)
: [value1] "r"(data1), "0"(data2));
3, 改动寄存器列表
编译器假设输入值和输出值使用的寄存器会被改动, 并且相应的作出处理。程序员不需要在改动的
寄存器列表中包含这些值, 如果这样做了, 就会产生错误消息. 注意改动的寄存器列表中的寄存器
使用完整的寄存器名称, 而不像输入和输出寄存器定义的那样仅仅是单一字母。 在寄存器名称前面
使用百分号符号是可选的。
改动寄存器列表的正确使用方法是, 如果内联汇编代码使用了没有被初始化地声明为输入或者输出
值的其他任何寄存器 , 则要通知编译器。编译器必须知道这些寄存器, 以避免使用他们。如:
int main(void) {
int data1 = 10;
int result = 20;
__asm__("movl %1, %%eax\n\t"
"addl %%eax, %0"
: "=r"(result)
: "r"(data1), "0"(result)
: "%eax");
printf("The result is %d\n", result);
return 0;
}
4, 使用内存位置
虽然在内联汇编代码中使用寄存器比较快, 但是也可以直接使用c变量的内存位置。 约束m用于引用输入值
和输出值中的内存位置。 记住, 对于要求使用寄存器的汇编指令, 仍然必须使用寄存器, 所以不得不定义
保存数据的中间寄存器。如:
int main(void) {
int dividentd = 20;
int divisor = 5;
int result;
__asm__("divb %2\n\t"
"movl %%eax, %0"
: "=m"(result)
: "a"(dividend), "m"(divisor));
printf("The result is %d\n", result);
return 0;
}
5, 处理跳转
内联汇编语言代码也可以包含定义其中位置的标签。 可以实现一般的汇编条件分支和无条件分支, 如:
int main(void) {
int a = 10;
int b = 20;
int result;
__asm__("cmp %1, %2\n\t"
"jge greater\n\t"
"movl %1, %0\n\t"
"jmp end\n"
"greater:\n\t"
"movl %2, %0\n"
"end:"
:"=r"(result)
:"r"(a), "r"(b));
printf("The larger value is %d\n", result);
return 0;
}
在内联汇编代码中使用标签时有两个限制。 第一个限制是只能跳转到相同的asm段内的标签,
不能从-个asm段跳转到另一个asm段中的标签。第二个限制更加复杂一点。 以上程序使用
标签greater和end。 但是, 这样有个潜在的问题, 查看汇编后的代码清单, 可以发现内联
汇编标签也被编码到了最终汇编后的代码中。 这意味着如果在c代码中还有另一个asm段, 就
不能再次使用相同的标签, 否则会因为标签重复使用而导致错误消息。还有如果试图整合使用
c关键字(比如函数名称或者全局变量)的标签也会导致错误。