avr-gccにおけるbool型の扱い

#いきなり結論

　AVR 向けのコンパイラ avr-gcc において，bool 型は uint8_t 型 (unsigned char 型) として扱われているようです。

# 知りたかったこと

　AVR のためのライブラリを作っている中で，uint8_t 型変数の任意のビットを置換する，以下のような関数を書きました。　置換対象の変数 src の offset で指定されたビットが bit で指定した状態に置換されて return されます。

```c
uint8_t ReplaceSingleBit(uint8_t src, int8_t offset, bool bit) {
  assert((0 <= offset) && (offset <= 7));
  return (src & ~(1 << offset)) | (bit << offset);
}
```

　さてこの関数，とりわけ bool 型の変数 bit はどのようにコンパイルされるでしょうか？ もしかして，int8_t とか uint8_t などの整数型を使ったほうが効率よいコードが吐き出されたりする，なーんてことはあるでしょうか？

　そんな疑問が湧いてしまったので，実際どうなるのか調べてみました。

# 実験
## 実験に使ったソースコード

　以下のようなソースコードを用意しました。

```c:test.c
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>

uint8_t TestA(uint8_t src, int8_t offset, bool bit) {
  return (src & ~(1 << offset)) | (bit << offset);
}

uint8_t TestB(uint8_t src, int8_t offset, uint8_t bit) {
  return (src & ~(1 << offset)) | (bit << offset);
}

uint8_t TestC(uint8_t src, int8_t offset, int8_t bit) {
  return (src & ~(1 << offset)) | (bit << offset);
}

uint8_t TestD(uint8_t src, int8_t offset, uint16_t bit) {
  return (src & ~(1 << offset)) | (bit << offset);
}

uint8_t TestE(uint8_t src, int8_t offset, int16_t bit) {
  return (src & ~(1 << offset)) | (bit << offset);
}
```

　TestA は，オリジナルの ReplaceSingleBit 関数と同様の構成です。 その他の関数は引数 bit の型をそれぞれ uint8_t，int8_t，uint16_t，int16_t に変えたものです。 いずれも assert は省略しました。

　で，このコードを，以下のように最適化付きでコンパイルしました。

```shell-session
avr-gcc -S -O2 test.c
```

## 得られたアセンブリコード

　結果出力されたのが，以下のアセンブリコードです。

```c
	.file	"test.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.global	TestA
	.type	TestA, @function
TestA:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r18,lo8(1)
	ldi r19,0
	mov r0,r22
	rjmp 2f
	1:
	lsl r18
	2:
	dec r0
	brpl 1b
	com r18
	and r18,r24
	mov r24,r20
	mov r0,r22
	rjmp 2f
	1:
	lsl r24
	2:
	dec r0
	brpl 1b
	or r24,r18
	ret
	.size	TestA, .-TestA
.global	TestB
	.type	TestB, @function
TestB:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r18,lo8(1)
	ldi r19,0
	mov r0,r22
	rjmp 2f
	1:
	lsl r18
	2:
	dec r0
	brpl 1b
	com r18
	and r18,r24
	mov r24,r20
	mov r0,r22
	rjmp 2f
	1:
	lsl r24
	2:
	dec r0
	brpl 1b
	or r24,r18
	ret
	.size	TestB, .-TestB
.global	TestC
	.type	TestC, @function
TestC:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r18,lo8(1)
	ldi r19,0
	mov r0,r22
	rjmp 2f
	1:
	lsl r18
	2:
	dec r0
	brpl 1b
	com r18
	and r18,r24
	mov r24,r20
	clr r25
	sbrc r24,7
	com r25
	mov r0,r22
	rjmp 2f
	1:
	lsl r24
	2:
	dec r0
	brpl 1b
	or r24,r18
	ret
	.size	TestC, .-TestC
.global	TestD
	.type	TestD, @function
TestD:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r18,lo8(1)
	ldi r19,0
	mov r0,r22
	rjmp 2f
	1:
	lsl r18
	2:
	dec r0
	brpl 1b
	com r18
	and r18,r24
	mov r0,r22
	rjmp 2f
	1:
	lsl r20
	2:
	dec r0
	brpl 1b
	mov r24,r18
	or r24,r20
	ret
	.size	TestD, .-TestD
.global	TestE
	.type	TestE, @function
TestE:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r18,lo8(1)
	ldi r19,0
	mov r0,r22
	rjmp 2f
	1:
	lsl r18
	2:
	dec r0
	brpl 1b
	com r18
	and r18,r24
	mov r0,r22
	rjmp 2f
	1:
	lsl r20
	2:
	dec r0
	brpl 1b
	mov r24,r18
	or r24,r20
	ret
	.size	TestE, .-TestE
	.ident	"GCC: (AVR_8_bit_GNU_Toolchain_3.4.5_1522) 4.8.1"

```

