osdev-jpでは、OS開発に有用な情報を収集し公開しています

View My GitHub Profile

OpeLa コンパイラの内部設計

整数のキャスト

整数のキャストとは uint8int64 などの整数型の間の型変換のことです。

ビット幅が小さくなる方向のキャストは、原理的には簡単です。単にビットを切り詰めるだけですから。

ビット幅が大きくなる方向のキャストは、キャスト前の型が符号付き整数型の場合に少々面倒です。 符号ビットを拡張するビットすべてにコピーする必要があるからです。

ビット幅が小さくなる方向のキャスト

N > M として、intNintM に切り詰める場合、上位 N-M ビットを 0 で埋めれば完了です。 この処理には典型的にはビット積(AND)を使います。 例えば RAX レジスタに 64 ビットの数値が入っていて、それを 32 ビット整数に切り詰めるなら、次のようなプログラムでキャストすれば良さそうです。

and rax, 0xffffffff

0xffffffff は 32 ビットの 1 が立っている数値です。これで RAX の下位 32 ビットだけが残り、上位 32 ビットは 0 クリアされます。

しかし、問題があります。上記のコードはアセンブルできません。 なぜなら AND 命令には即値に 64 ビット整数を取るモードは無く、32 ビット符号付き整数しか指定できないからです。 0xffffffff は 32 ビット符号付き整数では表せないため、エラーになってしまいます。

OpeLa 言語は任意のビット幅の整数型をサポートする予定ですから、当然 int58 のような型もあるわけです。 31 ビット以下の整数型にしかキャストできないのでは、不十分です。

ではどうするかというと、ビットシフトをすれば解決です。 左に 64-M ビットシフトし、再度右に同じだけビットシフトすれば、上位 N-M ビットが 0 でクリアされた状態になりますね。 (この方法だと N ビットを超えた部分も 0 クリアされますが、何も問題ありません)

shl rax, 32
shr rax, 32

OpeLa コンパイラ Ver.2 はこのやり方でビット幅が小さくなる方向のキャストを行っています。 https://github.com/uchan-nos/opela/blob/3027c9d951213641434b123a5852af4dd6adf51b/compiler/v2/main.cpp#L146-L148

ビット幅が大きくなる方向のキャスト

ビット幅が大きくなる場合(N < M)、キャスト元の型が符号無し整数であれば話は簡単です。 ビット幅が小さくなる場合と同じ手法を使って、上位 M-N ビットを 0 クリアすれば完了です。 いわゆる「ゼロ拡張」という処理です。

キャスト元が符号付きの場合に話が複雑になります。 キャスト元が符号付き整数で、そのビット幅を広げる場合、符号ビットを拡張するビットすべてにコピーしなければなりません。 これを「符号拡張」と言います。

愚直に実装しようと思うと、まず符号ビットを取り出し、それを 1 ビットずつコピーする、という処理を思いつきます。 ただ、この方法は効率が悪すぎます。 8 ビット整数を 64 ビットに拡張するために 56 回ものビットコピーが発生します。

改良版として、符号ビットが 1 だった場合に M-N ビットが 1 である数値とのビット和(OR)をとる方法が考えられます。 符号ビットが 1 の(つまり、負の数)8 ビット整数を 64 ビットに拡張するコードは次のようになりそうです。

or rax, 0xffffffffffffff00

これも、AND 命令と同様の制限により、アセンブルできません。

ではどうするかというと「算術シフト」という命令を使います。 算術右シフト(SAR)は、まさに符号ビットをコピーしながら右シフトをしてくれるというもので、今回の用途で使えます。

shl rax, 56
sar rax, 56

まず左に 64-N ビットシフトします。これで、キャスト元の数値が RAX に左詰で入った状態になります。

次に右に 64-N ビットシフトします。 このときに算術右シフト命令を使うことで、RAX の最上位ビット(キャスト元の数値の符号ビットでもある)がコピーされながら右シフトされます。 (この方法では M ビットを超えた部分も 1 で埋まりますが、何も問題はありません。)

左シフトと算術右シフトの動作の様子は下図のようになります。

左シフトと算術右シフトを用いた符号付き整数型のキャスト

OpeLa コンパイラでの実装はこの方法でビット幅が大きくなる方向のキャストを行います。 https://github.com/uchan-nos/opela/blob/3027c9d951213641434b123a5852af4dd6adf51b/compiler/v2/main.cpp#L152