本文收集Linux核心中用到的gcc的C擴充。轉載請註明出處http://blog.csdn.net/enjoysilence/article/details/8929729。
1. 運算式中的語句和聲明(Statements and Declarations in Expressions)
用一對大括弧{}把多條語句括起來,就構成了一條複合陳述式,再用一對圓排號()將這條複合陳述式括起就構成了一條複合陳述式運算式,這個運算式的值就是複合陳述式中最後一條子運算式的值,這與Shell指令碼有些類似,例如下面這條語句將foo()的絕對值賦值給整型變數a:
int a = ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; })
這個特性可以使宏定義更加安全,在標準C中,通常這樣來定義一個求最大值的宏:
#define max(x,y) ((x) > (y) ? (x):(y))
這裡的x和y被求值了兩次,對有副作用(如i++)的運算元會產生錯誤的結果。但如果使用Gcc的複合運算式,就可以使宏定義變得安全:
#define max(x, y) ({ \ typeof(x) _max1 = (x); \ typeof(y) _max2 = (y); \ (void) (&_max1 == &_max2); \ _max1 > _max2 ? _max1 : _max2; })
這是一個泛型宏,其中的typeof就像typedef的逆運算一樣,取出運算元的類型,詳見下面關於typeof的說明。
(void) (&_max1 == &_max2);
語句用來確認x和y的類型是否相同 ,若不相同Gcc會給出錯誤提示,詳見另一篇Blog《關於(void)(&_min1 == &min2)》。
2. typeof
typeof的含義如同typedef的逆運算,即取出其運算元的類型。它的文法卻如同sizeof,其運算元可以是運算式也可以是類型,如下例所示:
typeof (a[0](1)) /*運算元是運算式*/typeof (char *) /*運算元是類型*/
typeof關鍵字常用來構造泛型宏,例如上方的max宏定義。
下面是更多的例子:
typeof (*x) y; /*聲明一個類型為*x的變數y*/typeof (*x) z[4]; /*聲明一個長為4的數組z,數組元素為*x類型*/ typeof (typeof (char *)[4]) a; /*等價於char *a[4]*/
3. 分支預測提示
學過彙編的同學都知道,條件陳述式由jmp族指令來實現。為了防止程式執行時過多的跳轉影響效能,通常將最可能的情況安排在緊鄰jmp指令之後。Gcc有個內建函數__builtin_expect用來指示編譯器可能的分支,它的聲明如下
long __builtin_expect (long exp, long c)
這個函數的語義是期望exp==c,傳回值是exp的值。如
if (__builtin_expect (x, 0)) foo ();
這條語句提示編譯器我們並不想調用foo(),因為我們期望x==0,編譯器可以據此來最佳化代碼。
Linux核心定義了兩個宏來使用這一特性(見$KERNEL_SRC$/linux/include/linux/compiler.h):
#define likely(x)__builtin_expect(!!(x), 1)#define unlikely(x)__builtin_expect(!!(x), 0)
這兩個宏可以望文生義,下面是個例子:
x = 5;if(likely(x, 1)) { /*提示編譯器執行x++分支的可能性很大*/ x++;}4. inline
將函式宣告為inline是建議Gcc在處理對該函數的調用時產生更加快速的代碼,Gcc可以將該函數的代碼在其調用者內展開,從而避免了函數調用的開銷。需要注意的是,inline只是建議而已,不能強制Gcc進行展開處理。inline通常與static關鍵字一起使用,其含義就是static與inline的組合,即(1)局部於編譯單元,即函數只能在其定義所在的.c源檔案中使用;(2)inline屬性,即建議Gcc在調用處展開。通常在需要引用函數地址和不能展開(如遞迴函式)的場合,Gcc不做展開處理,而像普通函數一樣為inline函數產生獨立的彙編代碼。
這裡有一篇關於Gcc中inline關鍵字的詳細介紹:http://xushouze2006.blog.163.com/blog/static/16230032200831710196672/。
6. __builtin_prefetch
這個Gcc內建函數的聲明如下
void __builtin_prefetch (const void *addr, ...)
其作用是在訪問資料前資料移到Cache中,以避免Cache未命中產生的延遲。可以在資料即將被訪問前插入這個函數調用,若目標平台支援,Gcc就可以產生資料預取指令。
addr參數指明預取資料的記憶體位址。另外還有兩個選擇性參數rw和locality。rw值為編譯時間常量0或1,0表明為寫操作預取;1為預設值,表明為讀操作預取。locality取值為0-3,0意味著資料沒有時間局部性,可以在訪問後即從Cache中移除,3表明資料的時間局部性很強,在訪問後應該儲存在所有的Cache當中,1和2則分別表明較低和中等的時間局部性,預設值是3。
未完待續……
參考資料:
1.Gcc官方關於C擴充的介紹http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/C-Extensions.html#C-Extensions
2.Linux 核心中的 GCC 特性http://www.ibm.com/developerworks/cn/linux/l-gcc-hacks/
3.Gcc官方關於內建擴充函數的介紹http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
4.Gcc中inline關鍵字http://xushouze2006.blog.163.com/blog/static/16230032200831710196672/