# アセンブリコードの比較

　それぞれのアセンブリコードを比べてみます。

|bool  |uint8_t|int8_t|uint16_t|int16_t|
|------|------|------|------|------|
|TestA:|TestB:|TestC:|TestD:|TestE:|
|.L__stack_usage = 0 |.L__stack_usage = 0 |.L__stack_usage = 0 |.L__stack_usage = 0 |.L__stack_usage = 0 |
|ldi r18,lo8(1)      |ldi r18,lo8(1)      |ldi r18,lo8(1)      |ldi r18,lo8(1)      |ldi r18,lo8(1)      |
|ldi r19,0           |ldi r19,0           |ldi r19,0           |ldi r19,0           |ldi r19,0           |
|mov r0,r22          |mov r0,r22          |mov r0,r22          |mov r0,r22          |mov r0,r22          |
|rjmp 2f             |rjmp 2f             |rjmp 2f             |rjmp 2f             |rjmp 2f             |
|1:                  |1:                  |1:                  |1:                  |1:                  |
|lsl r18             |lsl r18             |lsl r18             |lsl r18             |lsl r18             |
|2:                  |2:                  |2:                  |2:                  |2:                  |
|dec r0              |dec r0              |dec r0              |dec r0              |dec r0              |
|brpl 1b             |brpl 1b             |brpl 1b             |brpl 1b             |brpl 1b             |
|com r18             |com r18             |com r18             |com r18             |com r18             |
|and r18,r24         |and r18,r24         |and r18,r24         |and r18,r24         |and r18,r24         |
|mov r24,r20         |mov r24,r20         |mov r24,r20         |                    |                    |
|                    |                    |clr r25             |                    |                    |
|                    |                    |sbrc r24,7          |                    |                    |
|                    |                    |com r25             |                    |                    |
|mov r0,r22          |mov r0,r22          |mov r0,r22          |mov r0,r22          |mov r0,r22          |
|rjmp 2f             |rjmp 2f             |rjmp 2f             |rjmp 2f             |rjmp 2f             |
|1:                  |1:                  |1:                  |1:                  |1:                  |
|lsl r24             |lsl r24             |lsl r24             |lsl r20             |lsl r20             |
|2:                  |2:                  |2:                  |2:                  |2:                  |
|dec r0              |dec r0              |dec r0              |dec r0              |dec r0              |
|brpl 1b             |brpl 1b             |brpl 1b             |brpl 1b             |brpl 1b             |
|or r24,r18          |or r24,r18          |or r24,r18          |or r24,r18          |or r24,r18          |
|ret                 |ret                 |ret                 |ret                 |ret                 |
|.size TestA, .-TestA|.size TestB, .-TestB|.size TestC, .-TestC|.size TestD, .-TestD|.size TestE, .-TestE|

　結果からすると，引数の型を bool としたときに出力されるコードは，引数の型を uint8_t とまったく同じになりました。 というわけで，冒頭の結論に至ったわけです。

　ちょっと意外だったのが，int8_t 型の時の挙動。 引数の型を int8_t にした時だけ，r20 (引数 bit の値) を r24 にコピーした後でご丁寧に r25 の符号処理を行っています。 後の処理では使われないのに……？

　また，16 bit 整数型を使うと，bool 型や 8 bit 整数型を使った場合と違って，r20 (引数 bit の値) を r24 へコピーする処理がありません。 結果，関数内の命令数が一番少ないのは 16 bit 整数型を使った場合となりました。 しかしながら，この関数は bit の値が 0 か 1 でないと期待した動作をしてくれません。 ので，引数 bit の型を bool 以外にした場合，値が 0 または 1 であることのアサーションが必要になります。 こういう点まで考えると，引数 bit の型には bool を選択するのがよさそうです。

　とりあえずの興味は満たされたので，今のところはこれ以上深追いはしない予定です。