linux內核中的宏ffs(x)

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關於位操做的函數,能夠做爲本身寫應用程序時的有用參考.

相關文章
相關標籤/搜索