Last Updated: 2023-10-02 14:19:01 Monday
-- TOC --
通过汇编代码,看看volatile变量的真实面目,测试用C代码如下:
#include <stdio.h>
int f(){
volatile int a = 1;
int b = a;
int c = a;
return a;
}
int main(){
f();
return 0;
}
-O0
不开优化,汇编如下:
f():
push rbp #
mov rbp, rsp
mov DWORD PTR [rbp-12], 1
mov eax, DWORD PTR [rbp-12]
mov DWORD PTR [rbp-4], eax
mov eax, DWORD PTR [rbp-12]
mov DWORD PTR [rbp-8], eax
mov eax, DWORD PTR [rbp-12]
pop rbp
ret
main:
push rbp
mov rbp, rsp
call f()
mov eax, 0
pop rbp
ret
标准的prologue和epilogue,用寄存器中转变量值。如果将C代码中的volatile去掉,汇编不变。
-O1
f():
mov DWORD PTR [rsp-4], 1
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
ret
main:
call f() # 有个push操作
mov eax, 0
ret
因为a被申明为volatile,因此,每次对a的访问,都是访问内存。如果去掉volatile,汇编会优化得比C代码还简单:
f():
mov eax, 1
ret
main:
mov eax, 0
ret
-O3
在-O3下,默认inline了:
f():
mov DWORD PTR [rsp-4], 1
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
ret
main:
mov DWORD PTR [rsp-4], 1
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
mov eax, DWORD PTR [rsp-4]
xor eax, eax
ret
去掉volatile后:
f():
mov eax, 1
ret
main:
xor eax, eax # rax=0
ret
volatile到底是啥?
就是强制通过内存来得到变量的值!
在嵌入式开发中,有些内存区域会被代码以外的其它因素影响,因此读取这些变量,一定要访问内存。
用xor置零
并不是因为xor比mov快,而是xor指令更短,可以节省字节数。
不过,xor会改变flags,mov不会!
xor reg, reg
是所有CPU的最佳选择,没有其他方法比它有任何优势,它至少比任何其他方法都有一些优势。它是由英特尔和AMD正式推荐的。
在64位模式下,仍然使用xor r32, r32
,因为写32位寄存器会将高32位也置零。xor r64,r64会浪费一个字节,因为它需要一个REX前缀。
x64规定操作64位寄存器的指令,必须要带上1byte的REX前缀,作为指令标识。
还有些其它好处...比如对某些CPU,节省功耗。
本文链接:https://cs.pynote.net/hd/asm/202302092/
-- EOF --
-- MORE --