滅入るんるん

何か書きます

【C#】CILの算術演算命令

C#をコンパイルするとCILになりますが、CILの情報は結構少ないので、メモがてらに書いておきます。

基本

たとえば1 + 10とするC#コードがあったとして、これをCILにすると以下のようなものに論理的にはなります。

ldc.i4.1
ldc.i4.s 10
add

逆ポーランド記法とかを知っている人ならどういう変換をされたかわかるかと思います。
C#での+の数値の加算はCILではaddになりますが、このaddはスタックにある2つの値を加算するものになります。
日本語で言うと「1 足す 10」は「1と10を読み込んで、読み込んだ2つの値を足す」になります。
どうしてこのような変換をするかとかは逆ポーランド記法を調べたら利点がわかるかと思いますので割愛。

ちなみに、先ほど「以下のようなものに論理的にはなります」と言いましたが、実際のところは1 + 10ぐらいはコンパイラーが最適化してldc.i4.s 11になってしまうことがあります。

命令一覧

スタック操作

命令 動作
nop 何もしない
dup スタックのトップの値を複製
pop スタックのトップの値を破棄

定数読み込み

命令 動作
ldc.i4 {value} int32な{value}を読み込む
ldc.i4.s {value} int8な{value}を読み込む
ldc.i4.m1 -1を読み込む
ldc.i4.0 0を読み込む
ldc.i4.1 1を読み込む
ldc.i4.2 2を読み込む
ldc.i4.3 3を読み込む
ldc.i4.4 4を読み込む
ldc.i4.5 5を読み込む
ldc.i4.6 6を読み込む
ldc.i4.7 7を読み込む
ldc.i4.8 8を読み込む
ldc.i8 {value} int64な{value}を読み込む
ldc.r4 {value} float32な{value}を読み込む
ldc.r8 {value} float64な{value}を読み込む

ldc.i4.m1~ldc.i4.8は短縮命令です。

算術演算

命令 動作
add 加算(+)
sub 減算(-)
mul 乗算(*)
div 除算(/)
div.un 符号無し整数の除算のときに使われる
rem 乗除(%)
rem.un 符号無し整数の乗除のときに使われる
neg 符号反転(-)

ビット演算

命令 動作
and 論理積(&)
or 論理和(|)
xor 排他的論理和(^)
not ビット反転(~)
shl 左シフト
shr 右シフト
shr.un 符号無し右シフト

変換

命令 動作
conv.i1 int8へ変換
conv.u1 uint8へ変換
conv.i2 int16へ変換
conv.u2 uint16へ変換
conv.i4 int32へ変換
conv.u4 uint32へ変換
conv.i8 int64へ変換
conv.u8 uint64へ変換
conv.i native intへ変換
conv.u native uintへ変換
conv.r4 float32へ変換
conv.r8 float64へ変換
conv.r.un uintをnative floatへ変換

まとめ

数が多すぎて一覧にはポインターやオーバーフローチェックのことは載せきれませんでしたが、単純な算術演算は表を見れば理解できるようになるかと思います。

実際のC#コードでは、これの応用で引数やローカル変数などからの読み込みとかもあるので、もっとややこしくなりますが……