contributed by < laneser >
已知我們使用的微處理器架構為 64 位元,且 unsigned long 為 8 位元組寬度 (符合 LP64 資料模型),> 以下是可能的 GENMASK 巨集的定義:
先拆解題目的結果, 會是兩個值做 bitwise AND:
((~0UL) >> (LEFT))
: 這個值是 ~0
(all 1) 往低位移 bits, 所以一定是高位 0, 低位 1 的形式.((~0UL) >> (l) << (RIGHT))
: 先把 ~0 往低位移, 再往高位移, 所以會是高位跟低位為 0, 中間為 1 的形式.所以這樣就可以得知, 如果我們需要 h
表示高位的 1 起點, l
表示低位的 1 終點, 那麼
((~0UL) >> (LEFT))
: 這個值是 ~0 (all 1) 往低位移 bits, 所以一定是高位 0, 直到 h
低位 1 的形式.((~0UL) >> (l) << (RIGHT))
: 先把 ~0 往低位移, 再往高位移, 所以會是高位跟低位為 0, 中間 為 1 的形式. 而高位的 h
以前的 0 交給前者負責, 所以這裡的值只要確保低位的 l
右邊都是 0 就可以.那麼, LEFT
就是 63-h
, RIGHT
是 l
.
想到 ((~0UL) >> (l) << (RIGHT))
如果目的只是高位為 1, 低位為 0 的格式會更合理, 所以 ((~0UL) << (l))
難道不好嗎 ?
比較 Linux 核心 GENMASK 巨集的實作,闡述其額外的考量
Linux 當中的 GENMASK 在 bits.h:
GENMASK_INPUT_CHECK
如文字所述就是檢查輸入的參數是否合格.
所以本體是 __GENMASK(h, l)
, 而也是兩個部分, 只是把往高位移的部分放在前面:
~UL(0) << (l)
, 而是 ~0UL - (1UL << l)
, 這樣會做出一個從低位元算過來第 l
位是 0 , 其他都是 1 的 mask, 接著再 + 1
就會讓第 l
位置 0 右邊的 1
都變 0
, 然後讓原本 l
位置的 0
進位變成 1
.((~0UL) >> (63 - h))
格式一樣舉出 Linux 核心原始程式碼中二處 GENMASK 巨集和 include/linux/bitfield.h 的應用案例