linux內核中ffs(x)宏是平臺相關的宏,在arm平臺,該宏定義在linux
arch/arm/include/asm/bitops.h算法
#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); })
static inline int fls(int x) { int ret; if (__builtin_constant_p(x)) return constant_fls(x); asm("clz\t%0, %1" : "=r" (ret) : "r" (x) : "cc"); ret = 32 - ret; return ret; }
__t & -__t 等於找到__t 第一個爲1的位(從低位開始),並把該位保留爲1其他位清0. 函數
例如 一32位整形數 6,用二進制表示它的低8位:00000110, 都知道負數爲最高爲1其他位取反加1.-6即 11111010ui
相與得 00000010,即6&-6. 把該值傳遞給函數fls().spa
再看fls函數.code
if (__builtin_constant_p(x))
return constant_fls(x);
blog
__builtin_constant_p 是Gcc的內建函數 ,用於判斷一個值是否爲編譯時常數,若是參數的值是常數,函數返回 1,不然返回 0。it
若是是常數就用下面函數計算00000010中1的位置asm
static inline int constant_fls(int x) { int r = 32; if (!x) return 0; if (!(x & 0xffff0000u)) { x <<= 16; r -= 16; } if (!(x & 0xff000000u)) { x <<= 8; r -= 8; } if (!(x & 0xf0000000u)) { x <<= 4; r -= 4; } if (!(x & 0xc0000000u)) { x <<= 2; r -= 2; } if (!(x & 0x80000000u)) { x <<= 1; r -= 1; } return r; }
算法就是折半法,這個函數計算的是從高位起第一個1位的位置.00000010, r=2. 因爲__t&-__t只有一個1,因此就是找到該1的位置.編譯
若是輸入參數是00001010 那麼 r=4.
若是參數是變量,直接使用arm指令運算.
執行 asm("clz\t%0, %1" : "=r" (ret) : "r" (x) : "cc");
ret = 32 - ret;
CLZ(Count Leading Zeros)指令對Rm中值的高位(leading zeros)個數進行計數,結果放到Rd中。若源寄存器全爲0,則結果爲32。若[31]爲1,則結果爲0。
clz指令計算高位0的個數, 而後拿32-ret 算出1的位置.
因此,ffs(x)這個宏的值就是x的第一個1的位置(從低位開始,數值從1開始,0表明x全0).
另外,該文件中還有不少linux關於位操做的函數,能夠做爲本身寫應用程序時的有用參考.