在閱讀本文之前,請先閱讀gcc的相關文檔,確保對如何在c中使用彙編語言有個基本的認識。
文檔地址爲:
https://gcc.gnu.org/onlinedoc...
- basic asm 以及沒有 output operands 的 extended asm 默認就是volatile 的,所以它們不用顯式指定 volatile。
- volatile 的最終目的是爲了防止gcc的某些錯誤優化,所以它只需要用在那些可能發生錯誤優化的地方,濫用 volatile 會導致本應該優化的代碼無法優化,最終導致性能損耗。
- gcc如果發現 asm 語句的 output operands 在c語言中沒有被使用,則優化後的代碼可能會直接移除該語句。
- gcc如果認爲一個c函數中的多條相同的asm語句的 output operands 結果相同,則可能會只保留其中一條asm語句,在該c函數使用到這條 asm語句 output operands 的地方,統一用相同的結果(比如,如果asm語句在循環中,則會提到循環外,如果asm語句在一個c函數中被順序執行,則只保留第一條asm語句,刪除後面的asm語句)。
- 上面兩個優化場景就是gcc可能發生錯誤優化的地方,這些地方要注意是否要使用 volatile。
- volatile 無法保證多條asm語句在優化前後順序相同,如果要保證順序,可以把多條asm語句中的彙編代碼都寫到一個asm語句裏。
下面用示例講解下相關概念:
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
void do_check(uint32_t dwSomeValue) {
uint32_t dwRes;
asm("bsfl %1,%0" : "=r"(dwRes) : "r"(dwSomeValue) : "cc");
assert(dwRes == 3);
}
int main(int argc, char *argv[]) { do_check(8); }
編譯後再以彙編形式查看do_check方法:
$ gcc -O3 main.c && objdump --disassemble=do_check a.out
0000000000001190 <do_check>:
1190: 0f bc ff bsf %edi,%edi
1193: 83 ff 03 cmp $0x3,%edi
1196: 75 01 jne 1199 <do_check+0x9>
1198: c3 retq
1199: 50 push %rax
119a: e8 c1 ff ff ff callq 1160 <do_check.part.0>
由於assert宏中使用了asm語句中的輸出參數dwRes,所以即使asm語句沒有指定volatile,do_check方法也是在正常執行的。
下面看下把assert方法去掉之後的do_check彙編代碼:
$ gcc -O3 -D NDEBUG main.c && objdump --disassemble=do_check a.out
0000000000001130 <do_check>:
1130: c3 retq
由上可見,因爲我們在執行gcc時加了-D NDEBUG參數,定義了NDEBUG宏,所以上面的assert宏最終會變爲空指令。
也就是說,do_check方法中沒有任何地方在使用asm語句中的輸出參數dwRes,所以gcc就會在優化後的代碼中刪除掉該asm語句,所以上面的do_check方法最終變成了空方法。
那我們將上面的asm語句加上volatile再試下:
void do_check(uint32_t dwSomeValue) {
uint32_t dwRes;
asm volatile("bsfl %1,%0" : "=r"(dwRes) : "r"(dwSomeValue) : "cc");
assert(dwRes == 3);
}
編譯後再以彙編形式查看do_check方法:
$ gcc -O3 -D NDEBUG main.c && objdump --disassemble=do_check a.out
0000000000001130 <do_check>:
1130: 0f bc ff bsf %edi,%edi
1133: c3 retq
由上可以看到,這次即使指定了-D NDEBUG參數,使assert宏變成了空操作,導致do_check方法中沒有任何地方使用dwRes變量,但由於volatile的存在,該asm語句還是沒有被優化掉。
通過上面的例子,我們就可以看到 volatile 是如何防止 gcc 優化代碼的,但是在上面的例子中,該優化是一個正確的優化,所以不應該加 volatile。
如果有其他的asm語句,雖然它的輸出參數沒有被使用,但也不應該被優化掉,這個時候就應該使用 volatile 了。
希望對你有所幫助。
完。
更多原創文章,請關注我微信公衆號: