<< セキュリティキャンプ2024 2日目のCコンパイラゼミ | Cコンパイラゼミ2024 | セキュリティキャンプ2024 4日目のCコンパイラゼミ >>
R15を挟むのをやめる
セキュリティキャンプ2024 2日目のCコンパイラゼミでR15を挟む形で実装していたが、これR15を挟む意味がないので挟まないようにした。
今日の最初の作業
複合リテラル、そんなものがあったんだ
仕様読み
ドーナツがセグフォするで整数拡張と算術型変換まわりをちゃんと実装しないといけなくなったので、仕様書を読むところから始める。
英語を読むのが辛いので、代わりにJIS規格を読んでいる。C99の少し古い仕様だけれど、特に問題はないでしょ。
char型
Transclude of char(C言語)
算術型変換
Transclude of 算術型変換
ポインタの変換
未定義動作の嵐
- 任意のポインタ型は整数型に型変換できる。結果は処理系定義とする。結果が整数型で表現できなければその動作は未定義とする
- 関数ポインタを変換するとき、異なる型の関数ポインタを無理やり動かした場合の動作は未定義とする
整数拡張
Transclude of 整数拡張
整数拡張と算術型変換の動作
char + char
を計算する場合
- 計算をする時点で整数拡張が行われ、charは勝手にintになる
- よって
int + int
をする
このようなコードの場合、結果は-4ではなく36になる
int * long
をする場合
- intとlongは異なる型であるため算術型変換が行われ、intがlongへと変換される
- よって
long * long
をする
余談
intはアーキテクチャにとって自然な長さだから、それより短い型は自動的にintへ変換される。自然な型というのは計算しやすい型でもあるので、charやshortではなくintで計算する。
単項演算子でも昇格は発生する。+ c1
とか! c1
とか
-200 / 2
が21億になってしまう
もう一人の受講生「-200が42億だと思われてそう。32ビットで計算するべき値を64ビットで計算していそう」
cqoについて
- 割り算をする直前にcqoという命令を呼んでいる
- cqoは、「RAX を符号拡張して RDX:RAX に設定」する命令
cdqe命令を足すと上手く動いた。これだと64ビット除算が動かないけれど、とりあえずこれで通すことにした
int型からvoid型への変換のエラー
今、空のreturnは42を入れる形にしていた。とりあえずint型からvoid型へは無条件に変換できるようにしてお茶を濁すことにした。
整数拡張のテストが通らない
これの2つ目のテストが通らないというものだった。
- char の
0xfc
が、int の0xfffffffc
ではなく int の0x000000fc
になっている - どこかでmovsxが抜けていそうという考えに至る
昼食を食べた後の作業
- プリンターでアセンブリを印刷して紙の上でデバッグして見つけた
- movsxにするべき箇所がmovzxになっていた
- (後から振り返ると、先にmovsxが抜けていそうだと判断したならば、アセンブリ中のmovzxに注目して見ることが出来たはず)
- アセンブリを読むスピードが上がってきた感じがする
2147483647 * 2 + 2
に関するテスト
longの値の範囲を調べるテストが失敗する
このとき、l1 != l2
はfalseと判定される。これまでこれは間違った仕様だと思っていたけれど、実は正しい仕様。2147483647 * 2 + 2
というのは、long型ではなくint型として計算される。すると、オーバーフローが起きた結果0となる。整数拡張の仕様をちゃんと把握していないとここで間違える。
宣言時に代入すると左辺の型に引きずられる
これを動かすと、1が出てしまう。正しい整数拡張について、代入は対処したが、宣言時の代入の方はまだ対応できてなかった。
ドーナツでセグフォ
これの実行結果が1になってしまう。負数の比較で整数拡張が上手く出来てない。
eaxに0xffff_ffff
を入れた場合、それに対してcmp rax, rdi
するときを考える。するとraxは42億になってしまう。これに対処するために、適切に整数拡張を行うか、cmp eax, edi
などの命令を使うようにする必要がある。
よくよく考えると他のオペランドも同じような問題がある。なぜここまで露呈しなかったのかというと、整数に対して剰余を取るという演算は準同型写像になる。2022年セキュリティ・キャンプL3ゼミ(Cコンパイラ班)ログ。この手のバグは割り算と比較以外で露呈しづらい。
整数拡張・算術型変換の実装のセグフォ
ポインタの足し算のテストでセグフォする
このプログラムをコンパイルしようとするとセグフォが出る。
analyze_semantics
で型を入れるようにして、大体の式で自動で型が入るようにした。
analyzeでセグフォする
これで上手く行くかと思ったらもっと悪化した。
どこかでanalyze_semantics
のtypeにnullを渡していた箇所があるらしい。nullの場合は無視する形にした所上手く動いた。
剰余算でfloating-point exception
剰余算でidiv
の前にcdqe
を使っていたが、これはrdxを触らない。なので、idiv
の前に置く命令としては不適当。cdqe
ではなく、cdq
を使うべきだった。
昨日今日あたりでアセンブリの使ってる範囲の命令に対しての知識がだいぶ増えた気がする。
これで整数拡張・算術型演算の実装は出来たので、次はドーナツを動かしてみる。
2フレーム描画したところでセグフォ
Cコンパイラはつらいよ。絶賛上映中