# 2022q1 Homework3 (quiz3)
contributed by < [`leewei05`](https://github.com/leewei05) >
###### tags: `linux2022`
## [測驗 1](https://hackmd.io/@sysprog/linux2022-quiz3#%E6%B8%AC%E9%A9%97-1)
:::info
- [x] 解釋上述程式碼運作原理
- [ ] 比較 Linux 核心 GENMASK 巨集的實作,闡述其額外的考量
- [ ] 舉出 Linux 核心原始程式碼中二處 GENMASK 巨集和 include/linux/bitfield.h 的應用案例
:::
- GENMASK(6, 4) 產生 $01110000_2$ (2 進制)
- GENMASK(39, 21) 產生 0x000000ffffe00000 (64 位元)
已知我們使用的微處理器架構為 64 位元,且 unsigned long 為 8 位元組寬度 (符合 LP64 資料模型),以下是可能的 GENMASK 巨集的定義:
```c
#define GENMASK(h, l) \
(((~0UL) >> (LEFT)) & ((~0UL) >> (l) << (RIGHT)))
```
首先 GENMASK 代入(6, 4) 先右移 57 再左移 4
```c
result = 0x000000000000007f(01110000)
(~0UL) >> (4) = 00001111
(~0UL) >> (LEFT) = 11111111 or 01111111
LEFT = 56 or 57
(~0UL) >> (4) << (RIGHT) = 01111000 or 11110000
RIGHT = 3 or 4
// Cannot be LEFT = 56, RIGHT = 4
```
接著 GENMASK 代入(39, 21) 先右移 24 再左移 21
```c
result = 0x000000ffffe00000(24*0 19*1 21*0)
(~0UL) >> (21) = 0x000007ffffffffff (21*0 43*1)
(~0UL) >> (LEFT) = 0x000000ffffffffff (24*0 40*1)
LEFT = 24
(~0UL) >> (21) << (RIGHT) = 0xffffffffffe00000(43*1 21*0)
RIGHT = 21
```
`LEFT = 63 - h`
`RIGHT = l`
以下取自 [Linux Kernl 程式碼](https://elixir.bootlin.com/linux/latest/source/include/linux/bits.h#L37)
```c
/*
* Create a contiguous bitmask starting at bit position @l and ending at
* position @h. For example
* GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
*/
#if !defined(__ASSEMBLY__)
#include <linux/build_bug.h>
#define GENMASK_INPUT_CHECK(h, l) \
(BUILD_BUG_ON_ZERO(__builtin_choose_expr( \
__is_constexpr((l) > (h)), (l) > (h), 0)))
#else
/*
* BUILD_BUG_ON_ZERO is not available in h files included from asm files,
* disable the input check if that is the case.
*/
#define GENMASK_INPUT_CHECK(h, l) 0
#endif
#define __GENMASK(h, l) \
(((~UL(0)) - (UL(1) << (l)) + 1) & \
(~UL(0) >> (BITS_PER_LONG - 1 - (h))))
#define GENMASK(h, l) \
(GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
#define __GENMASK_ULL(h, l) \
(((~ULL(0)) - (ULL(1) << (l)) + 1) & \
(~ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h))))
#define GENMASK_ULL(h, l) \
(GENMASK_INPUT_CHECK(h, l) + __GENMASK_ULL(h, l))
```
## [測驗 2](https://hackmd.io/@sysprog/linux2022-quiz3#%E6%B8%AC%E9%A9%97-2)
:::info
- [ ] 解釋上述程式碼運作原理
- [ ] 在 Linux 核心原始程式碼找出類似技巧的實作 (例如檔案系統) 並解說
:::
考慮以下程式碼:
```c
struct foo;
struct fd {
struct foo *foo;
unsigned int flags;
};
enum {
FOO_DEFAULT = 0,
FOO_ACTION,
FOO_UNLOCK,
} FOO_FLAGS;
static inline struct fd to_fd(unsigned long v)
{
return (struct fd){EXP1, v & 3};
}
```
## [測驗 3](https://hackmd.io/@sysprog/linux2022-quiz3#%E6%B8%AC%E9%A9%97-3)
:::info
- [x] 解釋上述程式碼運作原理
- [ ] 在 Linux 核心原始程式碼找出類似技巧的實作並解說其應用場景
:::
考慮以下程式碼,能將輸入的 8 位元無號整數逐一位元反轉,如同 LeetCode 190. Reverse Bits 的描述。
前後四位元進行互換 `x = (x >> 4) | (x << 4);`。`0xCC` 的二進制為 `11001100`,`0xAA` 的二進制為 `10101010`。推斷 `EXP2 = (x & 0x33) << 2)`, `EXP3 = (x & 0x55) << 1)` 因為依序是左右互換 4, 2, 1 的位元。
```c
#include <stdint.h>
uint8_t rev8(uint8_t x)
{
x = (x >> 4) | (x << 4);
x = ((x & 0xCC) >> 2) | (EXP2);
x = ((x & 0xAA) >> 1) | (EXP3);
return x;
}
```
## [測驗 7](https://hackmd.io/@sysprog/linux2022-quiz3#%E6%B8%AC%E9%A9%97-7)
:::info
- [ ] 解釋上述程式碼運作原理
- [ ] 在 Linux 核心原始程式碼找出類似實作並解說其應用場景
- [ ] 研讀論文《Using de Bruijn Sequences to Index a 1 in a Computer Word》,探討缺乏硬體 ctz/clz 指令的微處理器上,如何實作 branchless 的 ilog
- [ ] 運用〈你所不知道的 C 語言:前置處理器應用篇〉和〈你所不知道的 C 語言:數值系統篇〉提及的技巧,實作編譯時期 (compile-time) 的 ilog2 實作
:::
考慮 ilog32 函式可針對給定的 32 位元無號數,計算扣除開頭的 0,最少需要多少位元才可儲存 (the minimum number of bits required to store an unsigned 32-bit value without any leading zero bits),以下是一個可能的實作:
```c
int ilog32(uint32_t v)
{
int ret = v > 0;
int m = (v > 0xFFFFU) << 4;
v >>= m;
ret |= m;
m = (v > 0xFFU) << 3;
v >>= m;
ret |= m;
m = (v > 0xFU) << 2;
v >>= m;
ret |= m;
m = EXP10;
v >>= m;
ret |= m;
EXP11;
return ret;
}
```
> 6.5.8 Relational operators
> Each of the operators < (less than), > (greater than), <= (less than or equal to), and >=
(greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.92)
The result has type int.
如果 `v > 0` 則 `ret` 為 1; `v <= 0` 則 `ret` 為 0。
`0xFFFFU` 全為 1 的 16 位元數