System Learning Daily 7
GCC 内联汇编的使用方法的自用记录。
GCC Extended Asm
asm-qualifiers
有三个可选:
volatile,inline,goto.
volatile
- 代码有副作用(side effect)的时候使用。
- 如果汇编语句中没有Output内容,则默认 volatile。
- 如果没有volatile且汇编语句的Ouput内容没有被该语句下面的代码使用,则该汇编语句有可能被编译器优化掉。
inline
简单来说就是使得生成的汇编代码体积最小。
goto
没用过,似乎也没有用的理由,跳过。
OutputOperand
给例子,直接对比。
推荐借助Compiler Explorer来学习以下内容。
example 1, “inc”
1 |
|
gcc 生成的无优化代码:
1 | inc1: |
- inc1() 是正确写法。
- inc2() 没有提前将
a写入eax。 - inc3() 没有在
eax自增之后回写到a。 - inc4() 和 inc1() 有着相同的机器码,也是正确写法。不知道是不是完全相同的写法。
example 2, “bts”
1 | void bts1() { |
gcc 生成的无优化代码:
1 | bts1: |
- bts1() 是
bts a b的正确写法。 - bts2() 的OutputOperands前缀是
=,则输入a与输出b都采用了同一个寄存器eax。 - bts3() 在InputOperands中使用Operands标号可以将一个InputOperand和一个OutputOperand“连接(tie)”起来,使用同一个寄存器或者同一块内存地址。
- bts4() 是
bts a a的正确写法,可以吧InputOperands直接全部去掉。 - bts5() 一个”连接(tie)“的例子,使用
eax,输入b把结果放入a中。
example 3, multiple assembler instructions - 1
1 | // multiple assembler instruction |
gcc生成的无优化代码:
1 | mai: |
这个例子中,最下面的ebx告诉编译器,我们需要使用这个寄存器,所以编译器将该寄存器直接压栈保存,在leave中弹栈返回。
example 4, multiple assembler instructions - 2
an example about earlyclobber
1 | int baz1() { |
1 | ; gcc -m32 -O2 ...` |
对于 baz1()来说,编译器认为对 1的所有读都在写a之前,那么1和a自然可以使用同一个寄存器eax来存储数据,但这是不正确的,因为并不是读都在写之前。
对于 baz2()来说,我们使用了&来告知编译器,所以编译器不会把一个寄存器既当做输入,又当做输出了。