GCC 内联汇编器对正确的规范非常敏感。
特别是,您必须非常精确地指定正确的约束,以确保编译器不会决定“优化”您的汇编代码。有几件事需要注意。举个例子吧。
以下两个:
int myasmfunc(int arg) /* 肯定有问题 ... */
{
注册 int myval asm("r2") = arg;
asm ("添加 r1, r0, #22\n" ::: "r1");
asm ("添加 r0, r1, r0\n" ::: "r0", "cc");
asm ("subeq r2, #123\n" ::: "r2");
asm ("subne r2, #213\n" ::: "r2");
返回myval;
}
和
int myasmfunc(int arg)
{
int myval = arg,加 = arg;
asm ("添加 %0, #22\n\t" : "+r"(加号));
asm ("添加 %1, %2\n\t"
“子当量 %0,#123\n\t”
“subne %0,#213\n\t”:“+r”(myval),“+r”(加号):“r”(arg):“cc”);
返回myval;
}
乍一看可能看起来很相似,但您会天真地认为它们也是如此;但他们离那还很远!
此代码的第一个版本存在多个问题。
- 一方面,如果您将其指定为单独的
asm() 语句,编译器可以在其间插入任意代码。这尤其意味着 sub 指令,即使它们自己不修改条件代码,也可能与编译器选择插入的内容发生冲突。
- 其次,由于在指定单独的
asm() 语句时指令的拆分,不能保证代码生成器在两次都选择相同的寄存器来放置myval,变量声明中的asm("r2") 规范尽管如此。
- 第三,第一个假设
r0包含函数的参数是错误的;编译器在到达汇编块时可能已选择将此参数移动到其他任何地方。更糟糕的是,您再次使用了 split 语句,并且无法保证 在 两个 asm() 之间会发生什么。即使您指定__asm__ __volatile__(...);,编译器也会将两个这样的块视为独立实体。
- 第四,您没有告诉编译器您正在破坏/分配
myval。它可能选择暂时将其移至别处,因为您正在破坏“r2”,并且在返回时,决定从...恢复它(???)。
只是为了好玩,这里是第一个函数的输出,针对以下四种情况:
- 默认 -
gcc -c tst.c
- 优化 -
gcc -O8 -c tst.c
- 使用一些不寻常的选项 -
gcc -c -finstrument-functions tst.c
- 那加上优化 -
gcc -c -O8 -finstrument-functions tst.c
段.text的反汇编:
00000000 :
0: e52db004 推 {fp} ; (str fp, [sp, #-4]!)
4: e28db000 添加 fp, sp, #0 ; 0x0
8: e24dd00c sub sp, sp, #12 ; 0xc
c: e50b0008 str r0, [fp, #-8]
10:e51b2008 ldr r2,[fp,#-8]
14: e2811016 添加 r1, r1, #22 ; 0x16
18: e0910000 添加 r0, r1, r0
1c: 0242207b subeq r2, r2, #123 ; 0x7b
20: 124220d5 subne r2, r2, #213 ; 0xd5
24: e1a03002 移动 r3, r2
28: e1a00003 移动 r0, r3
2c: e28bd000 添加 sp, fp, #0 ; 0x0
30: e8bd0800 弹出 {fp}
34:e12fff1e bx lr
部分.text的反汇编:
00000000 :
0: e1a03000 移动 r3, r0
4: e2811016 添加 r1, r1, #22 ; 0x16
8: e0910000 添加 r0, r1, r0
c: 0242207b subeq r2, r2, #123 ; 0x7b
10: 124220d5 subne r2, r2, #213 ; 0xd5
14: e1a00003 移动 r0, r3
18: e12fff1e bx lr
部分.text的反汇编:
00000000 :
0: e92d4830 推 {r4, r5, fp, lr}
4: e28db00c 添加 fp, sp, #12 ; 0xc
8: e24dd008 sub sp, sp, #8 ; 0x8
c: e1a0500e mov r5, lr
10: e50b0010 str r0, [fp, #-16]
14:e59f0038 ldr r0,[pc,#56]; 54
18: e1a01005 移动 r1, r5
1c:ebffffffe bl 0
20:e51b2010 ldr r2,[fp,#-16]
24: e2811016 添加 r1, r1, #22 ; 0x16
28: e0910000 添加 r0, r1, r0
2c: 0242207b subeq r2, r2, #123 ; 0x7b
30: 124220d5 subne r2, r2, #213 ; 0xd5
34: e1a04002 移动 r4, r2
38:e59f0014 ldr r0,[pc,#20]; 54
3c: e1a01005 移动 r1, r5
40:ebffffffe bl 0
44: e1a03004 移动 r3, r4
48: e1a00003 移动 r0, r3
4c: e24bd00c sub sp, fp, #12 ; 0xc
50: e8bd8830 pop {r4, r5, fp, pc}
54: 00000000 .word 0x00000000
部分.text的反汇编:
00000000 :
0: e92d4070 推 {r4, r5, r6, lr}
4: e1a0100e mov r1, lr
8: e1a05000 移动 r5, r0
c: e59f0028 ldr r0, [pc, #40] ; 3c
10: e1a0400e mov r4, lr
14:ebffffffe bl 0
18: e2811016 添加 r1, r1, #22 ; 0x16
1c: e0910000 添加 r0, r1, r0
20: 0242207b subeq r2, r2, #123 ; 0x7b
24: 124220d5 subne r2, r2, #213 ; 0xd5
28:e59f000c ldr r0,[pc,#12]; 3c
2c: e1a01004 移动 r1, r4
30:ebffffffe bl 0
34: e1a00005 移动 r0, r5
38: e8bd8070 弹出 {r4, r5, r6, 电脑}
3c: 00000000 .word 0x00000000
如您所见,这些都没有达到您希望看到的效果;不过,gcc -c -O8 ... 上的代码的第二个版本最终是:
段.text的反汇编:
00000000 :
0: e1a03000 移动 r3, r0
4: e2833016 添加 r3, r3, #22 ; 0x16
8: e0933000 添加 r3, r3, r0
c: 0240007b subeq r0, r0, #123 ; 0x7b
10: 124000d5 subne r0, r0, #213 ; 0xd5
14: e12fff1e bx lr
这就是您在程序集中指定的内容以及您所期望的内容。
士气:明确和准确地处理您的约束、操作数分配,并在相同 asm() 块中保持相互依赖的汇编行(编写多行语句)。