2022年セキュリティ・キャンプL3(Cコンパイラゼミ)ログ

編纂者:hsjoihs

はてなブログへのバックリンク

概要

2022年セキュリティ・キャンプL3(Cコンパイラゼミ)は基本的に Discord で行われた。したがって、テキストチャットとボイスチャットが混在する形態となったが、講師である hsjoihs がゼミ中の会話を全て手動でテキスト起こしした。

OS自作ゼミ・Cコンパイラゼミは、もとから自分が興味を持っていて、かつDiscordでやってることが見えやすかったのでよかったです。特にCコンパイラゼミは会話がまるごとテキストに起こされてチャンネルに流されていたので、マジで良かったです。これ絶対大変だと思うんですけど、めちゃくちゃ助かりました。 セキュリティ・キャンプを終えて(感想とか) - わたすけのへや

セキュリティ・キャンプCコンパイラゼミの創設者である@rui314さんの以下の思想に基づき、Cコンパイラゼミの会話のうち、発言者からの公開許可を頂けた発言を一般公開する。会話のテキスト起こしも Discord に最初からテキストとして書かれたものも区別せずに表示している。

セキュキャンは参加者はお金を払わないし、物理的制約で選考というものがある中で通るか否かいうのは時の運によるものが多いので(年齢制限もあるし)、行ったかどうかであまり差がつくというのは望ましくないと思う。なので可能な限り教材をやれば誰でもできるというようにしたいとは思います。

— Rui Ueyama (@rui314) April 15, 2019

セキュキャンに来なくて大丈夫なようにコースの詳細な資料を全公開するとなぜか逆に応募が増えるという。でもインターネットってそういうものかも。

— Rui Ueyama (@rui314) May 28, 2019

なお、ここで言及されている「コースの詳細な資料」とは「低レイヤを知りたい人のためのCコンパイラ作成入門」のことである。今年の C コンパイラゼミでは、これに加えて前橋和弥「新・標準プログラマーズライブラリ C言語 ポインタ完全制覇」も用いた。

ログ本体

2022年6月17日

hsjoihs
hsjoihs
えーととりあえずメンション飛ばしておくか @hsjoihs (L3講師) @uint256_t @れもん(Lちゅた @tamaron(L3チューター) @L3ゼミ受講生

2022年6月23日

lemolatoon
lemolatoon
Cで書いてるCコンパイラの事で質問なのですが、パーサーと、意味解析(?)([]演算子を*に読み替えたり、配列をポインタに暗黙変換したりする所)が現時点では同じコードになっているのですが、それらを分けようと考えています。データの持ち方で相談なのですが、この場合、パーサーで構築するASTのノードとなるstructと、その後意味解析によって変換されたstructはそれぞれ別のものを用意するべきですかね?この場合、パーサーのstructはなるべくコードの構造をそのまま持つべきかなと思ったのですがどうでしょうか。(declarationならtypespecifierや direct declaratorなどで分けて持つ)Ruiさんのwebサイトを参考にしていたので今はただ、Nodeというstructがあるのみの状態です。
hsjoihs
hsjoihs
まあどちらでも書けるとは思いますが、別の型になっていたほうが「あっ、このノードだけ意味解析するの忘れてた!」というミスを未然に防げるかもしれません。
uint256_t
uint256_t
今後のことを考えると、分けたほうがいいかもしれません。Node にあまりにも多くの情報を持たせると、わかりにくくなるだけなので。
uchan
uchan
単純な興味なんですがパーサーと意味解析(って言うのかな)を分けるうまみって何ですか?
hsjoihs
hsjoihs
分けないで書くこともできますし、2018年にやった際は私はかなりのところまで「構文解析・意味解析・コード生成」をまとめて行うワンパスコンパイラを書いてました。
特に C は古い言語である以上「計算資源とかが貧弱だった時代でもコンパイラが書ける」という設計なので、意外と無理せずにワンパスコンパイラを書くことが可能だったりします。
lemolatoon
lemolatoon
なるほど。1度分ける方向性で書いてみようかなと思います。僕の場合は、意味解析とパーサーが合わさっていないほうが暗黙の型変換などがきれいに抽象化できるかなと思ってやってみてるという感じです。
uint256_t
uint256_t
もちろんワンパスで書くことも可能で、しかしながら今後最適化などをやるのであれば、分かれていたほうが扱いが楽かなとは思いました。
hsjoihs
hsjoihs
はい、一番のうまみは見通しの良さだと思います
uchan
uchan
ワンパスは興味深いんですけど、代入式のコンパイルってどうやってワンパスでできますか?
i = 42; で i を読んだ段階だと、iを左辺値として生成するか、右辺値とするか、分からない気が。
先読みすりゃできるのか。
uint256_t
uint256_t
そうですね、そのレベルの先読みはパーサではよくあることです
hsjoihs
hsjoihs
先読みをしない場合、スタックにこっそりアドレスの情報を待避させておいて、代入演算子が登場したときだけそれを使う、とかいう暴挙により可能になります
uchan
uchan
良い感じに分離できたらまたお話聞かせてください! > lemolatoonさん
(あ、でも良い感じにならなくても、ダメだった話は面白そうだから聞きたいですw)
hsjoihs
hsjoihs
hsjoihs-c-compiler のかつての実装の場合、「構文解析・意味解析・コード生成」までが全部くっついてたので、
assignment-expression:
                 conditional-expression
                 unary-expression assignment-operator assignment-expression
と定義されている assignment-expression を処理する際に
「試しにunary-expressionを読んでみて、その直後にassignment-operatorが来ていなかった場合はその情報を破棄し、conditional-expressionとして読む」と実装しなければならない。しかし、現状ではparseしたら同時にコード生成もしてしまう。
という問題があったりしました。
まあなので、uchan さんの問いかけに関しての答えは「関数の責務が小さければ小さいほど、コードが読みやすくメンテしやすい」ということに尽きると思います。実装者に無限に知力を仮定していいなら、ワンパスの方が実行時間とかメモリ効率とかの面で嬉しいんでしょうけど、残念なことに人類の知性は有限なので、読みやすくメンテしやすく再利用しやすいコードの方が一般に望ましくなると言えそうです

2022年6月24日

hsjoihs
hsjoihs
@L3ゼミ受講生 @uint256_t @れもん(Lちゅた @tamaron(L3チューター) 本日 20:00 からミーティングやります。L3 受講生以外も C コンパイラに興味ある方は是非。
れもん
れもん
ニジュウジ!!??
了解です
hsjoihs
hsjoihs
セルフホストしていない bar.c が変化すると、第一世代が変化し、なんと赤紫 foo.s が変わる………!
これがセルフホストデバッグの恐ろしさ(bar.c が foo.s に因果的影響を出せる)
れもん
れもん
初期のCで開発されていたCコンパイラです。興味ある方はどうぞ
Nimda
Nimda
証明の計算木のパースとコンパイラのパースの雰囲気が同じ、カリー=ハワード同型対応だなぁと思っていました
rustの最初のコンパイラはOCaml実装のはずなので、OCamlをCで実装すればマルチホップでセルフホストできる?
itoooak
itoooak
私も同じような状況ですね【編注: hsjoihs の露骨な宣伝がきっかけでセキュキャンを知った、という話がされていた】
hsjoihs
hsjoihs
応募を露骨に催促する私の様子:

2022年6月27日

hsjoihs
hsjoihs
@L3ゼミ受講生 そういえば、お送りした「C言語ポインタ完全制覇」って開いてみました?
lemolatoon
lemolatoon
なんかデータ構造の直前らへん?までさーっと読んでみましたが、「そうなんだ」みたいなところが結構多くて面白かったです
uint256_t
uint256_t
そうなんだ、ってところを(自作コンパイラの)バグとして踏みがちなんですよね...
hsjoihs
hsjoihs
そうなんですよね。「この本を私は事前に読んでいたので、実は当時 C を書いた経験がなかったにもかかわらず C を C で書くことができた」といって差し支えない程にはよくまとまった本だと考えています
mikiken
mikiken
自分は本格的にCを触るようになったのが最近なので、かなり役立ちそうな本だなあと思いました(小並感)
(セキュキャンの応募課題に取り組むまで、ポインタをほぼ使ったことがなかったぐらいなので)
かりんとう
かりんとう
まだ序盤までしか読めていませんが、図やハンズオンが豊富で普段なら曖昧に理解するところが具体的に解説されていてわかりやすいと思いました。
本筋とは関係ないですが、どの機能がどの規格で実装されたのかを読んで、へぇ~ってなってました。(一行コメントがC99なのは意外だった)
kanataso
kanataso
1.4.7の補足の配列の範囲チェックの話に、ポインタを構造体のようにして範囲チェックを実行時に行うという話があって、応募課題問6に似た感じのことを書いたな~って思いました。
3-5-7の
void (*signal(int sig, void (*func)(int)))(int);
はパースできる気がしないです....

2022年6月30日

hsjoihs
hsjoihs
前橋和弥「プログラミング言語を作る」の continue 文の実装にバグ発見!
れもん
れもん
これか

2022年7月1日

hsjoihs
hsjoihs
@L3ゼミ受講生 @れもん(Lちゅた @uint256_t @tamaron(L3チューター) 先週と同じ時間で今日も会やります
@人々 L3以外の方も歓迎しますので、是非
@L3ゼミ受講生 @uint256_t @tamaron(L3チューター) @れもん(Lちゅた 30分後ぐらいからやりますが、せっかくなので私はもう通話に入っておきます
かりんとう
かりんとう
フランケンコンパイルつらい
hsjoihs
hsjoihs
そういうおもろい話は是非我々にメンション飛ばして一緒に見せてくださいよ
uint256_t
uint256_t
そのために我々がいるんですからね
かりんとう
かりんとう
構造体の中でポインタを読む際に、ずれてて変なアドレスを読んでた
uint256_t
uint256_t
でもそれでgdbでデバッグできてるんなら十分だと思いますよ
かりんとう
かりんとう
時間かかりまくっちゃって
uint256_t
uint256_t
でもそんなもんですよ。1週間とか掛かったりする
個人的にはgdbよりlldbの方が使いやすい
かりんとう
かりんとう
_Alignof さんの C コンパイラ輪読会に出たんですよね
compilerbook のなかった時代ってどう学んでたんですか
uint256_t
uint256_t
8cc はやってたので、それを見ながらやってた
私は文章読むよりコード見て学ぶタイプ
そもそも知識が足りなかったら教科書とか読みますよね
かりんとう
かりんとう
前提知識が足りないなぁってことはありますね
uint256_t
uint256_t
まずは教科書読むところなんじゃないですかね
あるところから、OSSがパッと読めるようになる
かりんとう
かりんとう
そのレベルまで到達できるようになりたい
#ifdef の多いヘッダファイルってどう読みます?
uint256_t
uint256_t
エディタの機能で要らないところを灰色にするとか
プリプロセッサを走らせて必要な部分だけ読んだりするとか
かりんとう
かりんとう
gcc -E と cpp って同じなんですか
hsjoihs
hsjoihs
基本的に gcc とか clang というのはサブコマンドをいい感じに呼び出す働きをします
uint256_t
uint256_t
gccはそう。clangはプリプロセッサを中に組み込んでるはず
かりんとう
かりんとう
フランケンコンパイルの Makefile の書き方がわからない
uint256_t
uint256_t
別にシェルスクリプト書いてもいいんですよ?
hsjoihs
hsjoihs
まあ C で書いても Python で書いても別にいいんですよね
uint256_t
uint256_t
ファイルは細かくしたほうがどこが壊れてるのかわかっていい
でも読みやすいので、分けるのもそんなに大変ではないかと
かりんとう
かりんとう
プリプロセスしたあとに標準のヘッダファイルを読み込んじゃうと対応してない構文がでてきちゃう。どうするんだと思ったらダミーヘッダーを書いてた。賢いなぁと思った
uint256_t
uint256_t
結構プリプロセッサめんどいですからね
かりんとう
かりんとう
#include もどきを実装している
hsjoihs
hsjoihs
そういや radare を愛用してるとか
れもん
れもん
愛用ってほどでもないけど。まんべんなく使ってる
radare は静的な解析がメインだが、デバッガとしても使うことができて、アセンブリの解析とかが得意なツール。どっからどこにジャンプして、とかが矢印で色分けされてくれたりするのが便利。
かりんとう
かりんとう
いまアセンブリをgdbで見てるので、ぜひやっていきたい
れもん
れもん
ただしradareは操作のクセがすごい
かりんとう
かりんとう
れもん
れもん
そういうふうにグラフィカルに表示してくれるモードもある。
みなさんこの main の第三引数 envp ってご存知ですか
hsjoihs
hsjoihs
C 側じゃなくて POSIX 側の仕様ですよねたしか
uint256_t
uint256_t
これって読み『ラダレ』なんですね。てっきり『ラデア』かと
hsjoihs
hsjoihs
作者がスペイン語圏の方のはずで
イワシイラ
イワシイラ
radare2だ
かりんとう
かりんとう
LaTeX とかラテックスとか読んでたわ
hsjoihs
hsjoihs
mikikenさんとkanatasoさんもぜひ
mikiken
mikiken
来週編入試験なのであんま触れていない。ローカル変数が、動いていない。とりあえず、『一文字変数だけ』のやつで、トークナイズして、パースするところも書いてるけどどっかでエラーが出てて、放置中
hsjoihs
hsjoihs
先程言ったように、我々は他人のコンパイラが動いてない様を見たいから講師をやっている。気兼ねなく連絡しましょう
mikiken
mikiken
現バージョンはまだコミットできてない。分割コンパイルまでしかコミットしてない。動くようになったらコミットしようかな、と
hsjoihsuint256_t
hsjoihsuint256_t
ブランチ切ってコミットしましょう。main ブランチは動くやつだけにしよう
hsjoihs
hsjoihs
git 慣れてないなら、VSCode の拡張とか入れて、GUI でやったらどうでしょう
mikiken
mikiken
Atom 使ってて、そろそろ VSCode にしようかなぁと
れもん
れもん
そろそろ Atom 終わるんじゃなかったっけ
uint256_t
uint256_t
移行してもらったほうがいいですね多分
hsjoihs
hsjoihs
VSCode は共同編集もあるのでリモートでやるのに便利
mikiken
mikiken
Google Docs みたいな?
hsjoihs
hsjoihs
そう
kanatasoさんはどうでしょう
kanataso
kanataso
先週からは進捗がなく、この Discord の最初の方で『構文・意味・コード生成分けたほうが後々いいよね』とあったけど、いま自分のコードが、『構文+意味』と『意味+コード生成』になっちゃってる
hsjoihs
hsjoihs
それ自体が直ちにまずいとは言いません。不便だと思ったらリファクタリングする、ぐらいでも十分である可能性はあります
kanataso
kanataso
分けてる途中で『分けても機能増えないからつまらないなぁ』となり
hsjoihs
hsjoihs
そうですよね。私もそのノリでワンパスのまま放置しまくったので
れもん
れもん
今から切り分けるのは大変だと思う。うまく言ってるならとりあえず放置でもいいかも。
かりんとう
かりんとう
自分ももう切り分けしないまま結構進めてタイミングを逃した人なんですが、コード量が多いとやっぱりつらい。でももう遅いのよなぁ
れもん
れもん
そして実は、いま既存のコードをどうこうするより作り直したほうが早い可能性はあるが、それは求めない
hsjoihs
hsjoihs
私が分割したときは、コードを三回コピペしていらないコード全部削りましたね。詳しくは hsjoihs-c-compiler の開発日記などご参照ください
かりんとう
かりんとう
Cコンパイラとあまり関係ないんですけど、yacc とかを使って言語を作りたいなというときに、わりと普通のCとかで簡単に作れるのかなぁと思ってたのだけど、構文解析のアクションの部分で、if 文書けないのではとなって
hsjoihs
hsjoihs
yacc で実装してる本:
かりんとう
かりんとう
セキュキャンって一つのゼミの人数ってどう決めるんですか?Lの中ではCコンパイラ班が一番大きいですよね
hsjoihs
hsjoihs
『様々な事情を鑑み、総合的に決めます』とだけ言っておきます。そもそも応募者の人数もありますし
L3 は非常に人気が高かったです
uint256_t
uint256_t
その反対のゼミもあるということで
かりんとう
かりんとう
hsjoihs さん Twitter でめちゃめちゃ宣伝してましたよね。あと復活したゼミだからというのもあるんですかね。あと CPU 自作ゼミも面白そうだなぁと。RISC-V。
uint256_t
uint256_t
CPU書いて、OS書いて、その上で自作コンパイラ動いたらうれしいですよね
かりんとう
かりんとう
自作ブラウザもありますよね
hsjoihs
hsjoihs
B トラックですね
ゆみや
ゆみや
1つのコースにしか参加できないのが残念すぎるので、セキュキャン外でも1人で勉強できるような教材が各コースに整備されたらいいなあとか思っている
hsjoihs
hsjoihs
かりんとう
かりんとう
成果物が合わさるってのはめちゃめちゃおもしろそう
uint256_t
uint256_t
それができる人が集まってますからねぇ。ぼくも自作ブラウザしてましたからね
ゆみや
ゆみや
各トラック/ゼミにどれくらい応募者がいたのかはちょっと気になる
hsjoihs
hsjoihs
uint256_t さんの成果発表時に『見慣れないプレゼンソフトだな』と思ったら、自作ブラウザだった、ってのはあった
れもん
れもん
ゲーム機からプレゼンしてる人のプレゼンは面白かった
hsjoihs
hsjoihs
osdev-jp のアレだよね。なんだっけなぁ〜
録画残ってたっけなぁ。多分 @hikalium か @内田公太(Y1講師、TDDモブ講師) 辺りに確認したほうがいいかも
れもん
れもん
hsjoihs
hsjoihs
@mikiken ソース読みたいのでとりあえず push しておいてください〜(push のやりかたで詰まったりしてたら躊躇なくお教えください)
れもん
れもん
uint256_t
uint256_t
lemolatoon
lemolatoon
ログ残して頂いてありがとうございます。どんな話がされてたのか何となく分かりました。
私がRustで書き直してるのはこれで、今は1文字のローカル変数を書き直したぐらいです。トークナイズ、パース、意味解析、コード生成で分けてやってみてます。

2022年7月2日

mikiken
mikiken
一応1文字のローカル変数が動くようになったのでpushしました~
uint256_t
uint256_t
なんとなくテストを並列化しました
れもん
れもん
ところでx86には便利命令leaがありまして。
lea rax, [rbp - 0x8]
などとするとraxにはrbp - 0x8が代入されます。
uint256_t
uint256_t
よく見かけますね
lemolatoon
lemolatoon
これって
mov rax, rbp
sub rax, 0x8,
mov rax, [rax]
を一行で書けるということですか?
uint256_t
uint256_t
mov rax, rbp
sub rax, 0x8
だと思います
lemolatoon
lemolatoon
なるほど、デリファレンスしてないのに[]がついてるんですね。
れもん
れもん
mov rax, rbp
sub rax, 0x8,
mov rax, [rax]
は mov rax, [rbp - 0x8] ですね。どちらかというとx86のメモリアドレッシングが高機能なだけです。
lemolatoon
lemolatoon
これらの違いってCPUが命令を実際に読む機械語も違うんですかね。それとも、アセンブラがいい感じに変換している?
uint256_t
uint256_t
機械語が違うと思います
lemolatoon
lemolatoon
ありがとうございます。CPU側がそれぞれ用意してくれているとおもうとちょっと機械語も高級にみえてきますね。なんかsyntax sugarみたいに見えるので(実際機械語が異なるのでsyntax sugarとは呼べないかもですけど)
uint256_t
uint256_t
『どちらかというとx86のメモリアドレッシングが高機能なだけです。』この言葉通りで、アドレッシングの機能で引き算もできてお得だよね、という話だと認識しています。
lemolatoon
lemolatoon
アドレッシングという用語を初めて知ったのですが、CPUの中で右辺値みたいな感じでアドレスを扱えるみたいな感じなんですね。(間違ってるかも)
uint256_t
uint256_t
そうですね、命令のオペランドにアドレスを指定できて、そのアドレスをどう計算するかはアドレッシングモードとか呼ばれてますね
lemolatoon
lemolatoon
なんとなくわかりました。ありがとうございます。言われてみれば即値でなく、レジスタを指定している時点でアドレッシングモードが違ったんですね。
hsjoihs
hsjoihs
なんか14時間ぐらい寝た(おはようございます)
とりあえず通話には入っておきます
TumoiYorozu
TumoiYorozu
lea (%rdx,%rdx,2),%eax で eax = rdx * 3 を代入したり、lea (%rdx,%rdx,4),%eax で eax = rdx * 5 を代入したりする lea テクを思い出した(役に立つかは状況次第…)
hsjoihs
hsjoihs
godbolt で -O3 とかの最適化オプション掛けて、入力を定数倍する関数を書いてみると、とある定数までは mul 系統の命令が呼ばれず lea とかで処理されるんですよね。ぜひ皆さんお試しあれ
lemolatoon
lemolatoon
O0とO3

2022年7月3日

かりんとう
かりんとう
@hsjoihs (L3講師) @uint256_t 現段階で機能を妥協したものも多いですが、一応セルフホストができました。ですが判定方法が正しいかあまり自信がないので、確認をお願いしたいです。
uint256_t
uint256_t
こちらは、とりあえず make test1; make test2; ... と実行すれば良いでしょうか
かりんとう
かりんとう
ごめんなさい。動かし方の説明をしていませんでした。
そうです。以下コマンドの説明です。
make test1 => 第1世代に対するテスト
make test2 => 第2世代 ...
make test3 => 第3世代 ...
make testall => test1, test2, test3を実行
make diff => selfhost/gen2/*.sとselfhost/gen3/*.sを比較する。
EXCLUDE_FILES変数に指定したファイルがgccでコンパイルされるように設定しました。最新のコミットではそこが空文字で、全ファイルに自作コンパイラを使用しています。
run~コマンドは手元で任意のコードを簡単に動かすためのものです。
uint256_t
uint256_t
なるほど。test2の時点で手元でSEGVしてしまったので一応報告しておきます
あとで何が起きているのか見ておきますね (他の人が解決してくれるかもしれないけど
かりんとう
かりんとう
確認ありがとうございます。
僕の環境でも一度cloneをやり直しても動いたのですが、環境依存のバグでしょうか。。
uint256_t
uint256_t
まぁ私の環境がおかしいかもしれないので...
hsjoihs
hsjoihs
おっ!!!
読んでみようっと
lemolatoon
lemolatoon
僕の環境だと成功しました。
hsjoihs
hsjoihs
おや
uint256_t
uint256_t
私がおかしかったか..
hsjoihs
hsjoihs
でもデバッグにはそういう細部の違和感が往々にして重要では
uint256_t
uint256_t
有益な情報が得られれば良いのですが。
hsjoihs
hsjoihs
とりあえず手元の WSL2 では通りましたね全部
こういうのは未定義動作と最適化とかでなんか起きてそうだし、ちょっくらサニタイザでも掛けてみますかね
せっかくなのでボイチャにいます。是非
@here セルフホストに成功したCコンパイラが、環境によっては動かないらしい?という話を調査します!L3 以外の方も是非!!!!
lemolatoon
lemolatoon
今ボイスチャット入れないので環境だけ送っておきます。
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
hsjoihs
hsjoihs
ありがとうございます~
@かりんとう ここで node を定義するだけして使ってないのって正しいんです?
その上にある i = 0; も一切使われてないのが気になる
vec_delete が void* を返してますが、return 文も無いですし、戻り値を誤って使ってるコードもないので、これは void の誤りかと思われます
おぉっとサニタイザ付けたらテストが落ちた!!!
かりんとう
かりんとう
そこは一切必要ないですね。。
ごめんなさい、機能追加を優先しすぎて無駄なコードが多いと思います。
あと僕もvoiceチャットに参加したいのですが、外にいるので難しそうです。帰宅したら色々確認しなおします。
hsjoihs
hsjoihs
@かりんとう とりあえずテストが落ちた状況のを push しておきました
.globl init_array8
init_array8:
  push rbp
  mov rbp, rsp
  sub rsp, 20
  mov rax, 0
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 1
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  mov rax, 1
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 2
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  push rax
  pop rax
  mov rax, 0
  push rax
  pop rax
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  mov eax, [rax]
  cdqe
  push rax
  mov rax, 4
  push rax
  pop rax
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  mov eax, [rax]
  cdqe
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  push rax
  pop rdi
  movsx rax, edi
  jmp .L.return.init_array8
  pop rax
  push rax
  pop rax
.L.return.init_array8:
  mov rsp, rbp
  pop rbp
  ret
↑ clang にしてサニタイザ掛けた後
↓ gcc
.globl init_array8
init_array8:
  push rbp
  mov rbp, rsp
  sub rsp, 20
  mov rax, 0
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 1
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  mov rax, 1
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 2
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  mov rax, 2
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 3
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  mov rax, 3
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  mov rax, 4
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  mov rax, 5
  push rax
  pop rdi
  pop rax
  mov [rax], edi
  push rdi
  pop rax
  push rax
  pop rax
  mov rax, 0
  push rax
  pop rax
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  mov eax, [rax]
  cdqe
  push rax
  mov rax, 4
  push rax
  pop rax
  push rax
  mov rax, 4
  push rax
  pop rdi
  pop rax
  imul rax, rdi
  push rax
  mov rax, rbp
  sub rax, 20
  push rax
  pop rax
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  mov eax, [rax]
  cdqe
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rax
  push rax
  pop rdi
  movsx rax, edi
  jmp .L.return.init_array8
  pop rax
  push rax
  pop rax
.L.return.init_array8:
  mov rsp, rbp
  pop rbp
  ret
ん~ 54 行目以降に差がありますねぇ
「kcc を gcc でコンパイルしたもの」と「kcc を clang でコンパイルしたもの」で挙動が異なるというのは嫌ですねぇ
ん? gcc でコンパイルすると
test9:
  .long 1
  .long 2
  .long 3
だが clang でコンパイルすると
test9:
  .long 1
  .long 2
これは明らかにおかしそう
となると、initialize_array 周りがおかしいんかなぁ
あー children_cap を 2 から 100 にしたら直った!
なるほどね。ベクタの実装がバグってて未定義動作踏んでるのか
未定義を踏んでるのか、はたまた realloc でポインタが無効になってるのか。どっちだ?
children_cap が 1 か 2 か 3 か 4 のときに落ちる
| children_cap | test9 | init_array8 |
|--------------|-------|-------------|
| 1            | fail  | fail        |
| 2            | fail  | fail        |
| 3            | ok    | fail        |
| 4            | ok    | fail        |
| 5            | ok    | ok          |
int test9[] = {1, 2, 3};
int init_array8() {
    int a[] = {1, 2, 3, 4, 5};
    return a[0] + a[4];
}
なので、「要素数を宣言していない配列の長さより、children_cap が下回っていると、gcc ではコードをちゃんと吐いてくれるが、clang では children_cap の個数しか出力をしてくれなくてバグる」というバグが存在することが確認できました
……ということはわかったが、ソースコードを見ても一向になぜバグってるのかが見えてこなくて困っている
えーっマジでわからん。これはなに。
・children_cap が変わるだけで、fail するか ok するかが変わる。配列の長さ i 自体は正しく記録されているし、children_cap はパーサのローカル変数だから、realloc の有無とか以外で副作用をもたらすとは考えられない
・「入力の長さ > children_cap」==「失敗する」なので、なんらかのメモリ関連バグとしか思えないが、clang のサニタイザもなにも指摘してこない
ふむ~~~
とりあえず、children_cap が 2 のとき、コードジェネレート時に printf デバッグすると、gcc は「test9 の長さは 3」と主張し、clang は「test9 の長さは 2」と主張することが分かった
ということで、var->ginit->len にマズい値が入っているのがダメ。
var->ginit->len はどう初期化される?
ginit は常に Vector として new_vec() と vec_push で操作が行われている。じゃあさっきの手動 realloc してたやつはいつ Vector になるんだ?
initialize_array initialize2 initialize_array initialize2 initialize
この initialize が new_node_init も呼んでて、こいつのなかの new_node_init2 が init->var->ginit に vec_push してるっぽいな
よっしゃ init->len: -1094795586 とか出てきた!未初期化のメモリかなんか読んでますねこれは
これ 16 進にすると 0xBEBEBEBE なので、多分 clang のサニタイザかなんかが未初期化のメモリに埋めといてくれてる値かなんかですね
init->len への read が max(入力長 - children_cap, 0) + 1 回発生していて、2 回目以降の read で clang が init->len: -1094795586 を返している
これは「『入力の長さ > children_cap』==『失敗する』」という実験データに整合するものである
一次元配列においては、本来は new_node_init2 の呼び出しは一つの初期化式に対して一回であるべきはずだ。しかし、「サニタイザ付きの clang では」max(入力長 - children_cap, 0) + 1 回発生してしまっているようだ。
それはなぜ?
考えられるのは、ループの終端条件が == NULL であるところで、gcc ではゼロ埋めされていてたまたまループが止まっているが、clang ではゼロ埋めされておらずループが止まってない、などのパターンだ。
あ、ループというか再帰呼び出しか。他にコードパスないわ。
if (init->children) の条件で再帰呼び出ししてるからだわ
はいご名答~~
test_gcc_and_clang.sh: kcc1_gcc start compileing tests/init2.c before: 0x56156ec602d0, 2 after : 0x56156ec60450, 4 before: 0x56156ec60450, 4 after : 0x56156ec60620, 8 final len: 5 init->children: 0x56156ec60620 init->len: 5 init->children: (nil) init->children: (nil) init->children: (nil) init->children: (nil) init->children: (nil) before: 0x56156ec630d0, 2 after : 0x56156ec63250, 4 final len: 3 init->children: 0x56156ec63250 init->len: 3 init->children: (nil) init->children: (nil) init->children: (nil) test_gcc_and_clang.sh: kcc1_clang start compileing tests/init2.c before: 0x607000000020, 2 after : 0x60e000000040, 4 before: 0x60e000000040, 4 after : 0x612000000040, 8 final len: 5 init->children: 0x612000000040 init->len: 5 init->children: (nil) init->children: (nil) init->children: 0xbebebebebebebebe init->len: -1094795586 init->children: 0xbebebebebebebebe init->len: -1094795586 init->children: 0xbebebebebebebebe init->len: -1094795586 before: 0x607000000090, 2 after : 0x60e000000120, 4 final len: 3 init->children: 0x60e000000120 init->len: 3 init->children: (nil) init->children: (nil) init->children: 0xbebebebebebebebe init->len: -1094795586
訂正:
一次元配列においては、本来は非NULLなinit->childrenを伴ったnew_node_init2 の呼び出しは一つの初期化式に対して一回であるべきはずだ。しかし、『サニタイザ付きの clang では』max(入力長 - children_cap, 0) + 1 回発生してしまっているようだ。
となると原因が見えてきたな。
① このコンパイラでは、メモリ確保は memory_alloc という関数を使って行っているが、こいつは裏で calloc を呼び出しているので、必ずゼロ埋めされたメモリ領域が返ってくる ② しかし、メモリ再確保は libc の realloc をそのまま呼んでおり、こいつはメモリをゼロ埋めするとは限らないし、なんなら clang のサニタイザは意図的に 0xBEBEBEBE で埋めた領域を返してくる ③ で、未初期化のフィールドを読んでるので、ヌルポインタが入ってるところが期待されるべき場所で 0xBEBEBEBE を読んで、コンパイラがバグっている ④ 解決策としては、以下の二つのどちらか(あるいは両方)が考えられる。 A.「このコンパイラでは、ヒープ領域のうち代入してないフィールドは常に 0 かヌルポインタであることを仮定する」という暗黙の規約を守るために、realloc(ソースコード中で 2 回登場)した領域を必ずゼロクリアする B. ヒープ上に確保した構造体のありとあらゆるフィールドに 0 を入れて回る
個人的には A の方が圧倒的に楽だと思います。
@かりんとう 事故調査、完了しました!
ということで、バグ修正と、サニタイザ付き clang での挙動も確かめる make testextra を足した Makefile を含んだプルリクエストを投げておきました!
uint256_t
uint256_t
私が外出している間にすべて終わっていた...
かりんとう
かりんとう
おおこの短時間で。。
ありがとうございます!
後ほど調査していただいた内容を細かく確認します〜
hsjoihs
hsjoihs
第一世代での問題だったので比較的短時間でなんとかなった
しかし果たしてこれの解決になってるのかはそちらで試してもらわないとわからんのですよね
uint256_t
uint256_t
やっぱり落ちてしまう
どうやら "引数の個数が正しくありません。" を表示する時に落ちているようで、fprintfに渡されているstderrが.commで宣言されてるのを消したら動きました
hsjoihs
hsjoihs
あ~~ stderr の comm
私もかつてハマった気がする
uint256_t
uint256_t
その他のcommも消さないと、また別の所で落ちそうです
hsjoihs
hsjoihs
2018年8月31日 (Day 50) セルフホストに向けて 構造体を関数に渡すのは放棄しよう。スタック使いたくないし。 ということで構造体を関数で渡している部分をポインタ渡しにするようにしたので、 codegen_expression.c をセルフコンパイルできた。 グローバル変数へのアクセスを全部GOTPCREL経由にしたところ、stderrがちゃんと使えるようになった。よって std.c と error.c をセルフコンパイル。 「今構造体周りってどこまでできてたっけ?」と思ったので実験をしている。a.b.cとか普通にできるのか。
私が Mac でやってたときのログ
あと、環境によっては stderr がシンボルじゃなくてマクロだったりして、そこで詰まる場合もあったはず
ちなみに uint256_t さんの環境お伺いしていいですか?
uint256_t
uint256_t
# uname -a
Linux uint 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
# gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
たぶん comm だけで実体がないと、空っぽの領域が確保されちゃうんですよね
hsjoihs
hsjoihs
私も貼っておこう
# uname -a
Linux LAPTOP-BKHPSENK 4.19.128-microsoft-standard #1 SMP Tue Jun 23 12:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
uint256_t
uint256_t
なかなかバグに気づきにくいですね

2022年7月4日

れもん
れもん
gcc9, 11, 12の環境が手元にあるのですが、ldに何か変更があったのか11以降のはグローバル変数被りがリンクできませんね…一応-z muldefsフラグを付ければビルドは通りはします。
lemolatoon
lemolatoon
この並列化を試してみてすごく速くなってよいのですが、テストに失敗したことがわかりにくく常にスクリプトが0を返すため、並列化したテストが失敗したら最後にFailedを表示させようと思ったのですが&を最後につけた関数の中からグローバルな値に書き込んでも反映されないようで、失敗してもresult変数が書き換わらず常にPassedになってしまいます。これを回避するような方法、具体的にはシェルスクリプトで&をつけた関数の中からグローバルな情報を編集する方法ってありますかね?
hsjoihs
hsjoihs
シェルスクリプト、そういうめんどいのはファイルシステムに押し付ける印象がある(偏見)
つまり、失敗したときにだけ生える一時ファイルを用意し、それを後で読んで「失敗」と報告するという素朴な実装ですね
uint256_t
uint256_t
書き換えておきました。こんな感じでしょうか。
lemolatoon
lemolatoon
なるほど、後で適用してみます。ありがとうございます!
uint256_t
uint256_t
このツイートを見ていて思い出しましたが、Goのコンパイラも読んでみると色々と学びがあるかもしれません
Cコンパイラを作る上で役に立つかはわかりませんが、Goはいろんなものをフルスクラッチしているので。(アセンブラとかも)

2022年7月5日

かりんとう
かりんとう
.commによるバグですが、他のグローバル変数では起こらずstderrで発生する原因は何でしょうか?
stderrは処理系によってマクロ定義で、自作では名前が"stderr"のシンボル。標準ライブラリの実態の方では別の名前のシンボルになってリンク時に上手く結び付けが出来ていないと予想しましたが、いまいち理解が難しかったです。
uint256_t
uint256_t
おそらく
#include <stdio.h>
int main() {
  fprintf(stderr, "hi");
}
のようなコードをgcc,clangでコンパイルしても、.commディレクティブは出てこないと思うのですがいかがでしょうか. (出てきたらごめんなさい)
やっぱり .comm stderr,8 みたいに書くと、glibcとかのstderrがリンクされなくなりますね
かりんとう
かりんとう
僕のWSL環境だとgccで出力されるアセンブリに.comm stderr, 8はありませんでした。ですが、このアセンブリにそれを加えアセンブルしても正しくstderrにリンクされました。この違いがstderrの定義方法の違いなのかな?と思ったりですね。
僕の環境です
$ uname -a
Linux KARINTO 4.19.128-microsoft-standard #1 SMP Tue Jun 23 12:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
uint256_t
uint256_t
そういえば、みなさんは何のリンカを使っていますか。(私はlld)
かりんとう
かりんとう
ld (collect2?)です。
hsjoihs
hsjoihs
stderr まわり、『Rui 本に明示的な記載がなく、しかもあまり自明ではないハマりポイント四天王』な気がしてきたな
(残り3つは
・配列へのポインタ(二次元配列の実装に必要) ・構造体のアラインメントとパディング ・構造体を関数に渡したり関数から返したりするときのABI
かなぁ)
かりんとう
かりんとう
構造体や可変長の引数は仕様の理解で詰まりそうだな~と思ってました。
僕が遭遇したのでは、errnoを特に考えずに宣言するとTLSに確保されていないよと怒られることがありましたね~
Thread周りも少し考慮する必要があるのかと困惑しました。 (実際はマクロで別の関数を呼び出していた)
hsjoihs
hsjoihs
あー、たしかに可変長引数を受け取る関数は若干面倒ですね(渡す方は特に考えずに渡せばいいんですが)

2022年7月6日

hsjoihs
hsjoihs
@L3ゼミ受講生 最近どんな感じですか?
nyaarch64
nyaarch64
進捗ダメです
lemolatoon
lemolatoon
ポインタ演算までは実装しました
hsjoihs
hsjoihs
Rust での再実装の方で、ですか?
lemolatoon
lemolatoon
そうです
kanataso
kanataso
プリプロセッサをコンパイルしようとしてます(難航中)
hsjoihs
hsjoihs
おっ。どういう難航のしかたしてます?
kanataso
kanataso
構造体のポインタへの代入ができてないのが原因の問題が起きている気がするけれど、できているようにも見えます。
詳しくは帰宅次第確認したい....
hsjoihs
hsjoihs
新幹線に乗ってて暇なので @kanataso さんのデバッグしようかな。リポジトリどれでしたっけ
たしかこれだった気がする
kanataso
kanataso
あ、今の状態をコミットしました
hsjoihs
hsjoihs
ありがとうございます(上記リポジトリで合ってますよね?)
kanataso
kanataso
合ってます
hsjoihs
hsjoihs
やったぁ
かりんとう
かりんとう
今 kcc を手元で動かしたら、普通にセルフホストできていました。たぶんこの実装で大丈夫なはず。(お手数おかけしました)
やった~~
externが付いた変数は出力しないよう修正しましたが、それで解決したようです。
hsjoihs
hsjoihs
たしかにそれでよさそう
Darwin ってことは macOS だと思うんですが、エントリーポイントが _main じゃなくて main になるのってどういう環境で走らせてます?デフォルトの『gcc を名乗る clang』がいる macOS 環境だと _main になるかと思うんですが
uint256_t
uint256_t
確かに。
kanataso
kanataso
gccを名乗るclangです
hsjoihs
hsjoihs
普通に uname -a に詳しくないので調べなきゃならん
uint256_t
uint256_t
今試してみたら、clangがmainを吐いてますね... (Darwin)
hsjoihs
hsjoihs
あーじゃあこっちの macOS かclang かなにかが古いのが原因っぽいな
uint256_t
uint256_t
Darwin uintnoMBP 21.5.0 Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:37 PDT 2022; root:xnu-8020.121.3~4/RELEASE_ARM64_T6000 arm64 arm Darwin
(M1ですが)
hsjoihs
hsjoihs
$ uname -a
Darwin MacBook-Pro-de-admin-2.local 19.6.0 Darwin Kernel Version 19.6.0: Mon Apr 18 21:50:40 PDT 2022; root:xnu-6153.141.62~1/RELEASE_X86_64 x86_64
uint256_t
uint256_t
AppleのClangはよくわからないLLVM-IRを吐いたりして、あまり好きではない
hsjoihs
hsjoihs
そう、Apple clang と一般 clang ってなんか違うらしいんですよね
2017年に買った MacBook だし、しかもまだ Catalina までしか上げてないんだよな
あっ理解。9cc フォルダと prpr フォルダにある Makefile は、リポジトリ直下の Makefile を使って間接的に呼び出さないといけないんですね。直に make しにいってコケてました。すいません
で、『プリプロセッサをコンパイルしようとしてます(難航中)』は make prpr1 を走らせるとセグフォするので困っている、ということでしたか、なるほど。
『えっなんで標準エラー出力のリダイレクト効かないの?』と思ったら、stderr を fopen("/dev/stderr", "a") にすり替えるマクロが仕込まれているのか。なるほどそりゃリダイレクトも効かないはずだ
んで、なんか make clean した直後の make mprpr1 は落ちないにもかかわらず、そのあともう一回 make mprpr1 すると落ちる。これはなに。
よし、とりあえずサニタイザを突っ込んだら落ちた
AddressSanitizer:DEADLYSIGNAL
=================================================================
==27109==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x0001089add29 bp 0x7ffee7250dd0 sp 0x7ffee7250da8 T0)
==27109==The signal is caused by a READ memory access.
==27109==Hint: this fault was caused by a dereference of a high value address (see register values below).  Dissassemble the provided pc to learn which register was used.
    #0 0x1089add29 in .Lbegin8+0x55 (prpr:x86_64+0x100001d29)
    #1 0x1089aea01 in .Lswitch1+0x16 (prpr:x86_64+0x100002a01)
    #2 0x1089ae908 in .Lend35+0x1d (prpr:x86_64+0x100002908)
    #3 0x1089aeaac in .Lswitch6+0x41 (prpr:x86_64+0x100002aac)
    #4 0x1089ae391 in .Lend26+0x7f (prpr:x86_64+0x100002391)
    #5 0x1089ae4e9 in include+0x14a (prpr:x86_64+0x1000024e9)
    #6 0x1089aea21 in .Lswitch2+0x16 (prpr:x86_64+0x100002a21)
    #7 0x1089aeef0 in main+0x3a0 (prpr:x86_64+0x100002ef0)
    #8 0x7fff71a72cc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

==27109==Register values:
rax = 0x02ffffff00000002  rbx = 0x00007ffee7253640  rcx = 0x0000000117b3de6c  rdx = 0x0000000000000000  
rdi = 0x0000000000000000  rsi = 0x00000000000120a8  rbp = 0x00007ffee7250dd0  rsp = 0x00007ffee7250da8  
 r8 = 0x00000000000130a8   r9 = 0x0000000000000000  r10 = 0x00007ffee7250dc4  r11 = 0x00007fff9859abf0  
r12 = 0x0000000000000000  r13 = 0x0000000000000000  r14 = 0x0000000000000000  r15 = 0x0000000000000000  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (prpr:x86_64+0x100001d29) in .Lbegin8+0x55
==27109==ABORTING
調査途中の様子を置いておこう
れもん
れもん
はい、strndupの宣言が為されていないため戻り値がintとなってしまっていて、頭がちょんぎられたアドレスにアクセスしてるっぽいですね
hsjoihs
hsjoihs
あっなるほど
れもん
れもん
それはそれで別のセグフォが起こるな
調査方法としてはbacktraceを取っているだけです。
他のセグフォも同様にstrndupのせいでした。
$ gdb prpr/prpr
@(gdb) run (適当なcファイル)
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f0af0b in ?? () from /usr/lib/libc.so.6
@(gdb) backtrace
#0  0x00007ffff7f0af0b in ?? () from /usr/lib/libc.so.6
#1  0x0000555555555636 in read_file (name=0x5555d2a0 <error: Cannot access memory at address 0x5555d2a0>)
    at fileutil.c:16
#2  0x00005555555563ba in main (argc=2, argv=0x7fffffffe6f8) at main.c:33

2022年7月7日

kanataso
kanataso
とりあえずexternを作って
#define stdin __stdinp
extern void *__stdinp;
するようにしました

2022年7月8日

kanataso
kanataso
セグフォの原因判明しました
#define bool int
してたら、sizeof(_Bool)は1のようで、
struct s
{
  void *a;
  bool  b;
  void *c;
};
みたいなstructのサイズと要素のoffsetがずれてました
_Boolを作って解決....
hsjoihs
hsjoihs
あぁなるほど
uint256_t
uint256_t
そういえば、次のミーティングはいつになったんでしたっけ。

2022年7月9日

hsjoihs
hsjoihs
なんとまだ決めてないんですよね。
uint256_t
uint256_t
了解です。いつにしましょうね。
hsjoihs
hsjoihs
@L3ゼミ受講生 @uint256_t @れもん(Lちゅた @tamaron(L3チューター) ということで決めましょう。予定合いそうなのが土日しかないので今日と明日で。
lemolatoon
lemolatoon
明日ならいつでも、今日なら14時45分までに終わるか、22時以降ならできます。
かりんとう
かりんとう
明日の20:00から予定があるので、それ以外ならいつでも大丈夫です。
れもん
れもん
いつでも
uint256_t
uint256_t
ちょっと今から用事があるので、今日の夜なら。(or 明日)
mikiken
mikiken
明日は23時くらいまで外出してるので、今日の夜希望です。(ただ進捗ないので、私抜きで明日やってもらっても大丈夫です)
kanataso
kanataso
今日は21~23時以外。明日ならいつでもOKです
hsjoihs
hsjoihs
うお〜絶妙にみなさんの予定が合わない
れもん
れもん
今日の23時から?
hsjoihs
hsjoihs
それは私のリパライン語上級があるんよ
れもん
れもん
あ~
hsjoihs
hsjoihs
まあ並行してやることはできますけど(『リパライン語上級』は冒頭以外こっちからは喋らないので)
@L3ゼミ受講生 @uint256_t @れもん(Lちゅた @tamaron(L3チューター) 今日の 23 時からやりましょう。私の方の予定はこっちでなんとかします
それはそれとして、暇なのでもう通話に入っておくか
んじゃ(受講生と講師は)揃ったのでやっていきますか
かりんとう
かりんとう
セルフホストができてから、ソースコードをリファクタリングしたりバグ修正したり switch とか bool とか union とかを足して C89 に近づけている。develop ブランチで作業している。
今後どうするかを若干悩んでいて、とりあえず主要な機能、関数ポインタとかネストの型宣言とか、タグのスコープとか、そういうのを実装しようと思う。C89 にただ近づけても味気ないしどうしようかなと悩んでいる。それで応募したときに『OSSコンパイルする』ってのがあったと思うけど、どれをコンパイルするのかと気になってた。あとは独自拡張の方向に変えようかなとも思っている。GC とか string 型とか。過去の参加者のレポジトリを見ると、リンカとかアセンブラとか。そっちもアリなのかなと思っている。ただ、やりたいのは独自機能、オレオレコンパイラ。そのような感じですかね。
uint256_t
uint256_t
オレオレコンパイラ作る方向でいいと思います。どの OSS をやるかは決まってなくて、みんなに持ち寄ってもらおうと思ってた。『はりぼて OS』とか
かりんとう
かりんとう
それはなに
OSSのCってGCC拡張多くないですか?
uint256_t
uint256_t
小数はレジスタが増える
かりんとう
かりんとう
そっちの方向も面白そう
uint256_t
uint256_t
『はりぼてOS』は『30日でできるOS』のやつ
かりんとう
かりんとう
みかん本?
uint256_t
uint256_t
みかん本は C++
フリーなOSは他にもある
かりんとう
かりんとう
自作の上で自作、いいですねぇ
uint256_t
uint256_t
もちろん独自機能を突き詰めてもいい
かりんとう
かりんとう
少し考えてみたけど、結局他のコンパイラで実装されてる機能をCに持ってくるだけにならないかという悩みが
string といっても C++ だしなぁ
そこの機能を、新たな機能を考えるときのコツというのは?
uint256_t
uint256_t
多分可変長配列みたいなものが中に入りますよね?
かりんとう
かりんとう
演算を糖衣構文にして、みたいなのを考えてた
車輪の再発明では?
uint256_t
uint256_t
他の言語のいいところを持ってくるのはいいと思う
hsjoihs
hsjoihs
まあプログラミング言語の歴史というのを見ても、他の言語の特徴的な部分を持ってくるというのはよくあって、
典型例としては、Cのプロトタイプ宣言はC++からの輸入
あとは、Python のリスト内包表記 は Haskell から影響を受けていたはず
まあ、プログラミング言語の設計時には、前例とか先人の試行錯誤とかをいろいろ眺めるというのは普通のことだと思います
かりんとう
かりんとう
自分で考えても『これだ』っていう機能を……あーでも、他の……考えがまとまってないな
あんまいろんな言語に触れてないので、Rust の所有権の話とか興味ある。いいと思った機能を輸入するみたいな方向かなぁ
uint256_t
uint256_t
所有権周りは大変そう
hsjoihs
hsjoihs
個人的には、Rust を真似るなら、『継承なしの、impl でメソッドとトレイトだけやるあの仕組み』とかおもろいかなぁと思ってます
かりんとう
かりんとう
Rust のトレイトを知らん
uint256_t
uint256_t
『この型はこういうことができます』という仕組み
hsjoihs
hsjoihs
uint256_t
uint256_t
公式ドキュメントを読んでみよう。日本語がちょっと怪しいけど
かりんとう
かりんとう
Rust は公式のチュートリアル充実してますよねぇ
uint256_t
uint256_t
軽く Rust 触ってみて、ときめいた機能があったらそれを輸入してみるとか
かりんとう
かりんとう
心躍る機能を探してみます
uint256_t
uint256_t
たぶんなにかあるはず。パターンマッチとか
かりんとう
かりんとう
if が式とか
uint256_t
uint256_t
(Rustは)だいたい式ですね
hsjoihs
hsjoihs
その流れで言うなら、GNU 拡張にある『文式』とかどうですか?
かりんとう
かりんとう
({})
hsjoihs
hsjoihs
↑これです
かりんとう
かりんとう
実はもう実装してます
hsjoihs
hsjoihs
おっ
かりんとう
かりんとう
chibicc のテストにあったので実装した。Cの仕様なのかGNU拡張なのかわかんないことがある
hsjoihs
hsjoihs
まあこれが実装されてるなら if 式要らないのでは
かりんとう
かりんとう
int a = ({int a = 0; a;});
kanataso
kanataso
文式って最後の式の結果が返るんですか
hsjoihs
hsjoihs
そのはずです
uint256_t
uint256_t
chibiccのテストケースを眺めるのもいいかもしれない
かりんとう
かりんとう
kanataso
kanataso
じゃあ次僕が行きます
先週は、プリプロセッサを。コンパイラとプリプロセッサを分けて作ってたので、プリプロセッサだけ分けてコンパイルしよう、として、バグを見つけたりしてた。今もバグを探している。
uint256_t
uint256_t
今のところバグがない?
kanataso
kanataso
いまのところは。
今は、グローバル変数の宣言と同時に代入、とかを実装している
かりんとう
かりんとう
プリプロセッサも自作しているの凄い。cppに逃げてしまった。。
kanataso
kanataso
バグってる部分をテストケースに追加していったりしてる
hsjoihs
hsjoihs
これは人によると思うんですが、『常に一個だけ落ちるテストケースを入れてコミット』っていう流儀の人もいますね
一ヶ月とかリポジトリを放置したときに、『今なにが動いてないんだっけ』を確認するのは大変なので、という
uint256_t
uint256_t
CI が落ちそうで嫌だなと思いました
僕はコードに todo をまき散らしてます
hsjoihs
hsjoihs
まあそっちの方が多いですよね
uint256_t
uint256_t
今はプリプロセッサのバグの洗い出し?
kanataso
kanataso
というより、『プリプロセッサをコンパイルすることで、本体のバグを洗い出す』をしてます
uint256_t
uint256_t
そうやったほうがセルフホストには近い
その過程で詰まったりしました?
kanataso
kanataso
詰まるけど、一行一行生成されたコードを読んだり、どの関数で落ちてるかとかを二分探索して、とかしかやってない
それでできてます
uint256_t
uint256_t
一番原始的だけど簡単だし手っ取り早いですよね
あとはデバッガ使うとかしか思いつかないけど、他に工夫方法の案あります?
かりんとう
かりんとう
あまり工夫ができてない
printfデバッグですね
uint256_t
uint256_t
僕も結局そうなっちゃう
かりんとう
かりんとう
gdb でどこで落ちてるのかだけ探し、あとは printf
そもそも gdb の使い方をよく分かっていない
lemolatoon
lemolatoon
VSCode の gdb 拡張機能は IDE みたいで使いやすい
かりんとう
かりんとう
なるほど、それは使っていなかった
uint256_t
uint256_t
最初の方は GUI の方がやりやすいのかもですね
かりんとう
かりんとう
gdb 使ってて困るのは、値がおかしくなってるところを探したいけど、結局コードの量が膨大だとステップで追うのがつらい。何回もループしてるとブレークポイント貼ってもつらいし
エラー出た箇所から少しずつ戻れたらいいんだけどなぁ
れもん
れもん
伝家の宝刀『二分探索』
uint256_t
uint256_t
結局二分探索が強すぎる。42億行あっても32回で終わる
hsjoihs
hsjoihs
次の人移りましょうかね?
lemolatoon
lemolatoon
僕がやります。Rust で書き直してて、いまのところ、ポインタを足したり引いたりするところまでやって、テストケースをCで書いたり。あと関数呼び出しとか。
音声が途切れてしまったかもしれない
hsjoihs
hsjoihs
(音声が聞こえていません)
lemolatoon
lemolatoon
関数呼び出しとかをしてテストケースを書いたが、大学のテストが近くなっており、あんま触れてないなという感じ。
uint256_t
uint256_t
僕も大学のテストが近いです
lemolatoon
lemolatoon
Cで書いてた頃に『解析部分を分ける』とか言ってたが、Rust でそれをやってて、4段階に分けている。
型を分けるかという話をしてたけど、それを Expr を ConvertedExpr にしてるという感じで、めんどいけどすらっと書けてる
uint256_t
uint256_t
型システムの恩恵を受けた方があとあと楽
リポジトリ見てたけど、src ディレクトリの lib.rs にテストがいっぱい並んでるけど、tests ディレクトリに移動させるのもいいし、あとこうやってノードをたくさん並べるときは、insta というのがあって、
実行してできたASTをプリントしてファイルに保存してくれて、二回目以降はそのファイルの内容と同じASTが生成されてるのかを確認してくれる
hsjoihs
hsjoihs
スナップショットが変化してないことをテストする、ということですよね?
『昨日のコードが出力したのと同じASTを、今日も出力していますか?』というテストをするための仕組み
uint256_t
uint256_t
what it looks like ってところに書いてあるとおり。初回はスナップショットを print し、2 回目以降はそれと比較して調べてくれる
lemolatoon
lemolatoon
破壊的変更をするとリセット?
uint256_t
uint256_t
まあ diff をきれいに見せてくれます
プリティプリントがファイルに保存される
めっちゃ長いASTのトークン列とかを書かなくて済むのでうれしい
lemolatoon
lemolatoon
新しい機能を足したときに破綻しない?最初ミスってたら
uint256_t
uint256_t
そこは人間が見て調べる。cargo のサブコマンドとしてそれが提供されている。
lemolatoon
lemolatoon
最初に間違った AST が出力されても diff が出ずに気づけないのでは?
望み通りの AST が出てるテストはあったほうがいいのでは?
uint256_t
uint256_t
そういう意味で、assert_debug_snapshot を通って、その内容をファイルに保存するかどうかを人間が決められる。そこで人間がチェックを入れることができる。初回はそうする
実際に使ってもらった方が分かりやすい
じゃあ次の人
mikiken
mikiken
今日編入試験があったのでなんも進んでない。来週で頑張る。変数とか制御構文辺りをやっていきたい
uint256_t
uint256_t
if とかからですよね?最初にやるとどこかがバグると思う
mikiken
mikiken
制御構文辺りから難しくなってきそう
uint256_t
uint256_t
制御が飛ぶといろんなところが壊れたりする。ためらわずにリファクタリングしたり printf デバッグしたりしよう
かりんとう
かりんとう
continue とか break とかを実装するときに入れ子で苦戦した。永遠に無限ループ。ラベルの ID の管理がめんどくさかった
uint256_t
uint256_t
あれはグローバルに管理しないとダメだからなぁ
かりんとう
かりんとう
continue_label を持つのと、ジェネレータの中でもグローバルでも管理。ちょっときたないなと思ってるのでリファクタリングしたい
uint256_t
uint256_t
unique なラベルを作る関数を作ればいいというのはある
ラベルを作るための変数が複数あってきたない、ということですよね?
かりんとう
かりんとう
入れ子に対応するために、グローバルだけでなくローカルでも持ってる。
continue もグローバルで持たないと上手く行かなかった。continue は再帰呼び出しになって、for 文のジェネレータで呼ばれるから、どこに戻ればいいのか分からなくなるから、どこの for 文にもどるのかという情報をグローバルに持ってる。それをいい感じにする方法がわからない
uint256_t
uint256_t
入れ子構造はよくスタックとかで管理される。マシンスタックじゃなくてソースコード上でのスタック。新しく for ができるたびにその for に関する情報を載っけていく、とかよくやる。continue とか break は、スタックの一番上だけ見ればいい
かりんとう
かりんとう
そしたらグローバルだけでいけますね。いや、グローバルでも要らなくなるのか?トップの番号を見れば次の番号が分かる?
uint256_t
uint256_t
多少はマシになるんじゃないかな
スタック的な構造をグローバルに1個置いておくだけでもいいかも
static int continue_label; とかがグローバルにあるんですね、なるほど
今から書き直してもらえば全然いい
これで一応みなさん終わりましたね?
Y4の人がいますね。RISC-V でしたっけ?x86 に飽きたら RISC-V さんで作ってもいいですよ
かりんとう
かりんとう
_Alignof さんが作ってましたね
uint256_t
uint256_t
物理マシンがないのが欠点
かりんとう
かりんとう
入手するのが困難と言うこと?
uint256_t
uint256_t
あるにはある。こんどラップトップが出るらしい
確実に RISC-V が使われてる場所は増えてて、最近は GPU の中にもいたりするらしい
まあなくても QEMU を使えばいい
かりんとう
かりんとう
CPU の上に OS の上にコンパイラ、というのは、CPU が RISC-V なので、えーと今のコンパイラは動かないのか。RISC-V を吐くコンパイラを作って、そのコンパイラを RISC-V 上で動かすことも?
uint256_t
uint256_t
セルフホストできるならそれもできる
かりんとう
かりんとう
無限ループしそうで頭がグチャグチャになりそう
uint256_t
uint256_t
セルフホストが一回できるとそういう妄想ができるようになって良い
純粋に、x86-64 だけじゃなくて、他のものを触ってみると楽しい
かりんとう
かりんとう
あんまり触ったことがないので試したい
uint256_t
uint256_t
ARM とかでもいい
かりんとう
かりんとう
ラズパイとか触ったことがない
uint256_t
uint256_t
まあ買わないといけないですからね
かりんとう
かりんとう
在庫がないとかなんとか
uint256_t
uint256_t
Amazon 曰く 2 万円、それは高すぎないか
lemolatoon
lemolatoon
LLVMってあるじゃないですか、いろんなアーキテクチャにやってくれる、と。あれもやろうと思えば結構簡単にできるんですか?
uint256_t
uint256_t
その通りで、そのためにLLVMがある
clang を使えば中間表現を吐くことができる
ああいう中間表現のデザインを学ぶのもいいかもしれない。アーキテクチャに普遍的なものが垣間見える
一応去年の夏に LLVM にコントリビュートしてたので。とはいえあれは巨大すぎるので、手元のパソコンでビルドするのが大変。ノートパソコンだとキーボードが熱くなるしメモリが足りなくなる
かりんとう
かりんとう
なんか、clangを動かそうと思って、公式を見たら『makeしてね』って書いてあって、試したら終わらなくて諦めた
uint256_t
uint256_t
メモリ足りそうでした?並列処理の数とか制限できるので、制限したら終わるかもしれない
制限すればメモリに収まるかも。だいぶ時間かかるけど。
かりんとう
かりんとう
インストールするとなったらどういうのが正解?
uint256_t
uint256_t
プルしてきてビルドするしかない。Ubuntu ならパッケージマネージャで入るけど。
かりんとう
かりんとう
バージョンが古かったりとかする?
uint256_t
uint256_t

2022年7月10日

uint256_t
uint256_t
最新のを入れることができます、こうやって
なので自分でビルドする必要はないですよ
かりんとう
かりんとう
公式のでも書いておいてくれよ~
uint256_t
uint256_t
手元だと4分で終わりそう。でも32コアあるからなぁ。そこまでやれとは言わん
かりんとう
かりんとう
ノートパソコンだと無理だなぁ
uint256_t
uint256_t
最近の MacBook だと 10 分ぐらいでできる。まあ、素直にできたものをダウンロードしましょう。
ninja は勝手に並列化してくれるので好き
最近のコンパイラは大きい
LLVM は C++ でも Rust でも、Rust だとほんと簡単に使えるのでやってみるといい。inkwell というライブラリがあって、
LLVM の FFI そのままではなく、Rust 向けにすごくいい感じに組み直されたもの。Example の通りにやるとサクッと動く
かりんとう
かりんとう
LLVM の Qiita 記事読んでたけどよく分かんなかったからなぁ
uint256_t
uint256_t
LLVM は破壊的変更が多い
さっき触ってたらあった
ver 13 から 14 に上げたら動かない、とか。嫌になる
ver 15 なんだけど、未だに 3 とか 4 とか使ってる人がいる
僕が子供の頃 3 だったからなぁ
そんな感じで、なんか微妙
かりんとう
かりんとう
3 から今のバージョンに上げるのとかやりたくないだろうしなぁ
uint256_t
uint256_t
JIT 辺りが本当にひどくて
一つのライブラリなのにいろんな方法が乱立してる
どんどん新しくなっていくせいで、いまなにを使えばいいかわからんし、チュートリアルも微妙に古いし
まあでも、LLVM が偉大なのは変わらないので
hsjoihs
hsjoihs
れもん氏も言ってたな。V言語の作者は LLVM の破壊的変更が多すぎてサポートしてらんないとキレてたらしい
uint256_t
uint256_t
気持ちはわかる
スピード感があって良いんだけど、ということは変更が雑
れもん
れもん
clang と Rust ぐらい力があれば、(LLVM の開発速度に追従して)押せると思うけど
uint256_t
uint256_t
意外とカジュアルにコード書き換えちゃっていいんですよね
LLVM自体の開発を去年の夏にやってた。中間言語を読み込んでパースする部分から、各 ISA に向けて最適化してコードジェネレート、まで全て含んで LLVM。だからデカい。全部だもん
だからビルドが遅いのもしょうがない。OSSというのは時間が経つと肥大化する
かりんとう
かりんとう
LLVM の JIT という話が気になったけど、JIT を使うとインタプリタで実行することになって、LLVM 用のインタプリタが存在するってことですか?
uint256_t
uint256_t
要するに、中間言語が即座に各アーキテクチャごとの機械語に翻訳される、というJITコンパイラの話をしている
それとは別に、中間言語のインタプリタも一応あって、一応動くけど、最近はそのインタプリタの中にJITが侵入してきている
lemolatoon
lemolatoon
JIT コンパイラというのは、機械語まで変換してメモリに書き込んでジャンプ、みたいな感じなんですかね
uint256_t
uint256_t
そうです。機械語流し込んで、リンクまで行っちゃう。まあ JIT できる処理系って最近スクリプト言語でも多いので。でもバックエンドに LLVM 使ってるのあんまり多くないと思う。巨大すぎるので
巨大すぎて、依存関係としてくっつけるのが嫌がられがち
LLVM の小さいバージョンを勝手に作ってる人はいくらでもいる(僕を含めて)けど
最適化が強力で、いろんなアーキテクチャに対応してるので、それだけでデカくなる
なので、セキュキャンとしていろいろ学んで、より良いものを作って欲しい
かりんとう
かりんとう
精進していきたい。他のコンパイラがどうなってるかは気になってる。Go の話とか Discord に貼ってたと思うけど
Go が吐いてるアセンブリを見て思ったのが、名前空間のラベルがきれいだなぁとか思ってた
uint256_t
uint256_t
バイナリを見るだけでも言語ごとに味が違いますよね
Go は専用のアセンブラとかディスアセンブラとかあって、バイナリに落ちる前のそういうのを見てみてもいいかも
Goは至る所がGoで書かれてるので、セルフホストしたい人にはいいのかも
この夏で C コンパイラ読めるようになったら、他の言語のも試してもらって
かりんとう
かりんとう
Go は string 型の足し算を愚直にやると遅い、みたいな話を前に聞いて、ビルトイン関数を読んだりしたけど、かなり読みやすかった。どこのファイルにどの機能があるのかとか分かりやすかったし、すっきりしてた
uint256_t
uint256_t
Go をやった人はそういう感想を抱きがちですよね
Go は『これがいい』じゃなくて『これでいい』、とはよく言われる
Rust は『これがいい』、Go は『これでいい』、と、なぜか対比される。そういうことじゃないとは思うけど
hsjoihs
hsjoihs
Go は "Better C" としての哲学、Rust は "Better C++" としての哲学、とはよく言われますよね
uint256_t
uint256_t
だからあんま比較するもんじゃないと思うんですけどね。今後 C コンパイラ拡張するときには、他の言語からいいところをバンバン持ってきましょう。今のCがCの最終形態ではない。僕たちがCを進化させることができる
hsjoihs
hsjoihs
そもそもRustというのは、究極的には『Cに対して所有権のアノテーションを付ける』というところから始まってる
【編注:ホンマか?たしかCに対して所有権のアノテーションを付けるような研究はあったはずで、それがRustに影響を与えてはいるはずだが、『究極的に~始まってる』は言いすぎでは?】
uint256_t
uint256_t
昔の Rust コンパイラは OCaml 製だったし
れもん
れもん
uint256_t
uint256_t
だいたい『思いつきで作り始めて、どんどん人が寄ってきて大きくなる』というものなので
hsjoihs
hsjoihs
皆さまにはお配りしていない、旧い版の『C言語ポインタ完全制覇』には、
いや新版にも載ってるな。p.21
このように、Cは、現場の人間が、自分たちの用途のために、必要に応じて作成した言語です。その後もCは(中略)かなり行き当たりばったりに機能拡張を繰り返してきました。
p.31
Cは、プログラマーは全知全能であるという理念の元に設計されています。Cの設計で優先されたのは、 ・いかにコンパイラを簡単に実装できるか(Cを使う人が、如何に簡単にプログラムを実装できるか、ではない) ・いかに高速な実行コードを吐くソースが書けるか(いかにコンパイラで最適化して、高速な実行コードを吐くか、ではない)
uint256_t
uint256_t
でも今はコンパイラが中途半端に賢いので、高級アセンブラとして使うと未定義動作でタイムマシンする
あーこれは説明が要るな
hsjoihs
hsjoihs
れもん
れもん
コンパイラだけじゃなく、CPU もタイムトラベルを起こす
uint256_t
uint256_t
LLVM にもこういうの検知するための仕組みがある
最適化を検証するためのツール
かりんとう
かりんとう
話を少し戻すんですが、タイムトラベルの意味とは?
uint256_t
uint256_t
タイムトラベルという言い方が分かりづらいと思うんですが、
hsjoihs
hsjoihs
記事を読んでもらうのが良い
つまり、未定義動作のある関数というのは、その関数を実行した【後】に迷惑を掛けるだけでなく、その関数を実行する【よりも前】に迷惑を掛けることがあるぞ、という話を『タイムトラベル』と呼びます
かりんとう
かりんとう
いまいちピンとこない
hsjoihs
hsjoihs
C の仕様上としては、『そういうアカンものを含むものは、そもそも C ではありません』というスタンス。
uint256_t
uint256_t
つまり、人間が完璧であることを求める
hsjoihs
hsjoihs
ここの話はややこしいので日を改めた方がよさそう
uint256_t
uint256_t
本質的に難しいんですよ
hsjoihs
hsjoihs
そして、配った本にも載ってない
uint256_t
uint256_t
というかこれが載ってる本あります?
hsjoihs
hsjoihs
少なくとも和文だと知らない
こういうのを和文で提供する意義はデカいので、ついに講師が教材を作るときが来たのでは?なんと今まで教材という教材を作ってないので
uint256_t
uint256_t
そういう動作をトリガーしてしまう最適化の話もしたい
hsjoihs
hsjoihs
はい、それは前提のつもりでした
uint256_t
uint256_t
いま作ってるCコンパイラに最適化を付け加えてくれてもいい。そういう質問も歓迎
かりんとう
かりんとう
最適化を実装するときに参考にするものが欲しい
SSAあるんだ~ってレベルです
uint256_t
uint256_t
SSA重要です。というとか最近SSA以外使ってない
LLVM-IR が SSA なんですよね
SSA とは、static single assignment 『静的単一代入』で、一つの変数には一回しか代入できない
Wikipedia 見たら最適化の話も載ってるな
本格的に最適化するなら SSA を使った方がいい
かりんとう
かりんとう
変数の生存期間がより分かりやすくなる?
uint256_t
uint256_t
その通り。ほぼ自明になる
かりんとう
かりんとう
ヒープは?
uint256_t
uint256_t
変数と言ってるが、式を計算してるときの中間の値を代入するところとかも含む
SSA の文脈ではスタックとかヒープとかあんま考えてなくて
れもん
れもん
ほぼ『名前への束縛』
かりんとう
かりんとう
コンピュータとは完全に切り離して、別名みたいなものだと思った方がいい?
uint256_t
uint256_t
まあそう……そうですね。本当に難しいな。なんて言えばいい?
かりんとう
かりんとう
資料を見ながらやった方がよさそう
何を見て勉強された?
uint256_t
uint256_t
LLVM IR のデザインを見て勉強した
SSA はヒープ領域とかまで面倒を見てはくれない
かりんとう
かりんとう
具体的にあまりピンときていない
uint256_t
uint256_t
ちゃんと文章を書いた方がいいですね
hsjoihs
hsjoihs
講師で書きましょう。あ、それをやる上で
みなさん英語読むのってどれくらい慣れてます?
@かりんとう @lemolatoon @kanataso @mikiken お答えください
lemolatoon
lemolatoon
単語を調べながらなら
mikiken
mikiken
技術系の英文はあまり読んだ経験がないですが、DeepLフル活用ならなんとか読めそうです()
kanataso
kanataso
同じく...
かりんとう
かりんとう
同じく...
hsjoihs
hsjoihs
なるほどです
uint256_t
uint256_t
これはどういう文脈で?
hsjoihs
hsjoihs
読み物というのは常にターゲット層を意識して書かなきゃいけないので、それの把握のためですね
いや、要は全員が私の読む速度で英文読めるんだったら英文のまま引用するんですけど、ってこと
多分そんなことはないので真面目に日本語でやります
たしか『1 秒あたり 10 単語強』とかだったはずです、私の英語読書速度
uint256_t
uint256_t
hsjoihs
hsjoihs
hsjoihs 試しに実測してみました。 https://en.wikipedia.org/wiki/Serif の本文で 4分半ぐらいですかね 16:04 えーと 2800 単語を 270 秒で読んでるので、1 秒に 10 単語強ですかねぇ 16:10 https://irisreading.com/what-is-the-average-reading-speed/ これの本文が2分半。1525 単語あるっぽいので、やっぱり 1 秒に 10 単語強ですね
uint256_t
uint256_t
最適化・未定義動作・SSA入門、とかですかね
hsjoihs
hsjoihs
そうですね
Rui 本が和文であることの意義ってめちゃめちゃ大きいんですよ
あれのおかげで、なんなら英語圏よりも多くの C コンパイラが日本語圏で書かれてます
uint256_t
uint256_t
数的にはそうですよね
かりんとう
かりんとう
英単語が分からないのか、それとも概念が分からないのかが分かんなかったりするから和文ありがたい
lemolatoon
lemolatoon
よくある
uint256_t
uint256_t
なるべく平坦な英語で書いてくれればいいんですけど
かりんとう
かりんとう
論文とか読める気がしない
uint256_t
uint256_t
論文はまだ読みやすい
英語でドラマとか見てるとよくわかんない。小説も
かりんとう
かりんとう
口頭でしか使わないやつとか、日常生活で使う単語が多いから、とかですか?
uint256_t
uint256_t
書き言葉は話し言葉と違うので。というかこういうのは hsjoihs の方が詳しいですよね?
hsjoihs
hsjoihs
はい。
uint256_t
uint256_t
今は youtube ありますけど、とはいえインタラクティブにコミュニケーションしないと分からないことというのはある
hsjoihs
hsjoihs
かりんとう
かりんとう
雑音が入るだけで何言ってるか分からないというのは全然ある
hsjoihs
hsjoihs
とまあそういうことで、はい、教材頑張ります
でも今テスト近いんでしたっけ?
uint256_t
uint256_t
みなさん近いんじゃないですか?でもそんなに気にしなくていいですよ。課題とテストに押しつぶされるのはよくあること
かりんとう
かりんとう
でもそれにプラスアルファは
uint256_t
uint256_t
まあ気合いですよ。少なくともこれは嫌なことではないので
全然やりますよ。受講生の方は心配しなくていい
書きながら公開すると思うので、どんどん質問とか文句とか
hsjoihs
hsjoihs
いや、なにかというと、私は夏休みで、しかもセキュキャン以外の労働を入れてないので
uint256_t
uint256_t
セキュキャン終わったら僕もインターンですね
僕はどこ行ってもコンパイラ書いてるだけですけど
かりんとう
かりんとう
低レイヤ寄りのインターンってどう探す?
uint256_t
uint256_t
全般は分からない。僕は PFN で。低レイヤ全般ってどんなのがあるんですかね。院生に訊いた方がよさそう
hsjoihs
hsjoihs
れもん氏~
uint256_t
uint256_t
僕はただの大学三年生なので
hsjoihs
hsjoihs
私は応用物理・工学物理科なので
uint256_t
uint256_t
インターン先にスパコンがあって、それに向けて何かをします
言える範囲でお伝えできれば
hsjoihs
hsjoihs
時間も遅いしそろそろ解散?
uint256_t
uint256_t
来週はどうする?金曜に戻せる?
金曜がダメな人います?
かりんとう
かりんとう
いろいろあって、全員受講の 15 と 22 のは大丈夫
hsjoihs
hsjoihs
えーとつまり、
uint256_t
uint256_t
次は金曜日でいいってこと?
lemolatoonかりんとう
lemolatoonかりんとう
大丈夫そう
kanatasoかりんとうmikiken
kanatasoかりんとうmikiken
👌
lemolatoon
lemolatoon
20:30まで全員受講のがある
uint256_t
uint256_t
じゃあ金曜日 21:00 から
じゃあ、今日のところはこんな感じで
なにに焦点置きましょうかね
hsjoihs
hsjoihs
『SSA』 vs. 『未定義動作+最適化』で二本立てだと思います
SSA は実装、『未定義動作+最適化』は理論なので
uint256_t
uint256_t
すぐに役立つかはわからないけど、(殆どの人がセルフホスト段階だし)、それ以降に欲が出るから教材は必要
hsjoihs
hsjoihs
というか『これです』と言って指差せる和文教材がないのは普通に困るんですよね
uint256_t
uint256_t
ないわけではないけど、一応本はあるけど
SSA も理論では?
hsjoihs
hsjoihs
じゃあ pragmatism vs. idealism っていう表現に改めておきます
ある意味で、工学 vs. 法学、とも言える
uint256_t
uint256_t
僕の頭が実装に傾いてて理論がおろそか
hsjoihs
hsjoihs
逆に私は language lawyer 見習いをやる行為の方が好きなので
uint256_t
uint256_t
昔書いていたものの残骸:
hsjoihs
hsjoihs
おー
uint256_t
uint256_t
教材を組み立てるのが下手すぎるんですよね
hsjoihs
hsjoihs
あー。私は教材の組み立て方を考えるの割と好きですね。なんならそれで就活して内定いただけたし。
uint256_t
uint256_t
組み立てて書ければいいんですけど、組み立てて終わりそう
hsjoihs
hsjoihs
そこはインクリメンタルですよ。常に『不完全ながら教材として使える』を維持し続ける
実装で todo!() を書くのと同様、最初は『このリンク先読んで!』とだけ書いておくんですよ
工数が確保できたら、そこの部分もちゃんと説明を書く
これをやると、最終的にそこそこ自立した教材になっている
れもん
れもん
ねむい
hsjoihs
hsjoihs
解散!
uint256_t
uint256_t
SSA黎明期:

2022年7月11日

hsjoihs
hsjoihs
@L3ゼミ受講生 そういやお配りした本ってどこまで読みました?
lemolatoon
lemolatoon
四章まで目を通しました。

2022年7月12日

かりんとう
かりんとう
2章の前半まで読みました。今週中にでも時間を確保して進めます。
uint256_t
uint256_t
もちろん読み進めてもらって大丈夫なのですが、別にノルマがあるわけではないので、興味のありそうな部分から読んでいっても問題無いです。
hsjoihs
hsjoihs
別に『読んでないと困る』というものではないので、ご自分のペースでどうぞ~
あと、未定義動作について少しずつ書いてるんですが、とりあえずこの記事とかがあるのを思い出したので貼っておきます
Kenta IDA
Kenta IDA
Rustとかである操作が必ずpanicしますって言うと、でも死ぬんでしょ?って言われることちょいちょいありますけど、未定義動作(何が起きるか分からない)より「必ず死ぬ」のが大事やねんでとかそういう話が意外と広まってないよなとか思ったりしました。

2022年7月14日

kanataso
kanataso
たぶんセルフホストできました😶‍🌫️

2022年7月15日

hsjoihs
hsjoihs
@L3ゼミ受講生 @tamaron(L3チューター) @れもん(Lちゅた 金曜日21:00からでしたね
未定義動作のやつがまだ書けていません
uint256_t
uint256_t
最適化のやつ、どんなこと書くといいんですかねぇ
かりんとう
かりんとう
そもそもどういうものなのかがわかってないので、初歩的なところからお願いしたい
れもん
れもん
『未定義動作とは』
uint256_t
uint256_t
そこが大事ですよね
hsjoihs
hsjoihs
章立てとしては、『どうして未定義動作なんてことになったのか』『未定義動作のうれしさ』『provenance』『strictly conformingは幻』みたいな話を書こうと考えているんですが、今の話だと『未定義動作とはなんぞや』を書き忘れていることに気づいたので、それもやります
れもん
れもん
あとは未定義動作の見つけ方とか対処の仕方とか
かりんとう
かりんとう
見つけてくれるツールとかあるんですか
hsjoihs
hsjoihs
UBSanといいます
uint256_t
uint256_t
sanはsanitizerのsan
あとはアドレスサニタイザ、ASan、とかがある
かりんとう
かりんとう
サニタイザ使ったことなかったけど、hsjoihsさんのデバッグのときに使ってたやつ?
hsjoihs
hsjoihs
clang のサニタイザはかなり優秀なので、積極的に使っていってもいいと思います。わたしも自分のCコンパイラをサニタイザで洗ったりしたことがある
まあもちろん safe な Rust で書けばサニタイザが要らないという話はあるが……
そのための言語なので……
かりんとう
かりんとう
lemolatoonさんは Rust で書いてますよね
lemolatoon
lemolatoon
そうですよね、進捗あんま出てませんが。もともとCよりRustの方が書いたことがあって、応募するときに他の言語でもいいと言われたのでRustで
かりんとう
かりんとう
言語なんでもいいですよというのは惹かれるものがあった
hsjoihs
hsjoihs
ならよかったです
uint256_t
uint256_t
Cに限定しないほうがいいよねという話はしてたので
かりんとう
かりんとう
別の言語でやりたかったけど、Cで途中で進めたかったのと、セルフホストしたかった
GoかRustを考えてた。Rustは触れたことがないので勉強しながらという感じだったけど。Goは書きやすい言語なのかというのは正直よくわかってないけど、木構造とかどう管理するんだろうという気持ちはある。連結リストを使うの?vector / slice で頑張るの?
Rustは?
lemolatoon
lemolatoon
式だったら式で、enum、あ、Rust の enum はタグと一緒に構造体を持てます。それをパターンマッチすると過不足なくできているかをコンパイラが見てくれる
hsjoihs
hsjoihs
そうなんですよね。便利
れもん
れもん
Goはインターフェースとかでどうにかすると思う
uint256_t
uint256_t
GoはGoのコンパイラ読めばいい
かりんとう
かりんとう
Rustのenumのところ、あんまり理解できていない
hsjoihs
hsjoihs
前に作ったスライドがあるので貼る
uint256_t
uint256_t
chibicc はソースコードがフラットに置いてあるけど、でかいと大変。ところでLLVMってのがあって、数多の最適化のファイルがフラットに並んでいる
hsjoihs
hsjoihs
パターンマッチの話は、↓ の冒頭数枚とか
tamaron
tamaron
こんばんは、先週から時間勘違いしていました。帰宅中なのでミュートで参加します
uint256_t
uint256_t
ほんの一例:
Cの方、匿名のunionでいいのでは?
hsjoihs
hsjoihs
これは私のCコンパイラから取ってきたコード。当時 union を実装していなかった
あと、これ三項演算子なら ptr1, ptr2, ptr3 全部使いますからね?union では即座には解決しない
uint256_t
uint256_t
あーそうか
hsjoihs
hsjoihs
Box というのは、ヒープにメモリを確保するというもので、まあ要は malloc と同じようにヒープにメモリを確保する。これがなぜ必要かというと、さもないと構造体ってそのメンバーの分だけサイズがかさむ。ということは、Expr の中に Expr を直に置けない
uint256_t
uint256_t
無限になっちゃう
かりんとう
かりんとう
Cでもポインタしか置けない、ってやつと同じか
hsjoihs
hsjoihs
はい、Box はポインタで、ポインタが指してる中身の所有権を持ちます。Box が死ぬ(デストラクトされる)と、メモリが解放されます
かりんとう
かりんとう
うまいことやってくれるんですね、すごい。所有権、理にかなってるし、変数の寿命に着目して、引数とか戻り値とか見ながら、ヒープに確保したメモリのポインタの値が一つしかないようにする?みたいな
hsjoihs
hsjoihs
だいたいそんな感じです
lemolatoon
lemolatoon
可変参照は一つ、不変参照はいくつでも
hsjoihs
hsjoihs
Rust は不変がデフォルトで、書き換え可能なときに mut をつけます
かりんとう
かりんとう
デフォルトが不変というのは新しいですね
れもん
れもん
struct Expr {
  enum ExprCategory category;
  union {
    struct {
      enum UnaryOp op;
      struct Expr* ptr1;
    } unary;
    struct {
      enum BinaryOp op;
      struct Expr* ptr1;
      struct Expr* ptr2;
    } binary;
  };
};
switch(expr.category) {
  case UNARY_EXPR: // expr.unary.opとか
  case BINARY_EXPR: // expr.binary.opとか
}
// expr.category == UNARY_EXPRのときにexpr.binaryにアクセスできてしまう
uint256_t
uint256_t
Cでも↑みたいなことはできるけど、コンパイラは保証してくれない
lemolatoon
lemolatoon
Cにタグ付き union 実装できたらたのしそう
かりんとう
かりんとう
unionってタグ付きでは?
hsjoihs
hsjoihs
union 自体は、単に『重ねて配置する』というだけの機能であって、実際 union を実装するとき自体にもかなり struct が流用できます
かりんとう
かりんとう
実装してて思ったのは、union を構造体として見なせないのか?構造体として扱えないのか
hsjoihs
hsjoihs
まさにそれだけで、union の最も素朴な実装は、『全メンバのオフセットが 0 であるような構造体』
れもん
れもん
サイズは気をつける必要がある
hsjoihs
hsjoihs
アラインメントはメンバの最大のやつ、サイズはメンバの最大のやつを含むことができつつアラインメントを満たすもの、でいいはず
かりんとう
かりんとう
シンプルに実装できそうですね
uint256_t
uint256_t
アラインメントが面倒なんですよね
かりんとう
かりんとう
以前 union を実装したときのは ↓ なのだけど、コード量が多くなってしまった。もっといい方法があったのではと感じた
うまく抽象化したかった
hsjoihs
hsjoihs
でも C ならこんなもんでは?
かりんとう
かりんとう
同じ関数で union か struct かで処理を変える、みたいなのにしたところもあるけど、全体としては
hsjoihs
hsjoihs
下手に抽象化すること自体を目的にしすぎるのもそれはそれで、とは思いますよ。特に C だし。
uint256_t
uint256_t
早すぎる抽象化はやめたほうがいい
かりんとう
かりんとう
追加機能を実装する際に抽象化が仇となるということ?
hsjoihs
hsjoihs
たまに『抽象化したけど結局分離しなきゃいけないじゃん』はある
れもん
れもん
オブジェクト指向の限界ってやつ
かりんとう
かりんとう
未来まで見据えるのは高度な技になりそうだ
uint256_t
uint256_t
だからこそ、すぐに書き直せるコードというのは大事
かりんとう
かりんとう
今週は進捗報告やらないんです?
hsjoihs
hsjoihs
じゃあやりましょうか
かりんとう
かりんとう
この1週間だと、コミットが5つぐらいしかなくてあまり進んでいない。何をやったかというと、2進数リテラル・16進数リテラル、あとは型のBNFを直したり。BNFをしっかり見ないで『こんなもんだろ』と実装してたので、C89のBNFを見ながら実装をリファクタリングしている途中。型を付けたい。long long とかの並び順とかがテキトーでもいいということらしいので、今まで typedef int A; とかしか認識なかったけど、int typedef A; とかでも認識できるようにしたい。chibicc を見たらフラグ管理で書いてたのでそうしている。
long long とかも long int long とか書いてもいいんですね
int signed long typedef long A;
hsjoihs
hsjoihs
れもん
れもん
2進数リテラルってC23では?
hsjoihs
hsjoihs
実はまだ標準入り『は』してないんですよね。ほとんどのコンパイラが勝手に実装してるけど
せっかく自分で作ってるんだからどんどん勝手に足してもいいんですよ
かりんとう
かりんとう
任意進数リテラルとか
hsjoihs
hsjoihs
構文がぶつからないようにする方法が腕の見せ所ですよね
かりんとう
かりんとう
そんな感じで、型のBNFを書いている。あと、新たな機能を追加したいけど何をするか、というので、Rustを見てみている。Rustのチュートリアルとか。match文とか。まあなんか、全てにmatchしないといけないってのをコンパイラが見てくれてエラーを吐いてくれるというのはありがたい
tamaron
tamaron
Rust の機能を実装するなら、↓ を見ると、コンパイラがどうなってるかが実装含めて書いてあって、おすすめ。所有権チェックとかも書いてある
かりんとう
かりんとう
参考になる
難易度としてはどんな感じ?
tamaron
tamaron
型検査だったらそんなに難しくない。もともとソースコードは木の形なので、導出木の形がソースコードの木の形と対応してて、そんなに難しくない。最初は関数型言語の型検査機とか読んでみるといい
かりんとう
かりんとう
関数型言語全然しらない
tamaron
tamaron
MLからスタートするのがいいかも
かりんとう
かりんとう
みんなRustやってるなぁ
hsjoihs
hsjoihs
tamaronさんはRustのlinterにコミットしてる方なので
uint256_t
uint256_t
多分Rustに一番詳しいのでは
tamaron
tamaron
インターフェースは見てるけど、内部は見てない
uint256_t
uint256_t
僕はバックエンド読んでました
tamaron
tamaron
ソッチのほうが難しいと思います。ボローチェックは木の形だとできないので、コントロールフローグラフにする必要があって、それが手間なんじゃないかな
hsjoihs
hsjoihs
昔のRustはスコープで所有権やってたので、lexical lifetime でいいんじゃないですか?かりんとうさんは所有権をやるとは一言も言ってないですが
かりんとう
かりんとう
所有権ってスコープとかですよね?C++ ではあるんですか?ムーブセマンティクスとかの話とか見たことがあるけど
uint256_t
uint256_t
デストラクタはありますけど
かりんとう
かりんとう
freeみたいな?
スコープを抜けるとfreeされる?
uint256_t
uint256_t
そうです
hsjoihs
hsjoihs
Bjarne Stroustrup が、『C++で一番好きなトークンは?』と聞かれて『閉じ波括弧』と答えたという逸話があります
C++で一番好きな機能: } (閉じ波括弧) 閉じ波括弧でスコープが保証される ここはちょっと驚きだった。閉じ波括弧というなんともマニアックな機能がお気に入りだとのこと。たしかに,閉じ波括弧でスコープを形成でき,これにより自動的にオブジェクトの解放もできるので,大事なのかもしれない。
uint256_t
uint256_t
C++11からムーブセマンティクスなんですね
hsjoihs
hsjoihs
まあただあれは分かってないと使えないからなぁ。Google のコードベースでもムーブセマンティクスをやりそこねたやつがあったはず
れもん
れもん
C++のライフタイムについていいドキュメントないかなぁ
hsjoihs
hsjoihs
じゃあ他の皆様のも訊いていきますかね。ってかもう 1 時間経っちゃったよ
じゃあ次は……mikikenさん
mikiken
mikiken
ローカル変数をトークナイズできてるけどパースでsegfaultする。変数が入るやつに鳴るとセグフォする
こういうのってどうデバッグするんですか?デバッガを入れる?これまでデバッガ使ったことがなくて
hsjoihs
hsjoihs
別にprintfデバッグがダメというつもりはないですが、普通にデバッガは便利です
かりんとう
かりんとう
あとはデバッグ用関数を用意するとか。木構造をCLIに再帰的に表示してくれる関数とか。なんかおかしくなってないかを目視で確認できるように
mikiken
mikiken
なるほど
hsjoihs
hsjoihs
コンパイラの場合は、大量にポインタをたどる木構造になることが多いので、全部をprintしてくれる関数を作ってprintfデバッグするのはありです
れもん
れもん
というかsegfaultしてるならデバッガでバックトレース見ましょう
mikiken
mikiken
バックトレースとはなんですか?
れもん
れもん
この関数の中でこの関数を呼んで、その中でこの関数を呼んで、みたいなのを追ってくれる
どこで落ちたかを見ることができる
hsjoihs
hsjoihs
どの関数に呼ばれたどの関数が死にましたか?を調べる
uint256_t
uint256_t
デバッガ使ってたら落ちたところからのバックトレースが普通に出る
かりんとう
かりんとう
落ちたところの関数名は見れていたなぁ、と
れもん
れもん
gdbだとこれ【編注: 7月6日の末尾を参照】です
mikiken
mikiken
あー、落ちたところの関数が出て、なおかつここを読んでるときに落ちましたよ、みたいな感じですか
れもん
れもん
何行目で落ちたのかが出るというだけでも便利
WSL1だとできなかったりする
かりんとう
かりんとう
ノードのデバッグ
hsjoihs
hsjoihs
めちゃめちゃいいですね
かりんとう
かりんとう
わりと簡単に実装できるのでおすすめ
uint256_t
uint256_t
デバッグはなるべくしやすいほうがいい
hsjoihs
hsjoihs
私もこんなの書いてた気がするけど覚えてないな
Rust の場合は Debug ってのがあるのでそれを使えばいいです
uint256_t
uint256_t
あれは便利
lemolatoon
lemolatoon
CからRustをFFIして呼んでみたりした
hsjoihs
hsjoihs
その手があったか
ちなみに、セグフォった時点でメンション飛ばしてもいいんですよ
いまこの話をしながらコードを読むのも難しいので
れもん
れもん
ミーティング後の雑談とかででもやりますか
hsjoihs
hsjoihs
それでもいいんですけど、こちらとしてもメンションは早く来たほうが予習時間は増える
mikiken
mikiken
動いてないけどコミットするかぁ
hsjoihs
hsjoihs
動いてないなら別ブランチにコミットしてもいいかもですね。コミットメッセージに NOT WORKING ってでかでかと書いてコミット、とかでもいい気がしますけど
Gitまわりの話もミーティング後にやりましょうか?
あ、もう別ブランチになってるんですね。じゃあコミットして良さそう
残り二人訊いておきますか。kanatasoさん
kanataso
kanataso
セルフホストができました!
hsjoihsuint256_tれもんtamaronmikikenlemolatoonかりんとう
一同
パチパチパチ
kanataso
kanataso
それ以降は、今は union を作ってます
セルフホストは、簡単ですね!
難しくはない気がしてきた
uint256_t
uint256_t
いいですね、その感覚
hsjoihs
hsjoihs
セルフホストの面倒を避けるためにコンパイラの実装本体で使うのを避けたCの機能ってあります?
kanataso
kanataso
可変長引数を受け取る関数を最後の最後にしたのだけど、未だにやってないのは、struct A だけ書いて中身を宣言するみたいなやつが、まだできない。でもすぐできるはずです
hsjoihs
hsjoihs
不完全型の概念ちょっとめんどくさいんですよね〜
かりんとう
かりんとう
typedefは?
kanataso
kanataso
その場で宣言してます
hsjoihs
hsjoihs
そんなところですかね
kanataso
kanataso
はい
hsjoihs
hsjoihs
じゃあ最後にlemolatoonさん
lemolatoon
lemolatoon
今週忙しくてなにもできてない。cargo doc を GitHub Pages につけた。それ以外はやってない。insta とかも試してみたいけどやってない
uint256_t
uint256_t
でもちゃんと doc コメント書いてるんですね
lemolatoon
lemolatoon
30分の1の確率ぐらいでしか書いてない
hsjoihs
hsjoihs
cargo doc 偉大ですよね〜
lemolatoon
lemolatoon
やるとテンション上がる
hsjoihs
hsjoihs
レンダリングされたHTML見ると、コードを読む人の立場が体験できるので、ドキュメント足りてないところに自然に書き足したくなりますよね
lemolatoon
lemolatoon
書きたくなりますよね
hsjoihs
hsjoihs
全員分訊けたのでよかったです
じゃあミーティング自体は終了して、mikiken さんのやつのをデバッグする時間にしますか
uint256_t
uint256_t
いまやってます
hsjoihs
hsjoihs
配信できます?
uint256_t
uint256_t
配信できるかなぁ。ラップトップからデスクトップにsshしないといけない
mikiken
mikiken
オフセット計算してるところで落ちてるっぽい?
uint256_t
uint256_t
あーでも必ずセグフォしますね
mikiken
mikiken
(gdb) run "a=2; a;"
Starting program: /home/mikiken/compiler/9cc/9cc "a=2; a;"

Program received signal SIGSEGV, Segmentation fault.
0x00005555555561e3 in primary () at parse.c:190
190             lvar->offset = locals->offset + 8;
uint256_t
uint256_t
locals がおかしいよなぁ
この locals はどこで初期化してるんだろう
mikiken
mikiken
もしかして動的確保し忘れてる? 9cc.h にあるんだけど
uint256_t
uint256_t
なんか locals がヌルのポインタ参照してるんですよね
mikiken
mikiken
確保し忘れてる気がしてきた
uint256_t
uint256_t
locals の中身を見てみたら 0 が入ってて
かりんとう
かりんとう
locals が 9cc.h で定義されていて、初期化されているところを確認できていない
mikiken
mikiken
とりあえずそれを書いていきたいと思います
hsjoihs
hsjoihs
ヘッダファイルでグローバルで宣言されてるからゼロで埋まってるのか
uint256_t
uint256_t
そうですね、グローバルなので
れもん
れもん
グローバル変数はヘッダに書かないほうがいいという話が
かりんとう
かりんとう
hsjoihs
hsjoihs
これって C のは -O0 でやった感じです?
uint256_t
uint256_t
Python は JIT ないですねぇ。PyPy とか Cython とか
仮に JIT を使ったとしても、まあ JIT にしただけで速くなるだけではなくて、ループのたびに型が変わらないことをプロファイリングして、みたいなのを JavaScript がやってる
hsjoihs
hsjoihs
地味に重要なのは、Pythonは『速度が要るやつは C か C++ で書いて呼び出す』という文化なので、速度に関してカリカリのチューニングに至るモチベがない
uint256_t
uint256_t
ただまあ GIL (Global Interpreter Lock) を取り除こうと頑張ってる。これがなくなると JIT が現実的になってくる。GIL がある範囲でしか最適化ができない。なければもっとやりたい放題できるのに
python_benchmark.png
かりんとう
かりんとう
命令一つ一つに対して演算が多いから重くなってしまう?でもどうなんだろう
結局動的型付けによって重くなってるんですかね。毎回型チェックが入ってるからだよ、と答えるのは正しいんだろうか
uint256_t
uint256_t
完全には正しくなりそうだけど
tamaron
tamaron
Python は型タグですよね?
static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
    // bpo-44378: Don't use Py_TYPE() since Py_TYPE() requires a non-const
    // object.
    return ob->ob_type == type;
}
かりんとう
かりんとう
型タグとは?
uint256_t
uint256_t
これポインタ比較でいいんですね
tamaron
tamaron
オブジェクトの中に型タグの情報を持っている感じですね?
かりんとう
かりんとう
PyUnicode_CheckExact(left)
#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)
#define PyUnicode_CheckExact(op) Py_IS_TYPE(op, &PyUnicode_Type)
uint256_t
uint256_t
init_var とか pyobject_malloc とか重そう
ループ回すたびにmallocしてる?見方間違ってるかな。ループの回数の倍ぐらいmallocしてる?
malloc みたいな名前の関数がループの中にあるのはちょっと嫌ですね
かりんとう
かりんとう
あと GC あるから、reference count とかも毎回やってるのかな
uint256_t
uint256_t
GCだからというか、reference count だから、かな
かりんとう
かりんとう
積み重なって重くなってるんですかね
uint256_t
uint256_t
そうか、s+1の1が毎回オブジェクトになってるのか
s = s + 1 ですもんね
なんか『高速化しよう』という努力を感じないですよね。Rubyとかもっと頑張ってる
かりんとう
かりんとう
なんか Python 3.11 でフィボナッチがかなり高速化されたとか
uint256_t
uint256_t
たしかに早くなってるっぽい
hsjoihs
hsjoihs
Most Python function calls now consume no C stack space. This speeds up most of such calls. In simple recursive functions like fibonacci or factorial, a 1.7x speedup was observed.
uint256_t
uint256_t
かりんとう
かりんとう
このフレームというのは?処理系を見ているとよくフレームという言葉が出てくる
hsjoihs
hsjoihs
スタックフレームの話は『Cポインタ完全制覇』に書いてあった気もする。どうだっけ
uint256_t
uint256_t
関数から見て、自分からいじれるところのことをフレームと呼びがち
かりんとう
かりんとう
ローカル変数とか?
今回の更新見てると、またプロファイリングやりたくなってきたな
uint256_t
uint256_t
プロファイリングもいいけど、中身を読んだほうが早い気もする
かりんとう
かりんとう
膨大なソースコード見るのに慣れていない
uint256_t
uint256_t
特定の言葉で grep してみるとか
tamaron
tamaron
こういうのあるんですね
hsjoihs
hsjoihs
最近は ripgrep ってのが人気みたいですね
ripgrep は .gitignore とか見てくれるのも嬉しい
かりんとう
かりんとう
今日はこんな感じですかね
hsjoihs
hsjoihs
ですかね。mikiken さんのやつってデバッグされました?
mikiken
mikiken
セグフォしなくなったけど、複数の変数があるとバグる
1個目のやつしか読めてないっぽい?gccでコンパイルされたやつと出力されたアセンブラを比較しようとしている
uint256_t
uint256_t
次に集まるのいつにします?
また 21:00 からとかですかねぇ
かりんとうlemolatoonkanataso
かりんとうlemolatoonkanataso
大丈夫です
mikiken
mikiken
来週も事前講義ありましたっけ
かりんとう
かりんとう
あります
hsjoihs
hsjoihs
じゃあ、次回も同様に金曜日 21:00
uint256_t
uint256_t
質問は積極的に。15分考えてわかんなかったら積極的に Discord で聞きましょう
かりんとう
かりんとう
Rustやんなきゃ
hsjoihs
hsjoihs
Rustやることについてもアドバイスを是非求めてみてください

2022年7月17日

uint256_t
uint256_t
ゆっくりと書いています。

2022年7月22日

hsjoihs
hsjoihs
@L3ゼミ受講生 金曜日 21:00 からやります
@L3ゼミ受講生 @uint256_t @tamaron(L3チューター) @れもん(Lちゅた 30分後から
かりんとう
かりんとう
hsjoihs さんが書いてくださったやつ、16 ページもある
hsjoihs
hsjoihs
まだ最初の 10 ページ分しか書けてないです。残りは下書きというかネタ帳
かりんとう
かりんとう
以前 uint256_t さんも最適化の記事を書いてるわけですが、集まったときに輪読って感じですか?
uint256_t
uint256_t
各自で大丈夫ですよ。分からなかったら逐次質問を。その方が記事の質が上がる
hsjoihs
hsjoihs
はい、まったく同意見です
uint256_t
uint256_t
中途半端なところで止まっててすいません。これくらいなら皆さん知ってますよね?
かりんとう
かりんとう
定数畳み込みは聞いたことがあったなぁ、と。僕は。Go だとチュートリアルにも書いてある。
hsjoihs
hsjoihs
いつも通り、皆さんがどうしてるかを聞くか
かりんとう
かりんとう
じゃあいつも通り私から。ちょっとまだ今週は若干しか進んでないのですけれども、型の BNF 周りをリファクタリングして、変わった int typedef みたいなやつも順番に関係なくできるようにした。あと、ネストした型定義とか const とか。ちょっとまあ、Cの機能追加しかやってなくて、最適化とか、Rustからの輸入とかは進んでいない。他のGoとかRustとかに触れたりしているのだけど、そこまで本格的にできていないのと、そもそもどの機能を実装しようかなと言うのをいまだに悩んでる。手をつけなさすぎてるので、簡単な機能で、newとかdeleteとかをとりあえず新機能として実装しようかなぁと考えている。今後は、次はRustとか本格的に触れていきたいなぁと思っている
uint256_t
uint256_t
newとかdeleteとか面白いですね
かりんとう
かりんとう
newを単純にmallocとかcallocとかにするのかを悩んでます。malloc自体を実装すると言うこと?
uint256_t
uint256_t
いや、構文を扱えるようになれば、その裏側で malloc や calloc を呼ぶとか、自分のを呼ぶとか。C++ の既存の処理系を見るのもいいのかもしれないけど
かりんとう
かりんとう
Go の make とかがどうなってるのかは若干気になる。C++は言語に慣れていないので
uint256_t
uint256_t
でも結局僕たちが読むのはアセンブリなので
あと Rust もまあ時間があれば
かりんとう
かりんとう
Rust をちょっとだけ、所有権とかのチュートリアルとかを読んでたけど、今まで触ってきた言語とは違うなぁというか
uint256_t
uint256_t
コンパイラが文句を言ってくれるので、バグったコードが動くことが全然ない
かりんとう
かりんとう
とりあえず、Rust でまだコードを全然書けていないので、テキトーなコードを組んで慣れてみるかなぁ、と。Rust で compilerbook の最初の方を実装し直すとか
uint256_t
uint256_t
手は動かした方がやっぱりいいと思う
かりんとう
かりんとう
僕はそんな感じです
hsjoihs
hsjoihs
分かりました、ありがとうございます。次の人いきますかね
lemolatoon
lemolatoon
じゃあ僕が。とりあえず今日でテストが一段落したので、昨日ちょっと書いた。リファクタリングした。あと、この前 uint256_t さんが言ってた insta クレートを使ってみた
uint256_t
uint256_t
どうですか insta クレート
lemolatoon
lemolatoon
よさそうなんですけど、人間がテストケースを見るのが大変
uint256_t
uint256_t
人間が見やすいように、小さいテストケースを作りましょう
lemolatoon
lemolatoon
小さくても入れ子が深くて大変
uint256_t
uint256_t
ASTをverifyできる関数が作れたらいいんだけど、そもそも作れたら insta があんまり要らないからなぁ
正直そこも僕は答えが出ていない。頑張って目視で答えを出すとか
lemolatoon
lemolatoon
一つは assert_eq! でやって、いろんな組み合わせとかはinstaクレートかなぁ、と
uint256_t
uint256_t
言語処理系のテストって難しいですよね
lemolatoon
lemolatoon
Rust だといろいろできるのが嬉しい
uint256_t
uint256_t
Cのときよりバグが見つけやすい
lemolatoon
lemolatoon
テストを書くことで、書きたい機能も見つかる
uint256_t
uint256_t
後からテストを足すのもだいぶ楽なので
lemolatoon
lemolatoon
Rust うれしいなぁ
hsjoihs
hsjoihs
そう、なんか、みなさんもっと C 以外で各問だと思ってた
uint256_t
uint256_t
セルフホストしたい人が多かったですね
Rust で書いて C に移植するのは楽なのか?
lemolatoon
lemolatoon
C ではない、自分が欲しい機能を全部入れて、実装した自分の機能を使って書く、とかをやりたい
uint256_t
uint256_t
なるほど、C より書くのが楽になるんですね。まあインクリメンタルにやれば比較的やりやすいけど、まあ大変か
lemolatoon
lemolatoon
まあ一回は Rust で書こうかな、と
uint256_t
uint256_t
了解です、ありがとうございます
hsjoihs
hsjoihs
じゃあ次の人
mikiken
mikiken
先週からの進捗としては、まず複数文字のローカル変数が動くようになって、if else return while が追加されて、ブロックを足せてたと思ってたけど、for とか if が入れ子になったらバグったので、それを直さなきゃいけないなぁ、と。たぶんなんだけど、コード生成でジャンプするところのラベルが上手く作れてなくて、入れ子になってるときにネストの深さを反映しないといけないのだろうけど、できてない。それを直したい
hsjoihs
hsjoihs
なるほど
uint256_t
uint256_t
いまコード読んでましたけど、label_if_count とかの変数名でラベルが管理されてるんでしょうか。ネストすると壊れるんですか?
mikiken
mikiken
どういうケースで落ちてるかというと、
tmp.s: Assembler messages:
tmp.s:49: Error: symbol `.L.begin0' is already defined
sum=0; for(i=1;i<=5;i=i+1){for(j=1;j<=5;j=j+1){sum=sum+j;}sum=sum+i;} return sum/18; => 5 expected, but got 6
make: *** [Makefile:11: test] Error 1
↑こういうケースのときにバグってる
れもん
れもん
gen の中で label_if_count がインクリメントされてるので、インクリメントされた label_if_count が print されてしまっていませんか
uint256_t
uint256_t
label_if_count がグローバル変数だからいけないってことですかね
れもん
れもん
同じラベルになっていない
uint256_t
uint256_t
一度ローカル変数に突っ込んでみるといいんじゃないですかね
label_for とか label_if とかは区別しなくてもいいのでは
mikiken
mikiken
番号が衝突してたので分けてたのだけど、それもローカルに入れたら直るんですかね
uint256_t
uint256_t
ラベルって衝突しますか?
れもん
れもん
インクリメントする場所も悪いのでは?んーでもわからんな
uint256_t
uint256_t
if とか for を生成するとき、はじめにローカル変数に保存しておいて、生成するときはその数字を使って、生成し終わったらグローバル変数にその値を突っ込む?
れもん
れもん
それは再帰で壊れる
uint256_t
uint256_t
あーそれはそう。生成したタイミングでそれをローカルに保存して、そのタイミングでグローバルも更新、か
mikiken
mikiken
とりあえず自分はこんな感じです
uint256_t
uint256_t
ありがとうございます
hsjoihs
hsjoihs
じゃあ次行きますか
kanataso
kanataso
私は今週は、少し忙しかったので、あまり進んでいないのですが、条件演算子を作ろうとしていました。C#とかにnull合体演算子とかあるじゃないですか、あれ欲しいなぁと思っています
ただの if の糖衣構文なので、難しくはないと思います
hsjoihs
hsjoihs
さっきのラベルですが、私の実装は
    case IF_STATEMENT: {

        int label1 = get_new_label_name(ptr_prs);
        int label2 = get_new_label_name(ptr_prs);
        const struct Expr expr = ref_sta->expr1;
        print_expression(ptr_prs, &expr);

        gen_if_zero_jmp_nbyte(
            size_of_basic(&expr.details.type, "condition of `if`"), label1, 0);
        gen_discard();

        print_statement(ptr_prs, ref_sta->inner_statement);

        gen_jump(label2, "if statement");
        gen_label(label1);
        gen_discard();

        gen_label(label2);

        return;
    }
コツは、get_new_label_name がラベル番号をインクリメントしてるんですけど、その結果の label1 と label2 がローカル変数で保存されてるので、jump と label にそれぞれ一貫した値が使えてる、って感じですね。この中の print_statement の呼び出しでまたラベルが請求されることがあるんですけど、そのときには新たなラベルが発注されてるので問題ない
uint256_t
uint256_t
僕も作るならこういう実装をしますね
この ptr_prs ってなんですか
hsjoihs
hsjoihs
ああ、PrinterState の略ですね
uint256_t
uint256_t
context とかでよかったのでは
hsjoihs
hsjoihs
わかるなぁ
uint256_t
uint256_t
まあとにかく、こういう実装にすればバグらないと思います
hsjoihs
hsjoihs
全員分がサクッとさらえてしまったな
かりんとう
かりんとう
コードジェネレータに print 文を書かないの、読みやすくていいですね
uint256_t
uint256_t
こうやって一段階ラップするだけでも見通しがいいですよね
かりんとう
かりんとう
print 文は別ファイルに全部書かれていた。これだと出力するアーキテクチャごとに書けるから嬉しそう
uint256_t
uint256_t
そうですね。ファイル差し替えるだけでいいですからね
hsjoihs
hsjoihs
まあ実際にはそんなに上手く行かないですよ
具体的には、構造体の ABI 周りが破綻すると思います
れもん
れもん
kanatasoさんの null 合体演算子なんですけど、GNU 拡張には↓という類似物があります
hsjoihs
hsjoihs
あー、あったなこんなの
uint256_t
uint256_t
普通にこれ実装すると便利そう
かりんとう
かりんとう
三項演算子を実装したらわりとすぐに実装できそう
受講生のソースコードで互いにバグを探しあうと面白そう
というのは、Codeforces だっけ
hsjoihs
hsjoihs
あれの hack ですよね?
かりんとう
かりんとう
そうそう、ゲーム形式で。自分のコンパイラとかバグだらけだと思うので、発見したいし、してもらいたい
lemolatoon
lemolatoon
互いのテストコードを交換するのも面白そう
uint256_t
uint256_t
他の人の設計を見るのは勉強になるし、バグるコードを探すのはCTFっぽくて楽しい
hsjoihs
hsjoihs
私もこの前人のやつの hack やりましたね
マジで14年間誰も気づいてなかったのかとはなる
uint256_t
uint256_t
気づいても報告してなかった人もいそう。正しいコードだけ入れてても気づきませんもんね。めちゃくちゃなソースコードを突っ込むとボロがでて楽しいですよ
hsjoihs
hsjoihs
ファジングとかいうやつですね
uint256_t
uint256_t
ちょっと学んでみると面白いかも
人がテストケースを作ると、まともな入力しか来ないので、機械にある程度ランダムなテストケースをつくってもらう、という
かりんとう
かりんとう
自分で書くと自分のコンパイラへの無意識の願望が働きますからね
uint256_t
uint256_t
空のファイルで落ちることありますからね
hsjoihs
hsjoihs
私も、『識別子でプログラムが終わるとセグフォ』ってのに最後の最後まで気づかなくて、プリプロセッサ書いて #endif で終わるソースコードが発生して初めてこのバグに気づいた、ってのがある
kanataso
kanataso
こういったエラーの時はこういうエラーを出して欲しい、というテストケースほしいですかね?
uint256_t
uint256_t
作れると理想的
lemolatoon
lemolatoon
Rust なら Result でできそうだけど、今は全部 panic してる
uint256_t
uint256_t
セグフォでテストごと落ちちゃうと困るので別のプロセスにしなきゃいけなかったり
lemolatoon
lemolatoon
落ちるコードを混ぜて、セグフォが落ちないか確認する、ぐらいだったらわりと簡単かも
uint256_t
uint256_t
たしかにそれくらいならやったほうがいいかも
かりんとう
かりんとう
セグフォなのかコンパイルエラーなのかをCで判断するの難しくない?const の正常なテストケースって構文しかチェックできない。セグフォで落ちたのかを判断してなくて
uint256_t
uint256_t
そういうテストケース、難しいので、考えられる構文エラーを enum とかで作っておいて、エラーが起きたらそれを伝播させて、『const なのに2回代入してる』ということに気づく、とか。まあつらそう
かりんとう
かりんとう
標準エラーを出してしまう関数で今のところやっている
uint256_t
uint256_t
stderr の内容を比較すればいいと思いますけどね
hsjoihs
hsjoihs
受講生の方で、/dev/stderr を fopen してたせいでリダイレクトが聞かなくてめちゃめちゃビビったのがあったな
かりんとう
かりんとう
それはどうしてリダイレクトが効かなくなってしまった?
hsjoihs
hsjoihs
真面目に追いかけてないんですけど、あれって stderr って書くたびに /dev/stderr を fopen してるんでしたっけ
kanataso
kanataso
あれは stderr をプリプロセッサで fopen で置き換えてます
かりんとう
かりんとう
発想がおもしろい
stderr ってもとの実装がどうなってる?ヘッダファイルでは定義されてるけど実態はどうなんでしたっけ
uint256_t
uint256_t
まあ libc の中ですかねぇ
かりんとう
かりんとう
まあそうなりますか
uint256_t
uint256_t
いずれにせよプログラム起動時に初期化されるはず
どこで何が起きてるのか僕も知らない
hsjoihs
hsjoihs
『ハロー "Hello, World"』 辺りに書いてないかな。手元にあるので見てみます
多分 fopen したときのファイルデスクリプタを追った方がいいんだろうな。標準出力のは 1 だけど、複数回 fopen を呼ぶとどうなるの。
かりんとう
かりんとう
マクロが多くてよくわからん
hsjoihs
hsjoihs
musl とかマクロが控えめだったような?
uint256_t
uint256_t
かりんとう
かりんとう
musl とは?
uint256_t
uint256_t
libc の実装っていろいろあるので
hsjoihs
hsjoihs
musl は、『僅かすぎる高速化のために頑張るよりも、読みやすい方がいいよね』というコンセプトの libc だったはず
musl is lightweight, fast, simple, free, and strives to be correct in the sense of standards-conformance and safety.
かりんとう
かりんとう
hsjoihs
hsjoihs
で、この .fd = 2 がファイルデスクリプタですよね
uint256_t
uint256_t
そうですよね。stderr だから 2 ですよね
かりんとう
かりんとう
開いているわけではなさそう
hsjoihs
hsjoihs
このファイルデスクリプタというのは、Linux カーネルとお話しするときに使うやつなんですよ
uint256_t
uint256_t
そうですね
あー、syscall 読んでますね
かりんとう
かりんとう
どこでですか
uint256_t
uint256_t
すごく読みやすいですね
かりんとう
かりんとう
前に挫折したのでこれありがたいです
lemolatoon
lemolatoon
インラインアセンブリって C 標準なんでしたっけ。__asm__ ってのがありますけど
hsjoihs
hsjoihs
asm がキーワードなのは C++ だけのはずで。だから __asm__ ってしてるんですけど
When compiling in ISO C mode by GCC or Clang (e.g. with option -std=c11), __asm__ must be used instead of asm.
標準にない機能なので、規格準拠しましょうというオプションがついてる場合は、__asm__ という名前になる。なぜなら、C の規格上は asm って名前の変数とかがあるコードをコンパイルできなきゃいけないので
7.1.3 Reserved identifiers 1 Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers. All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use. All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. Each macro name in any of the following subclauses (including the future library directions) is reserved for use as specified if any of its associated headers is included; unless explicitly stated otherwise (see 7.1.4). All identifiers with external linkage in any of the following subclauses (including the future library directions) and errno are always reserved for use as identifiers with external linkage.184) Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.
__ で始まる変数名は、プログラマは使ってはいけないし、コンパイラはそこに機能をねじ込んでよい。なので、皆さんが独自機能を実装するときには、ここが使える
lemolatoon
lemolatoon
規格に準拠しながら独自機能が足せるってことですね
uint256_t
uint256_t
asm キーワード、定義されてません?
hsjoihs
hsjoihs
えっでもこれ Annex J ですよね
J.5 Common extensions 1 The following extensions are widely used in many systems, but are not portable to all implementations. The inclusion of any extension that may cause a strictly conforming program to become invalid renders an implementation nonconforming. Examples of such extensions are new keywords, extra library functions declared in standard headers, or predefined macros with names that do not begin with an underscore.
だから、『採用すると規格準拠ではなくなってしまう、よくある拡張』という位置づけのはずです
uint256_t
uint256_t
あ、『採用すると規格準拠ではなくなってしまう』という話も規格には書いてあるんですね
hsjoihs
hsjoihs
そう、これ informative セクションなので。C の規格にまつわるお役立ち情報まとめ
uint256_t
uint256_t
『有益情報』欄でしたか。やっぱ仕様は読んだ方がいいですね
lemolatoon
lemolatoon
じゃあ __ って作っておいて、#include の中で #define し直す、とかなら準拠してますよね
hsjoihs
hsjoihs
さっきの musl のとかで _IO_stderr とかそういう名前になってたのは、ここはコンパイラや標準ライブラリの実装が機能をねじ込んでよい場所だから、です。プログラマが勝手に使わないことが保障されているので、コンパイラや標準ライブラリの実装がここを使っていい、という場所なので
かりんとう
かりんとう
C は名前空間がないからこういうので対処してるんですね
hsjoihs
hsjoihs
この記事とかいいかも
uint256_t
uint256_t
なんかこれ読んだ気がします
lemolatoon
lemolatoon
C 言語の規格には『このヘッダにはこの名前があってください』も含んでいます?
hsjoihs
hsjoihs
はい
lemolatoon
lemolatoon
それは libc とは別の存在ですか?
hsjoihs
hsjoihs
標準ヘッダファイルは、libc を正しく呼び出すために必要な情報とかが入ってるファイルなので、libc そのもの……ではないです
lemolatoon
lemolatoon
libc を使うためのファイル、と
uint256_t
uint256_t
stdio.h がどうあるべきか、は↓に書いてある
libc は、規格に則って誰かが作ったものであり、自分で作ってもいい
lemolatoon
lemolatoon
musl の syscall の第一引数が見当たらなくて、uio.h の中にありそうだけど、これ自体は C の規格にない
hsjoihs
hsjoihs
C の規格……えっと規格確認します
A preprocessing directive of the form # include <h-char-sequence> new-line searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.
えっこれ書けるの知らなかった
        #if VERSION == 1
              #define INCFILE            "vers1.h"
        #elif VERSION == 2
              #define INCFILE            "vers2.h"        // and so on
        #else
               #define INCFILE           "versN.h"
        #endif
        #include INCFILE
uint256_t
uint256_t
そんな不思議でもない気が
hsjoihs
hsjoihs
もちろん不思議ではないんですけど、明示的に許された挙動として決められているのが意外だった
uint256_t
uint256_t
結局 syscall(SYS_writev, f->fd, iov, iovcnt) の第一引数ってなに
hsjoihs
hsjoihs
0x80 とかのことだと思います。システムコールの番号。えーとちょっと写真貼りますね
uint256_t
uint256_t
syscall.h に入ってました。Linux が定めてるものなんですかね
/usr/include/x86_64-linux-gnu/bits/syscall.h:# define SYS_writev __NR_writev
hsjoihs
hsjoihs
そうですね、Linux カーネルの仕様だと思います
スマホの調子が悪くて本の写真が撮れないので、とりあえず Qiita 記事をば
ん?違う 0x80 は int 0x80 でシステムコールを呼ぶのであって、write() のシステムコール番号は 4 か
lemolatoon
lemolatoon
Windows だと違うんですかね
hsjoihs
hsjoihs
その可能性は全然あると思います
というか、環境ごとに libc を書いていただいて、C プログラムは全部 printf() とか puts() とか呼べばどの環境でも出力できますよ、というのが C のやりたいことなので
lemolatoon
lemolatoon
差異を吸収するのが libc の仕事の一つなんですね
hsjoihs
hsjoihs
たしかにその話を私の資料では書き漏らしてた。書いておこう
そういえば、超絶手抜き libc 実装の一つとして、C コンパイラ班 2018 の ushitora_anqou さんが書いたのがあって、これすごいのは、free() 関数が no-op なんですよ
uint256_t
uint256_t
テキトーにdeadbeef とか詰めてくれるといいですね
hsjoihs
hsjoihs
free って領域のサイズが引数で渡ってこないので、自前でサイズを管理しておかないとそれはできないんですよね
uint256_t
uint256_t
ああそうか。malloc 自分で作ってないってことですか
hsjoihs
hsjoihs
いや malloc は作ってて、OS からリクエストされた領域を一切管理せずにただ切り渡す
lemolatoon
lemolatoon
セルフホスト時にアロケータ書かなきゃいけないんですか
uint256_t
uint256_t
書かなくて大丈夫ですよ
lemolatoon
lemolatoon
もともとある malloc とリンクすればいいのか
hsjoihs
hsjoihs
anqou さんは libc も作りたいと言い出しまして。当時のスライド持ってくるか
かりんとう
かりんとう
アセンブラも書いてた方ですか
hsjoihs
hsjoihs
そうです。本人はめちゃめちゃチューターとして来たがってましたが予定が合わなかったそうです
かりんとう
かりんとう
スライド見て、半端ないレベルの人がいるなぁと思ってた
本番期間中にアセンブラを書き切った?いつの発表?
hsjoihs
hsjoihs
アセンブラは本番が終わった後です。発表自体は2018年9月24日の『Kernel/VM関西9回目』だそうです
かりんとう
かりんとう
このペースでコード書けるんですね
hsjoihs
hsjoihs
anqou さんは手が早い
uint256_t
uint256_t
定数伝播とか組んでたんですねぇ
かりんとう
かりんとう
速度を競うのも面白そう。競うのばっかだけど
uint256_t
uint256_t
スタックマシンをやめないと速度はできない
push と pop の対を消すのは、結構簡単な peephole 最適化でできますよ、という話を僕が書くべき
かりんとう
かりんとう
命令をトークンにして、とかやっていくのかな。『なにを参照しながらアセンブラ書いたのか』とか気になった
uint256_t
uint256_t
機械語は Intel のじゃないですかねぇ
hsjoihs
hsjoihs
便利なツールとして、 @hikalium 作の↑があります
かりんとう
かりんとう
めちゃめちゃ読みやすい
uint256_t
uint256_t
これ検索すればだいたい大丈夫
かりんとう
かりんとう
アセンブラを実装することになったらこれやってみます
やりたいことメモ
- 速度コンテスト - コンパイラハック
本番の日とかにやってもよいのかもしれない
uint256_t
uint256_t
むしろ本番の日だからこそ
lemolatoon
lemolatoon
コンパイラゼミだけどアセンブラを書く人になるかも
uint256_t
uint256_t
全然構わないし、リンカまで作ってもいい
lemolatoon
lemolatoon
リンカまでつくったらぜんぶ自分
hsjoihs
hsjoihs
あとは libc だけ
uint256_t
uint256_t
本番期間という大きな時間がありますから
夏休みの前ってなかなか忙しいですよね
かりんとう
かりんとう
本番で開発できる期間って 3 日ですよね
uint256_t
uint256_t
最後は発表会があるので
かりんとう
かりんとう
そのスライドも作らないとなのか
uint256_t
uint256_t
懐かしいなぁ。僕は自作ブラウザでスライド発表しました。自作ブラウザで自作 JavaScript 処理系の発表をした。結構ウケた
lemolatoon
lemolatoon
そこまで来ると全てを自作でやりたい
uint256_t
uint256_t
自作 CPU/OS/リンカ/コンパイラ
hsjoihs
hsjoihs
自作フレームワークでプレゼンとなると、地味に大変なのがフォントで、日本語は地味に文字が多い
いやもちろん全部ひらがなにするというポケモンテクニックがありますけど
かりんとう
かりんとう
逆にウケそう
uint256_t
uint256_t
全部アルファベットで
hsjoihs
hsjoihs
頑張ってフォントをつくっていただくという手も。流石に 30 日 OS とか Mikan とかでは外部の使ってましたね
かりんとう
かりんとう
それだけで 30 日経ちそう
lemolatoon
lemolatoon
30 日フォント本
hsjoihs
hsjoihs
まあひたすらペンタブなどで文字を書くという苦行をしてもいい
lemolatoon
lemolatoon
味があるフォントになりそう
かりんとう
かりんとう
自作感がより強くなる
hsjoihs
hsjoihs
ところで私はこういうの↑をやったことがありまして
ひたすらスマホのタッチパッドに文字を書く作業ならやったことがあります
lemolatoon
lemolatoon
データセットを、全部手書きで 5 万枚?
hsjoihs
hsjoihs
はい
かりんとう
かりんとう
データセットと言うことは、同じ文字を何枚も?
hsjoihs
hsjoihs
はい
uint256_t
uint256_t
何回ぐらい?
hsjoihs
hsjoihs
100~200とか?
uint256_t
uint256_t
同じ文字 100 回ですか?
hsjoihs
hsjoihs
もちろんシャッフルしてますよ
uint256_t
uint256_t
途方もないですね
かりんとう
かりんとう
小中学校の漢字のテストの追試を思い出す
uint256_t
uint256_t
小学生がデータセット作ってくれますね
hsjoihs
hsjoihs
もう 2 時間か。はやいな
uint256_t
uint256_t
もう 11 時ですね
かりんとう
かりんとう
コンパイラの話とは関係ないんですけど、明日は ISUCON があります。パフォーマンスチューニングコンテストです
私は出場予定。他に出る方いますか?
kanataso
kanataso
出ますけど、なにもやっていない
かりんとう
かりんとう
ぶっつけ本番ですか
uint256_t
uint256_t
ISUCON って言語選べましたよね
かりんとう
かりんとう
7~8言語だっけ。Rust はある
hsjoihs
hsjoihs
ISUCON は Go 強いって言いますよね
かりんとう
かりんとう
60% が Go で、上位もだいたい Go
速度が速いし、並列処理が簡単に書けるのでポイント高い
hsjoihs
hsjoihs
そこら辺については KOBA789 がいろいろ言ってたな
uint256_t
uint256_t
ISUCON だから早く寝るって言ってますね
かりんとう
かりんとう
9:30ぐらいから始まる。生活習慣が逆転していると厳しい
uint256_t
uint256_t
セキュキャンも早いんですけどねぇ
かりんとう
かりんとう
対面と違って起こしてくれる人がいない
uint256_t
uint256_t
例年だとドアを叩いてくれる人がいる
hsjoihs
hsjoihs
私は早寝早起きして早朝に部屋でコード書いてたな
uint256_t
uint256_t
一日だけ寝坊しかけた
8:00 に起きて、たしかレストランが 8:15 までで、走って行った
かりんとう
かりんとう
料理はどういうのが出たんです?
uint256_t
uint256_t
結構普通に豪華でしたね
最終日いろいろ出た
オフラインだとそういう楽しさがある
かりんとう
かりんとう
私は食べるのが好きなので、惜しい。最終日寿司とか出るんですか
uint256_t
uint256_t
写真があって、お米とか、というか写真を見てもらった方がいいかな
自分で焼くやつ
hsjoihs
hsjoihs
あー、あったなぁ
uint256_t
uint256_t
朝食はバイキングだったかな
かりんとう
かりんとう
皿が高そう
lemolatoon
lemolatoon
じゃあそろそろ
hsjoihs
hsjoihs
まあもう11時ですし
uint256_t
uint256_t
お疲れ様でした
lemolatoon
lemolatoon
次回の予定は
hsjoihs
hsjoihs
来週は事前学習ありましたっけ
lemolatoon
lemolatoon
ないです
hsjoihs
hsjoihs
じゃあ早めにしますかねぇ
uint256_t
uint256_t
20:00 で無理な人いますか
じゃあ来週は金曜日 20:00 からということで
hsjoihs
hsjoihs
uint256_t
uint256_t
hsjoihs
hsjoihs

2022年7月24日

hsjoihs
hsjoihs
また crowbar の実装にバグを見つけた

2022年7月25日

lemolatoon
lemolatoon
今各意味解析などの部分でpanicせずにResultを返すように変更してるんですが、普段は?を使えてとても嬉しいのですが、Optionをmapしてクロージャーの中でResultを返す関数を呼ぶと?が使えずにあまりナイスな実装がないのですが、このようなときは愚直に実装するしかないですかね?
uint256_t
uint256_t
mapがResult<Option<_>, Error>を返すようにできれば良さそうですね
lemolatoon
lemolatoon
↓ナイスじゃない実装
fn (self: Option<T>) -> Result<Option<_>, Error>がOptionの実装に生えない限りはできないですかね
ないとおもったらありました ::)
uint256_t
uint256_t
それですね。あとmap_orあたりを駆使すればいけるかと
lemolatoon
lemolatoon
init.map_or(None, |expr| self.down_expr(expr, lvar_map)?)
クロージャーの中だから ? を使って返せない?
uint256_t
uint256_t
? は外側ですね
lemolatoon
lemolatoon
map_or使ってもできない気がするんですが..
uint256_t
uint256_t
この場合だとmap_orじゃなくてよかったですね.
init.map(|expr|...).transpose()?
lemolatoon
lemolatoon
init.map_or(Ok(None), |expr| self.down_expr(expr, lvar_map).map(|expr| Some(expr)))?
これでも確かにmap_orでできそうです
でも .transpose のほうが簡潔ですね
hsjoihs
hsjoihs
なんか今まさにlemolatoonさんが機能実装してそうだしソース眺めるか
lemolatoon
lemolatoon
エラー処理回りやってました。ブランチ名と全然一致してないんですが。
hsjoihs
hsjoihs
とりあえず思ったこととしては、src/analyze.rs にある // do nothing ってコメント、git blame したら 2022-07-03 02:10:42 って書いてあるので、多分今や完全に outdated なのでは?コード書いてあるので何らかはやっていそう
lemolatoon
lemolatoon
ASTの変換の前後でただ、型が違うものに変換しているという意味で、do nothingと書いてました。そのmatchした後の処理の中でdown_*関数以外の処理をしてない場合=do nothingとなっている(はず)
hsjoihs
hsjoihs
なるほどなるほど。しかし非自明なことをしているかどうかというのはコードの長さで一目瞭然なので、いずれにせよこのコメントは不要に思えます。そもそも一つ以外の全選択肢に同じコメントがあるというのも微妙ですし、書くのなら match 文の前に『Declare 以外は型変換をしているだけ』とコメントを書くのはどうでしょう
uint256_t
uint256_t
(あまり関係ないですが、cargo clippy したら色々と言われたので、時間のある時に書き換えるといいかもしれないです)
hsjoihs
hsjoihs
(わかる)
lemolatoon
lemolatoon
確かにまとめた方が良さそうです。やってみます。
hsjoihs
hsjoihs
一般論として、コメントは『どのように』ではなくて『なぜ』を書くと良いと言われていたりします。何をしているのかを書くよりも、なぜそうしているのかを書くことを心がけてみるのもアリかもしれません
lemolatoon
lemolatoon
なるほど、個人的にはパッと見で分からない所を後の自分向けに書いてたんですがなぜという視点で書いてみます!
hsjoihs
hsjoihs
よかったら今からボイスチャットやります?
lemolatoon
lemolatoon
今実は外にいるので30分後くらいであれば是非ともやりたいです
hsjoihs
hsjoihs
了解です!
@lemolatoon そろそろですかね?(急かす意図はない)
lemolatoon
lemolatoon
まさに今帰ってきました
panic を消そうとしています
hsjoihs
hsjoihs
lib.rs に
#![warn(clippy::missing_panics_doc)]
って書くと、panic する関数が全部黄色線になります
他の警告もいろいろあるんですけど、
#![warn(clippy::pedantic, clippy::nursery)]
とやるといろいろ出ます
lemolatoon
lemolatoon
うおーめちゃめちゃ出る
hsjoihs
hsjoihs
めちゃめちゃ出るので、個人的には、最初は
#![allow(
    clippy::missing_errors_doc,
    clippy::must_use_candidate
)]
をしておいて、大事なところだけ直すのもいいかも
lemolatoon
lemolatoon
must_use_candidate とは?
hsjoihs
hsjoihs
戻り値がある関数には #[must_use] を付けましょうという警告で、まあ悪い警告ではないんだけど、作業段階の最初の方では邪魔になる
lemolatoon
lemolatoon
Self にしろとめっちゃ言われる
hsjoihs
hsjoihs
個人的には必ずしも Self にしなくてもいいとは思うけどねぇ
lemolatoon
lemolatoon
ターミナルのバッファが足りない
hsjoihs
hsjoihs
VSCode 上で黄色線で出ます
rust-analyzer で出るはず
lemolatoon
lemolatoon
出ないなぁ
hsjoihs
hsjoihs
rust-analyzer 再読込
lemolatoon
lemolatoon
再読込が必要ですと言われた
lib.rs に置いてるんですけどいいですか
hsjoihs
hsjoihs
はい
command palette の Rust Analyzer: Restart Server
lemolatoon
lemolatoon
nightly が悪いんですかねぇ。no_std のときもなんか上手く行かなかったんですけど
hsjoihs
hsjoihs
なんでだろうなぁ
cargo はどこで入れました?
lemolatoon
lemolatoon
公式サイトのシェルスクリプトです
hsjoihs
hsjoihs
私も今日それで入れて動いてるんだけどなぁ
れもん
れもん
rust-analyzer の設定の check on save に clippy が指定されてます?
hsjoihs
hsjoihs
あー多分それだ。身に覚えがある
lemolatoon
lemolatoon
されてないです
hsjoihs
hsjoihs
環境構築って一回しかしないから忘れるんですよね
lemolatoon
lemolatoon
真っ黄色になった
hsjoihs
hsjoihs
では見ていきましょう
関数に対する warning は関数全体に波線がつくというだけ
カーソルを載せて Ctrl + ピリオドで直ります、簡単なやつは
それは & の方が効率がいいですよ、という提案
lemolatoon
lemolatoon
今は無視します
hsjoihs
hsjoihs
無視する場合には、エラーメッセージにエラーの名前が書いてあるので、その名前を allow に足しましょう
allow はまとめることができます
clippy の lint 名には clippy:: を付けてください
unused &self なら、引数から削って self. を Self:: にすればいい
lemolatoon
lemolatoon
ここのライフタイムパラメータ削れるのか
hsjoihs
hsjoihs
そのコンストラクタ要らないのでは? Declarator { ty_name, ... } とかの方が引数の順番とか考えなくていい
lemolatoon
lemolatoon
個人的には必ず new で書きたい
hsjoihs
hsjoihs
……なるほど
変数名は F2 で改名できます
れもん
れもん
hsjoihs
hsjoihs
え、ptr - ptr は許されますよ
lemolatoon
lemolatoon
挙動は引き算?
hsjoihs
hsjoihs
引き算をして、sizeof で割ります

2022年7月26日

hsjoihs
hsjoihs
セルフホストされてる言語の開発が止まると、後世の人がそれを再始動させるのは大変だ、というお話
Shiho Midorikawa
Shiho Midorikawa
Cyclone, 『デバッグの理論と実践』で見たやつだ... (あれRustにつながってたんですねえ...)

2022年7月27日

mikiken
mikiken
この前のforやifがネストできないバグは解消したのですが、このバグを直してる時に↓みたいなテストケースを試していたら、また別のバグが見つかりました()
./test.sh
a=3; if(a==1) return 1; if(a==2) return 2; if(a==3) return 3; => 3 expected, but got 179
make: *** [Makefile:11: test] Error 1
出力結果をしばらく見ていたのですが、どこが間違っているのかなかなか分からないので、見ていただきたいです
9ccでのコンパイル結果↓
.intel_syntax noprefix
.globl main
main:
  push rbp
  mov rbp, rsp
  sub rsp, 8
  mov rax, rbp
  sub rax, 8
  push rax
  push 3
  pop rdi
  pop rax
  mov [rax], rdi
  push rdi
  pop rax
  mov rax, rbp
  sub rax, 8
  push rax
  pop rax
  mov rax, [rax]
  push rax
  push 1
  pop rdi
  pop rax
  cmp rax, rdi
  sete al
  movzb rax, al
  push rax
  pop rax
  cmp rax, 0
  je  .L.end.0
  push 1
  pop rax
  mov rsp, rbp
  pop rbp
  ret
.L.end.0:
  pop rax
  mov rax, rbp
  sub rax, 8
  push rax
  pop rax
  mov rax, [rax]
  push rax
  push 2
  pop rdi
  pop rax
  cmp rax, rdi
  sete al
  movzb rax, al
  push rax
  pop rax
  cmp rax, 0
  je  .L.end.1
  push 2
  pop rax
  mov rsp, rbp
  pop rbp
  ret
.L.end.1:
  pop rax
  mov rax, rbp
  sub rax, 8
  push rax
  pop rax
  mov rax, [rax]
  push rax
  push 3
  pop rdi
  pop rax
  cmp rax, rdi
  sete al
  movzb rax, al
  push rax
  pop rax
  cmp rax, 0
  je  .L.end.2
  push 3
  pop rax
  mov rsp, rbp
  pop rbp
  ret
.L.end.2:
  pop rax
  mov rsp, rbp
  pop rbp
  ret
uint256_t
uint256_t
遅くなりました、.L.end.* の後ろにある pop rax が悪さをしているように思えます (消せば動く)
mikiken
mikiken
ありがとうございます、見てみます
hsjoihs
hsjoihs
根本的な原因分かりました。最初期のころに 0; とだけ書いて 0 を返すコードを吐いていたわけで、そのために pop rax を書いていたわけですが、今や return なしでは値を返せないという意味論へと変更したので、既存のテストに全部 return を付けてやる必要があります。そうしてしまえば、uint256_t さんのご指摘があった箇所の pop rax を消すことでテストが全て通るようになります
プルリク投げた
mikiken
mikiken
return がまだなかった頃は、値を返すためにpop rax していたのが、今となってはreturn がその役割を担うようになったので、各ステートメントを出力するごとにpop rax する必要はない、っていうことですよね。昨日からここで詰まっていたので、かなり助かりました。ありがとうございます
hsjoihs
hsjoihs
@kanataso そういえば、error.c で strndup が使われていますが、一応 strndup は C23 までは C 標準にはない機能だったりします(ただしPOSIX では定義されている)
kanataso
kanataso
えー!知らなかった
hsjoihs
hsjoihs
あと isnumber も POSIX ですね。標準にあるのは isdigit
kanataso
kanataso
そういえば、標準見ながらstring.hとctype.hを作ったはずなのに何個か無かった関数があった気がしますね。それか
hsjoihs
hsjoihs
prpr1: 9cc prpr
    ./prtest.sh
で呼ばれてる prtest.sh が見当たりません。prtest1.sh のことですかね
kanataso
kanataso
はい

2022年7月29日

hsjoihs
hsjoihs
@L3ゼミ受講生 @れもん(Lちゅた @uint256_t @tamaron(L3チューター) 今日は 20:00 から(先週より 1 時間早い)
@L3ゼミ受講生 @れもん(Lちゅた @uint256_t @tamaron(L3チューター)
そろそろやりますよ~
かりんとう
かりんとう
直前の連絡で申し訳ありません。30分程度参加が遅れそうです。
hsjoihs
hsjoihs
じゃあやっていきましょうか
まあ、今週も、みなさんがなにやったかという話から
じゃあ mikiken さんから
mikiken
mikiken
いろいろとバグっているのを直していて、昨日と今日ぐらいから関数の呼び出しを作ってる。呼び出しをテストするときって、コンパイラの出力をオブジェクトファイルにして、別のを用意してリンクとかじゃないですか。どうやってテストするのかがよく分かっておらず。コマンドを打ってもエラーが出てテストできない
hsjoihs
hsjoihs
それは困りましたね
uint256_t
uint256_t
どういうエラーなのか見たいですね
hsjoihs
hsjoihs
デバッグに掛かる時間は読めないので、一旦後回しにして、他に機能面で言いたいことありますか?
mikiken
mikiken
今のところ特にないです
hsjoihs
hsjoihs
じゃあ次行きましょうか
lemolatoon
lemolatoon
僕がやったことは、まず変数宣言の時に初期化式も書けるようにした。あと sizeof を実装したり。その後に、今まで コンパイルエラー時に panic! してたのを、Result を返すようにして、『このコードにはこのコンパイルエラー』というテストも足したりした。その後に関数のプロトタイプ宣言もできるようにして、配列へのポインタを返す関数とかも呼べるようにして、あとは添え字でのアクセスを実装したり。今はスコープの概念を実装しようとしているが、どういう設計にしようかなぁ、と
hsjoihs
hsjoihs
じゃあ次にkanatasoさん……はテキストチャットかな。待ちます
kanataso
kanataso
あと数分で電車から降りれるので少しお待ちを...
hsjoihs
hsjoihs
あー了解です
uint256_t
uint256_t
あまり無理なさらずに
hsjoihs
hsjoihs
そういう場合も考えてこういう、テキストチャットとボイスチャットを共存させる形態にしてるというのもあるので
kanataso
kanataso
<<,>>,~,^,&演算子を作って、構造体をreturnできるようになりました
uint256_t
uint256_t
構造体の return いいですね
hsjoihs
hsjoihs
構造体の return いいですよね
私も、『構造体を直に返す機能を実装せず、全て必ず関数内で malloc してそのポインタを返す』、みたいな実装があんまり好きじゃなかったので、構造体を直に返す機能は自分の C コンパイラに作りましたね
uint256_t
uint256_t
呼び出す側に構造体を確保してそのアドレスを渡すとかですね
kanataso
kanataso
構造体を return できるようになりまして。最初 gcc の出力を見ても ABI を見てもよく分からなくて困ったけど、なんとかなった
構造体を返す関数を呼ぶ側で、なんか構造体を保存しておく用のアドレスを渡す、みたいなことが
あと、結果を持っておくようにスタックに領域を空ける、みたいなのも必要だったので、名前のない変数を用意してそこに入れたりさせました
hsjoihs
hsjoihs
そうですよね、そういう ABI ですよね
構造体を返すときの ABI、二種類ありましたよね?INTEGER_CLASS と MEMORY_CLASS
kanataso
kanataso
rax と rdx に入れる、みたいな話があって
hsjoihs
hsjoihs
そうですよね。それ両方ともできてる感じですか?
kanataso
kanataso
はい
hsjoihs
hsjoihs
わかりました、ありがとうございます
れもん
れもん
複合リテラルも名前のない変数を用意するみたいなことしますよね
hsjoihs
hsjoihs
えーっと複合リテラルってなんでしたっけ
あー、これか。C99 でできるようになったんだっけこれ
uint256_t
uint256_t
こんなのありましたね
hsjoihs
hsjoihs
これ規格のどこに書いてあるんだっけな
これに限らず、構造体というやつは、どうしても他の、スカラって言い方しますけど、単純なデータ型とは異なった扱いが必要になってくるんですよね。『完全制覇』の pp.33-34 にあるんですが、
スカラとは、char, int, double, 列挙型などの算術型、およびポインタを指します。(中略) 初期の C では、一度に扱えるのはスカラだけでした。 (中略) 初期の C では、一度にできることといえば、スカラという「小さな」型を、右から左に動かしたり、スカラ同士で演算したり、スカラ同士で比較したりすることだけでした。 C はそういう言語です。入出力はおろか、配列や構造体すら、言語自体の機能でまとめて扱うことを放棄した言語なのです。 ただし、ANSI C では、以下の点で、集成体型をまとめて扱うことが可能になっています。 ・構造体の一括代入 ・構造体を関数の引数として渡す ・構造体を関数の戻り値として返す ・auto 変数の初期化
こういうことを考えると、シンプルなコンパイラを作る上では、構造体をまとめて扱うという機能自体を実装しないという手もアリなんですよね。もちろん実装しても大いに結構ですが
uint256_t
uint256_t
hsjoihs
hsjoihs
公式のより読みやすそう
uint256_t
uint256_t
ただ、構造体の返却の話が載ってませんでした。渡すのはあります
hsjoihs
hsjoihs
他にしたい話とかあります?みなさん
lemolatoon
lemolatoon
スコープを実装しようとしているという話が聞こえたが、最初にスタックポインタから引くと思うんだけど、一番ローカル変数が取る領域を使うものなんですか?
uint256_t
uint256_t
それでいいと思います
lemolatoon
lemolatoon
スコープが入ると管理が大変そう
uint256_t
uint256_t
名前の管理の仕方を変えなきゃいけないかもですね。スタックみたいな形になるだけだと思いますけど
hsjoihs
hsjoihs
本当に最初期の C は、関数の先頭でしかローカル変数が宣言できなかったはず
uint256_t
uint256_t
そうです。まあ(その方が)明らかに簡単ですからね
hsjoihs
hsjoihs
関数の先頭を読むだけで、ローカル変数のために確保しなきゃいけない量が分かるのはうれしい
uint256_t
uint256_t
Pascal とかもそうだったと思いますね
hsjoihs
hsjoihs
だんだんその制約が緩んでいって今に至るわけですけど
実際、それを半分裏付けるように、『プログラミング言語C』第2版 (ANSI規格準拠)を見ると、p.11 に
C では、すべての変数は、一般的に使う前に、普通は関数の始めの実行可能分の前のところで宣言しておかなければならない。
このころにはブロックの先頭ではもう変数が宣言できるようになってたはずだけど、そうするのは当時は『普通』ではなかったということが傍証されるかな、と
p.297 compound-statement: { declaration-list_opt statement-list_opt }
このように、この時代はブロックの先頭なら宣言できるようになってるんですけど
そういえば、これ書き進めました。あとタイトル変えました
れもん
れもん
K&R 第 1 版でも usually at the beginning of ... とある
hsjoihs
hsjoihs
『K&R 第 1 版』『K&R 第 2 版』の話を説明してませんでしたが、『完全制覇』pp. 24-25 に載ってます。こういうのも載ってるからこの本を選んでる
uint256_t
uint256_t
結構なんでも載ってますね
hsjoihs
hsjoihs
一方で未定義動作の話は本当に少ししか載ってないので、いま資料書いてるというわけです
みなさんこの本どうですか?『面白かった』『役立った』とかならこちらとしても選んだ甲斐があった
lemolatoon
lemolatoon
知らない話がたくさん載ってておもしろい
hsjoihs
hsjoihs
こういう話は C の入門書にはまず載ってませんからねぇ
uint256_t
uint256_t
載せたら載せたで飛ばされそうですけど、でもかといって無視できることじゃないんですけどね
hsjoihs
hsjoihs
そうですよねぇ
そこから派生して、みなさんどういう経緯で C 言語に触れてきました?
lemolatoon
lemolatoon
Java, Python とやって、Rust か C++ をやるか迷って、『OS自作入門』を読むといいと言われ、C をやった
hsjoihs
hsjoihs
そっかいまそういう選び方になるのか
れもん
れもん
その会話に私も関わってました
hsjoihs
hsjoihs
なるほどなぁ
uint256_t
uint256_t
いま C から始める人はいないんですかねぇ
hsjoihs
hsjoihs
大学のカリキュラムで C から始める、という話はわりと聞きます
れもん
れもん
わたしも大学では C から習い始めましたけど、最近は Python にしたらしい
uint256_t
uint256_t
私は大学での最初の言語は Java だったかなぁ
TumoiYorozu
TumoiYorozu
なでしこから始めたなぁ
れもん
れもん
もともと JavaScript とかだったけど、C を大学でやり始めて
uint256_t
uint256_t
僕自身は『猫でも分かるC』、分かんなかったけど
hsjoihs
hsjoihs
わたしは……一応僅かに VisualBasic をやったけど、主に JavaScript スタートですね
れもん
れもん
初めてプログラミングに触れたのは、実はセンターの過去問の BASIC
TumoiYorozu
TumoiYorozu
ワイの代から BASIC なくなってしまった
hsjoihs
hsjoihs
私は C との接触方法がかなり変で、学校の図書館に坂井先生の『C言語 入門書の次に読む本』があって、入門書より先に読んだ。で、あと Web で『苦しんで覚える C 言語』、あとは図書室に『ロベールの C++ 入門講座』、これはかつての C++ の仕様をだいたい全部載せして紙で出したというヤバい本で、947 ページあるんですよ
uint256_t
uint256_t
『苦しんで覚える C 言語』『ロベールの C++ 入門講座』なつかしい
hsjoihs
hsjoihs
この本で『あんまり使わない機能も全部さらう』をやったのが今の私に効いてそう
uint256_t
uint256_t
それは効いてそうですね。そうじゃなきゃあんなの普通は知らないでしょうし
hsjoihs
hsjoihs
あとはまあ江添亮の『本の虫』ブログ読んでたのも影響してそう
uint256_t
uint256_t
たしかに有象無象が載ってそう
hsjoihs
hsjoihs
確認したら、7 年前に江添さんに規格について質問してて、『えっもう 7 年も規格追いとかやってるの』って気持ちになった
かりんとう
かりんとう
こんにちは
hsjoihs
hsjoihs
コンパイラどんな感じかお聞かせください
かりんとう
かりんとう
テスト期間なのであまり進んでない。new を実装し、Rust のチュートリアルの方を少し読んでいる。進捗としてはそれくらい
hsjoihs
hsjoihs
ログは取ってあるので読んでいただければ
かりんとう
かりんとう
めちゃめちゃ助かります
mikiken
mikiken
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ ./9cc "return foo();" > tmp.s
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ cc -c tmp.s
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ cc -o test tmp.o test.o
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ ./test
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ echo $?
0
hsjoihs
hsjoihs
現状把握はできたので、じゃああとはまったりしたり mikiken さんのバグ取りやったりですかね
mikiken
mikiken
雑談中にいろいろやってみたところ、エラーは出なくなった。でも実行結果が合わない
hsjoihs
hsjoihs
エラーというのは?
mikiken
mikiken
リンク時のエラー
tmp.s の内容
.intel_syntax noprefix
.globl main
main:
  push rbp
  mov rbp, rsp
  call foo
  pop rax
  mov rsp, rbp
  pop rbp
  ret
  mov rsp, rbp
  pop rbp
  ret
hsjoihs
hsjoihs
関数の戻り値って rax に入るんですけど、直後で pop rax すると rax が上書きされちゃうので、これは動かないかな、と
なので、2行しか読んでないけどとりあえずこの pop rax がマズいことだけはわかる
mikiken
mikiken
pop rax を消したらちゃんと動きました
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ cc -c tmp.s
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ cc -o test tmp.o test.o
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ ./test
mikiken@DESKTOP-CM4259U:~/compiler/9cc$ echo $?
5
hsjoihs
hsjoihs
じゃああとはコード生成を追うだけですね
uint256_t
uint256_t
return の生成の時にやってそう
hsjoihs
hsjoihs
なるほど。外側の return をするときにスタックトップの値を pop rax して自身の戻り値としてる、と
関数の戻り値をスタックに積み忘れてるんですかね
uint256_t
uint256_t
積まなくていいのでは?
hsjoihs
hsjoihs
いえ、関数の戻り値に 3 を足すコードを書いたりするときに、スタックマシン的に書いている現状ではスタックトップに値が必要
なので、push rax して、それが直に return されるときにはもう一回 return のコード生成で pop rax、とすることになる
画面共有していただければ。関数呼び出しのコード生成をいじるべき
その call の後に、push rax を足せばいいと思います
uint256_t
uint256_t
よさそうですね
mikiken
mikiken
いけました。ありがとうございます
hsjoihs
hsjoihs
これがクリアされた次はどこに行く感じですかね
mikiken
mikiken
引数を取れるようにするのが次のステップ
hsjoihs
hsjoihs
そうですね。関数の引数がさらに関数を呼び出し始めると、結構バグらせやすいのでお楽しみに
かりんとう
かりんとう
Rust をやってて思ったんですけど、match 文ってすべてのケースを網羅する必要があるじゃないですか。連続の値を判別するときにはデフォルトの値が必須になると書いてあって、
hsjoihs
hsjoihs
ほう
かりんとう
かりんとう
a が 1 の場合と 1 以外の場合、と書いても default が必要になる、と書いてあって
hsjoihs
hsjoihs
なにを読んでいらっしゃいます?
uint256_t
uint256_t
『1以外』をどう書くか、ですね
かりんとう
かりんとう
Rust の examples 見たんだっけな
hsjoihs
hsjoihs
ブラウザの履歴から漁れたりしませんか
ガード付きパターンマッチの話とかをしてる教材なんだろうけど、あんまり確証が持てない
かりんとう
かりんとう
どこに書いてあったか確証が持てません。Rust by Examples とか Rust book 日本語版とかかなぁ
uint256_t
uint256_t
hsjoihs
hsjoihs
ガード付きですよね
これはなにが言いたいかというと、『ガード付きのパターンマッチ、つまり、パターン if 条件 => を書いたなら、最後に _ を書かないといけないよね、というだけの話ですね
ガードなしの、『この値は、このパターンかこのパターンに合致するはずだ』はコンパイラが判断できるが、ガードのところにはなんでも書けちゃうので
かりんとう
かりんとう
そこの網羅性チェックは入らない?
hsjoihs
hsjoihs
できない
uint256_t
uint256_t
別に i にまつわらない式とかも書ける
hsjoihs
hsjoihs
ユーザーの入力を読み取って分岐、とか書けちゃうので
かりんとう
かりんとう
それは判別が無理そうですね
hsjoihs
hsjoihs
ガード付きのパターンマッチは、パターンマッチと if の組み合わせをすっきり書けて便利、以上のものではないので、まあそうなるよね、という話です
かりんとう
かりんとう
これはどういう点でうれしい?
hsjoihs
hsjoihs
たまにこれのおかげですっきり書けることはある
uint256_t
uint256_t
enum のマッチだと使う。AST とかで使える
hsjoihs
hsjoihs
実際に使った例とか見せるといいのかな
lemolatoon
lemolatoon
Box の値を enum の値として受け取って、Box をデリファレンスしてパターンマッチ、とかで書いた
どこに書いたっけなぁ
めちゃめちゃ長い match なんですけど
hsjoihs
hsjoihs
GitHub を貼るといい。GitHub を貼るときには、 y を押すと URL がコミット ID ベースにすり替わるのでうれしい。行を選択すると、その行へのリンクになる
lemolatoon
lemolatoon
515行目がガード式です
uint256_t
uint256_t
hsjoihs
hsjoihs
lemolatoonさんのは 514-515 行目にガード式 match がありますね
ここではまさに『別のローカル変数に言及する』目的で使ってますよね
ポインタとポインタの間は引き算しかできないので、引き算なら match してそうじゃなきゃエラー、と
かりんとう
かりんとう
ptrdiff_t になる演算でしたっけ
hsjoihs
hsjoihs
そうですね
かりんとう
かりんとう
C は暗黙の型変換が強いのに、なんで int とか long にしなかったの
lemolatoon
lemolatoon
hsjoihs さんの資料に ptrdiff_t の話がありましたね
hsjoihs
hsjoihs
書きました。大して書いてないんですけど、ptrdiff_t とは何かを普通に説明すべきだな。説明せずに唐突に出して『おもしろポイント』は不親切だろ
かりんとう
かりんとう
Rust やってて、i32 と i64 の足し算ができないので大量に as が入ってきたりしませんか
uint256_t
uint256_t
最初の間は多かった。徐々に慣れる。本質的に無理というところもあるけど
hsjoihs
hsjoihs
そうですよねぇ
uint256_t
uint256_t
謎の勘が。具体的にどういうところで?
かりんとう
かりんとう
具体的に遭遇してませんけど、型でエラーが起きる、なんというか、小数と int は起きるイメージがある。小数を定数倍したいときに
uint256_t
uint256_t
2倍なら 2.0 と書けばいい
かりんとう
かりんとう
まあたしかに
hsjoihs
hsjoihs
具体的に小数周りであったのは、サウンドプログラミングのときにかなり。C の実装を Rust に移して、同じファイルが出力されるかというのをやってたんですけど、C で暗黙の型変換が起こる場所に全部 as が必要になって、C の挙動を再現するにはここに型変換が入ります、ってのがコード上で明らかになったのは逆に嬉しかったかもしれない
かりんとう
かりんとう
書いてるときには型変換してるのはわかるけど、後から見たときによく分かんない、ってのがある
hsjoihs
hsjoihs
まあ C の型変換で超絶びっくりすることはあんまないですよね。問題は C++ ですよ
かりんとう
かりんとう
そんなに大変なんですか
hsjoihs
hsjoihs
この前ネクストキャンプの方を見に行ったんですけど、その際に、関数に () を付け忘れて cout << rand << endl
これ、何が出るかというと、なんと 1 って出力されるんですよ
TumoiYorozu
TumoiYorozu
ポインタだと思ったら、1 になるんだなというのを初めて知った
hsjoihs
hsjoihs
C++ の cout に普通のポインタを流すと、アドレスが出力される。これは暗黙で void * にポインタが変換されて、その void * を出力する関数が標準ライブラリにあるのでそれが呼ばれる
しかし、実は関数ポインタって規格上は void * に入れちゃいけないんですよ
かりんとう
かりんとう
もしかして未定義動作
hsjoihs
hsjoihs
ということは、暗黙の型変換で void * になることもなくて
uint256_t
uint256_t
bool になるのか
lemolatoon
lemolatoon
関数ポインタから bool はいいのか
hsjoihs
hsjoihs
さもないと if (p) できませんからね
れもん
れもん
C++ はオーバーロードとテンプレート解決がヤバい
uint256_t
uint256_t
オーバーロードこわい。最近も酷い目に遭った
かりんとう
かりんとう
じゃあ関数ポインタをどう表示したんですか
uint256_t
uint256_t
普通に無理矢理 void * にキャストすればいける
かりんとう
かりんとう
それはしていいんですか
hsjoihs
hsjoihs
しちゃいけないけど、普通は普通にそれで動く
lemolatoon
lemolatoon
『完全制覇』にも『関数ポインタを void * にしちゃいけない』は載ってる (p.97)
hsjoihs
hsjoihs
そんなことまで載ってるのか。すごいな
uint256_t
uint256_t
C-style cast だとできますけど static_cast だと怒られるんですね
TumoiYorozu
TumoiYorozu
reinterpret_cast じゃない?
uint256_t
uint256_t
たしかに。そっちなら怒られない
hsjoihs
hsjoihs
変換ができないことになってる理由は、一応 ANSI C Rationale に載ってます
いや、あんまりしっかりは載ってないな
多分 void * に突っ込むと困るアーキテクチャがあったんでしょ
ここの歴史も掘って書き足すかぁ
関数ポインタの実現方法がかなり変、というアーキテクチャがあったんじゃないかなぁ
TumoiYorozu
TumoiYorozu
関数だけ ROM に載ってるアーキテクチャとか
hsjoihs
hsjoihs
ハーバードアーキテクチャとかいうのもありましたね
uint256_t
uint256_t
プログラムとデータが別なアーキテクチャも
TumoiYorozu
TumoiYorozu
一応存在するからねぇ
hsjoihs
hsjoihs
そこら辺の事情なんでしょうけど、詳しくは調べないとわかんないな
On the MS/DOS 16 bit compilers, you had different "modes", and data pointers weren't necessarily the same size as function pointers. But at least on the ones I used, all data pointers (including void*) always had the same size. (Of course, you couldn't convert a function pointer to void*, since void* might be smaller. But according to the standard, you can't do that today, either.)
なんかこういう話が書いてあるなぁ
TumoiYorozu
TumoiYorozu
16 bit はまだまだ現役
hsjoihs
hsjoihs
関数ポインタのほうがでかくて void * に入らないらしい
MS/DOS の古い話、伝承としてしか知らないんだよな
You don't even have to have a Harvard architecture to have code and data pointers using different address spaces - the old DOS "Small" memory model did this (near pointers with CS != DS)
また DOS の話をしてるので、DOS の話を追わなきゃいけない
この時代の話を追うの、インターネットで追うより年齢層高めの講師に訊いたほうが速い説 is ある
TumoiYorozu
TumoiYorozu
坂井先生〜
hsjoihs
hsjoihs
For those who remember MS-DOS, Windows 3.1 and older the answer is quite easy. All of these used to support several different memory models, with varying combinations of characteristics for code and data pointers. So for instance for the Compact model (small code, large data): sizeof(void *) > sizeof(void(*)()) and conversely in the Medium model (large code, small data): sizeof(void *) < sizeof(void(*)()) In this case you didn't have separate storage for code and date but still couldn't convert between the two pointers (short of using non-standard __near and __far modifiers).
そろそろ解散しますかね。食事とかあるでしょうし。ありがとうございました〜
かりんとう
かりんとう
遅く来たのでもう少し残ろうかな
最近キャンプのチャットを追えていない
Y ゼミのボイスチャットの『サイゼリヤ』とかの名前いいですね。誰の発案なんでしょ
hsjoihs
hsjoihs
去年もこうでしたね。uchan さんとかの発案じゃないかな
かりんとう
かりんとう
なんでこの 3 択なんだろう
uint256_t
uint256_t
講師とチューターは府中行けるんですよね
hsjoihs さんは行くんですよね?私は行きません
行ってなにかするわけでもないし
TumoiYorozu
TumoiYorozu
IPA、府中でモニター配布してくれ~
uint256_t
uint256_t
コロナ前はセキュキャンそこでやってたんですよね
そこら辺のホテルより豪華
受講生にも会いたいですけどねぇ
かりんとう
かりんとう
顔も見てないですし
uint256_t
uint256_t
本番って Zoom とか使いますよね
Zoom 重いし C コンパイラゼミ自体は Discord でやろうかな
かりんとう
かりんとう
uint256_t
uint256_t
hsjoihs
hsjoihs
use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
    phones: Vec<String>,
}

fn typed_example() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into a Person object. This is exactly the
    // same function as the one that produced serde_json::Value above, but
    // now we are asking it for a Person as output.
    let p: Person = serde_json::from_str(data)?;

    // Do things just like with any other Rust data structure.
    println!("Please call {} at the number {}", p.name, p.phones[0]);

    Ok(())
}
本当に『自由にメンション飛ばしていいですからね』って言ってるのに受講生からメンションが飛んでこない
かりんとう
かりんとう
質問すべく言語化しようとするともう解決しちゃうことが多い
今週はみなさんわりと質問多かったかも?

2022年7月30日

hsjoihs
hsjoihs
ではでは〜
おや
かりんとう
かりんとう
来週金・土曜日難しいです。土曜の昼はいけます。でも日曜日ってキャンプ前日ですよね。テストとかレポートが大量に残っている
前日だけど、日曜日にあるとありがたいかも
hsjoihs
hsjoihs
@L3ゼミ受講生 来週の予定どうしましょうか
lemolatoon
lemolatoon
金曜日または日曜日ならば可能です。土曜だと22時からとかならできます。
mikiken
mikiken
木曜日: 20時以降ならOK
金曜日: いつでもOK
土曜日: いつでもOK
日曜日: いつでもOK
って感じです
kanataso
kanataso
いつでもOKです

2022年8月2日

hsjoihs
hsjoihs
@L3ゼミ受講生 本番も近づいてきましたし、せっかくならいまテキストでコンパイラの現状を尋ねておきますかね
mikiken
mikiken
現状、引数6つまでの関数呼び出しまで実装しました(関数定義はまだなので、今週中には実装したい)
kanataso
kanataso
ここ数日リファクタリングをしていて、構文解析と意味解析のパスを分離できました
hsjoihs
hsjoihs
お~
kanataso
kanataso
今はコード生成の前にASTを別の中間表現にするようにしようとしていて、その後にfloatとかdoubleとかunsignedとか作りたい
hsjoihs
hsjoihs
float, double, unsigned は実は意外とめんどいですよ!
もちろん実装したいのなら大いに歓迎しますが、わりとみなさんこの 3 機能は後回しにする傾向があります
2018 年受講生の C コンパイラを見てみると:
https://github.com/hsjoihs/c-compiler float, double, unsigned どれもなし(セルフホスト済)
https://github.com/ushitora-anqou/aqcc float, double, unsigned どれもなし(セルフホスト済)
https://github.com/ShinyaKato/thompson_hack float, unsigned なし、double あり(セルフホスト済)
2019 年:
https://github.com/Drumato/comcom/tree/master/src double, unsigned なし、float あり
lemolatoon
lemolatoon
スコープの実装をしています。
かりんとう
かりんとう
匿名構造体の実装をしてます。
れもん
れもん
float, double, unsignedは生成コードが結構使い回せないんですよね(꒪ཫ꒪; )
unsignedはロード(各bit幅の分だけ)と演算を用意する必要がありますし、floatやdoubleはロード、ストア、演算のほかに関数のコールについても考える必要があります……
(まぁでもそれだけ違う命令に触れられるということでもあるしやりがいはあると思います。私はunsignedを実装したことがあります)
uint256_t
uint256_t
浮動小数点数を実装するとなると使うレジスタが増えたり、ちょっと面倒な部分がありますね

2022年8月3日

かりんとう
かりんとう
unsignedってアセンブリレベルで何が違うのかいまいちわかってないですね。。
hsjoihs
hsjoihs
比較演算子をコンパイルする際に参照すべきフラグが変わったり、より大きな幅のレジスタへと拡張するときに上位ビットをゼロで埋めるか符号ビットで埋めるかの差などとして効いてきます
かりんとう
かりんとう
ところで一つ質問があります。
int a = {{{{1}}}};がgcc -std=c89でコンパイル可能でしたが、どのように解釈されているのでしょうか?(warningはでました)
uint256_t
uint256_t
文法的にはおかしくないはずなので、コンパイラが気を利かせてコンパイル可能にしているだけのような気がしました
一応述べられている..?
clangだとこのあたりでtoo many bracesと言っている
かりんとう
かりんとう
なるほど。規格上は一応許されていそうですね。ありがとうございます。

2022年8月4日

hsjoihs
hsjoihs
@L3ゼミ受講生 本番が近いので 2 日に 1 回のペースでみなさんの進捗を尋ねておくか
lemolatoon
lemolatoon
同じ関数内のfor文でiを何回も使えたりスコープがいい感じになったので次はグローバル変数を実装しようと思っています。グローバル変数のパースまではプロトタイプ宣言の時にやったのですぐに実装できる(と願っています)
かりんとう
かりんとう
無名構造体、共用体は実装できたので次に関数ポインターに取り組んでいます。
kanataso
kanataso
ASTから直接コード生成してたのを、AST->中間表現->コード生成にできました(゚∀゚)
mikiken
mikiken
大学の試験が終わったので、関数定義(引数なし)をまずは実装しているところです
kanataso
kanataso
(アーキテクチャに依存しているプログラムがコード生成部以外に少しだけ残っているので、それを移動したら別のアーキテクチャでもセルフホストしたい)

2022年8月5日

lemolatoon
lemolatoon
これ文法エラーになるんですけどどう書けばいいですかね
global:
  .long 0
main:
  mov rax, global
  mov rdi, 5
  mov DWORD PTR [rax], edi
れもん
れもん
offset globalと書けば左辺値取れます
lemolatoon
lemolatoon
なぜがこれclangでセグフォする
れもん
れもん
.dataセクションにグローバル変数を入れる必要がある
あとグローバル変数や関数はstaticでない限りは.globalを付けるとよいです。これで外からも見えるようになります。

2022年8月6日

hsjoihs
hsjoihs
@L3ゼミ受講生 本番が近いので 2 日に 1 回のペース
lemolatoon
lemolatoon
グローバル変数を実装してポインタ以外のグローバル変数の初期化式も実装しました。char型を導入したのですが、暗黙の型変換も明示的な型変換の実装もまだで、リテラルの数字は常にintになっているので、何もできません。今はまず暗黙の型変換を実装しようとしています。
かりんとう
かりんとう
関数をグローバル変数として扱う実装ができたので、関数型をパースできるように取り組んでいます。関数型は戻り値と引数の2種類の型情報を持つので、他の型と扱いをどのように変える必要があるのか考えています。
mikiken
mikiken
関数定義(引数なし)がなんとか動くようになったので、明日中には引数を取れるようにしたいです。(あと、そろそろコードや読みづらくなってきたのでリファクタリングしていきたい)
uint256_t
uint256_t
最適化の資料を、書きかけですが HackMD に移動させておきました。 だれでも編集できます:【編注:2022年8月26日現在、未だに『だれでも編集できる』状態になっているので、荒らし防止のために今は URL を開示しないでおきます】

2022年8月7日

lemolatoon
lemolatoon
こんな感じのコードってかけないんですかね?クロージャーについてあまり良くわかってなくて対処法がわからないです。
uint256_t
uint256_t
例えばこれなら動きますね
lemolatoon
lemolatoon
普通に借用できるんですね。知らなかったです。
色々試してたらこれでもできました。 &Fn(T) -> UにFnOnce(T) -> Uがimplされてるみたいです。
教えてくださってありがとうございます!
uint256_t
uint256_t
なるほど、そちらの方がよさそうです
@hsjoihs 今日は何時からか決まってましたっけ
hsjoihs
hsjoihs
@uint256_t なんも決めてないですが、私は一日中いつでもいいです
uint256_t
uint256_t
個人的には夜(21:00~)がいいですが、受講生の方にも聞いたほうがいいですね
lemolatoon
lemolatoon
僕も夜(21時からなど)希望です
mikiken
mikiken
僕も夜希望です〜
かりんとう
かりんとう
僕も夜(21:30-)希望です〜
kanataso
kanataso
何時でもいいです
uint256_t
uint256_t
一応L3-voiceで話しています
hsjoihs
hsjoihs
@れもん(Lちゅた @tamaron(L3チューター) ちなみにやってます
mikiken
mikiken
引数なしの関数の定義が動くようになって、いま関数定義の引数を取れるようにしていて、引数をパースするところができたっぽくて、いまはコード生成を書いてる
uint256_t
uint256_t
implement_functionブランチですか?なにか詰まってるところとかあります?
mikiken
mikiken
パースするところが日中詰まってたけど、なんか上手くいったので、コード生成部分。詰まったら聞きます
uint256_t
uint256_t
本番期間中もガンガン訊いてください
mikiken
mikiken
本番環境中は discord で繋いで分からないことがあったら訊く感じですかね?
uint256_t
uint256_t
本番は Zoom だったかな。hsjoihs さん知ってます?Google カレンダーだと Zoom のリンクがある。L3 用も
これ使えってことなんですかね?
hsjoihs
hsjoihs
Zoom、複数人の画面の同時共有ができないという一点で C コンパイラゼミに不適
uint256_t
uint256_t
勝手に Discord でやったら怒られるのかな
まあここら辺は講師がやることですね
Zoom 入れてない人がいたら一応入れといて。開会式は WebEx でしたっけ
mikiken
mikiken
そうなんですよね。なんなんでしょう
uint256_t
uint256_t
多分閉会式もそう。いまインストールしておくとよさそう
じゃあ次の方
lemolatoon
lemolatoon
前回から、グローバル変数を作って、そのあと char を導入して、char と int の暗黙型変換だけ実装して、array の initializer を実装したりした
uint256_t
uint256_t
ありがとうございます。develop ブランチですね?insta のスナップショットがある
lemolatoon
lemolatoon
スナップショットも増やしたりしてる。個人的には、気軽にテストケースが増やせていいなと思ってる
詰まってたところがあったが、さっき自力で気づいた
uint256_t
uint256_t
じゃあ大丈夫そうですかね
じゃあ次
kanataso
kanataso
8/4 からの進捗は、アーキテクチャに依存したプログラムを全部コード生成部分に移動して、今日移動し終わった。お昼ぐらいから RISC-V の上で動く Fedora の上でコンパイラを動かそうとしています
uint256_t
uint256_t
risc-v ブランチですか?
kanataso
kanataso
さっき中身が return だけの関数がやっと書けるようになりまして、ちょっとこれは時間掛かりそうだなと思っております
lemolatoon
lemolatoon
中間表現までは同じのを使ってってことですか
kanataso
kanataso
そうなんですよ
lemolatoon
lemolatoon
うれしみが深いですね
hsjoihs
hsjoihs
わりといけるんですね。大変そうという印象があった
uint256_t
uint256_t
コミットログを見ると、苦労してる様子が伝わってくる
kanataso
kanataso
コミットメッセージがテキトー
lemolatoon
lemolatoon
『睡眠』ってコミットいいですね
kanataso
kanataso
それは寝る前にコミットしたやつ
uint256_t
uint256_t
中間表現までを共通にできるのはいいですね
hsjoihs
hsjoihs
↑これ貼っとくか
普通の git の表示と逆で分かりにくいけど
uint256_t
uint256_t
たまに fix って書いたコミットしちゃう
hsjoihs
hsjoihs
コミットメッセージが refactor → refactor → refactor とかありますよね
かりんとう
かりんとう
進捗は、いまだに関数ポインタの実装をしていまして、関数ポインタをパースできるようになって、で、実行もできるようになったのですが、関数の呼び出しの BNF がちょっと違っていたり、関数ポインタ以外の普通の変数が呼び出せちゃったりとバグが多い。そこの修正をやっている
hsjoihs
hsjoihs
ふむふむ
かりんとう
かりんとう
BNF が違うというのは、primary のところで関数の呼び出しのカッコのところをやっていたのですけど、それを postfix のところに移さなきゃいけない。どういうときにバグるかというと、
(*a)(); などの、『関数のポインタへのポインタ』を実行するときに、それだと『識別子のあとにカッコ』しか呼び出せない今の実装ではダメ
そこら辺を修正しようとしていました
hsjoihs
hsjoihs
なるほど分かりました
そういや (******printf)("Hello"); ができるという話が
これも『C言語ポインタ完全制覇』に載ってます。なんでも載ってるからなあの本
かりんとう
かりんとう
関数自体をデリファレンスできるんですか
hsjoihs
hsjoihs
『関数自体が式の中に登場すると、関数ポインタに読み替えられる』というルールがあります
かりんとう
かりんとう
Node で型が関数のノードがあったときに、それを関数ポインタへと認識し直す、ということかな
hsjoihs
hsjoihs
それで実装できそうですね
かりんとう
かりんとう
完全に盲点でした。これも考慮しなきゃいけないのか
hsjoihs
hsjoihs
まあ『これが書ける』という話をする以外の目的でこれが使われてるの見たことないですけどね
かりんとう
かりんとう
関数型を sizeof したときに、gcc で試したら 1 と出てきたんですけど、なんでですか?
hsjoihs
hsjoihs
えーと、多分それは gcc のマニュアルかどっかに書いてあった、勝手に sizeof を定義してるやつだったような。裏取りしま~す
かりんとう
かりんとう
仕様で決まっているわけではない?
hsjoihs
hsjoihs
仕様は関数とか関数ポインタのメモリ上のうんたらについてなにも言っていないので。前に言ったとおり、規格上は関数ポインタは void * にすらできないので
lemolatoon
lemolatoon
clang でやったら invalid application of sizeof to function type って言ってきますね
hsjoihs
hsjoihs
gcc でも -pedantic でそれが出るんじゃないかな
lemolatoon
lemolatoon
あ、わたしも clang に既に -pedantic 付けてた
uint256_t
uint256_t
標準にダメって書いてありますね
hsjoihs
hsjoihs
やっぱそうですよね
The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member. The _Alignof operator shall not be applied to a function type or an incomplete type.
lemolatoon
lemolatoon
こういうときに shall 使うんですね,英語で
hsjoihs
hsjoihs
そうですね
かりんとう
かりんとう
shall ってあんまり見かけないけど、仕様書だとよく見るなぁ
uint256_t
uint256_t
formalな文章だと見かけますね
hsjoihs
hsjoihs
そうですね、そもそも shall の語義と言いますか、義務を私が規定します、という文脈なら shall なんですよね。shall の『過去形』である should が義務とか『そうあるべき』といったニュアンスなわけですけど、これは shall を『過去形』にすることで婉曲さというかなんというかが出るという表現なんですよね。しかし今回は規格書が『私は、Cはこうであるべきと宣言する』なので shall がピッタリはまるといいますか
かりんとう
かりんとう
普段なんとなく読み飛ばしてた
hsjoihs
hsjoihs
手元の『新マスター英文法』を引くと、『(1) 強い意志・決意 (2)命令・禁止 (3)規定 (4)予言・約束』としてますね
uint256_t
uint256_t
普通に勉強になりました
hsjoihs
hsjoihs
まあなんか意外と全単語を追わなくても自分で規格とかを読む分には困りませんからね
教材とか書くとちゃんと訳さないとなので、自信のない単語とか、語義は分かっても上手い訳語が思いつかない語は辞書引きますねぇ
かりんとう
かりんとう
さて、えっと明日は昼からでしたっけ
uint256_t
uint256_t
そうですね。13:30 ですね
かりんとう
かりんとう
開講式、LT、グループワーク
lemolatoon
lemolatoon
グループワークってなにやるんだろう
uint256_t
uint256_t
僕も忘れちゃった
かりんとう
かりんとう
『セキュリティ・キャンプ修了後に取り組むこと』がテーマです
hsjoihs
hsjoihs
過去 3 年セキュリティ・キャンプ携わった印象としては、グループワークが、運営の期待するような役割を果たし続けているグループは非常に少ないです
かりんとう
かりんとう
グループ内で交流が深まる、みたいな?
hsjoihs
hsjoihs
たとえば 2021 のだと、一つだけキャンプ修了後一年に亘って毎週ブログ出してるところがあって、多分運営はそういうのが起きて欲しいなと思って設けてるんでしょうけど、まあ、達成されてる率は……目測で 5%?まあ 5% あれば十分有意義という気もしなくはないが
かりんとう
かりんとう
去年のミニキャンプにも参加して、そこにもグループワークがあって、それもなんか同じテーマだったような気がする
uint256_t
uint256_t
そんなに使い回されてるんですね。もうちょいバリエーションがあってもいい気がしますけど
かりんとう
かりんとう
今でも交流がありますね
hsjoihs
hsjoihs
それはよかった
かりんとう
かりんとう
C コンパイラブック輪読回
hsjoihs
hsjoihs
あー _Alignof さんの
かりんとう
かりんとう
他数名と 2 週間に一回ぐらい。今日遅れたのもそれ関連
hsjoihs
hsjoihs
あ、そうなんですね
かりんとう
かりんとう
最近は輪読回を進めつつ雑談をしているという集まりに
hsjoihs
hsjoihs
そういうのもいいですよね~
かりんとう
かりんとう
月 1~2 回集まって、コンパイラについての雑談とか
グループワーク、でも、時間短くないですか?
hsjoihs
hsjoihs
そうなんですよ
去年のグループワーク周りで、一年続いたリレーブログ
これ後ろ向きのリンクだけがある連結リストなんですけど。『前回はこちら』からどんどん遡れます
uint256_t
uint256_t
シーケンシャルにしかいけないんですかこれ
かりんとう
かりんとう
7月3日のやつは両方向に繋がってますね
uint256_t
uint256_t
たまに doubly-linked になってる
Bigdrea6
Bigdrea6
両方向にするの、記事出してからになるからめんどくさいんですよね
hsjoihs
hsjoihs
そうですよね~
lemolatoon
lemolatoon
途中でリンクがなくなってる
Bigdrea6
Bigdrea6
自分な気がします
かりんとう
かりんとう
ゆるく続けられているの、いいですね。ゼミごとにグループを組むわけではないので、幅広くなるのが面白い
そもそもゼミごとに人数違うのか
hsjoihs
hsjoihs
そうですね。L に関しては L3 が他と比べて圧倒的にでかいですからね
かりんとう
かりんとう
8:30 スタートか、早いなぁ
来週、13・14コミケありますね
hsjoihs
hsjoihs
私も売り子をやる予定です
かりんとう
かりんとう
毎回参加されてますか?
hsjoihs
hsjoihs
そうですね、最近の対面開催はだいたい出てるかな
かりんとう
かりんとう
そうか、コロナでやってない期間が続いていた
第100回か、行きたいけど遠いしな……他に行かれる方っていますか?
lemolatoon
lemolatoon
かりんとうさんってどこでしたっけ
かりんとう
かりんとう
札幌辺りです
lemolatoon
lemolatoon
それは遠いですね
かりんとう
かりんとう
飛行機必要だし、キャンプ翌日だし……
uint256_t
uint256_t
コミケって予約しないと行けないんでしたっけ
hsjoihs
hsjoihs
そうですね、今年は。例年はそうではないんですけど、感染対策で
uint256_t
uint256_t
一応日曜日から東京行くんですけどね
100回目だから人が多そう
かりんとう
かりんとう
lemolatoonさんはどこです?
lemolatoon
lemolatoon
千葉なので近いんですが、コミケは行ったことが無い
かりんとう
かりんとう
アニメとかゲームとかは?
lemolatoon
lemolatoon
僕はあんまり見ない
hsjoihs
hsjoihs
私が出るのはこれです
lemolatoon
lemolatoon
架空の言語でゲーム作るやつやってましたよね hsjoihs さん
hsjoihs
hsjoihs
はい、それはやってますが、その集まりでコミケに出るわけではありません
このサークル名は、漢語音韻学の用語である『重紐』と、『超ひも理論』を掛けたものです
こういう本です
かりんとう
かりんとう
表紙はどうして唐揚げ?
hsjoihs
hsjoihs
作者が唐揚げが好きだからだそうです
uint256_t
uint256_t
ビッグサイトって海の近くなんですね。全然知らなかった
かりんとう
かりんとう
これ、キャンプ対面だったら終わった後そのままコミケ行けました?
hsjoihs
hsjoihs
はい
かりんとう
かりんとう
うわぁ~
uint256_t
uint256_t
土日暑そうですね。熱中症になりそう
hsjoihs
hsjoihs
そうですね~、例年も熱中症対策は必要です
かりんとう
かりんとう
やはり戦場
hsjoihs
hsjoihs
場所にもよります。こういう言語の本を配ってるところはあんまり戦場って感じじゃないです
uint256_t
uint256_t
エアコン効いてるってことですか
hsjoihs
hsjoihs
それもありますが、単純に需要。分野によっては本当に人が殺到するので、大変だと聞いています
かりんとう
かりんとう
今年だとなにが流行っているんだろう
自分のことですまないんですけど、明日辺り、LT に申し込んでしまって、タイトルが『C 言語でめったに見ない構文』みたいなもので、例えば switch 内の case はラベルなので、とかの話を書こうと思ってるんですけど、『これって全く見ないんですか?』というのがあって、感想が欲しい
こういう、関数宣言の戻り値の型のところに構造体の定義が書ける話とか
hsjoihs
hsjoihs
main(); って書くのが普通だった時代だと、 struct A { ... } の後にセミコロン忘れて main 書いて、関数から return した直後に落ちるってことがあったらしいですよ
かりんとう
かりんとう
int a = {{{{{3}}}}}; って規格上正しいんですか?gcc は -std=c89 付けたときにこれを通します
hsjoihs
hsjoihs
optionally enclosed in braces の braces をどう解釈するかだよなぁ。a pair of braces って書いてあってくれれば困らないのに
かりんとう
かりんとう
const ポインタって普通だったりしますか? const int * const * const とかの話って
uint256_t
uint256_t
まあ 3 つ以上は見ないでしょうねぇ
かりんとう
かりんとう
まああとカンマ演算子とか
uint256_t
uint256_t
内積がでてきそうですね、 (1, 2, 3, 4) * (5, 6, 7, 8)
かりんとう
かりんとう
あとは……なんの最適化でしたっけ、switch の
hsjoihsuint256_t
hsjoihsuint256_t
Duff's device
かりんとう
かりんとう
あとはさっきの (*****printf)、これ個数に制限ありましたよね
uint256_t
uint256_t
コンパイラ依存ですね
かりんとう
かりんとう
int **********a;
a = new (int*********);
*a = new (int********);
**a = new (int*******);
***a = new (int******);
****a = new (int*****);
*****a = new (int****);
******a = new (int***);
*******a = new (int**);
********a = new (int*);
*********a = new (int);
uint256_t
uint256_t
みんなの自作コンパイラにこういうの食わせてみたい
あんまエラー処理に力を入れてないと思うので。全然エラー処理の話をしませんでしたね、今まで
かりんとう
かりんとう
テストケースも通る方しか考えてなかった
そろそろ抜けます
hsjoihs
hsjoihs
そもそも明日がキャンプなので、早めに閉めた方がいい
uint256_t
uint256_t
じゃあお開きですかね
hsjoihs
hsjoihs
ではまた明日

2022年8月8日

れもん
れもん
ワー完全に失念。ログ読むか…
hsjoihs
hsjoihs
個人的には、自作プログラミング言語を作るノウハウはわりと飽和してるけど、『自作プログラミング言語にそれなりの開発体験を与える様々なツールチェーンの整備』とかは全く教材がないから書きがいがありそうなんだよな
シンタックスハイライトとか、コンパイラのエラー報告をエディタに波線で出すとか、そういうやつ
わたすけさんが書いてらっしゃるこれ、便利なのでここに貼っておくか
わたすけ
わたすけ
なんか間違えてたら教えてください
uint256_t
uint256_t
language server の話をまとめたいですね

2022年8月9日

hsjoihs
hsjoihs
@L3ゼミ受講生 @れもん(Lちゅた @tamaron(L3チューター) @uint256_t おはようございます~
ということで始めていきましょうか
まずは恒例通り、みなさまのコンパイラがいまどんな感じか
mikiken
mikiken
ポインタの間接参照演算子とアドレス演算子を実装してて、だいたいのテストケースは通っているけど、ポインタの指す先に値を再代入するところがバグってるというか、代入するところの処理をまだ書き足せていないので、書いている
かりんとう
かりんとう
未だに関数ポインタを実装途中で、デリファレンスが何回でもできるというのは飛ばして実装。このあと何を進めるつもりかというと、例外処理をちゃんとやり、パーサーを『AST の構築』と『expect とかのエラー処理周り』を分割しようかな、と。あとは構造体を引数として渡すとか、戻り値として返すとか、そこらへんも実装していけたらな、と
lemolatoon
lemolatoon
前回の一昨日からやったのは、グローバル変数の初期化でグローバル変数のアドレスを取れるようにした。この後は、文字列リテラルを書けるようにしていく
kanataso
kanataso
私は、あと 108 個テストケースを通したら、RISC-V でセルフホストできると思います
uint256_t
uint256_t
すごい
hsjoihs
hsjoihs
その 108 個といいますと?
kanataso
kanataso
もともと自分で 285 個用意してて、グローバル変数とかがまだないので
uint256_t
uint256_t
けっこうもう通ってる?
kanataso
kanataso
半分以上は
uint256_t
uint256_t
なるほど
hsjoihs
hsjoihs
これで全員分の現状把握ができましたね
今までは『この確認をして、雑談をして、解散』、みたいな感じでしたが、今日は一日中 C コンパイラを書くということで
さてどうするのがいいんかなぁ
uint256_t
uint256_t
ここからどうしましょうねぇ
hsjoihs
hsjoihs
せっかく Discord なので、受講生が画面を全部配信するという手がある
uint256_t
uint256_t
同時に見ることはできないけど
hsjoihs
hsjoihs
いやまあそれは対面でも同じなので。順繰りに
かりんとう
かりんとう
複数人が配信してるの初めて見た
今日が一番開発時間多いんですね
hsjoihs
hsjoihs
そうですね
uint256_t
uint256_t
木曜ってどうでしたっけ
hsjoihs
hsjoihs
資料作成とか
かりんとう
かりんとう
あと LT とか
mikiken
mikiken
こういうテストケースがあった
assert 7 '{ x=3; y=5; *(&x+8)=7; return y; }'
assert 7 '{ x=3; y=5; *(&y-8)=7; return x; }'
hsjoihs
hsjoihs
普通に余裕で未定義動作なのでびっくりしてしまった。そんなテストケース入れ込んでるのか Rui さん
この話はまさに『C と未定義を社会言語学からも』にも書きましたね
uint256_t
uint256_t
最適化したら落ちそう
mikiken さんのコンパイラのビルドが通らない。master でいいんですよね?
mikiken
mikiken
master で大丈夫です
uint256_t
uint256_t
これ make したら動く想定ですか?
mikiken
mikiken
make test したら動くはず
hsjoihs
hsjoihs
Mac で開発してるの誰でしたっけ
kanataso
kanataso
はーい
uint256_t
uint256_t
あ、mikiken さんの動きました。よかった
hsjoihs
hsjoihs
なので Mac のやつは Mac でビルドしなきゃいけなくて
uint256_t
uint256_t
たしかに、ビルドできない。Mac でやるか。あっこれ ARM だった
hsjoihs
hsjoihs
はい、Mac 機はこっちで用意してあるので
Zoom の部屋がいつまでも開かないので閉じよう
uint256_t
uint256_t
ですね
hsjoihs
hsjoihs
kanatasoさんの、バックスラッシュ (U+005C) じゃなくて、円記号 (U+00A5) が入ってませんか? test.sh に
if [ "`cat tmp/err | wc -l | tr -d ¥" ¥"`" != "0" ]; then
    echo "--- report ---------------"
    cat tmp/err | sort
    echo "test failed : `wc -l tmp/err | tr -d " tmp/er"` errors"
    exit 1
fi
Mac で入力してるんだと思うんですけど、Mac の日本語キーボードの円記号キーは本物の円記号 (U+00A5) が入っちゃいます
kanataso
kanataso
普段は Mac で ssh でやってるので、ときどき Mac を使った際にやってしまったな
hsjoihs
hsjoihs
バックスラッシュは Option + 円記号ですね
ということでこれは多分エスケープになってないです
lemolatoon
lemolatoon
普通にやると 3 になるのに assert 入れると 3 にならない。test30 ってのを書いてたんですけど
int exit(int status);
int test30()
{
    char char_var = -1;
    assert(30, -1, char_var);
    int int_var = 4;
    exit(int_var + char_var);
    assert(30, 3, int_var + char_var);
    assert(30, 3, char_var + int_var);
    return 0;
}
hsjoihs
hsjoihs
なにも見てないんですけど、関数呼び出しがあることでレジスタの値が変に書き換わってませんか?なにも見てないですけど
lemolatoon
lemolatoon
足し算があやしい気がする。他のテストケースは通ってるので
hsjoihs
hsjoihs
類似のテストケースでどういうのは通ります?
lemolatoon
lemolatoon
キャストという Expr_Kind にしてるはずなのに、足し算だけうまくいっていない。 AST を見ます
hsjoihs
hsjoihs
可能性があるのと言えば、足し算はポインタも受け付けなきゃいけないので、他の演算子と演算子を分けなきゃいけなくて、そこで一貫してない、とか?というか憶測を語らずに私はソースを読むべきで
lemolatoon
lemolatoon
どんな AST が生成されてるのか見てみます
hsjoihs
hsjoihs
lemolatoonさんブランチどこでしたっけ。develop?
lemolatoon
lemolatoon
develop です
足し算のところに char 側をキャストせずに呼んでる
hsjoihs
hsjoihs
add を見ると、Type::Base どうしだとバイト数の大きい方の型を取っている
lemolatoon
lemolatoon
けれども、キャストをしていない
hsjoihs
hsjoihs
あー、取るだけとってキャストしてないのね
えーと System V ABI だと char は signed か
kanatasoさんの画面、アセンブラから大量のエラーメッセージが出ておるな
uint256_t
uint256_t
そういえば Zoom って開いてるんですかね?開いてない?今日は使わなくていいか
lemolatoon
lemolatoon
さっきのバグなんですけど、片方を clang でコンパイルした関数に渡すとバグってる
hsjoihs
hsjoihs
じゃあ ABI をミスってるんですかね
lemolatoon
lemolatoon
一回計算して、関数に渡して、関数から返してから渡すとうまくいって
hsjoihs
hsjoihs
レジスタが化けてそうだなぁ
最小限でバグる例を作れたら、アセンブリリーディングやりましょう。こちらにはホワイトボードがあります
lemolatoon
lemolatoon
レジスタが間違ってるのかなぁ
リンクしたあとの方が分かりやすい?
hsjoihs
hsjoihs
いえ、リンク前のを
lemolatoon
lemolatoon
二つのアセンブリを
hsjoihs
hsjoihs
そうです
どんな感じですか今?小さくするとバグが消える?
lemolatoon
lemolatoon
そうです
hsjoihs
hsjoihs
じゃあ小さくないやつ読みますか
れもん
れもん
extend されずに push されてない?
符号拡張されずに push rax されてる
lemolatoon
lemolatoon
一応 movsx は書いたんですけど
あ、ちゃんとバグった。バグったのを渡します
hsjoihs
hsjoihs
じゃあまあ Discord に貼っていただくなどして
lemolatoon
lemolatoon
リンク先も minimize します
tmp.c
int assert(int index, int expected, int got);

int main()
{
  char char_var = -1;
  int int_var = 4;
  assert(30, -1, char_var);
  assert(30, 4, int_var);
  assert(30, 3, int_var + char_var);
  assert(30, 3, char_var + int_var);
  return 0;
}
link.c
#include <stdio.h>

int assert(int index, int expected, int got)
{
    printf("got: %d\n", got);
    if (expected != got)
    {
        assertion_failed(index, expected, got);
    }
    else
    {
        passed(index);
    }
    return 0;
}
hsjoihs
hsjoihs
tmp.c が自作コンパイラで、link.c が clang でコンパイルされて、リンク、ですよね?じゃあその結果のアセンブリも
このテストケースで、int と char を足してるところがバグるんですよね?
lemolatoon
lemolatoon
はい……いや、違う、これ最小のじゃない
clang で intel で出すのって
uint256_t
uint256_t
-masm=intel
hsjoihs
hsjoihs
まあ AT&T でも私は読めますけど(というか私は AT&T で C コンパイラ書いた)
lemolatoon
lemolatoon
tmp.s
.intel_syntax noprefix

.global main
.text
.global main
main:
  push rbp
  mov rbp, rsp
  sub rsp, 5
  mov rax, rbp
  sub rax, 1
  push rax
  push 0
  push 1
  pop rdi
  pop rax
  sub rax, rdi
  push rax
  pop rdi
  pop rax
  mov BYTE PTR [rax], dil
  push rdi
  pop rax
  mov rax, rbp
  sub rax, 5
  push rax
  push 4
  pop rdi
  pop rax
  mov DWORD PTR [rax], edi
  push rdi
  pop rax
  push 30
  push 0
  push 1
  pop rdi
  pop rax
  sub rax, rdi
  push rax
  mov rax, rbp
  sub rax, 1
  push rax
  pop rax
  mov al, BYTE PTR [rax]
  push rax
  pop rax
  movsx eax, al
  push rax
  pop rdx
  pop rsi
  pop rdi
  sub rsp, 8
  call assert
  add rsp, 8
  push rax
  pop rax
  push 30
  push 4
  mov rax, rbp
  sub rax, 5
  push rax
  pop rax
  mov eax, DWORD PTR [rax]
  push rax
  pop rdx
  pop rsi
  pop rdi
  sub rsp, 8
  call assert
  add rsp, 8
  push rax
  pop rax
  push 30
  push 3
  mov rax, rbp
  sub rax, 5
  push rax
  pop rax
  mov eax, DWORD PTR [rax]
  push rax
  mov rax, rbp
  sub rax, 1
  push rax
  pop rax
  mov al, BYTE PTR [rax]
  push rax
  pop rax
  movsx eax, al
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rdx
  pop rsi
  pop rdi
  sub rsp, 8
  call assert
  add rsp, 8
  push rax
  pop rax
  push 30
  push 3
  mov rax, rbp
  sub rax, 1
  push rax
  pop rax
  mov al, BYTE PTR [rax]
  push rax
  mov rax, rbp
  sub rax, 5
  push rax
  pop rax
  mov eax, DWORD PTR [rax]
  push rax
  pop rdi
  pop rax
  add rax, rdi
  push rax
  pop rdx
  pop rsi
  pop rdi
  sub rsp, 8
  call assert
  add rsp, 8
  push rax
  pop rax
  push 0
  pop rax
  jmp .Lmain_ret
.Lmain_ret:
  mov rsp, rbp
  pop rbp
  ret
『メッセージが長すぎます』
hsjoihs
hsjoihs
そういうときに勝手にテキストファイルにしてくれませんでしたっけ
lemolatoon
lemolatoon
Nitro にして、と
hsjoihs
hsjoihs
あーあれ Nitro 機能か
まあ clang 側はどうせ合ってるのでいいですよ。そもそもこっちで再現できますし
lemolatoon
lemolatoon
あれ、int + char は通ってる。char + int だけ落ちてる
あっ普通にコードのほうでミスってるかも
hsjoihs
hsjoihs
最後の呼び出し、普通に movsx がなくない?
lemolatoon
lemolatoon
あっ左辺をキャストしなきゃいけないときも右辺をキャストしてる
直したら通った!
hsjoihs
hsjoihs
やったぁ
lemolatoon
lemolatoon
ABI のミスとかだったら大変だったので、もっと単純なミスでよかった
hsjoihs
hsjoihs
今まで通ってなかったテストケースが一個追加で通ったら、コミットしちゃってもいいと思う
lemolatoon
lemolatoon
insta を足してます
hsjoihs
hsjoihs
あ、なるほど。それはしたほうがいいですね
uint256_t
uint256_t
見ないと意味ないですからね
lemolatoon
lemolatoon
無事通りました
uint256_t
uint256_t
これ全部 changelog 書いてるんですか
lemolatoon
lemolatoon
マージするときは必ず
uint256_t
uint256_t
すごいですね
hsjoihs
hsjoihs
ここまでしっかり書くのすごいな
uint256_t
uint256_t
changelog 書いたことないんですよね。コミットメッセージで済ませちゃう
lemolatoon
lemolatoon
changelog を書くことでバグるテストケースを思い付けたりする
implicit_cast って関数を作ってたのにそれ呼ばずにキャストしちゃってたな
hsjoihs
hsjoihs
そうですね、たまにはリファクタリングフェーズを挟むのもよいのかも
てか clippy の warning も溜まってますしね
uint256_t
uint256_t
まあ clippy のメッセージ、全部が全部直す必要があるわけじゃないですからね
hsjoihs
hsjoihs
いらないと判断したやつは積極的に allow していきましょう。そもそも clippy::pedantic ってだいぶ『それほんとにやる必要ある?』があるので
あー large_enum_variant ね
uint256_t
uint256_t
言いたいことは分かるけど
hsjoihs
hsjoihs
clippy の issue で『みんなが allow を付けたがるリスト』の上位にランクインしてたはず
lemolatoon
lemolatoon
やるのはそんなに難しくないですけど、どうなんですかね
match のときにめんどくさくなりそう
hsjoihs
hsjoihs
global に allow でもいいと思いますよ
次のこれは、『ワイルドカードで受けていると、あとで enum の variant が増えたときに見落とすぞ』という warning。まあコンパイラ実装する際ってわりと variant を途中で増やすので、この warning には従っておくといいかもしれない
lemolatoon
lemolatoon
ここに Array は来ないはずなんですよね
hsjoihs
hsjoihs
なら、『既に Ptr へとキャストされているべきである』みたいなメッセージ入りの unreachable!() を書くと良い
uint256_t
uint256_t
assert とか unreachable は最適化を助ける
lemolatoon
lemolatoon
最適化助けるんですね、これ
uint256_t
uint256_t
うまく assert を書くと配列の境界値チェックとか消滅する
hsjoihs
hsjoihs
Rust の unreachable!() は、間違って到達したら普通に panic してくれるので。もうひとつ unreachable_unchecked!() ってのがあって、これは到達すると未定義動作
uint256_t
uint256_t
そもそも使わないほうが良い
hsjoihs
hsjoihs
そもそも当然ながら unsafe マクロですので
uint256_t
uint256_t
関数の方のは未定義動作、か。絶対に到達できないならいいんですけど
lemolatoon
lemolatoon
だいぶこわいですよね
uint256_t
uint256_t
あんま人間を信用していないので
tamaron
tamaron
hsjoihs
hsjoihs
はい、私の『C と未定義を社会言語学からも』でもこれ引用してますね
ここの部分の説明まだ書けてないんだよな
tamaron
tamaron
unreachable_uncheckedがどうして存在するのか書いてあります
hsjoihs
hsjoihs
今のところ使ってない引数についてのエラーですか。まあ、将来的に使うかわからないなら勇気を持って削る方がいいかも
どうせ必要になったらそのとき気づくので、削ったほうが便利かもしれない
そう、いま手元に 2014 年の『LLVM/Clang 実践活用』って本があって、れもん氏がそれ眺めてる
uint256_t
uint256_t
持ってはないけど読んだことありますね
lemolatoon
lemolatoon
typo したら教えてくれる extension 入れたんですよね
typo めっちゃありそう
hsjoihs
hsjoihs
わたし typo の検出うまいのでやっておきます
uint256_t
uint256_t
最適化の資料、書いてるうちに書かなきゃいけないことが増える
hsjoihs
hsjoihs
わかるなぁ
uint256_t
uint256_t
ちっとも SSA にたどり着かない。すぐに役立つことから書くべきかもしれんな
多分いまスタックマシンなので、それ向けの最適化を書いた方がいいかもしれない。peephole するだけでだいぶマシになるはず。というか push と pop が減るのでデバッグしやすくなる
hsjoihs
hsjoihs
真面目に英文査読してほしい人います?エラーメッセージとか
lemolatoon
lemolatoon
英語はだいぶテキトーに書いてます
かりんとう
かりんとう
査読していただくとアラしか見つからないと思う
uint256_t
uint256_t
僕は clang のメッセージ真似てます。自分で考えるよりその方が早い
hsjoihs
hsjoihs
lemolatoonさんのやつ読んだんですけど、致命的にまずいのは、Not yet unimplemented になってる
これは Not yet implemented です
lemolatoon
lemolatoon
Rust の unimplemented で出るメッセージもそうですかね
uint256_t
uint256_t
そうです
れもん
れもん
エラーメッセージって独特の言い回しありますよね
hsjoihs
hsjoihs
Elm のエラーメッセージは、主語が I なんですよね
uint256_t
uint256_t
コンパイラが主語なんですか?
hsjoihs
hsjoihs
そうです
uint256_t
uint256_t
OCaml はフランス語を英語に訳してるのでエラーメッセージが独特
hsjoihs
hsjoihs
PHP はヘブライ語話者が書いてて、 :: のトークン名がヘブライ語
あっ Google が落ちてる
uint256_t
uint256_t
珍しい
hsjoihs
hsjoihs
DuckDuckGo 使うかぁ
あっ治った
T_PAAMAYIM_NEKUDOTAYIM ですね
『二重の二つの点』とかのはず
lemolatoon
lemolatoon
Google が未だに internal error を吐く
hsjoihs
hsjoihs
こんな事もあろうかと『現代ヘブライ語辞典』を持参しておいたんですよね
れもん
れもん
あー、点の双数形か
hsjoihs
hsjoihs
母音符号のことを表す『ニクダ』がそのまま『点』ですね
さてヘブライ語辞典ではなくて受講生のコードを見るべきで
lemolatoon
lemolatoon
文字列リテラルをやりたいんですが、ident と共通化できそうなのでやっている
hsjoihs
hsjoihs
なるほど。文字列リテラルにはエスケープシーケンスがありますけど
lemolatoon
lemolatoon
エスケープシーケンスもトークナイザでよんでる?
hsjoihs
hsjoihs
読まないとトークナイズできないので。どこで文字列が終わるかはちゃんと見ないとわかんない
uint256_t
uint256_t
無理に共通化しなくていいと思う
hsjoihs
hsjoihs
私もそう思います
uint256_t
uint256_t
早すぎる抽象化とかは
lemolatoon
lemolatoon
あー
hsjoihs
hsjoihs
で、これめんどくさいのは、エスケープシーケンスを読んでデコードされた文字列が手に入るわけですけど、その文字列自体をアセンブリ言語として出力する際に再エンコードが必要なんですよね。これはやらなきゃいけないですけど
lemolatoon
lemolatoon
アセンブリ言語と若干違うからそのままペタッとできない?
uint256_t
uint256_t
16進とかできましたっけ。そのままペタッとできれば楽ですけど
hsjoihs
hsjoihs
楽に見えますけど、いろいろと事情があり、まず文字列の終わりが検出できなきゃいけないし、文字列リテラルは連結されることがあるし、文字列リテラルは配列なので sizeof が取れるし、などなどを考えると、一度デコードしてから再エンコードしましょう
れもん
れもん
エスケープシーケンスの文法だけ見て範囲を掴むという手も
hsjoihs
hsjoihs
それやると結局 sizeof で困るので。あとエスケープシーケンスを単純に結合すると 16 進エスケープの後に A が来たときにバグる
#include <stdio.h>
void f(int n) {
    puts("\xA" "B");
    puts("\xAB");
}
.LC0:
  .string "\nB"
.LC1:
  .string "\253"
f:
  subq $8, %rsp
  movl $.LC0, %edi
  call puts
  movl $.LC1, %edi
  addq $8, %rsp
  jmp puts
あっ Java と C は使えるエスケープシーケンス普通に違いますよ
改行・バックスラッシュ・二重クオートはほしい。一応、 %c に 10 とか 34 とか投げる手はありますけど
10 は改行文字 (U+000A)、34 は二重クオート、云々
uint256_t
uint256_t
たまにやりますねそういうこと。そこら辺のコードポイント覚えちゃいますよね
hsjoihs
hsjoihs
これ貼っとくかぁ
#include <stdio.h>
const char*f="#include <stdio.h>%cconst char*f=%c%s%c;int main(){printf(f,10,34,f,34,10);}%c";int main(){printf(f,10,34,f,34,10);}
れもん
れもん
クワインだ
hsjoihs
hsjoihs
lemolatoonさん、文字列リテラルを処理してるのに変数名 ident ではマズいです。F2 キーを押してリネームしましょう
advance_and_get_token って名前で戻り値 pos なの?
lemolatoon
lemolatoon
むかし改名した名残だ
uint256_t
uint256_t
これはさすがに改名しましょう。この関数名なら絶対 token が返ってくるべきなので
hsjoihs
hsjoihs
そうか、先に pos を取ってるから get_pos_and_advance の方が関数名としていいのか
tamaron
tamaron
飲食物自分で買いに行かなきゃいけないんだよな
hsjoihs
hsjoihs
かつては菓子とかが差し入れられてたけど、今年は飲食物の共有がダメだから自分で買わなきゃなんだよな
uint256_t
uint256_t
なつかしい
hsjoihs
hsjoihs
そのテストケースは生文字列リテラルを使うといいです。 r#" ... "# ですね
lemolatoon
lemolatoon
文字列リテラルってなに expression ですかね
hsjoihs
hsjoihs
primary_expression では?でも文字列結合があるからな
lemolatoon
lemolatoon
仕様書を見るか
hsjoihs
hsjoihs
文字列結合ってプリプロセッサの仕事だったかも
uint256_t
uint256_t
プリプロセッサではないはず
hsjoihs
hsjoihs
プリプロセッサより後のはずですよね。size_t を printf するときのことを考えると。なんちゃら phases ってやつだった気がする
5.1.1.2 Translation phases 1 The precedence among the syntax rules of translation is specified by the following phases.6) 1. Physical source file multibyte characters are mapped, in an implementation- defined manner, to the source character set (introducing new-line characters for end-of-line indicators) if necessary. Trigraph sequences are replaced by corresponding single-character internal representations. 2. Each instance of a backslash character () immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. A source file that is not empty shall end in a new-line character, which shall not be immediately preceded by a backslash character before any such splicing takes place. 3. The source file is decomposed into preprocessing tokens7) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined. 4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted. 5. Each source character set member and escape sequence in character constants and string literals is converted to the corresponding member of the execution character set; if there is no corresponding member, it is converted to an implementation- defined member other than the null (wide) character.8) 6. Adjacent string literal tokens are concatenated. 7. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit. 8. All external object and function references are resolved. Library components are linked to satisfy external references to functions and objects not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment.
この 1~6 までが『前処理』だけど、狭義のプリプロセッサは 4. だけを指す、ということっぽいですね。ややこしい~
gcc -E は 6 をやらないっぽい
ということで、結論からいうと、文字列結合はパースよりも前に解決されているので、パーサが見る頃には分割不可な単一の式になってるので、primary_expression です
なので、文字列リテラルの結合は、前処理指令(プリプロセッサディレクティブ)(# なんちゃら) を処理した後で、(『隣接トークン』とあるので)トークナイズした後、パーサーに投げる前、のタイミングでやるのが正しいのかな
というか、プリプロセッサから見たトークン列が、前処理指令を通過して、パーサーへと投げられる前に、隣接した文字列リテラルトークンを結合する、ということか
これは罠なんですが、プロセッサは -or です
uint256_t
uint256_t
-er と -or ってどう区別するんですか
hsjoihs
hsjoihs
ラテン語かどうかです
uint256_t
uint256_t
だいたい調べて覚えて分かった気になってる
hsjoihs
hsjoihs
とりあえず、基本的に -ate は必ず -ator です
お、関数を返す関数だ
かりんとう
かりんとう
非常に苦戦しています
BNF が、過去に作ったやつが、宣言と定義で違って、それを一致させるというか、ネストの型宣言とかに対応させるためには、宣言の BNF を関数定義で使わなきゃいけなくて、どう直そうかなぁ、と
hsjoihs
hsjoihs
ふむふむ
かりんとう
かりんとう
ガッツリ変えなきゃいけない気がして
hsjoihs
hsjoihs
そうですかね?ややこしいだけで、直す箇所はそこまでではないと思いますが
かりんとう
かりんとう
ちょっと頭がこんがらがってるかもしれない
hsjoihs
hsjoihs
文字列リテラルの型は、char へのポインタじゃなくて char 配列です
uint256_t
uint256_t
じゃないと sizeof できませんよね
hsjoihs
hsjoihs
ヌルターミネータのこと考えてます?
lemolatoon
lemolatoon
自分で足さなきゃいけないんでしたっけ
hsjoihs
hsjoihs
ヌルターミネータなしで .string で吐くと、アセンブラ側でヌルターミネータを足してくれるけど、それはそれとして sizeof とかが見るのはヌルターミネータがあるサイズ
lemolatoon
lemolatoon
アセンブラに直にバイト列を書いちゃおうと思います
hsjoihs
hsjoihs
それもできます。その場合は当然ヌルターミネータを自分で足しましょう
.string "str" Copy the characters in str to the object file. You may specify more than one string to copy, separated by commas. Unless otherwise specified for a particular machine, the assembler marks the end of each string with a 0 byte. You can use any of the escape sequences described in section Strings.
c.into::<u8>() とは書けないんですよ。Into の定義を見ていただくと、型パラメータの位置がそこではないことがわかる
lemolatoon
lemolatoon
あーじゃあ u8::from(c) の方がいいか。あれ、通らない
hsjoihs
hsjoihs
char は u8 にはなりません。 u8::try_from(c).unwrap() ですね
デバッグ中にリファクタリングをするのはよくないですね、リファクタリングを提案してしまってすみません。とりあえず、declaration にバグがあって char* msg = "Hello world"; が通らないなら、 char * msg; msg = "Hello world"; にすれば declaration ではなく assign なので通りませんか
lemolatoon
lemolatoon
これエラーメッセージ自体がマズそうだな
いや、エラーメッセージは合ってそうだな
hsjoihs
hsjoihs
12:30 までなのでそろそろですね
uint256_t
uint256_t
次は 1:30 からでしたっけ
hsjoihs
hsjoihs
確認します。そうですね
じゃあ一旦解散
解散後は、また皆さんの進捗を言ってもらいます
昼休み中にコード読みました。いくつか気になるところありましたが、まずはとりあえずみなさまの現状訊いておきましょう
mikiken
mikiken
ポインタの指してる先に代入するのが動くようになって、int という keyword を処理できるようにした。ポインタ型を導入しようとしている
かりんとう
かりんとう
午前中はずっと関数ポインタの実装をしていて、それに伴って関数の呼び出しの BNF とかを少し書いたり。どこまでできたかというと、関数ポインタを返す関数のパースができるようになったので、関数ポインタの実装はこれで終わりにしようかな、と。次は、いままでリファクタリングとかバグ探しとかやってなかったので、やっていきたい
hsjoihs
hsjoihs
いくつか気になる点はあったので、やっていきましょうか
lemolatoon
lemolatoon
char に関するバグを直したあとで、clippy でリファクタリングし、文字列リテラルを作ろうとして、parse まではできてる
hsjoihs
hsjoihs
文字列リテラルのが動くようになったら、さっきの型のエラーメッセージの報告の改良しましょう。どうリファクタリングするかの目星を昼休み中に立てておいたので
kanataso
kanataso
午前中 108 個ぐらいだったのが、あと 42 個になりました。あと 42 個テストを通せば良いんですが、構造体のメンバーへのアクセスだったり、あと 8 個以上の引数があるときの関数呼び出しとかをこれからやります
hsjoihs
hsjoihs
ありがとうございます。あーそうか、『あと n 個通す』って方針でしたね。ピリオド一文字でビビったんですけど、大量にあるテストケースを通してるんだったらギリ納得できました
uint256_t
uint256_t
だいぶ減りましたね
kanataso
kanataso
だいたい構造体関連です
hsjoihs
hsjoihs
全部通したら risc-v ブランチが master にマージ、と
kanataso
kanataso
そうです
hsjoihs
hsjoihs
じゃあかりんとうさんのリファクタリングやりますか。他の皆さんはご自由にコンパイラ書きましょう
かりんとう
かりんとう
リファクタリングをやったことがない。変数名とかエラーメッセージとかの変なところを直すという曖昧なのしかない。どういうふうにしていこう。そもそも『良いコード』とは?今までいきあたりばったりでコードを書いてきた
uint256_t
uint256_t
なんか hsjoihs さん的に気になるところは?
hsjoihs
hsjoihs
変数名とかエラーメッセージとかは正にありましたけど、比較的すぐ直りますし、あんまり本質的ではないが、逆に本質と関係ないところでコードが読みにくくなるのはかなしいので、直しましょう。こういうのは一人だとむずいので
先頭からコード眺めますかね〜
uint256_t
uint256_t
main ですか
hsjoihs
hsjoihs
それもアリなんですが、まず非本質を直したいので、ファイル名順に先頭から
わかりやすいところでいくと、LOGICAL_OR で吐いてるラベル名が logicaland
L590、コメント部分、ATS ではなく AST
『プロトタイプ関数』とあるんですが、『関数プロトタイプ』です
関数そのものではなくて、関数がどんな形をしているのかを表すもの、なので、プロトタイプという種類の関数があるのではなく、関数のプロトタイプなんですね
『プロトタイプ関数』と prototype_func がそれぞれあったかな
あとこれはlemolatoonさんもなんですが、sugar は a
parse.c に has_lvar_in_all_params ってのがあって、これ『関数定義のときは、変数名のついたやつじゃないといけない』って意味ですよね
かりんとう
かりんとう
型だけではない変数だから lvar にしてたんですけど、まあ分かりにくいですよね
name とかですかね
hsjoihs
hsjoihs
has_variable_name_in_all_params かなぁ
もう一つの案として、『関数に切り出さずコメントで説明』。一回しか呼んでないし
かりんとう
かりんとう
関数名ってどれくらいの長さがいいんですか?
uint256_t
uint256_t
これぐらいなら長くないですよ
hsjoihs
hsjoihs
長さよりも、関数の意図をちゃんと伝えてるかを重視しましょう
適切な名前をつけて、長すぎるのならば、そもそも一つの関数が多くのことをやりすぎてるし、
逆に名前が妙に長いことによってそれを伝えることもできうる
uint256_t
uint256_t
短くしつつ、意味は最大にしましょう
hsjoihs
hsjoihs
そうですね
uint256_t
uint256_t
でもこの関数はどうしようかな
hsjoihs
hsjoihs
まあ切り出さないのもアリかなぁ。結局これって『関数定義』というただひとつの話に紐付いて存在してる仕様であり、 なんなら C23 でたしかこの制限が外れるんですよ確か ←あれ?そんなことはないっぽい?
uint256_t
uint256_t
とりあえず関数に切り出すのはやめますか
かりんとう
かりんとう
そうするとフラグで管理する必要があってコードが汚くなるかも?
uint256_t
uint256_t
そんなに変わらないと思います
hsjoihs
hsjoihs
関数名を短く云々、enum と lenum と genum で分かれてるの、普通に local_enum と global_enum って書いてください
かりんとう
かりんとう
パット見わかりづらいですか
hsjoihs
hsjoihs
はい
かりんとう
かりんとう
ちょっとでも短くしようと思ったからなんですけど、
hsjoihs
hsjoihs
そういう意味の短さは求めていない、というのが、さっきの話ですね
『短くすることを唯一の目的として、読みやすさを損ねる』というのはよくない、といいましょうか
lvar, gvar はギリ読めるし、実体として大きく別物なので、この2つを対比させる言い方は定着してるし、だからこそまあ読めはするけど、lenum と genum は…まあないかな。私なら lvar も local_var にしますけど、少なくとも lenum, genum はなぁ
かりんとう
かりんとう
そもそも local_enum と global_enum もやめたほうがいいという話ですか?スコープがあるのでそうした方がいいのかな、と
hsjoihs
hsjoihs
enum がスコープに従うようにすべき、というのは、本質の挙動の方の話であって、今は命名の話だけに集中しています。少なくともこれがよろしくない命名であるとは言えるかなぁ、と思いました
あとは eval_concat 関数かなぁ
まず関数名なんですが、もう答え言いますと、eval_binary_op とか eval_binary_expression
uint256_t
uint256_t
これはなにが concat なんですか?
hsjoihs
hsjoihs
2つのノードをくっつけて一つにしてるから concat ってことなんですかね
かりんとう
かりんとう
文字列が結合するパターンもあったからなんですが、
uint256_t
uint256_t
え、この関数は文字列も結合するんですか?
かりんとう
かりんとう
グローバル変数とかだと単純に計算するわけにはいかない
uint256_t
uint256_t
いずれにせよ concat ではないので、名前を変えましょうか
hsjoihs
hsjoihs
じゃあ eval_binary_op だと思います
もう一つ、eval_concat に len ってパラメータが渡ってると思うんですけど、これ演算子の文字列の『ヌルターミネータを含めた』長さですよね?
かりんとう
かりんとう
たぶんそうだと思います
uint256_t
uint256_t
はい、含んでいます
hsjoihs
hsjoihs
C の strlen という関数を見ていただきたい
これは C の世界では size と呼びたくなりますが、op_size もなんかなぁという感じはする
uint256_t
uint256_t
そもそも len を渡す必要あるんですか?
hsjoihs
hsjoihs
そうか、strlen(op) + 1 でいいですね
uint256_t
uint256_t
今の話とは関係ないんですけど、キャメルケースとスネークケースが混ざってます
かりんとう
かりんとう
キャメルケースとスネークケースが混ざってないかを調べる拡張ってないんですかね
hsjoihs
hsjoihs
多分、ガイドラインを指定できる拡張があるんじゃないですか?『関数名はこのプロジェクトではスネークケースにします』って config に書くと、それをチェックしてくれるツールはあるんだと思います
uint256_t
uint256_t
clang-tidy とかでいいのかな
hsjoihs
hsjoihs
あとは controle は現代英語では普通は control です
れもん
れもん
N2480 で出てる
hsjoihs
hsjoihs
えーとじゃあ C23 に不採用になったのかな?
れもん
れもん
どうなんだろ
採用されてます
hsjoihs
hsjoihs
あと type.c L.473 で MOD のエラーメッセージが DIV
startsWith がキャメルケース、あと advance が advanve になってる
uint256_t
uint256_t
Rust も starts_with ですね
hsjoihs
hsjoihs
いやぁ英作文難しいですよね
かりんとう
かりんとう
英語を真面目に考えるよりコードが書きたいので、コミットメッセージをテキトーにしちゃう
uint256_t
uint256_t
多分将来の自分が一番苦労するんですよね
かりんとう
かりんとう
まあそうですね
hsjoihs
hsjoihs
日本語で書くのも全然手だと思いますけどね。あとは日英両方で書いておくとか
両方で書くと、普通に『あっこっちでこれ書いてないな』とか分かって便利だったりする。私は
日本語でいうと、『列挙型』が一つ『列強型』になってます
あとは、エラーメッセージで、 "for,switchブロックの中でbreakを使用していません" ってのがあるんですけど、
これ意図していない意味に読めるので書き換えたほうがいい
"breakが、switch でもループでもない場所で使われています" とかにしましょう
さて一時間ぐらいか。流石にそろそろ他の人も見たほうがよさそうだな
4時間枠だから1時間ずつ回せばいいか
uint256_t
uint256_t
次誰がいいですか
lemolatoon
lemolatoon
じゃあ僕のお願いします。エラーメッセージ周り
hsjoihs
hsjoihs
文字列リテラルのは通るようになりました?
lemolatoon
lemolatoon
なりました
れもん
れもん
おー
lemolatoon
lemolatoon
sizeof やるときに、変数の宣言でサイズを指定せずにやるのをやってない
hsjoihs
hsjoihs
じゃあ機能を足し終わってるので気兼ねなくリファクタリングですかね
uint256_t
uint256_t
なんか tokenize のファイルで warning 出ましたけど
lemolatoon
lemolatoon
こっちだと出ない。push してないかな。一旦 push します
hsjoihs
hsjoihs
なにが途中です?
lemolatoon
lemolatoon
array の size_declarator を Option にしようかというところです。unwrap を書いてはいるので、ビルドとテストは通ります。insta だけ見て push します
hsjoihs
hsjoihs
まず、new_type_error で検索すると、lhs と rhs が渡ってます。よって、少なくとも new_type_error の引数名は lhs と rhs にしてよさそうです
すると、TypeErrorKind::Expr の定義も、丸括弧じゃなくて波括弧で lhs, rhs というメンバ名にしちゃいましょう
これで少しはスッキリしましたね。問題は残りなんですが
こいつらは配列の初期化子で呼ばれてるんですよね?配列の各要素が取るべき型が揃ってるかどうか、ですよね
これどうしようかなぁ、と
そうだ、この down_program って関数名もよくわかんないって話をしてたんだよな
lemolatoon
lemolatoon
構文木を下るから down かなぁ、と
hsjoihs
hsjoihs
はい、それはれもん氏が正しく推理してくれてやっと分かりました。traverse かなぁ
uint256_t
uint256_t
down というのは何をやる?
lemolatoon
lemolatoon
構文木を下って、各 Expr を変換します
uint256_t
uint256_t
型を付けてるんですか?
lemolatoon
lemolatoon
Expr とかを ConvExpr にしてます
uint256_t
uint256_t
Conv とは
lemolatoon
lemolatoon
converted です
hsjoihs
hsjoihs
私は UntypedExpr と TypedExpr にしてましたね
uint256_t
uint256_t
type をつける以外にもやってるんですよね?
lemolatoon
lemolatoon
そうですね
hsjoihs
hsjoihs
あと細かい話として、たまに declarator が declrtr ってちょっと略されているのは、あんまり意味がないかと
lemolatoon
lemolatoon
全部書くと長いかなぁと思って
hsjoihs
hsjoihs
全部略すならまだ分かるんですけど、d_declrtr に DirectDeclarator 型がついているというのは、いや両方フルで綴ればよいのでは、となる
uint256_t
uint256_t
そもそも declarator 普通は略さない
lemolatoon
lemolatoon
DirectDeclarator 長くないですか?
hsjoihsuint256_t
hsjoihsuint256_t
長くていいと思います
hsjoihs
hsjoihs
あと、analyze.rs の new_assign_expr_with_type_check の allow とコメントが共に時代遅れなので削除
lemolatoon
lemolatoon
down の適切な動詞、なんでしたっけ
hsjoihs
hsjoihs
traverse と言いました。もうちょい具体的に『この目的で traverse します』の方を名前にしたくはありますが、とりあえず down_ を traverse_ にすれば、とりあえず間違った命名ではないから…
uint256_t
uint256_t
現状の実装だとこれ以上の名前が思いつかない。analyze でもよかったかもしれないけど
lemolatoon
lemolatoon
まあ一旦良さそう
hsjoihs
hsjoihs
しょうもないリファクタリングは "refactor" ってコミットメッセージでどんどんコミットしちゃいましょう
『意味は変わらないが字面が変わる部分』であるリファクタリングと、『字面はそんなに変わらないが意味が変わるデバッグ』は分かれているべきで、というか分かれていないと追えなくなる
なので、リファクタリングは一個やるごとに refactor ってコミットしちゃってもいい可能性がある。ようは『大したことしてないけど字面が大きく変わる』vs. 『大したことをした』は分けたい
lemolatoon
lemolatoon
エラー周り、めんどくさくて全部 unimplemented_err になってる
hsjoihs
hsjoihs
そういうのの改善は普通に機能変更なので、どんどん書いていきましょう。当然リファクタリングとは別のコミットで
そうだ、もう一つしょうもないやつ。 derefered ってのがありますが、 dereferenced です
Rust で deref'd ってのは見たことがある
れもん
れもん
あと remaint → remainder
hsjoihs
hsjoihs
struct ConvStmt が enum ConvStmtKind だけを取っていますが、これは enum ConvStmt にすべき
具体的にどうリファクタリングすればいいかというと、
UnOp は UnaryOp にしましょう
lemolatoon
lemolatoon
rustc が UnOp だからいいかなぁって
hsjoihs
hsjoihs
え〜〜〜???
れもん
れもん
ほんとだ〜〜〜
hsjoihs
hsjoihs
あとは abstruct → abstract
比較演算子が BinOp に入ってないのはなぜですか?
lemolatoon
lemolatoon
入れたほうがいいですね
hsjoihs
hsjoihs
Assign は分けたくなる気持ちはまだわかるが、比較演算子は BinOp だと思います
せっかくなら単項チルダも足してみます?
cargo build はテストがコンパイルエラーでも通っちゃいますからね
lemolatoon
lemolatoon
cargo test したら insta が落ちた
hsjoihs
hsjoihs
たしかに。さてそろそろ次の人に行きますか。insta 確かめてチルダやっといてください
次はどちら?
kanataso
kanataso
これコードレビューですか?
uint256_t
uint256_t
やってほしいことならなんでもどうぞ
kanataso
kanataso
引数と戦っています。そもそもググって出てきた calling convention が合ってるのか
uint256_t
uint256_t
2015年……
kanataso
kanataso
書いていることは正しいっぽい。char が unsigned だったりすることが分かった
アラインメントは改造しなくても問題ないっぽい
あと 18 個エラーを消せばいい
hsjoihs
hsjoihs
通らないケースのソース一覧とか貼ってもらえます?
kanataso
kanataso
time : 194 sec
--- report ---------------
9CC COMPILE KO => arg11.c
9CC COMPILE KO => arg2.c
9CC COMPILE KO => arg3.c
9CC COMPILE KO => arg4.c
9CC COMPILE KO => arg5.c
9CC COMPILE KO => arg6.c
9CC COMPILE KO => ret_struct0.c
9CC COMPILE KO => ret_struct1.c
9CC COMPILE KO => ret_struct2.c
9CC COMPILE KO => ret_struct3.c
9CC COMPILE KO => ret_struct4.c
9CC COMPILE KO => ret_struct5.c
9CC COMPILE KO => varg.c
9CC COMPILE KO => varg3.c
9CC KO => arg7.c
9CC KO => arg8.c
OUTPUT KO varg2.c > ./tmp/act_varg2.c.txt ./tmp/exp_varg2.c.txt
STATUS KO global7.c > act:139 exp:0
test failed : 18 errors
グローバル変数への代入ができないですね
hsjoihs
hsjoihs
おや
kanataso
kanataso
push と pop がない説がある?
hsjoihs
hsjoihs
ふむ
uint256_t
uint256_t
ないと思います
kanataso
kanataso
非常に欲しいですね
uint256_t
uint256_t
でも push 相当のことはできますよね
まあ、ないものはないので、アセンブリのマクロとか書けばいいのかな?
hsjoihs
hsjoihs
ん、逆にその問題はなぜ今まで露見していなかったんですか?
kanataso
kanataso
露見はしてました
hsjoihs
hsjoihs
なるほど?
ん、通ってるテストケースは push と pop を使ってない?
kanataso
kanataso
スタックポインタをずらしてストアしてます
hsjoihs
hsjoihs
なるほど。ではいまなにで困っている?
kanataso
kanataso
グローバル変数への代入ができない
hsjoihs
hsjoihs
godbolt とかで正しい代入方法を調べましたか?要するに、私も正解を知らないので godbolt に訊いてください
kanataso
kanataso
godbolt に RISC-V あるのか
uint256_t
uint256_t
なんでもありますからね
アセンブリを見る限り、アドレスの high と low を取ってきてるだけっぽい
kanataso
kanataso
これは何をしてるんですかね、8 行目
uint256_t
uint256_t
変数 a に 1000 を入れてるだけだと思いますが
命令長の問題で、変数 a のアドレスの high と low が入ったレジスタ a0 と a1 を使って変数 a にアクセスしないといけない
ARM も同じですよ
x86 が楽すぎるだけ。デカい定数をアセンブラに書けちゃう
これ即値を 1000 じゃなくて 10000 にすると出るアセンブリが変わりますよ
先ほど言ってた『12 ビット』の話は、定数が 12 ビットという話
kanataso
kanataso
これで a のアドレスが入ってるとは思う
uint256_t
uint256_t
これ high とか low ってシフトしなくていいんでしたっけ。いや、テキトーなことを言っています
hsjoihs
hsjoihs
RISC-V なんも知らんので爆速で仕様書読むか
れもん
れもん
godbolt のディレクティブのところにチェック
漏斗みたいなアイコンをクリック
hsjoihs
hsjoihs
まあ、x86 ってもともと人間がアセンブリ言語で直書きすることを想定して作ってるので、相対的に便利なんですよね。RISC-V 大変そうだなぁ
kanataso
kanataso
グローバル変数を後ろで宣言するようにしたらできた
hsjoihs
hsjoihs
おめでとうございます
れもん
れもん
セクションってのが効いてるんだと思う
hsjoihs
hsjoihs
analyze_mul のエラーメッセージに "%sに演算子を適用できません(L*/%)" があるんですが、 % 自体を printf で出すときには %% です
uint256_t
uint256_t
別に実行時にエラーは出ないと思いますが
hsjoihs
hsjoihs
L*/%、普通に日本語で * か / か % の左辺 って書けばよくないですか
というか、* か / か % の左辺が整数ではありません というエラーメッセージにしましょう
三項演算子を lhs, rhs って言いたくないなぁ
uint256_t
uint256_t
left hand side と right hand side じゃないですからね
hsjoihs
hsjoihs
私が見たことがあるのは、condition, true_branch, false_branch
uint256_t
uint256_t
まあそれでいいような
hsjoihs
hsjoihs
あ~ C だから使い回すとそうなるのか
まあ、C ならしょうがない。いや union 使えばいいんですけど、まあ C だし
構造体の定義がでかい!
typedef struct s_node
{
    t_nodekind        kind;
    struct s_node    *lhs;
    struct s_node    *rhs;

    t_type            *type;

    t_lvar            *lvar; // local var
    struct s_node    *lvar_assign; // type ident = expr;

    t_str_elem        *def_str;
struct s_defvar
{
    char            *name;
    int                name_len;

    t_type            *type;

    // for global variable
    bool            is_extern;
    bool            is_static;
    struct s_node    *assign;
}    *var_global;

    struct s_node    *global_assign_next;

    // num
    int                val;

    // else of if
    struct s_node    *elsif;
    struct s_node    *els;

    struct s_node    *for_expr[3]; // for (0; 1; 2)

    // call
struct s_deffunc
{
    char        *name;
    int            name_len;

    t_type        *type_return;

    int            argcount;

    char        *argument_names[20];
    int            argument_name_lens[20];
    t_type        *argument_types[20];

    bool        is_static;
    bool        is_prototype;
    bool        is_zero_argument;        // func(void)
    bool        is_variable_argument;    // func(, ...)

    t_lvar        *locals;
    struct s_node    *stmt;
}    *funcdef;
    int                funccall_argcount;
    struct s_node    *funccall_args[20];
    struct s_deffunc*funccall_caller;

    t_member        *elem;

    // valとlabelでswicth-case
    t_switchcase    *case_label;
    bool            switch_has_default; // TODO labelstackに移動する

    t_lvar            *switch_save; // switch (lvar)

    t_labelstack    *block_sbdata;




    // analyze
    bool        is_analyzed;
    char        *analyze_source;

    char        *analyze_var_name;
    int            analyze_var_name_len;

    char        *analyze_funccall_name;
    int            analyze_funccall_name_len;

    char        *analyze_member_name;
    int            analyze_member_name_len;
}    t_node;
uint256_t
uint256_t
***_t じゃなくて t_*** なのか
ほら、 size_t とか time_t とか
hsjoihs
hsjoihs
uint256_t とか
uint256_t
uint256_t
だいたい僕は先頭にアンダースコアつけて終わりにしてますけどね
kanataso
kanataso
というわけで、あとは引数だけかな。あとは関数呼び出しだけです
hsjoihs
hsjoihs
てか sizeof(t_node) どんぐらいよこれ
れもん
れもん
408かぁ
bool    is_symbol(char str)
{
    return (str == '_');
}
hsjoihs
hsjoihs
せめて is_underscore とかにしましょう。というか == '_' を直に書けばいいのでは?
bool    issymbol(char c)
{
    return (c == '_');
}
これも同様
そろそろ 16:30 なので mikiken さん行きましょうか。どんな感じですかね
mikiken
mikiken
ポインタ演算を実装しようとしていて、方針がいまいち分からず、という
uint256_t
uint256_t
なるほど
mikiken
mikiken
Node に型の情報を持たせておいて、パースするところで足し算引き算するところの動きを Node の型情報を見て動きを変えなきゃいけないんだな、と思いつつ
hsjoihs
hsjoihs
はい、まさにそんな感じかと
mikiken
mikiken
あと、parse の program って関数が長すぎるのでどうにかしないとなぁと思いつつ後回しにしています
uint256_t
uint256_t
まあリファクタよりさきにポインタの演算やりますか
多分ノードに型を付けようとしていますよね
hsjoihs
hsjoihs
手が止まってるということは、実装の方針が立ってないという感じですか?
mikiken
mikiken
そうですね
hsjoihs
hsjoihs
ここらへんって compilerbook に書いてあったりしません?
mikiken
mikiken
hsjoihs
hsjoihs
ああなるほど、『これだけ書いてやるからここから自分で頑張れ』か
mikiken
mikiken
いま他の方のコードを読んでいます
hsjoihs
hsjoihs
何をやりたいのかという全体的な方針をいうと、いま AST はある、しかし、AST の各要素がどんな型を持ってるのかを調べていない。今までは調べずともやってくることができたが、
uint256_t
uint256_t
これからはキツイ、と
hsjoihs
hsjoihs
じゃあそれをどう解決するかというと、いま AST を構成してる Expr というのは、実のところ UntypedExpr であると
型のついてない式が木の実として木に実ってる
なので、それに一つずつ型情報を付与していく必要があります
やり方は二通りあって、一つは、いまある Node を書き換えて、型情報を書き込んでいく方法
もう一つは、今ある AST を見て、再帰的に『その AST の、型がついたバージョン』を作っていく方法
木の実に直にカバーを掛けるか、それとも木と実を見て新しく木と実を作るか
mikiken
mikiken
ほぼ同じ AST を型情報ありバージョンで作る、ってことですか
hsjoihs
hsjoihs
そうです
残り三人のみなさんはどうしました?
lemolatoon
lemolatoon
僕は『もう一つ同じようなのを作る』
かりんとうkanataso
かりんとうkanataso
木の実に直接書き込んでます
hsjoihs
hsjoihs
ということで、どちらでもできるというわけです
mikiken
mikiken
後から書き込む場合、どの段階で型情報を付与していく感じになるんですか?
hsjoihs
hsjoihs
みなさんはどうしました?
kanataso
kanataso
構文解析の後の意味解析の段階です
かりんとう
かりんとう
構文解析と意味解析を分けておらず、構文解析の段階で、Node どうしの足し算があったときに型をつけて足し算、とやってました。ポインタ + 整数だったら型のサイズを調べて掛け算して足すようにしてます
lemolatoon
lemolatoon
型無し AST を作った後に、別の型あり AST を作ってます
hsjoihs
hsjoihs
ありがとうございます
それぞれの方法の利点とか欠点ってどういうのが思いつきます?
lemolatoon
lemolatoon
C で書いてた頃はパースと一緒にやってましたが、型の部分とパースの部分がぐちゃぐちゃになって大変だったな、という気持ちがあります
かりんとう
かりんとう
たしかにlemolatoonさんの言うように、パーサーがこんがらがってくるというのは感じています。でもそれは後々苦しいのであって、最初にとりあえず実装したいだけであったら直接実に書き込むほうが簡単に実装できそうだけど、長期的にはあんまりよろしくないのかなぁ
kanataso
kanataso
自分はパースするとこに書いたままでセルフホストまでしました。ということでそのままでもできますよ
かりんとう
かりんとう
僕もセルフホストまではできました
mikiken
mikiken
正直構文解析と意味解析の違いがよく分かっていない。parse の中に構文解析と意味解析があるって感じですか?
uint256_t
uint256_t
parse は構文解析のことです
mikiken
mikiken
意味解析というのはどういう?
uint256_t
uint256_t
たしかに境目は曖昧なんですけど
hsjoihs
hsjoihs
こういうのが逆から考えたほうがよくて、『構文がダメ』をはじくのが構文解析、『意味がダメ』をはじくのが意味解析です
mikiken
mikiken
構文上正しいけど意味をなしていない…
hsjoihs
hsjoihs
そうです。簡単な例を挙げると、 int a = 3; a(); とか困りますよね
でも構文上はダメではない
こういうのを『ダメ』と宣告する行為が『意味解析』
結局ダメなので、どのタイミングではじいてもコンパイラは書けます
mikiken
mikiken
イメージ的に最後の方でやってそうに思えるんですけど、早めにできるんですか?
uint256_t
uint256_t
構文解析かそれ以降じゃないと難しいかと
hsjoihs
hsjoihs
まあそんな感じです。言っていただけたように、『意味上ダメ』の宣告は、まとまっていたほうが見通しはいいです。なので、構文解析を完全に済ませた後に、一気にまとめて型を見ていって『意味上ダメ』を言っていく、とするほうがバグらせにくいとは思います
ただし『分けなくてもセルフホストまで行けました』という方もいることからも分かるように、見通しが悪すぎて地獄、というほどでも……ないかな、と思います
まあ、あとでまとめてやるイメージが最初からあったなら、あとでまとめてやりますか
その場合は、『木を見ながら木を再構築』の方針のほうが見通しがいいと思います
struct Expr typecheck_expression(struct AnalyzerState *ptr_ps,
                                 const struct UntypedExpr *ref_uexpr)
UntypedExpr を取って、『変数がどの型ですよ』とかを覚えてる context (=ptr_ps)を取って、Expr を新造して返す、っていう関数ですね
mikiken
mikiken
context というと?
hsjoihs
hsjoihs
たとえば、 int a; というのを見たら、context くんが「a は int だな」と覚えておいて、次に a + 3 という式を見たときに、「これは int + int の式だなぁ」と判断し、型付き AST を返す
れもん
れもん
mikiken
mikiken
context はどういうデータ構造?
uint256_t
uint256_t
Node から型への map だったり。グローバル変数をまとめたものだと思えばいい
hsjoihs
hsjoihs
struct AnalyzerState {
    struct ScopeChain scope_chain;
    struct Map2 * /*<Type>*/ global_vars_type_map;
    struct Map2 * /*<Type>*/ func_info_map;
    struct Type func_ret_type;
    int newest_offset;
    struct Map2 * /*<StructOrUnionInternalCompleteInfo>*/
        global_struct_or_union_tag_map;
    struct Map2 * /*<struct Vector<EnumeratorAndValue>>*/ global_enum_tag_map;
    struct Vector /*<EnumeratorAndValue>*/ global_enumerator_list;
    const char *current_function_name;
};
要は名簿です。たとえば名簿に載ってない名前があったらエラー
変数名間違えてコンパイルエラーが出るのは、名簿とソースを照らし合わせて『知らない名前のやつがいる!』というわけですよ
れもん
れもん
要するに、解析しているときに覚えておきたい情報まとめ
mikiken
mikiken
変数名と型をセットでもってるようなやつ
hsjoihs
hsjoihs
『変数名と型をセットでもってるようなやつ』でステップ 19 はこなせると思います
mikiken
mikiken
連結リストにして、前から調べて、あるかないか、あったらそこから型情報を取って、と
hsjoihs
hsjoihs
全くもってそのとおりです
mikiken
mikiken
まず最初に木構造ができて、そのあとに意味解析というか、変数名と型から型情報を付与しつつ、もう一回木構造を作っていく、みたいな感じですかね
hsjoihs
hsjoihs
はい、そのとおりです
書きはじめのコードがないと手が動かないと思うので、書き始めましょう。書きはじめのコツは最もかんたんなのから書くことで、もっとも簡単な意味解析というのは、意味解析をしないという意味解析です
入れた木を見て、そのままその木構造を再構築するコードを書いてしまいましょう
パースした結果は…グローバル変数に入るのかな?
mikiken
mikiken
そうですね。グローバル変数 function_head から全て辿れる
hsjoihs
hsjoihs
ところでいま codegen 関数が、『グローバル変数 function_head から全て辿れる』ものを全てたどって、結果を出力してるわけですね?
ということは、意味解析は『グローバル変数 function_head から全て辿れる』ものを全てたどって、同等の構造のを作っていく行為ですから、codegen 関数が参考にできるわけですね。codegen 関数がやってる再帰を、使っていけばいい
mikiken
mikiken
意味解析は type.c に書いたほうがいい?
hsjoihs
hsjoihs
type.c というファイル名、果たしてどうなんですかね?
uint256_t
uint256_t
typing とかどうですか
hsjoihs
hsjoihs
type_analysis.c とかかなぁ。どうするのがいいんですかね
みなさんどうしました?
lemolatoon
lemolatoon
analyze.rs です
かりんとう
かりんとう
type.c にしました。型情報しか付与してないので
kanataso
kanataso
analyze.c だなぁ
hsjoihs
hsjoihs
Rui さんの chibicc は type.c で、型関連の関数とかとともに、add_type という関数が定義されてますね
Rui 9cc は sema.c です。semantic analysis(意味解析)ですね
Rui 8cc は……分離してないのかな?
ということで、あんま気にしなくていいのかもしれません〜
uint256_t
uint256_t
8cc はだいぶ逐次的に型を付けている印象があります
hsjoihs
hsjoihs
ということで、動けばどう実装してもいいということが分かりました。やったぁ
uint256_t
uint256_t
パースしながら型付けてますもんね、8cc のこれ
lemolatoon
lemolatoon
チルダ見てたんですけど、char とか関係なく 32 ビットになってる?
hsjoihs
hsjoihs
良い質問ですね!最後にその話をします
C と C++ には汎整数拡張というものがあって、int より小さい型の値に算術演算子とかを適用すると、int になります
#include <iostream>
int main() {
    std::cout << 'a' << std::endl;
    std::cout << (+'a') << std::endl;
}
a
97
lemolatoon
lemolatoon
char が数字になってしまった
れもん
れもん
C++ではね
hsjoihs
hsjoihs
実は、C では、文字リテラルの型は int です
ということで、夕食休憩です。解散!
uint256_t
uint256_t
次は 18:30
そろそろ疲れてきましたね
hsjoihs
hsjoihs
さすがに 4 時間喋り続けてるとね
かりんとう
かりんとう
Zoom が使えそうですけど
uint256_t
uint256_t
でも使うべきなんですかね
かりんとう
かりんとう
出席は
hsjoihs
hsjoihs
全員が全部出席してますので、私たちがそれを伝えればいいんですよ多分
uint256_t
uint256_t
まあ Zoom 重いし
hsjoihs
hsjoihs
まあ、ちゃんと講義やってることの記録が一番残ってるのここだから
休憩時間中、現地組は ARM の即値が賢いって話をしてました
uint256_t
uint256_t
シフトできるって話ですか
hsjoihs
hsjoihs
ビットローテートできるんですよ
uint256_t
uint256_t
足し算ついでにオペランドをビットずらして足す、とか
ここまで来ると果たして RISC なのか
かりんとう
かりんとう
速度的にいうと、2つ命令書くより速いんですか
uint256_t
uint256_t
速いんじゃないですか?マニュアル見たほうがいいですけど
hsjoihs
hsjoihs
さて、2 時間の枠があるわけです
かりんとう
かりんとう
最初に現状報告からですかね
hsjoihs
hsjoihs
そうですね〜
uint256_t
uint256_t
今度は下から行きますか
kanataso
kanataso
あとテストケース 4 個です
uint256_t
uint256_t
終わってから本番かもですね
kanataso
kanataso
テストが通ったらそれでセルフホストができるのでは、と
uint256_t
uint256_t
そううまくいきますかねぇ。できてたらとてもよいテストケースです
lemolatoon
lemolatoon
文字列リテラルを作れるようにし、リファクタリングし、ビット反転の単項演算子を追加
uint256_t
uint256_t
チルダですよね?
lemolatoon
lemolatoon
はい
uint256_t
uint256_t
単項 ! はないんですか?
lemolatoon
lemolatoon
たしかに
かりんとう
かりんとう
ひたすらリファクタリングやってました。refactor ってメッセージが 4 つ 5 つ。次も引き続きリファクタリングやろうかなと思っています。エラーメッセージを、どの行でどこでエラーが出たのか、とかを出すようにしようかな、と
uint256_t
uint256_t
エラー見やすいのはうれしいし、やったほうがいい
かりんとう
かりんとう
ただ、コードジェネレータでエラーが発生した場合、どのトークンでエラーが出たのかわからない
uint256_t
uint256_t
いま AST に行番号とか付いてるんでしたっけ?
かりんとう
かりんとう
トークンにしかついてないです
uint256_t
uint256_t
往々にして AST にも付けたりするものだけど、いろいろやり方がある
if とか statement とかは数行に渡ってノードになるんで、どこを示せばいいのかという話になる
最初と最後、というのがよくある実装
じゃあ次の方
mikiken
mikiken
さっき教えてもらったとおり、構築された木構造をもう一回再構築するやつを書いていきたい
uint256_t
uint256_t
ありがとうございます
関係ないんですが、現地組は三人で横並びになってますか?
hsjoihs
hsjoihs
そんな感じです
uint256_t
uint256_t
そう思うと僕も行ったほうがよかったんですかね
かりんとう
かりんとう
ゼミごとに部屋が分けられている?
hsjoihs
hsjoihs
そうです
かりんとう
かりんとう
どっかのゼミが朝にラジオ体操をやってたらしい
uint256_t
uint256_t
まあ眠いからなぁ
hsjoihs
hsjoihs
まあ残り 2 時間枠どうするかという話ですが、とりあえず朝最初の 2 時間枠で mikiken さんとかあまり見てないので
れもん
れもん
いや、4時間・4時間・2時間です
hsjoihs
hsjoihs
あっそうか
uint256_t
uint256_t
10時間やってるんですね。さすが開発コース
lemolatoon
lemolatoon
まあもう少し欲しいですけどね
uint256_t
uint256_t
まあだから事前学習があるんでしょうけど、時期が時期なので
かりんとう
かりんとう
どこかで以前言っていた、互いにバグを見つけ合うというのをやりたい
hsjoihs
hsjoihs
たしかにみなさん 8 時間自分のとにらめっこしてるわけですし
uint256_t
uint256_t
人のコードを見て勉強にするのもいいかも。似たようなものを他の人が書いているという体験は面白い
明日の夜とかにやりますかね?
昼の 4 時間は、参加者交流枠か。講師は関係ないんですかね?よくわかりません。まあいいや
かりんとう
かりんとう
一つ質問なのですけど、最終日に成果発表があるじゃないですか、成果発表をするのは各ゼミの講師が出るか出ないか決めるというのがあって
uint256_t
uint256_t
受講生の中から一人選ぶとかでしたっけ
hsjoihs
hsjoihs
例年そうですよね
uint256_t
uint256_t
僕もそうだった。講師が発表してもいいらしいけど、僕たちがやってもあんま意味ないので
かりんとう
かりんとう
成果報告資料作成という時間があるので、そこでスライド発表の準備するんですかね
uint256_t
uint256_t
発表はどうしますかね。じゃんけん?
れもん
れもん
非同期のじゃんけんかぁ
hsjoihs
hsjoihs
どうします?むしろ今から互いのコードレビューにします?
uint256_t
uint256_t
疲れてきたしそういうのもやりますかね。また明日新鮮な気持ちでコードが書けるかも
じゃあ誰が誰のを読みますかね。全員が全員のを読んでもいいですけど
リポジトリはこのチャンネルにピン留めしてあるので
かりんとう
かりんとう
とりあえずクローンします
hsjoihs
hsjoihs
実際私がやったとき他の人の全然読まなかったからなぁ。こうやって読む機会があるのもいいかもしれない
kanataso
kanataso
どんな環境で動かせばいいんですかねこれ
uint256_t
uint256_t
クローンしたら動きませんかね。Rust は Rust のツールチェーンが要りますね
hsjoihs
hsjoihs
あとは Mac のやつは Mac でしか動かない
まあとりあえず眺めていくという感じで。もちろん走らせてもいいわけですけど
かりんとう
かりんとう
ブランチはみなさん master です?
uint256_t
uint256_t
そこはバラバラですね
セキュキャン終わる頃には master にマージできるといいかもしれない
lemolatoon
lemolatoon
kanatasoさんのが通らない
hsjoihs
hsjoihs
make のオプションに arch=x8664 が必要だったはず
かりんとう
かりんとう
あれ、動かない
lemolatoon
lemolatoon
implicit declaration of strndup
hsjoihs
hsjoihs
kanatasoさんの master はそれで動かない。risc-v ブランチで、 arch=x8664 を make の引数に追加で与えると成功するはず
uint256_t
uint256_t
prpr の由来を知りたい
hsjoihs
hsjoihs
まあ preprocessor なんでしょうねぇ
そう、意外と他人のリポジトリを正しく make するの難しいんですよね
uint256_t
uint256_t
README の重要性が分かりますね
書いといたほうが、世界の誰かが見つけてくれたときに動くので
たまに、Rust で書いてあるプロジェクトにビルド方法を訊く issue とか立つので、README に rustup の話とか書いておくと喜ばれる傾向が
かりんとう
かりんとう
Error: character following name is not '#' と言われる
kanatasoさんの make 9cc3 で
uint256_t
uint256_t
arch の指定ってどう書く?
kanataso
kanataso
make 9cc3 arch=x8664 です
uint256_t
uint256_t
なんかリンカのエラーが出た
hsjoihs
hsjoihs
まだ成功の報告を聞いてないけど
4 x 3 本の矢印はあるんだからどこかは成功してほしい
uint256_t
uint256_t
prpr のビルド時に main 関数がないと言われる
あ、動いた。と思ったら…なんだこれ
アセンブラのエラーが出た
lemolatoon
lemolatoon
かりんとうさんは develop ですか?
かりんとう
かりんとう
セルフホスト成功したっぽい
kanatasoさんは Mac だから Windows だと相性が悪い?
lemolatoonさんの master が test_all でエラー吐いた
hsjoihs
hsjoihs
そうそう、初回だけは特定の順序で動かさないと動かない Makefile とかよくありますよ
かりんとう
かりんとう
expected a `FnMut<(char,)>` closure, found `[char; 10]`
lemolatoon
lemolatoon
rustup update
uint256_t
uint256_t
Mac の gcc 特有なのか、アセンブラが通らないなぁ
lemolatoon
lemolatoon
9cc/9cc> make test ARCH=x8664

9CC COMPILE KO => void1.c
9CC COMPILE KO => void2.c
9CC COMPILE KO => while1.c
9CC COMPILE KO => while2.c
test failed : 287 errors
make: *** [Makefile:15: test] Error 1
やっぱ Mac じゃないとダメなのか?
uint256_t
uint256_t
arch って大文字でしたっけ
lemolatoon
lemolatoon
小文字ですかね
hsjoihs
hsjoihs
いやぁみなさん手間取ってますね
lemolatoon
lemolatoon
asm のコメントアウトがダメ?
hsjoihs
hsjoihs
あっそれ同人誌に書いた気がする
同人誌に書いてあるのは、『行の途中から // コメント をやると、Ubuntu 側のアセンブラでは動かない。行頭の // コメント は問題ない』ですね
なので # もそうなってる可能性がある
かりんとう
かりんとう
Rust のアップデートをしたらlemolatoonさんのは動きました
kanataso
kanataso
QEMU で RISC-V の Fedora を動かしてもらったらできますよ〜
hsjoihs
hsjoihs
mikiken さんはlemolatoonさんのやつ動かせたかな
mikiken
mikiken
clone されて、Rust はインストールされました
lemolatoon
lemolatoon
C でヘッダファイルじゃなくて実際のコードの方に F12 でジャンプしたい
hsjoihs
hsjoihs
あー
mikiken
mikiken
これ↓実行したら
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Cargoってインストールされますよね?
これを実行したら cargo がインストールされるんですか?
mikiken@DESKTOP-CM4259U:~/ironcc$ make testall
cargo test
make: cargo: Command not found
make: *** [Makefile:30: cargo_test] Error 127
uint256_t
uint256_t
インストールしたときに、一番最後に path を通すかどうかとか訊かれませんでしたっけ
VSCode にも clang-d があるんですね。C/C++ の language server、いろいろあってどれを使うのがいいのかわからんけど、僕のところにいま入ってるのは clang-d
lemolatoon
lemolatoon
mymath.c にある mylog2 って使う機会あったんです?
kanataso
kanataso
使いました。どこだっけな。size を指定するときに。でも無くても動いた
lemolatoon
lemolatoon
かりんとうさんのプリプロセッサは -E ですか?インクルードとかもできますかね?
かりんとう
かりんとう
標準ライブラリのインクルードは厳しいので、自分で作ったファイルをインクルードするようにしている
kanatasoさんのパーサーを見てるんですが、波括弧のブロックスコープはどう定義されてます?
kanataso
kanataso
波括弧でスコープは作っていない。関数で一個のスコープ
hsjoihs
hsjoihs
かつての JavaScript を思い出す
uint256_t
uint256_t
なつかしい。いろいろ思い出すなぁ
lemolatoon
lemolatoon
int main()
{
    return sizeof("123456789") / sizeof(char);
}
かりんとうさんのやつのバグ見つけたかも。↑が 10 ではなくて 8 になる
hsjoihs
hsjoihs
char[10] じゃなくて char* になってるんでしょうね
lemolatoon
lemolatoon
自分のテストケース食わせたらこれだけ落ちた
かりんとう
かりんとう
char * の方が実装が簡単だからそうしちゃった
lemolatoon
lemolatoon
sizeof だけかもですね、これでバグるの
hsjoihs
hsjoihs
私がさっきlemolatoonさんが書いてるの見て止めたところですね
lemolatoon
lemolatoon
僕も危うくそうなるところでした
hsjoihs
hsjoihs
せっかくなら私のコンパイラも落とします?
kanatasoさんの誰も通せてないですね
uint256_t
uint256_t
x86_64 の Mac があればよかったんですけど。GitHub Action に Mac のインスタンスありませんでしたっけね
hsjoihs
hsjoihs
私のは Mac と Linux 両対応です、多分
かりんとう
かりんとう
kanatasoさんの、おそらく (f)(); が動かないのでは?
kanataso
kanataso
もちろん動かないです
uint256_t
uint256_t
なんか CI とか設定したほうが良い気もしますね
hsjoihs
hsjoihs
ああそうそう、私の README はそれなりに最低限は書いてるので参考になるかもしれません
いま自分のコンパイラ読み返してるんですけど、自分なりの工夫としていくつかあるので書いておきますかね
コード出力の途中で死んだ場合には、exit するまえに出力に %%% poisoning the assembly とか書いて、強制的にアセンブリとして違法なファイルにしてますね。間違ってテストが通っちゃうのが嫌なので
かりんとう
かりんとう
printf を使うときに include するときって
hsjoihs
hsjoihs
std という名前のついてるどれか
#include "std_io.h" ですね
こんなの書いたなぁ
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif
#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
             struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
規格準拠のコンパイラは __STDC__ を定義しているので、gcc / clang に読ませたい定義と、自分のコンパイラに読ませたい定義を分けることができるんですね
あー、これも涙ぐましい努力
#ifdef __STDC__
#define size_t2 unsigned long
#endif
#ifndef __STDC__
#define size_t2 int
#endif

void *calloc(size_t2 nmemb, size_t2 size);
void *realloc(void *ptr, size_t2 size);
そう、私のコンパイラ、C コンパイラゼミの中では多分一番 compilerbook の影響が少ない実装なので、見比べてみると面白いかもしれません
もともと compilerbook が今ほど固まってなかった時期に、Rui さんの実装をガン無視して作ったやつなので、いろんなところの設計がかなり違います
uint256_t
uint256_t
僕も compilerbook のない時代に作ってましたからね
mikiken
mikiken
はすじょいさんのコンパイラをmake test_all_ したら、動いたんですが、謎のwarningが
mikiken@DESKTOP-CM4259U:~/c-compiler$ make
gcc -Wall -Wextra -DOVERRIDE_STD -g std.c codegen.c alignment.c parse_analyze_toplevel.c parse_analyze_statement.c codegen_expression.c main.c vector.c typecheck_expression.c parse_expression.c error.c type.c parse_type.c map.c print_x86_64.c print_x86_64_unofficial.c lexer.c codegen_switch.c file_io.c cpp.c -DLINUX -o out/compiler.out
typecheck_expression.c: In function ‘is_strictly_equal’:
typecheck_expression.c:159:2: warning: #warning should typecheck [-Wcpp]
  159 | #warning should typecheck
      |  ^~~~~~~
typecheck_expression.c: In function ‘is_compatible’:
typecheck_expression.c:244:2: warning: #warning should typecheck [-Wcpp]
  244 | #warning should typecheck
      |  ^~~~~~~
cpp.c: In function ‘replace_recursively’:
cpp.c:338:2: warning: #warning only one token [-Wcpp]
  338 | #warning only one token
      |  ^~~~~~~
cp -p out/compiler.out out/compiler_1stgen.out
hsjoihs
hsjoihs
はい、それは #warning で書いていることから分かるように、私が意図的に書いてる warning です。要は TODO
uint256_t
uint256_t
これ便利ですね
かりんとう
かりんとう
あー、それはうれしい
lemolatoon
lemolatoon
mikiken さんのって関数の定義ってあります?
hsjoihs
hsjoihs
『関数の定義を読めますか?』という質問ですかね
mikiken
mikiken
int のみで引数 6 つまで
hsjoihs
hsjoihs
なんだかんだいい時間になったな
uint256_t
uint256_t
あと 30 分ですか
lemolatoon
lemolatoon
main()
{
    assert(0, 3, test0());
    assert(0, 2, test0_2());
    assert(0, 10, test0_3());
    assert(1, 9, test1());
    assert(2, 5, test2(5));
    assert(3, 5, test3(5));
    assert(4, 2, test4(3));
    assert(4, 3, test4(4));
    assert(5, 10, test5(5));
    assert(6, 2, test6(2));
    assert(7, 0, test7(1));
    assert(7, 0, test7(91));
    assert(7, 1, test7(109));
    print_ok();
    return 0;
}

test0() { return 3; }

test0_2() { return g(2); }

test0_3() { return h(5); }

g(int n) { return n; }

h(int n)
{
    if (n > 5)
    {
        return 5;
    }
    else
    {
        return 10;
    }
}

test1() { return f(5); }

f(int n)
{
    int i;
    i = 3;
    if (n > 4)
    {
        return f(n - 1) + 2;
    }

    return i + n;
}

test2(int i)
{
    if (i == 0)
    {
        return 0;
    }
    if (i == 1)
    {
        return 1;
    }

    return i;
}

test3(int i)
{
    if (i == 0)
    {
        return 0;
    }
    if (i == 1)
    {
        return 1;
    }

    return test3(i - 1) + 1;
}

test4(int i)
{
    if (i == 0)
    {
        return 0;
    }
    if (i == 1)
    {
        return 1;
    }

    return test4(i - 1) + test4(i - 2);
}

test5(int i) { return add(i, 1000000) + sub(i, 1000000); }

int add(int x, int y)
{
    return x + y;
}

int sub(int x, int y)
{
    return x - y;
}

test6(int i)
{
    if (i == 0)
    {
        return 1;
    }
    return i * test6(i - 1);
}

test7(int n)
{
    if (n == 1)
    {
        return 0;
    }

    int result;
    result = 1;
    int i;
    for (i = 2; i < n; i = i + 1)
    {
        if (n % i == 0)
        {
            result = 0;
        }
    }
    return result;
}
mikiken
mikiken
剰余演算子がないので↑ができません
hsjoihs
hsjoihs
関数の戻り値の型が必須なのでは
mikiken
mikiken
そうですね、int test6() とかにすれば動くかな
かりんとう
かりんとう
任意のコードはどう動かします?
lemolatoon
lemolatoon
cargo run ファイル名.c
mikiken さんってブランチどれですか
mikiken
mikiken
master です
hsjoihs
hsjoihs
いやぁやってよかったなぁ互いの動かすの
kanataso
kanataso
test1 で test ができて、test2 だと何ができるんですか?かりんとうさんの
かりんとう
かりんとう
第2世代に対するテストです
kanataso
kanataso
通らない〜
kanapo@pon:~/seccamp/kcc$ make test2
sh tests/test-control.sh kcc2
test-control.sh: kcc2 start compileing tests/align.c
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
hsjoihs
hsjoihs
で、でた〜〜〜
アセンブリから実行ファイルにするところで -no-pie をつけるとうまくいきませんか?
uint256_t
uint256_t
これはリンカが言っていることで、
かりんとう
かりんとう
StackOverflow でも同趣旨のが書いてあるな
uint256_t
uint256_t
PIE は position-independent executable なんですけど、overflow ってことは遠すぎたんですかね
アドレス遠すぎってことですか
hsjoihs
hsjoihs
この PC がプログラムカウンタで、プログラムカウンタから 42 億か 21 億かのどっちかの範囲に収まらないとき、とかだった気がする
uint256_t
uint256_t
やっぱ遠すぎってことですよね
hsjoihs
hsjoihs
同人誌でその一個前に書いてあるやつで、max +/-2GB とか書いてあるので、21 億っぽい
uint256_t
uint256_t
なんでも載ってるんですか!?その同人誌
hsjoihs
hsjoihs
macOS でも Ubuntu でも同じコードで動かすための秘伝のタレです
from _main (0x100000FA5) to __stderrp@0x0008E9A0
ほぼ 2^32 離れたところに置かれるっぽいんですよね、なんか。それでなんか落ちる。真面目に追ってないので、『なんか』以上のことが言えません
かりんとう
かりんとう
離れすぎててはいけない…
uint256_t
uint256_t
そうですね
かりんとう
かりんとう
大文字の変数が落ちます
lemolatoon
lemolatoon
あ、小文字しかやってないかも
hsjoihs
hsjoihs
言及した『同人誌』のわたしの書いた部分です
8人で書いた。技術書典で出したんですよね
かりんとう
かりんとう
こういう知識ってネットで調べてもまとまった情報が出てこない
hsjoihs
hsjoihs
mac と Ubuntu で自作 C コンパイラ動かそうって人あんまいませんからね
これ参考にするとkanatasoさんのコードが Linux 対応できる可能性があります
kanataso
kanataso
まず main の
hsjoihs
hsjoihs
そう、初っ端から main が違う
さてそろそろ 20:30 ですね
かりんとう
かりんとう
2 日目も終わりですか
明日も朝早いですからね
hsjoihs
hsjoihs
まあ、現地組のこの部屋は 22:00 まで開いてるらしいので、まったり C コンパイラではない話とかするのもよいかもしれません
かりんとう
かりんとう
ずっと座ってると首が痛い
uint256_t
uint256_t
たまに立たないとですね
lemolatoon
lemolatoon
論理 not 実装するか
uint256_t
uint256_t
logical_not と bitwise_not とか呼ばれますね
lemolatoon
lemolatoon
bit_invert にしちゃった
hsjoihs
hsjoihs
そっちのほうが分かりやすいかもしれない
かりんとう
かりんとう
bitwise って『ビットごと』って意味なんですね
hsjoihs
hsjoihs
この wise の語源知らんなぁ
さっきの同人誌は 2019 年の夏に出したやつなので 3 年前ですね
かりんとう
かりんとう
思わぬ形で役に立ちましたね
hsjoihs
hsjoihs
C コンパイラゼミでこそ役立つ情報なので、あまり『思わぬ形』ではないかも
というか私がマイク付けてると残り二人がマイクミュートになるのでチューターの声を皆さんにお届けできないんだよな。明日はチューターのほうにマイクを付けてもらうか?
kanataso
kanataso
容量がなくて bash で補完できない
-bash: cannot create temp file for here-document: No space left on device
hsjoihs
hsjoihs
こまったなぁ
でもちゃんと考えてあるんですねそういうときのこと。ハングするとかじゃなくて
uint256_t
uint256_t
普通にファイルが作れなかったときのがそのまま出てるだけだと思いますね。No space left on device は Linux 側か Mac 側が返してそう
hsjoihs
hsjoihs
明日もあさってもありますよ
lemolatoon
lemolatoon
非常に楽しみです
かりんとう
かりんとう
9 月辺りに別でイベント開催が予定されているみたい?
hsjoihs
hsjoihs
へー
かりんとう
かりんとう
開発ゼミ発表の場をつくると kintone に書いてあった
hsjoihs
hsjoihs
まあ、ということで、みなさん README の大事さがわかったかと
かりんとう
かりんとう
README は大切です。英語で書いたほうがいいんだろうなぁ
hsjoihs
hsjoihs
まあ日本語でもいいのでは
lemolatoon
lemolatoon
なにもないよりは日本語のほうが
hsjoihs
hsjoihs
両方提供しちゃえばいいんですよ。私のコンパイラはサボって英語しか書いてないですけど
lemolatoon
lemolatoon
先に日本語書こうかなぁ。Rust のインストールの仕方も書こうかね
hsjoihs
hsjoihs
今日のテキストログで質問が出たところ全部書くといいと思いますよ
lemolatoon
lemolatoon
そろそろ一回マージもしたいな。README.md 書くかぁ
かりんとう
かりんとう
そういえばグループワークは何やるか決めました?
lemolatoon
lemolatoon
うちのとこはブログサイト自作
Web 系全然わかんないので楽しみ
かりんとう
かりんとう
その上でブログリレー?
lemolatoon
lemolatoon
C コンパイラみたいに、インクリメンタルに作っていきたい。記事もインクリメンタルに?
kanataso
kanataso
Web サーバーをインクリメンタルに?
lemolatoon
lemolatoon
どこ作るかってのまだやってない
かりんとう
かりんとう
僕のところはいくつか案が上がって、輪読会かブログリレーかとなって、最終的にブログリレーになった。途中で終わらせないよう頑張っていきたい。OS 自作輪読会とかもやりたかったんですけど
mikiken
mikiken
CTF に出てみようぜという話になりました
どれにするかは次回決めていく感じです
かりんとう
かりんとう
CTF って常にいろんなのが開催されているイメージだから、一旦ハマると抜けられない
mikiken
mikiken
常設のやつが結構あるって聞きますもんね
かりんとう
かりんとう
CTF Times っていう、開催中の CTF の一覧とかもある
mikiken
mikiken
みなさん CTF の経験は
lemolatoon
lemolatoon
全く無いです
かりんとう
かりんとう
少しだけ。おすすめですよ
Web 系をやっていたので、Web 問を永遠に解くことが楽しかった。あとはリバースエンジニアリングとか、低レイヤ寄りですよ
あとは flag を手に入れたときの興奮はアドレナリン全開
mikiken
mikiken
チーム名、ノリで『岡崎フラグメント』になった
lemolatoon
lemolatoon
cargo できないってのはどうやって解決しました?
mikiken
mikiken
shell を起動し直したら path が通りました
lemolatoon
lemolatoon
README にそれも書いておきます
hsjoihs
hsjoihs
セキュキャン、いろいろといい機会ですからね
lemolatoon
lemolatoon
OST、テーマの決まった雑談みたいな話なんですかね
かりんとう
かりんとう
oVice でやるのかな
lemolatoon
lemolatoon
そんな感じっぽい
かりんとう
かりんとう
テーマかぁ
hsjoihs
hsjoihs
みなさんも是非私のコンパイラを落としていただいて
かりんとう
かりんとう
機能はどこらへんまで?
hsjoihs
hsjoihs
詳しくは README にある『日記』に書いてありますけど、
セルフホストのあとちょっと進めてて、ないものとしては、typedef、複数トークンに展開される #define、unsigned、浮動小数点数、
かりんとう
かりんとう
あとはビットフィールド
hsjoihs
hsjoihs
ないですね。VLA もない
型名が無である列挙体がない
かりんとう
かりんとう
関数ポインタ
hsjoihs
hsjoihs
あります
かりんとう
かりんとう
じゃあ試したいことがあるので、やってみます
hsjoihs
hsjoihs
おっ
『これでは落ちませんでした』も、テストケースの増量に繋がるので是非
n 年ぶりにいじるのもアリかもな、hsjoihs-c-compiler
lemolatoon
lemolatoon
さっき書いた mikiken さんのやつ、せっかくならプルリク送るか
かりんとう
かりんとう
int(*a)[2](void);
これは通してはいけないものです。gcc だとエラーがでて、関数の配列はダメと言われます
hsjoihs さんのやつで試すとコンパイル自体は通っちゃいますね
hsjoihs
hsjoihs
通すべきでないものについての穴もあるかもしれません。そういうのも是非是非
そっちは結構あると思いますよ
なんなら chibicc とかもそれ狙えるかもですね
私は一回 chibicc 落とせましたね。構造体 u, v に対する (v = u).a とか (flag ? u : v).a
かりんとう
かりんとう
chibicc も int(*a)[2](void); 通しちゃいました
これってほんとに違法なんですかね
hsjoihs
hsjoihs
じゃあせっかくなので今から規格読んで私に教えて下さい
lemolatoon
lemolatoon
僕も探そう
かりんとう
かりんとう
shall not は『義務ではない』?
hsjoihs
hsjoihs
shall not は『してはならない』です
かりんとう
かりんとう
関数は関数型や配列型を戻り値型にしてはならない、へー
chibicc でやってみるか。お、エラーになる
chibicc は関数を返す関数ができてしまうな
hsjoihs
hsjoihs
『関数を返す関数』ですか?『関数型を戻り値型にする関数』ですか?
かりんとう
かりんとう
同じことではない?
hsjoihs
hsjoihs
普通に関数を関数を返そうとすると、勝手に『関数ポインタを返す関数』になります
かりんとう
かりんとう
chibicc は『関数型を戻り値型にする関数』を許してしまいますね。gcc は通さなかった
int(test())(void) {}
hsjoihs
hsjoihs
こういうケースがサクッと書けるようになってる時点で『C の型宣言読み検定卒業』ですね
かりんとう
かりんとう
最初は signal のシグネチャとかこんなもん読めるかいと思ってましたけど、今見たら普通じゃんと思ってしまう
hsjoihs
hsjoihs
『完全制覇』でも signal の話がありましたね
みなさん『完全制覇』どれくらい読みました?
lemolatoon
lemolatoon
この前以降あんまり読んでない
かりんとう
かりんとう
あんまり進められていない。2 章とか
hsjoihs
hsjoihs
別にいつまでに読めというのはないです
lemolatoon
lemolatoon
もらったときにバーッとは読みました
hsjoihs
hsjoihs
まあ、一番言いたかったのは第一章の話なので
詰まってますね規格リーディング。function で調べて詰まってるなら、array で調べては如何
これは流石に出題が難しすぎたかなぁ
かりんとう
かりんとう
許されないものは constraints セクションに書いてありそうだと思ったんですが
hsjoihs
hsjoihs
それはとてもよい目星です
流石にキツイので答えを貼るかぁ
かりんとう
かりんとう
既に見つけられていたのか
hsjoihs
hsjoihs
まず、Array 章でも Function 章でもなくて、Type 章でした
Any number of derived types can be constructed from the object and function types, as follows: An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ''array of T ''. The construction of an array type from an element type is called ''array type derivation''.
C11: Types are partitioned into object types (types that describe objects) and function types (types that describe functions). At various points within a translation unit an object type may be incomplete (lacking sufficient information to determine the size of objects of that type) or complete (having sufficient information).
かりんとう
かりんとう
Types are partitioned into object types (types that describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).
hsjoihs
hsjoihs
この完全型・不完全型の話は、恒例のごとく『完全制覇』に載ってます
索引を引くと、p.184 と p.300 と書いてある
ということで、やっぱりこの本はいい本なので、お時間のあるときにでもお読みください
かりんとう
かりんとう
読んでいて思ったこととして、typedef をした場合前方宣言ができるんですね。あれ、できたかな?最初に struct A で定義して、それを構造体のメンバとして使って、後から A の中身を定義するってできましたっけ
hsjoihs
hsjoihs
まさにその話が p.184 のちょっと前にあります
かりんとう
かりんとう
ポインタを使ってるのか
hsjoihs
hsjoihs
そうです
かりんとう
かりんとう
サイズがわかんないので
hsjoihs
hsjoihs
実際に C コンパイラを書くと、この『サイズが決まらないので』という理由付けの意味がよく分かると思います
かりんとう
かりんとう
hsjoihs さんはどういうふうに『関数型の配列はダメだ』を見つけたんですか
hsjoihs
hsjoihs
えーと、最初に array type で検索掛けた気がする。『配列をどう定義するか』ではなく、『配列とはなにか』のところにありそうだと見込んだので
まあ私は規格リーディング少なくとも 7 年やってますからね
かりんとう
かりんとう
他の言語の規格書もこんな感じですか?
hsjoihs
hsjoihs
ない言語は多いです。『C と未定義を社会言語学からも』で書きましたけど、黎明期にここまで大量の実装が乱立した言語って他にまず例がないので
一つの実装をみんなが使ってる言語だったら、『規格書』じゃなくて『コンパイラ・インタプリタのドキュメント』になりますよね
かりんとう
かりんとう
JavaScript に規格書があるのも同じ経緯?ECMAScript
hsjoihs
hsjoihs
はい、まさにそういうことです
かりんとう
かりんとう
Rust は
hsjoihs
hsjoihs
そうですね、『規格書』ではないですね。そもそも Rust は更新速いですし
Rust は The Rust Reference って言ってますね
かりんとう
かりんとう
C の規格書より何倍も読みやすそう
hsjoihs
hsjoihs
はいw
話が変わるんですが、RISC-V でのって Fedora でしたよね。Fedora って _ が関数名の頭に付きます?
kanataso
kanataso
付かないです
hsjoihs
hsjoihs
あーじゃあそれの対応はもうされてるんですね
じゃあわりとすぐに x86_64 Ubuntu 対応できると思いますよ
kanataso
kanataso
まあ、微妙に違うだけだと思う
hsjoihs
hsjoihs
明日以降どうしようかなぁ。今日みたいなのの繰り返しでいいんですかねぇ
lemolatoon
lemolatoon
期間中に構造体まで作りたいですね
hsjoihs
hsjoihs
私はこんな感じのを発表しましたね
あ〜当時は Sublime Text 使ってたな〜
そろそろ解散ですかねぇ
今日話した話で 3 年詰まってらっしゃる方がいるのか

2022年8月10日

hsjoihs
hsjoihs
そういやこんなことしてたな〜
@L3ゼミ受講生 @tamaron(L3チューター) @れもん(Lちゅた @uint256_t おはようございます〜
もう恒例の進捗報告しましょう
lemolatoon
lemolatoon
前回からは、ビット反転・論理反転を書いて、README を書いた
uint256_t
uint256_t
README いいですね
kanataso
kanataso
可変長引数と、16 バイト以上の struct のテストケースを省いたところ、そこ以外は RISC-V でセルフホストできました
hsjoihsuint256_tれもんmikikenlemolatoonかりんとう
一同
すごい
mikiken
mikiken
意味解析、型をつけるための関数を書こうとしてて、宣言のリストみたいなのを作って、パースのところで宣言のリストに追加するみたいなのを書きつつあって、型付きの AST を作る関数をこれから書こうとしている、という感じです
Nimda
Nimda
(RISC-V自作ゼミのCPU上で自作コンパイラの吐いたバイナリが動こうとしてますか......?
かりんとう
かりんとう
昨日終わってから別の用事があって、そこから進んでいない。前回の進捗報告でやると言った、エラーメッセージをもう少し。あとグローバル変数を減らすとか、そこらへんのリファクタリングをやろうとしてる。README を変えたい
uint256_t
uint256_t
自作ゼミって 32 bit RISC-V ですか?
kanataso
kanataso
あーそうだあと、__stdinp は RISC-V の Fedora だとないので、どうしようかなぁ、と。対象のアーキテクチャによって読み込むヘッダファイルを変えなきゃいけない
hsjoihs
hsjoihs
まあそれは、私のが macOS と Ubuntu 両対応してるんですけど、そこではフラグを渡してもらってるし、やりようはいくらでもありますが、コンパイラを走らせる人に申告願う、というのが一番簡単ではあります
今日は 4 時間枠と 2 時間枠がありますね
みなさんには、是非 C コンパイラを書いていただくということで
じゃあ画面の配信などしていただければ
uint256_t
uint256_t
for 文の第三式は f->next じゃなくて f = f->next にしないとですね
hsjoihs
hsjoihs
というか、タイピングをすることが目的というわけではないので、既に自分で書いた codegen.c をコピペして必要な部分全部書き換えるというのでもいいんですよ
なんか落ちてますねかりんとうさん
かりんとう
かりんとう
ダブルクオーテーション一つだけ入力したら落ちました
hsjoihs
hsjoihs
お、仕様書読んでいらっしゃる
lemolatoon
lemolatoon
struct の宣言方法を確認してます
hsjoihs
hsjoihs
lemolatoonさん、ちなみに include_str! っていうマクロが Rust にはあります。シンタックスハイライトあったほうが C 書きやすいでしょ
uint256_t
uint256_t
あんま書くとテストのファイル長くなりますし
lemolatoon
lemolatoon
Rust に関数名へと展開されるマクロってあります?
hsjoihs
hsjoihs
__func__ は諸事情で『入れないことに決めた』気がする。関数名に応じて読み込むファイルを変えたいのだろうが、そもそも include_str! は文字列リテラルしか書けない
uint256_t
uint256_t
テスト関数を作るマクロを書いた方が早い。そこに引数として関数名とファイル名を渡すのがよいかと
れもん
れもん
lemolatoonさん、構文規則で list * になってるところは list です
hsjoihs
hsjoihs
たしかに
一旦お手洗い行ってきます
復活
uint256_t
uint256_t
今はなにで困ってます?
lemolatoon
lemolatoon
struct を足してます。struct をタグだけ定義したいときのために、 {} のところを Option にしたらエラーが出ていろいろ直しているという感じです
uint256_t
uint256_t
なるほど
hsjoihs
hsjoihs
unwrap しまくってる話は配列の話だっけ
lemolatoon
lemolatoon
unwrap しまくってますね
kanataso
kanataso
完全にセルフホストできた説があるのでテストを眺めている
uint256_t
uint256_t
すごい
kanataso
kanataso
通りました
hsjoihsuint256_tれもんmikikenlemolatoonかりんとう
一同
すごい
kanataso
kanataso
float 足したい
uint256_t
uint256_t
float 足すとできること増えますよね
hsjoihs
hsjoihs
float 周りだと、スタックのアラインメントとかがちょっとめんどい?あーあとは数値型の暗黙の型変換の難易度上昇
kanataso
kanataso
かなりだいぶテキトーにやってるのがバレてしまう
hsjoihs
hsjoihs
hsjoihs-c-compiler で関数の配列を宣言することを禁じました。昨日言われたやつですね
lemolatoon
lemolatoon
struct のタグってスコープないですよね?
hsjoihsuint256_t
hsjoihsuint256_t
ありますよ
hsjoihs
hsjoihs
なので、関数の中で定義したら外からは見えない
lemolatoon
lemolatoon
struct って関数の中で宣言できるんですか
hsjoihsuint256_t
hsjoihsuint256_t
できます
かりんとう
かりんとう
struct A と union A って共存できますか?
hsjoihs
hsjoihs
えっどうだっけ
gcc が怒ったので多分ダメです
lemolatoon
lemolatoon
構造体のアラインメントはどこで規定されてます?
hsjoihs
hsjoihs
System V ABI ですね
lemolatoon
lemolatoon
hsjoihs
hsjoihs
それは個別の scalar type についてですね
uint256_t
uint256_t
aggregates and unions ってところに
lemolatoon
lemolatoon
ローカル変数も align しなきゃダメですか?
hsjoihs
hsjoihs
ローカル変数は align しなくても動いてしまいがちです
なぜなら、x86_64 はアラインされていない位置の 4 バイト整数とかを読める
uint256_t
uint256_t
読めないアーキテクチャもある
hsjoihs
hsjoihs
もともと x86 って人間が書くことを想定して設計されてるので。最初期の最初期は
kanataso
kanataso
float より先に unsigned 作ります
もともと、char が RISC-V は unsigned で x86-64 は signed だったのでごちゃごちゃになっていて、それを整理するためにやる必要があります
かりんとう
かりんとう
文字列リテラルって間に演算子なしで連結するんですね
hsjoihs
hsjoihs
そうです
uint256_t
uint256_t
マクロが置換された結果そうなることがある
hsjoihs
hsjoihs
これは int64_t とかを正しく出力したいときに必要になるんですよね
#include <inttypes.h>
int64_t t;
printf("%" PRId64 "\n", t);
uint256_t
uint256_t
賢いのか賢くないのかよくわかんないですね、この仕組み
そろそろ進捗報告やりますか
かりんとう
かりんとう
午前中は、エラーメッセージの改良と、 hsjoihs さんのテストケースがどれくらい通るのかを試していました。エラーメッセージの改良なんですけど、どこの行でエラーが出ているのか調べて、分かりやすいように色づけとかやってみました。以上です
hsjoihs
hsjoihs
いいですね。ちなみにコンパイルエラーの方のテストケースもあるので、是非
uint256_t
uint256_t
エラーのケースはいいですね
hsjoihs
hsjoihs
私も発表するか。昨日、終わった後に、『配列型を返す関数』とかを禁じ忘れてるとのご指摘を頂いて、それを書いてました
ただ、それでやってたら、エラーメッセージを吐かずに EXIT_FAILURE しちゃって、私のコンパイラは EXIT_FAILURE の前にエラーメッセージを吐くようにしているので、どうしてだろうなぁとデバッグに苦しんでました
結論からいうと、 int test()[3]; を見て、 int test() まで読んで『これは関数だな』と判断しちゃってて
その後に波括弧じゃなくて角括弧が来てるので落ちる
int test()[3] とかは違法だけど、構文上は合法なので、ちゃんとこの角括弧まで読んでから落とさないとダメ、ということに気づきました
K&R 見たらここはいくつでも連ねられるという定義になっていて、合法なのは [][] ... [] または () だけなのでその二つしか対処してなかったが、構文解析では []()[]() も通しておいて意味解析で落とさないといけないのだなぁ、と
lemolatoon
lemolatoon
struct はパースはできた。アラインメントとかも計算しようと思ったら、ローカル変数もアラインメント要るんじゃないかと気づき、やってた。
int main() {
        int a;
        {int *k;}
        {int *i; {int k;}}
        int c[2];
    }
一回スコープ抜けたローカル変数は同じところを再利用してるのかと思ったが、-O0 だと再利用しないんだなと気づいた
hsjoihs
hsjoihs
そもそも関数の先頭で一気に確保しますからね、多くの実装は
lemolatoon
lemolatoon
スコープのところでオフセットを足したり引いたりしたときの、最大のしか -O0 は引いていなかった
hsjoihs
hsjoihs
ありがとうございます
mikiken
mikiken
とりあえず、型ありの AST を作るような関数を書いていて、わりとできてきたけど、まだできてない部分もあるので、追加して、それに伴ってコード生成も書き直さなきゃいけないので、それを書いていく
kanataso
kanataso
キャストをどうにかしようとしています。いままでがテキトーにやりすぎていたので
hsjoihs
hsjoihs
ということで、12:30 から休憩で、えーと 4 時間枠はなくて、18:30 からか
uint256_t
uint256_t
いまから参加者交流ですね
hsjoihs
hsjoihs
はい、全員揃いましたね
皆さんが参加者交流をやってた時間に、『低レイヤーガール』ってYouTube チャンネルからの出演依頼が来て、出てきたんですよ
かりんとう
かりんとう
hsjoihs
hsjoihs
やりましょう、C コンパイラゼミ
uint256_t
uint256_t
https://github.com/HariboteOS/
かりんとう
かりんとう
なにげに開発時間が残り半分を切っている
uint256_t
uint256_t
なんかやり方変えてみます?そろそろ成果発表に向けてなにかやる、というか、分かりやすいことをまとめてみるというのもいいかもしれませんね
lemolatoon
lemolatoon
実装された機能で何かを書く、とか
uint256_t
uint256_t
他の人の C コンパイラをビルドする……は、試してみてもいいかもですね
かりんとう
かりんとう
なんかの OS
uint256_t
uint256_t
はりぼて OS ですかね
ただ、ちょっとビルドが複雑かな?
もうちょっと他にあるかもしれない
みなさんのテストケースって見てて面白いのってあります?ライフゲームとか。そういうのをやってみるとか。ライフゲームならできるんじゃないですかね
lemolatoon
lemolatoon
見たことあるけどどんなものか分かってない
uint256_t
uint256_t
既に出来上がってるものを動かしたほうが早いか。昔書いたな
結構面白いんですよ。見てるだけでも
かりんとう
かりんとう
わりと簡単な
hsjoihs
hsjoihs
そう、わりと簡単な規則です
uint256_t
uint256_t
昔、打倒 LLVM を目指してたときのライフゲーム実装があった
そんな難しいファイルじゃないから動くと思う
Rust から LLVM みたいなものが使える、みたいな
hsjoihs
hsjoihs
あー、エスケープシーケンス
uint256_t
uint256_t
それだけ対応すればいいかな
かりんとう
かりんとう
include は clang で処理かなぁ
uint256_t
uint256_t
必要な関数のヘッダがあればいい
hsjoihs
hsjoihs
そもそも、なんでヘッダファイルというものがあるかって、今回のゼミを踏まえてみなさん説明できます?
lemolatoon
lemolatoon
ヘッダファイルには struct の定義とか型の情報しか書いていないから、型の不一致を調べたりサイズを計算したり、とか
uint256_t
uint256_t
最悪ヘッダファイルなんてなくてもいいんですよね
かりんとう
かりんとう
別のファイルに書かれた関数の型情報がわからないと、構造体とか bool とかはサイズがわからないとつらいから使うのかなぁ、と
uint256_t
uint256_t
どういう答えを想定していましたか?
hsjoihs
hsjoihs
C の歴史の話をすると、プロトタイプ宣言って C++ から輸入された機能なんですよ。なので C の歴史上比較的新しい
かりんとう
かりんとう
それまで関数どうしてたんだろうな
lemolatoon
lemolatoon
でも戻り値……
hsjoihs
hsjoihs
そう、そこです
これは用語の話なんですけど、戻り値の方だけ書いてあって引数リストに何も書いてないやつってのは、プロトタイプ宣言じゃないんですよ。これは昔からあった。なぜかというと、そうしないと戻り値を受け取る部分のコード生成ができないんですよ
そういった都合上、C は『戻り値の型の宣言がされていなければ、戻り値が int であると仮定してコード生成をする』という仕組みになっています
uint256_t
uint256_t
IOCCCで悪い使われ方をするやつですね
hsjoihs
hsjoihs
ということで、逆にいうと、ヘッダファイルがなくても、戻り値の方だけを宣言したやつを #include すれば、コード生成はできる
なので、ヘッダファイルは『自分のコンパイラが読むしょぼいものを作る』ができます
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif
#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
             struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
正しい定義をしないと gcc や clang は怒るので、それらのコンパイラでは __STDC__ が 1 なので、そこで条件分岐して、まともなプロトタイプを読んでもらう
hsjoihs-c-compiler は __STDC__ を定義しないので、しょぼい方が読まれる
かりんとう
かりんとう
vfprintf はどうして引数の型が書いてあるんですか?
hsjoihs
hsjoihs
私のコンパイラでも書けるからです
かりんとう
かりんとう
可変長引数未対応?
hsjoihs
hsjoihs
当時は。今はできます。できるけど restrict キーワードが通らないので
uint256_t
uint256_t
読むだけ読んで無視すればよかったのでは
hsjoihs
hsjoihs
全くもってその通りです。今から足しても良い
uint256_t
uint256_t
なので、ヘッダファイルというのは好きなようにしてもいい。僕は頑張ってヘッダファイル読めるようにしましたけど
かりんとう
かりんとう
古い C では関数を宣言するときに『引数になにもない』やつは『型がなにも決まっていない』、ですよね。何も取らないやつは void を書かなきゃいけない、という
uint256_t
uint256_t
今だと効かないですよね
hsjoihs
hsjoihs
C23 から消えます。これで Ruby の処理系が普通に困るとかいう話が Twitter に流れてきてた
uint256_t
uint256_t
あ、まだあるんですね
かりんとう
かりんとう
話は変わりますが、先程のライフゲーム、エスケープシーケンスのところがうまく行っていないんですけどなんでなんでしょう。見ていただけないでしょうか
uint256_t
uint256_t
gcc で試して動きました?
lemolatoon
lemolatoon
試してないです。試すか。お〜
uint256_t
uint256_t
エスケープシーケンス対応してないだけでは?
hsjoihs
hsjoihs
gcc でこの文字列をバイト列として print してください。for 文かなんかで
\n を 10 にしているのと同じように、\e を 0x1b に置き換えればいいんですが、いま printf をループしてもらった作業って実は要らなくて、
『\ とe を見たら、'\e' を吐いてくれ』というコードで動きます
なぜなら、みなさまのコンパイラを最初にコンパイルするのは gcc か clang であって、gcc や clang は '\e' の値を既に知っているからです
    // This switch-cases is an interesting example of magical aspects
    // of self-hosting compilers. Here, we teach the compiler about
    // escaped sequences using escaped sequences themselves.
    // This is a tautology. The information about their real character
    // codes is not present in the source code but propagated from
    // a compiler compiling the source code.
    // See "Reflections on Trusting Trust" by Ken Thompson for more info.
    // http://cm.bell-labs.com/who/ken/trust.html
    switch (c) {
    case '\'': case '"': case '?': case '\\':
        return c;
    case 'a': return '\a';
    case 'b': return '\b';
    case 'f': return '\f';
    case 'n': return '\n';
    case 'r': return '\r';
    case 't': return '\t';
    case 'v': return '\v';
    case 'e': return '\033';  // '\e' is GNU extension
    case 'x': return read_hex_char();
    case 'u': return read_universal_char(4);
    case 'U': return read_universal_char(8);
    case '0' ... '7': return read_octal_char(c);
    }
あっ \e って GNU 拡張なのか
規格にないコンパイラの機能の話として、すごくしょぼいやつに今日直面しました
トップレベルの余計なセミコロン
;;;;;;
int main() { return 0;;;;;; }
かりんとう
かりんとう
バグを探そうと思って、いろいろ試していて、とりあえずトップレベルにセミコロンを書いたら通らなくて『通らないんだなぁ』ってなっていた
れもん
れもん
SL コマンドとかワンチャン行けるのでは
hsjoihs
hsjoihs
なるほど!
uint256_t
uint256_t
たしかに SL の方がウケがいいかもしれない
ライフゲーム見せて『ふーん』となったあとに SL やるとウケそう
hsjoihs
hsjoihs
なんなら… ls の musl 実装ってある?
syscall はただ呼ぶだけなので、軽い実装があれば行けるかもしれない
れもん
れもん
ない
hsjoihs
hsjoihs
そっかぁ
SL いいと思います。ぜひ見てみたい
uint256_t
uint256_t
普通に動きそうなコードで書いてあるので、ちょっとの労力で動きそう
かりんとう
かりんとう
試してみます
とりあえずいろいろ動かしたいと思ってたので
ちょうどいい C のオープンソースのコードってどう探せばいいんだろう、と
uint256_t
uint256_t
hsjoihs
hsjoihs
便利に実用するためのツールはそうですよね
uint256_t
uint256_t
なので、ジョークとか、ゲームとか
あとは 2048 とか
hsjoihs
hsjoihs
mikiken さんこれ狙えませんか?配列を『宣言』する方法はありますか?
mikiken
mikiken
ポインタがまだ完全ではない。そこができればワンチャン
hsjoihs
hsjoihs
struct をコピーしてるところが一つだけある
lemolatoon
lemolatoon
ライフゲームやろうと思ったら logical or があった
uint256_t
uint256_t
ちょっと実装がめんどくさい
れもん
れもん
ちょっとした if
uint256_t
uint256_t
ちょっとした if にします。そうしないと短絡評価ができない
AST レベルでやるといいかもですね
hsjoihs
hsjoihs
厳密には2回評価しないようにする工夫が要りますが、初期のコンパイラでここを 2 回評価するやつは評価するやつは一応ある
uint256_t
uint256_t
logical or も logical and もあるけどソースをいじればいい
takesako
takesako
テトリスとかウケるのでは
uint256_t
uint256_t
テトリスは複雑なので言うのやめた
hsjoihs
hsjoihs
テトリス、組むのはめんどいけど組まれたものを動かすのはやりやすいかもしれない
皆さん是非積極的に試してみて、『私の C コンパイラは、これがコンパイルできるので C コンパイラです』って言っちゃいましょう
かりんとう
かりんとう
ライフゲーム、\e を \033 にしたら動きました
takesako
takesako
クワインとか標準出力だけでできますよね
hsjoihs
hsjoihs
クワインはもうテストケースに入れてる人もいます
かりんとう
かりんとう
コードを動かすとき、include をいちいち足さなきゃいけないのがつらい
hsjoihs
hsjoihs
逆にいうと、その作業をしておいて梱包すれば他でも応用が効くかもしれない
Rui さんの chibicc は git とかコンパイルしてましたね
uint256_t
uint256_t
そういえばそうでしたね
かりんとう
かりんとう
Python をコンパイルさせてる人の話は聞いたことがある
uint256_t
uint256_t
なんか聞いたことがありますね
takesako
takesako
Windows だと、dll に関数呼び出しできる記法とかを採用してる HSP がありましたね。いまだと Linux の low layer を呼び出すと
hsjoihs
hsjoihs
syscall の周りに C ABI でラップしてあるやつはいくらでも呼べますね
かりんとう
かりんとう
GUI 動かせると楽しそうですね
hsjoihs
hsjoihs
単純にそこそこめんどくさいかもという一点を除けば面白いかも
uint256_t
uint256_t
あとは Brainf*ck のインタプリタとか。これで実質なんでもできる
かりんとう
かりんとう
自作コンパイラで別の言語のコンパイラ
hsjoihs
hsjoihs
Brainf*ck のコンパイラを書いちゃうのも手かもしれませんね
あー crowbar と Diksam どうだろう。誰かできそう。試してみるか
crowbar と Diksam というのは、『C 言語ポインタ完全制覇』の作者の自作言語
かりんとう
かりんとう
複雑そうですけど
hsjoihs
hsjoihs
変な C の仕様を使い倒してるわけではないので。yacc が吐くのも C ですし
たどたどしくとも C という言語を話すことができると、いろんな可能性が広がるんですね
かりんとう
かりんとう
とりあえずヘッダファイルを作るかぁ
uint256_t
uint256_t
こういうのをモチベーションにするとカバレッジを上げるのがたのしくなる
セルフホストという難しい目標をクリアしてる人がいっぱいいるので
takesako
takesako
もうセルフホストできてるんですか
hsjoihs
hsjoihs
はい
かりんとう
かりんとう
僕はただただ昔からやってただけですけど
signal の型宣言
hsjoihs
hsjoihs
でた〜
void (*signal(int sig, void (*func)(int)))(int);
kanataso
kanataso
extern…
hsjoihs
hsjoihs
extern ねぇ
自作コンパイラでコンパイルしたやつのデバッグって難しいんですよね
uint256_t
uint256_t
怪しいところをコメントアウトして再コンパイル
lemolatoon
lemolatoon
printf が出てないってことは
hsjoihs
hsjoihs
stdout はバッファリングされてるので、『セグフォは printf より前のコードが原因』とは限りません
takesako
takesako
ポインタのバイト数を 1 にしててエラーを起こしたことがあります
hsjoihs
hsjoihs
mikiken さんなにやってらっしゃいます?
mikiken
mikiken
型付き AST を作っていってる。statement の連結リストが型なし AST で書かれてるのを再帰的に変換する
hsjoihs
hsjoihs
地味に木を全部辿らなきゃいけないので書く量は多い
lemolatoon
lemolatoon
gdb 使います
hsjoihs
hsjoihs
いいですね
lemolatoon
lemolatoon
CLI きついので VS Code にある GUI のを
hsjoihs
hsjoihs
へーそんなのが
lemolatoon
lemolatoon
fprintf の中でセグフォしたけど fputs だと動いた
hsjoihs
hsjoihs
fprintf は浮動小数点数を扱えるので、16 バイトアラインされてるかを厳しく見ます。fputs は要らないので真面目には見ない
mikiken さんが gdb 動かしてる
mikiken
mikiken
一気にコード足してたらセグフォした
lemolatoon
lemolatoon
ライフゲーム、usleep でセグフォする
uint256_t
uint256_t
usleep どんなアセンブリで試しました?さっきのスタックがアラインしてないような
push rbp
mov edi, 100000
call usleep
pop rbp
ret
hsjoihs
hsjoihs
そろそろ締めの時間なので、なにやったか話してもらいます
かりんとう
かりんとう
あと 4 時間ぐらいコンパイラ書きたい
mikiken
mikiken
型情報ありの AST を作るところを書いたけど、セグフォした
kanataso
kanataso
sl をやる過程で、機能が足りないことに気づき、グローバル変数を宣言と同時に代入するときに負の数が通せないのを直した
lemolatoon
lemolatoon
ライフゲームをやろうとして、usleep でセグフォするのが治らない
かりんとう
かりんとう
さっきの時間は 2048 をずっと動かそうとしていた。動きました
エスケープシーケンスとか unsigned とかが足りないのでソースに手は入れてます
hsjoihs
hsjoihs
でも動いてるのでえらいです
かりんとう
かりんとう
こういうコードって、2048 は MIT ライセンスですけど、サンプルとして使いたいなぁというときには、著作者の名前を出せばいい?
uint256_t
uint256_t
ライセンスによる
hsjoihs
hsjoihs
実行したときの画面にも URL を出しちゃう、という手もあるかも
takesako
takesako
hsjoihs
hsjoihs
かりんとう
かりんとう
takesako
takesako
lemolatoon
lemolatoon
for 文の第三式を push したままだったのを直したらアラインメントがちゃんとなりました

2022年8月11日

Imperi
Imperi
`print *((Node *) ~ec30で終わるアドレス~ ) ->next` を試してください
ポインタを関数に渡すと Node が壊れるのかも?
Node * を返すべき関数で return してないの、これはまずそうですね
hsjoihs
hsjoihs
なるほど解決!!!!
Imperi
Imperi
まとめると、まず switch に網羅性チェックがなかったので、ND_FUNCALL に対するパスで return がないということが検査されず、その結果、関数末尾のときの rax がそのまま返り、死んでいた。なるほど return 欠如
hsjoihs
hsjoihs
C 言語は高級言語、いざバグるその時までは!!!
lemolatoon
lemolatoon
-Wall -Werror
Imperi
Imperi
return ないのは警告出るだろと思ったら、網羅性が切れてると出ないのか。default を置いてエラーを置いて落とすべき
lemolatoon
lemolatoon
unreachable
hsjoihs
hsjoihs
unreachable じゃなくて、
*************************
* INTERNAL COMPILER ERROR
*************************
とか吐くと良いですよ
Imperi
Imperi
デフォルトだと怒らないんですねこれ
hsjoihs
hsjoihs
私の場合、コンパイラオプションは、make warn している場合については
CLANG_WARN=-Wall -Wextra -Wimplicit-fallthrough -Weverything -Wno-documentation -Wno-padded -Wno-missing-prototypes -Wno-switch-enum -Wno-sign-conversion -Wno-format-nonliteral
ぐらい盛ってます
warn:
    make format
    clang $(CLANG_WARN) -DOVERRIDE_STD $(SRC) $(OSFLAG) -o out/compiler.out 
    clang $(CLANG_WARN) misc/assembly_sandbox.c print_x86_64.c  print_x86_64_unofficial.c $(OSFLAG) -o out/assembly_sandbox.out
    clang -Wall -Wextra -Wimplicit-fallthrough $(OSFLAG) $(TYPECHECKSRC) -o out/typeparse_check.out
普段は gcc -Wall -Wextra です
int add(int a, int b) { return a + b; }
int main() {
    int c = add(2, 3);
    return 0;
}
これを考えたとき、これは c に対して正しい代入がされてる、というのはわかりますね?
これはなぜかというと、 add という関数は int を返す関数であって、
したがって add(2, 3) というノードには int 型を付けねばならないのです
これは、 2 + 3 というノードに int 型をつける必要がある、というのと全く同等です
mikiken
mikiken
ここでいう FUNCALL ですか?
hsjoihs
hsjoihs
はい。 Node { kind = FUNCALL, funcname = "add", args = [2, 3] } というノードです
Node { kind = ADD, lhs = 2, rhs = 3} というノードに int 型がつくのと同様に、
Node { kind = FUNCALL, funcname = "add", args = [2, 3] } というノードには int型がつく
なので、この FUNCALL Node の型を求める必要がある
で、C においては、その型というのは、関数宣言の戻り値型を見る、というのがルール
int foo(); というのが既に出てきてたら、Node { kind = FUNCALL, funcname = "foo", args = なんちゃら } は int 型として処理
int *bar(); というのが既に出てきてたら、Node { kind = FUNCALL, funcname = "bar", args = なんちゃら } は int * 型として処理
関数名を見て、戻り値の型を判断
宣言を信じる。逆にいうと、宣言を信じるしかない。だから、ヘッダファイルが C には必要
(gdb) layout asm
set  disassemble-next-line on
show disassemble-next-line
valgrind
valgrind --leak-check=no myprog arg1 arg2 
文字列型の変数の初期値に NULL を入れたせいで strcmp 内で死んでいた。初期値を "" にしたら SIGSEGV はしなくなった
フォーマッタを入れよう。眠いときにはどんどんツールに頼るべきなので
流石に一旦抜けるか【時刻: 5:23】
mikiken
mikiken
ありがとうございました🙇‍♂️🙇‍♂️🙇‍♂️
uint256_t
uint256_t
おはようございます【時刻: 8:34】
hsjoihs
hsjoihs
じゃあまた報告から
lemolatoon
lemolatoon
昨日はライフゲームを動かそうとして、エスケープ文字を追加したり、スタックアラインのバグ取りをし、盤面の初期化をいい感じにするために 2 進リテラルと bitand と shift を足しました
  int neighbour_count[20][20];
  int grid[20][20];
  int grid_binaries[20] = {
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000111111111100000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00000000000000000000,
      0b00111100000000000000,
      0b01000100000000000000,
      0b00000100000000000000,
      0b01001000000000000000,
      0b00000000000000000000,
      0b00000000000000000000};
  init_grid(grid, grid_binaries);
  int i;
  int j;
  int steps;
hsjoihs
hsjoihs
二次元配列の初期化を足すのはちょっときついかもという判断で、こうなりました
mikiken
mikiken
いろんな方に手伝ってもらい、型付けのセグフォがほとんど取れたが、関数宣言がまだ処理できてないので頑張る
kanataso
kanataso
ログを見る限り、関数型を導入しようとして、あまり進んでいないという感じかなぁ。ローカル変数の配列の初期化を作りました
かりんとう
かりんとう
昨日やってたのは、SL コマンドを動かそうと格闘し、ncurse の curse.h をいい感じにやったら動きました。ちょっといま動かしてみます
今日は構造体の宣言をカンマで分ける宣言とか追加したい
hsjoihs
hsjoihs
ありがとうございます。波括弧の中の初期化子 (brace-initializer) めんどくさいですよね。めんどくさいけど欲しい機能ではある。hsjoihs-c-compiler には未だにありません
私もライフゲームやるかぁ
れもん
れもん
たいへん今起きて朝食中【時刻: 08:50】
かりんとう
かりんとう
流石に疲労が溜まってきてますねみなさん
hsjoihs
hsjoihs
徹夜したからなぁ
かりんとう
かりんとう
カンマ演算子が終わった。次なにやろうかな
uint256_t
uint256_t
いいですね
lemolatoon
lemolatoon
トレイトを初めて書いていってるんですが、From と Into みたいな関係って自分でできますか?
hsjoihs
hsjoihs
はい、できます
かりんとう
かりんとう
unsigned と unsigned int って同じ意味なんですね。逆に char と unsigned char と signed char は全て別物なんですね
hsjoihsuint256_t
hsjoihsuint256_t
そうですね
uint256_t
uint256_t
C++ とかでオーバーロードすると、char と unsigned char と signed char で別だったりする
かりんとう
かりんとう
3つ違う型だとは書いてあるが詳細がわからない
uint256_t
uint256_t
char が符号付きか否かって決まってないですよね
lemolatoon
lemolatoon
こういうトレイトって where でやって動かないな
uint256_t
uint256_t
そもそもこれ trait でやる必要あるんですかね
あと、Into って重い処理をするときの名前ではなかったような
hsjoihs
hsjoihs
なんかどこかにそんなこと書いてあったかもしれない
uint256_t
uint256_t
hsjoihs
hsjoihs
TryFrom のドキュメント https://doc.rust-lang.org/std/convert/trait.TryFrom.html に "Simple and safe type conversions that may fail in a controlled way under some circumstances" とありますね
uint256_t
uint256_t
検索する場合があるんだったら『検索』みたいな名前の関数にしたい。あと、naming guideline をサラッと見ておくと良いかもしれない。直接いま役立つというわけではないけど
lemolatoon
lemolatoon
try_search_and_convert にしておきます
uint256_t
uint256_t
ここまでくると trait である必要あります? Analyzer で実装すれば
普通に違う関数にしたらダメですかね
lemolatoon
lemolatoon
でもほぼ同じ処理なんですよね
hsjoihs
hsjoihs
ほぼ同じで、かつコピペするのが嫌な長さの処理があるなら、その処理は関数に切り出して、片方で呼び出したときに軽い変換を挟んでからその関数に渡せば良いんじゃないですか?
uint256_t
uint256_t
関数の引数で Type または TypeSpecifier しか受け付けない、とかをトレイトで書くのはアリですけど。TypeLike とか
lemolatoon
lemolatoon
でも変換には結局アナライザが要るんですよね。まあそもそも Type に変換してから渡せばいいや
かりんとう
かりんとう
一つ質問なんですが、BNF、いっつも K&R の BNF を見ていた。K&R と C89 だと BNF ももちろん異なってくる。でも C89 の BNF を探しても見つからない
hsjoihs
hsjoihs
仕様書のあちこちに細切れで置いてありますよね
uint256_t
uint256_t
探せばありそうですけど、まあ普通は仕様書読みますよね
hsjoihs
hsjoihs
hsjoihs-c-compiler に -pedantic を足して、非 -pedantic のときだけ \e に対応しました
uint256_t
uint256_t
なるほど、正しいですね
lemolatoon
lemolatoon
hsjoihs
hsjoihs
お、mikiken さんは関数宣言が実装できている?
mikiken
mikiken
コメントアウトしたところを外しても動くようにはなったが、最後のテストケースで誤った値が返ってきている
hsjoihs
hsjoihs
ならアセンブリ読んでみますか。やっとアセンブリ読めますよ
あれ、ポインタ演算してるのに掛け算のアセンブリが見当たらない?
uint256_t
uint256_t
そもそも 『PTR と INT を add してるとき』の if 文の中に入れてます?
hsjoihs
hsjoihs
左辺の Node が Lvar だったから、そこに PTR がついてないのかな?
最後の return がまずかったか。せっかく作った multiply_offset とかを使わずに、もともとの lhs と rhs で返しちゃってる
これで compilerbook のステップ 19 がクリア
uint256_t
uint256_t
次にいけますね。ステップ 20、sizeof
hsjoihs
hsjoihs
sizeof は calloc に対して渡しまくるので、ほしいですね。いやまあ hsjoihs-c-compiler には sizeof 値 が無いんですけど
mikiken
mikiken
compilerbook には『パーサでは、sizeof演算子が出現したら、その引数になっている式を通常通りにパースして、その結果の構文木に紐づいている型がintならば4、ポインタならば8という数に置き換えるようにしてください』ってありますけど、パース時にはまだ型情報がない
hsjoihs
hsjoihs
はい、もちろん意味解析のときにやります
sizeof 値、hsjoihs-c-compiler にも足しました
takesako
takesako
sizeof に関する雑談:
#include <stdio.h>
int main() {
  printf("%s", sizeof('C')==1?"C++":"C");
}
#include <stdio.h>
enum{a=0,b=1};
int main() {
  if(sizeof(enum{b=0,a=1})); 
  printf("C%d\n",a?89:99);
}
かりんとう
かりんとう
前に hsjoihs さんが言ってた、『C では文字リテラルは int 型』かぁ
hsjoihs
hsjoihs
run_test 379 "int main() { char a; return sizeof a; }" 1
run_test 380 "int main() { char a; return sizeof +a; }" 4
run_test 377 'int main() { int a[2][3]; return sizeof a; }' 24
run_test 378 'int main() { int a[2][3]; return sizeof (a+0); }' 8
run_test 379 "int main() { return sizeof 'C'; }" 4
run_test 380 "int main() { char a; return sizeof a; }" 1
run_test 381 "int main() { char a; return sizeof +a; }" 4
あっ run_test 378 'int main() { int a[2][3]; return sizeof (a+0); }' 8 が hsjoihs-c-compiler で落ちる!
lemolatoon
lemolatoon
concat!("insta_srcs/", stringify!($file_name)) は insta_srcs/$file_name になってしまう
uint256_t
uint256_t
あんますごいことを Rust のマクロでしないほうがいいってことですね
hsjoihs
hsjoihs
直した
いい質問ですね。そもそもアラインメントはなんのためにやるかというと、この int が常に 4 の倍数のアドレスを持つためにやる
じゃあ、この struct の sizeof が 9 だったら、これの配列作ったらどうなりますか?
ということで、構造体の _Alignof は、メンバのアラインメントのうち一番きついもの
uint256_t
uint256_t
あっこれ g 発音するんですね
g を読むか否かってどう区別するんですか
hsjoihs
hsjoihs
慣習
まあオチを言うと、ラテン語での語形として通るかどうかをよく考えるとわかるんですが、これは『規格書のこことこことここを合わせてよく考えると当然こう』以上に非自明
lemolatoon
lemolatoon
無事 sizeof が 12 になりました
かりんとう
かりんとう
C89 は "The size is determined from the type of the operand, which is not itself evaluated. The result is an integer constant." と
hsjoihs
hsjoihs
英語の謎挙動は中英語とフランス語とラテン語を考慮に入れてよく考えると分かるんですが、それは『C の謎挙動はハードウェアの事情と C の歴史を考慮に入れるとよく分かる』ぐらいにはまあ『既に知ってないと絶対分からない』です
かりんとう
かりんとう
#include <stdio.h>

int main() {
    sizeof(
        struct A {
            int a;
        });
    struct A s = {1};
    printf("%d\n", s.a);
}
4 NOTE 3 Expressions that are not evaluated do not access objects.
Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.
7.31 Future library directions 7.31.13 String handling <string.h> 1 Function names that begin with str, mem, or wcs and a lowercase letter may be added to the declarations in the <string.h> header.
hsjoihs
hsjoihs
驚くべきことに、"All external names described below are reserved no matter what headers are included by the program." と書いてあるんですよね
"What we think we reserve" というタイトルがもう既にいい。"Abstract: The C standard normatively reserves identifiers and some of the reservations impose onerous requirements on programmers. The most severe requirements are generally unknown to programmers, not checked by tools, and demonstrate a disconnect between the C standards committee and the language as it is used by programmers." ほんまね
これ "I am proposing we modify our set of reserved names to match industry practice even if it means giving up rights to names we believe we already have rights to." っていう言い方なのめちゃめちゃいいな。
enum structure { // reserved
 isomorphic, // reserved
 nonisomorphic
};
void memorize_secret( // reserved
 const char *string // reserved
);
struct toxicology { // reserved
 enum condition {
 cnd_clean, // reserved
 cnd_dirty // reserved
 } cnd;
};
#define ENTOMOLOGY 1 // reserved
#define SIGNIFICANT_RESULTS 1 // reserved
#define TIME_TO_EAT 1 // reserved
#define ATOMIC_WEIGHT .000001f // reserved
#define INTERESTING_VALUE_MIN 0 // reserved
https://mobile.twitter.com/sksat_tty/status/1557519326734823424 で sksat が『一般に丸一週間集中してなんかやるとクソ疲れる→思い出すシリーズ: セキュキャン』って言っておるな。わかるよ
サクッとなにやったか言ってもらって
lemolatoon
lemolatoon
struct が今までパースだけだったのが、アラインメントも sizeof もできた
hsjoihs
hsjoihs
_Alignof を足してもいいかもしれません。単純にテストが書きやすくなる
lemolatoon
lemolatoon
sizeof と同様、変数名を取ればいい?
hsjoihs
hsjoihs
いえ、_Alignof は型名だけを取る
_Alignof 読んで、カッコ読んで、型名(ただし識別子なし)読んで、閉じカッコ
mikiken
mikiken
関数の宣言が動くようになり、関数呼び出しも動くようになり、ポインタの加算と減算が動くようになって、sizeof が追加された。次は配列を実装しようとしています
kanataso
kanataso
コンマ演算子と、型として関数を読み込めるようにしようとしているのができそうです
hsjoihs
hsjoihs
関数型のパース、なるほど
かりんとう
かりんとう
goto 文とラベルを追加して、グローバル変数でラベル管理してたけどローカルごとに管理されるものだから修正してた。わりとシンプルな goto だったら動くように
hsjoihs
hsjoihs
たしか私のテストケースにはループの中に飛び込む goto とかもあったはず
uint256_t
uint256_t
だいぶバグらせそう
hsjoihs
hsjoihs
スタックのアラインメントとか壊れないように気をつけて
かりんとう
かりんとう
for 文の中に飛び込むと初期化どうなるの
hsjoihs
hsjoihs
では一旦~
じゃあまた再開ということで
uint256_t
uint256_t
さっき進捗の話しはしたので、どんどん進めていきましょうか。コードを書く時間は 16:30 のはずで、そこから 1 時間は資料作成の時間ですね
かりんとう
かりんとう
書き足りなかったら深夜の会がまた?
uint256_t
uint256_t
LT 会とかがある分、2 時間枠がないので
かりんとう
かりんとう
なにげに開発する時間最後ですね
hsjoihs
hsjoihs
所定の枠だけでもうみなさん 20 時間 C コンパイラ書いてますけどね
lemolatoon
lemolatoon
開発コースは後に発表、とかありましたよね
mikiken
mikiken
kintone にそういうこと書いてありましたよね
uint256_t
uint256_t
kintone 慣れない
mikiken
mikiken
連絡ツール多すぎ
かりんとう
かりんとう
kintone の通知見づらい
hsjoihs
hsjoihs
わたしもライフゲームやろうかな
lemolatoon
lemolatoon
私のコンパイラで動くようにしたバージョンはこれです
hsjoihs
hsjoihs
-pedantic だと 2 進リテラル落とすか
れもん
れもん
-std=c23 とか実装しましょうよ
hsjoihs
hsjoihs
作業量が 5 倍になるんだよな
uint256_t
uint256_t
でもいろいろオプションつけることができるのはたのしい
hsjoihs
hsjoihs
へー、八進数の書き方ってプログラミング言語ごとにバラバラなのか。https://en.wikipedia.org/wiki/Octal#In_computers には "For example, the literal 73 (base 8) might be represented as 073, o73, q73, 0o73, \73, @73, &73, $73 or 73o in various languages." とか書いてある。最近は 0o が流行りですよね。"The prefix 0o also follows the model set by the prefix 0x used for hexadecimal literals in the C language; it is supported by Haskell,[19] OCaml,[20] Python as of version 3.0,[21] Raku,[22] Ruby,[23] Tcl as of version 9,[24] PHP as of version 8.1,[25] Rust[26] and it is intended to be supported by ECMAScript 6[27]"
uint256_t
uint256_t
0o だけでいいと思いますけどねぇ
hsjoihs
hsjoihs
0 自体は 8 進数リテラル、って話があったりしますよね
uint256_t
uint256_t
そうなんですよねぇ
mikiken
mikiken
compilerbook にもありましたね
れもん
れもん
ここで JavaScript を見てみましょう
mikiken
mikiken
かりんとう
かりんとう
桁数揃えて書きたいときに先頭に 0 置けないの不便
hsjoihs
hsjoihs
uint256_t
uint256_t
円周率 w/o 浮動小数点数 https://xn--w6q13e505b.jp/program/spigot.html
かりんとう
かりんとう
三項演算子とカンマ演算子の兼ね合いが動かない
hsjoihs
hsjoihs
あーそこね。第二項は優先順位が他と違うんですよ。BNF 見ましょう
かりんとう
かりんとう
k -= t = 1 みたいなの書けるんですね。知らなかった
uint256_t
uint256_t
k;
double sin(), cos();
main() {
  float A = 0, B = 0, i, j, z[1760];
  char b[1760];
  printf("\x1b[2J");
  for (;;) {
    memset(b, 32, 1760);
    memset(z, 0, 7040);
    for (j = 0; 6.28 > j; j += 0.07)
      for (i = 0; 6.28 > i; i += 0.02) {
        float c = sin(i), d = cos(j), e = sin(A), f = sin(j), g = cos(A),
              h = d + 2, D = 1 / (c * h * e + f * g + 5), l = cos(i),
              m = cos(B), n = sin(B), t = c * h * g - f * e;
        int x = 40 + 30 * D * (l * h * m - t * n),
            y = 12 + 15 * D * (l * h * n + t * m), o = x + 80 * y,
            N = 8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n);
        if (22 > y && y > 0 && x > 0 && 80 > x && D > z[o]) {
          z[o] = D;
          b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
        }
      }
    printf("\x1b[H");
    for (k = 0; 1761 > k; k++)
      putchar(k % 80 ? b[k] : 10);
    A += 0.04;
    B += 0.02;
  }
}
れもん
れもん
自分のコンパイラで 2048 動いた
mikiken
mikiken
配列の宣言をパースするところは(多分)できたっぽいんですが、そのあとスタックを確保する処理を書くところで詰まりました
uint256_t
uint256_t
とりあえずは、 int なら 4 つずつ、配列なら配列の大きさずつ、オフセットをずらすといいのでは
hsjoihs
hsjoihs
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#define FloatTimes10000 int

FloatTimes10000 mul(FloatTimes10000 a, FloatTimes10000 b) {
  return a * b / 10000;
}

int k;

void update2(FloatTimes10000 *cos, FloatTimes10000 *sin, FloatTimes10000 cos_delta, FloatTimes10000 sin_delta) {
  FloatTimes10000 new_cos = mul(*cos, cos_delta) - mul(*sin, sin_delta);
  FloatTimes10000 new_sin = mul(*sin, cos_delta) + mul(*cos, sin_delta);
  *cos = new_cos;
  *sin = new_sin;
}

int main() {
  FloatTimes10000 zTimes10000[1760];
  char b[1760];
  printf("\e[2J");
  FloatTimes10000 cosATimes10000 = 10000; FloatTimes10000 sinATimes10000 = 0;
  FloatTimes10000 cosBTimes10000 = 10000; FloatTimes10000 sinBTimes10000 = 0;
  for (;;update2(&cosATimes10000, &sinATimes10000, 9992, 400), update2(&cosBTimes10000, &sinBTimes10000, 9998, 200), usleep(50000)) {
    memset(b, 32, 1760 * sizeof(char));
    memset(zTimes10000, 0, 1760 * sizeof(FloatTimes10000));
    FloatTimes10000 sinthetaTimes10000 = 0; FloatTimes10000 costhetaTimes10000 = 10000;
    for (int theta_times_14 = 0; theta_times_14 < 88; theta_times_14++, update2(&costhetaTimes10000, &sinthetaTimes10000, 9974 + theta_times_14 % 2, 714)){
      FloatTimes10000 sinphiTimes10000 = 0; FloatTimes10000 cosphiTimes10000 = 10000;
      for (int phi_times_50 = 0; phi_times_50 < 314; phi_times_50++, update2(&cosphiTimes10000, &sinphiTimes10000, 9998, 200)) {
        FloatTimes10000 hTimes10000 = costhetaTimes10000 + 20000;
        FloatTimes10000 DTimes10000 = 100000000 / (
          mul(mul(sinphiTimes10000, hTimes10000), sinATimes10000) 
          + mul(sinthetaTimes10000, cosATimes10000)
          + 50000);
        FloatTimes10000 tTimes10000 = mul(mul(sinphiTimes10000, cosATimes10000), hTimes10000) - mul(sinthetaTimes10000, sinATimes10000);
        int x = 40 + 30 * mul(DTimes10000, mul(mul(cosphiTimes10000, cosBTimes10000), hTimes10000) - mul(tTimes10000 , sinBTimes10000)) / 10000,
            y = 12 + 15 * mul(DTimes10000, mul(mul(cosphiTimes10000, sinBTimes10000), hTimes10000) + mul(tTimes10000 , cosBTimes10000)) / 10000, 
            o = x + 80 * y,
            N = 8 * (
                mul(mul(sinthetaTimes10000, sinATimes10000) - mul(mul(sinphiTimes10000, cosATimes10000), costhetaTimes10000), cosBTimes10000)
                - mul(mul(sinphiTimes10000, sinATimes10000), costhetaTimes10000)
                - mul(sinthetaTimes10000, cosATimes10000)
                - mul(mul(cosphiTimes10000, sinBTimes10000), costhetaTimes10000)
              ) / 10000;
        if (22 > y && y > 0 && x > 0 && 80 > x && DTimes10000 > zTimes10000[o]) {
          zTimes10000[o] = DTimes10000;
          b[o] = ".,-~:;=!*#$@"[N >= 1 ? N : 0];
        }
      }
    }
    printf("\e[H");
    for (k = 0; 1761 > k; k++)
      putchar(k % 80 ? b[k] : 10);
  }
}
#include<string.h>
#include<stdio.h>
#include<unistd.h>
int m(int a,int b){return a*b/10000;}void a(int*c,int*s,int d,int t){int k=m(*c,d)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l;}
int main(){int z[1760];char b[1760];printf("\e[2J");int q=10000;int r=0;int u=10000;int v=0;for(;;a(&q,&r,9992,400),a(&u,&v,9998,200),usleep(50000)){memset(b,32,1760);memset(z,0,1760*sizeof(int));int l=0;int p=10000;for(int i=0;i<88;i++,a(&p,&l,9974+i%2,714)){int w=0;int e=10000;for(int j=0;j<314;j++,a(&e,&w,9998,200)){int f=p+20000;int g=100000000/(m(m(w,f),r)+m(l,q)+50000);int t=m(m(w,q),f)-m(l,r);int x=40+30*m(g,m(m(e,u),f)-m(t,v))/10000,y=12+15*m(g,m(m(e,v),f)+m(t,u))/10000,o=x+80*y,N=8*(m(m(l,r)-m(m(w,q),p),u)-m(m(w,r),p)-m(l,q)-m(m(e,v),p))/10000;if(22>y&&y>0&&x>0&&80>x&&g>z[o]){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0];}}}printf("\e[H");for(int k=0;k<1761;k++)putchar(k%80?b[k]:10);}}
れもん
れもん
for 文の中身を関数化したら、スタックのゴミが毎回消えるのでドーナツ動いた
hsjoihs
hsjoihs
a(&p,&l,9974+i%2,714) が地味にポイントですね。9974.5 と書きたいところを、ループカウンタの剰余で処理
ドーナツがだんだん小さくなるのは、わり算が常に切り捨てだからですね。5000 足したらよくなったりする?しそう
void *memset();int printf();int usleep();int putchar();
int m(int a,int b){return (a*b+5000)/10000;}void a(int*c,int*s,int d,int t){int k=m(*c,d)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l;}
int main(){int z[1760];char b[1760];printf("\e[2J");int q=10000;int r=0;int u=10000;int v=0;for(;;a(&q,&r,9992,400),a(&u,&v,9998,200),usleep(50000)){memset(b,32,1760);memset(z,0,1760*sizeof(int));int l=0;int p=10000;for(int i=0;i<88;i++,a(&p,&l,9974+i%2,714)){int w=0;int e=10000;for(int j=0;j<314;j++,a(&e,&w,9998,200)){int f=p+20000;int g=100000000/(m(m(w,f),r)+m(l,q)+50000);int t=m(m(w,q),f)-m(l,r);int x=40+30*m(g,m(m(e,u),f)-m(t,v))/10000;int y=12+15*m(g,m(m(e,v),f)+m(t,u))/10000;int o=x+80*y;int N=8*(m(m(l,r)-m(m(w,q),p),u)-m(m(w,r),p)-m(l,q)-m(m(e,v),p))/10000;if(22>y&&y>0&&x>0&&80>x&&g>z[o]){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0];}}}printf("\e[H");for(int k=0;k<1761;k++)putchar(k%80?b[k]:10);}return 0;}
lemolatoon
lemolatoon
void を実装しました
れもん
れもん
なんかを動かすの、ゴールとしてやっぱりいいですね
uint256_t
uint256_t
一応こんな感じで昔は発表しました
かりんとう
かりんとう
hsjoihs
hsjoihs
                   int putchar();void*
               memset();int m(int a,int b)
            {return (a*b+5000)/10000;}void a(
          int*c,int*s,int d,int t){int k=m(*c,d
        )-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
      ;}int usleep();int printf();int main(){int z[
     1760];char b[1760];printf("\e[2J");int s=10000;
    int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
   ,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
  0,7040);int l=0;int           p=s;for(int i=0;i<88;++
  i,a(&p,&l,9974+i                 %2,714)){int w=0;int
 e=s;for(int j=0;                   j<314;j++,a(&e,&w,s-
 2,200)){int f=p                     +2*s;int g=s*s/(m(m
(w,f),r)+m(l,q)+                     5*s);int t=m(m(w,q),
f)-m(l,r);int x=                     40+30*m(g,m(m(e,u),f
 )-m(t,v))/s;int                     y=12+15*m(g,m(m(e,v
 ),f)+m(t,u))/s;                    int o=x+80*y;int N=8
  *(m(m(l,r)-m(m(w                 ,q),p),u)-m(m(w,r),p
  )-m(l,q)-m(m(e,v),p           ))/s;if(y>0&&g>z[o]&&22
   >y&&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]
    ;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
     (k%80?b[k]:10);/*printf("Originally written by"
      " @a1k0n. Rewritten by @hsjoihs so that it c"
        "ompiles without any floating-point arit"
          "hmetics.foobarbazloremipsumfo\n");*/
            usleep(5*s);}return 0;}/*foobarba
               brfnuwdoigjkrtfdgjvkmlsfdgr
                   donutdonutdonutdon*/
ドーナツを手でやるのつらすぎるので、donutifier を作ろうと思います【編注:まだ作っていない】
『言語弁護士』に関する http://www.catb.org/jargon/html/L/language-lawyer.html の `A language lawyer is distinguished by the ability to show you the five sentences scattered through a 200-plus-page manual that together imply the answer to your question “if only you had thought to look there”` という説明、本当にその通りなんだよな。なにか仕様について説明をしなきゃいけないとき、往々にしてそれに関するものは仕様書全体に散らばってて、言語弁護士と呼ばれる人々は基本的にそれをピンポイントで提示できる
そもそも『これはチュートリアルではない』`Although this International Standard is intended to guide knowledgeable C language programmers as well as implementors of C language translation systems, the document itself is not designed to serve as a tutorial.` って規格書の一番先頭に書いてありますからね
あんこさん、こういう話があります
あんこ
あんこ
これ第 2 世代と第 3 世代って別物なんですか?
hsjoihs
hsjoihs
同じであってほしいです。それはそのとおり。なぜなら、第1世代と第2世代はともに foo.c と bar.c をソースコードとするから。しかし、自作コンパイラにバグがあるとその限りではない。第 1 世代が foo.c をミスコンパイルしてしまったら、その結果の foo.s は、GCC の吐いた foo.s とは『機能的に別物』である可能性がある。そうすると、『第1世代』と『第2世代』は(そのバイナリ上の組成だけでなく)機能としても別物のアプリケーションとなることがありえ、そうすると赤紫 foo.s と青紫 foo.s が別物になる可能性がある。逆にいうと、赤紫 foo.s と青紫 foo.s の間に diff があったら、それは
・『foo.c を第 1 世代が喰って吐いた foo.s』が
・『foo.c を gcc が喰って吐いた foo.s』と異なる挙動を持つ
つまり、『foo.c を第 1 世代が喰って吐いた foo.s』には必ずミスコンパイルがある、ということがわかる。これをやると、一切の動的解析なしでバグの存在が検出できる
Imperi
Imperi
アセンブリはそのままエスケープシーケンスにしてた
hsjoihs
hsjoihs
\e は標準 C にはありません。EBCDIC に対応する文字がない、という理由で C89 委員会にリジェクトされました。\x1b か \033 で埋めるとアセンブラは通してくれると思います
かりんとう
かりんとう
いまはなにを?
hsjoihs
hsjoihs
あんこさんは、セルフコンパイルはできているけれどセルフホストはできていないそうです。なのでそのバグの洗い出しをやっています
かりんとう
かりんとう
あー、第二世代・第三世代で落ちている、と
hsjoihs
hsjoihs
今貼った方法は、いわゆる動的解析を頑張らずに、テキストの diff を取るだけでバグの存在を保証できる、すごい方法なんですよ
引数リストを空にすると、それは関数の引数を型チェックしないという意味になります(空の引数リストであることを表す宣言は f(void) の形)。この仕様は、C コンパイラ自作のための手抜きの友なので、是非とも実装しましょう
あんこ
あんこ
まだバグがあります
hsjoihs
hsjoihs
見つかりましたか?
あんこ
あんこ
直してます
hsjoihs
hsjoihs
lemolatoonさんかりんとうさん、上であざらしさんとのやりとりのスクショを使ったのと同様、人に C コンパイラを教える目的で今回のログを転用していいですか?
lemolatoonかりんとう
lemolatoonかりんとう
いいですよ
lemolatoon
lemolatoon
変数に対して 10000 で割るとバグってる
Imperi
Imperi
64 ビットに拡張するときにミスってないですか?上まで符号ビットを詰めないとですよ
lemolatoon
lemolatoon
あっ、やってない
Imperi
Imperi
unsigned の実装はそれやることになるのか。たいへんだなぁ
かりんとう
かりんとう
それってマイナス全般でエラー起きますか?
lemolatoon
lemolatoon
そんな気がします。正の数しかテストしてこなかったのが徒
kanataso
kanataso
ドーナツがなにかおかしい
hsjoihs
hsjoihs
[H が出てるなら、\eができてないんだと思います。\e は標準にはないんですが、まあ実装してるコンパイラは多い
https://twitter.com/t3mpwn/status/1557697433294934016 の『セキュリティキャンプはマクドナルドのノリで入れてしまう焼肉食べ放題の店なので満腹の先が見えてくる』、本当に至言
Imperi
Imperi
マックではないが、大盛り目のラーメンを期待してたらマシマシの二郎が来た感じではある
どの事前課題見ても、『これ解いてる人はそれなりの覚悟で来てるだろ』という気はする
hsjoihs
hsjoihs
事前課題、知らない分野の見るとこんなん解ける気しないってなる
kanataso
kanataso
ターミナルこわれた
hsjoihs
hsjoihs
日本語のコメントだけ生きてる、なるほど!?
kanataso
kanataso
ドーナツ実行したらこわれたので、ドーナツが悪い
アセンブリを書き換えてエスケープ文字を入れたらこわれた
lemolatoon
lemolatoon
さっきのバグ、割り算だけバグるんですよね
hsjoihs
hsjoihs
一応数学科なので、群と環の話をします
オチから言うと、2^N で剰余 (mod) を取った体系というのは、加算・減算・乗算に対しては、うまくいく
要するに、10進数で『下 n ケタだけ見る』という計算をしたときに、加算・減算・乗算については、これらは、専門的に言うと『構造を保つ』
Imperi
Imperi
switch ってネストできましたっけ
hsjoihs
hsjoihs
はい、できます。ところで hsjoihs-c-compiler それ対応できているのかな。自信ないぞ
これのことを、数学では『準同型』と言います。非常に重要な概念です
lemolatoon
lemolatoon
圏論をちょっとだけやろうと思って、本の最初をやったときにちょろっと出てきた
hsjoihs
hsjoihs
圏論というのは抽象化の学問であって、圏論の教科書を読むことはわりとできてしまう一方で、
lemolatoon
lemolatoon
具体例が
hsjoihs
hsjoihs
まさにそう。スタックとヒープの話を知らずに『自動変数』とかの話をいきなり見に行ってしまうのと似た結果をもたらすとは思う
lemolatoon
lemolatoon
線形代数とかもやってないからよくわかんないなぁ、とか、いろんな関手の種類とか、そういうところで飽きて終わっちゃった
int を int で割ったときのをどうすればいいのかが分かっていない
movsx は char で出てきたなぁ、と
hsjoihs
hsjoihs
それこそ compilerbook に解説ありませんでしたっけ
lemolatoon
lemolatoon
-2 / 3 をしようとすると、わけの分からない大きい値が出る
hsjoihs
hsjoihs
2の32乗が、たしか42億9496万7296 なので、そこから 2 を引いて 3 で割ると 14億3165万5784、だよね?
lemolatoon
lemolatoon
16進だと 0x55555554
hsjoihs
hsjoihs
これだと分かりやすいですよね。-2 ってどう表されるんでした?
lemolatoon
lemolatoon
0xfffffffe、なのでそれを 3 で割ってるってことですね
hsjoihs
hsjoihs
つまり、割り算は『準同型』ではない、つまり、構造を保たないので、2の32乗で余りを取った体系だということを思い出さなきゃいけなくなる
これはやるかどうか分かんないんですけど、みなさんの C コンパイラについて『このコンパイラはここは実装してない』とかを一覧できるサイトがあったら素敵じゃないかな、と
lemolatoon
lemolatoon
自動で作るってことですか
hsjoihs
hsjoihs
『どうテストするか』はみんなバラバラなので、そこは人間が手作業で『このリポジトリにファイルを渡すにはこれ』みたいなインターフェースをポータルサイトのリポジトリに書いて、後は自動、みたいなことやりたいですよね
なんというか、みなさんの思い思いの実装をコレクションさせていただいているという立場なので、『こうビルドしてください』とお願いするのではなく、そこは人力で頑張るという方針です
多分私がとても頑張るとこれを実装できるんだけど、私よりもこういうの得意そうな人がいそうなので誰かやってくれないかなぁ~
lemolatoon
lemolatoon
標準化すると GitHub Action として提供したりできて使う側もうれしいのでは
hsjoihs
hsjoihs
まあそうなんですけど、まあそこんとこのスタンスはあの資料を見ていただくと分かるかと
Imperi
Imperi
この割り算がわかんない
hsjoihs
hsjoihs
eax に書くと、上位 32 ビットは 0 で埋まります
かりんとう
かりんとう
char は拡張しないといけないけど int は拡張しなくていい、って話でしたっけ
hsjoihs
hsjoihs
compilerbook にそういう説明がある
『4バイトの値を読み込む時は、EAXのような下位32ビットのエイリアスのレジスタに普通のmovで読み込むだけでよかったので、charを読み込むときには、普通のmovでALにロードするだけでよさそうです。しかしそれではきちんと動きません。この謎の答えはx86-64の仕様にあります。x86-64では、下位32ビットのエイリアスのレジスタに読み込むと上位32ビットは0にリセットされます。しかし、下位8ビットのエイリアスのレジスタに読み込むときには、上位56ビットは以前の値がそのまま入ったままになります。』と書いてあります
なので、32 bit の負の数を eax に突っ込むと、rax の上位ビットは 1 じゃなくて 0 で埋まるんですね
私のコンパイラちゃんと対処できているかな
lemolatoon
lemolatoon
ドーナツが動いてる人は問題ないと思います

2022年8月12日

Imperi
Imperi
割り算するときに 8 バイト分の値として計算してるのがマズい
hsjoihs
hsjoihs
https://docs.oracle.com/cd/E19455-01/806-3773/instructionset-49/index.html を見ると、"cltd converts the signed long in EAX to a signed double long in EDX:EAX by extending the most-significant bit (sign bit) of EAX into all bits of EDX." と書いてある
除数がサイズ 4 バイトのときの div の被除数は EDX:EAX なので、これでいけるはず
私は AT&T で書いてるので、 idivl という表記で 4 バイトを明記できてる
compilerbook は『idivは符号あり除算を行う命令です。x86-64のidivが素直な仕様になっていれば、上のコードでは本来idiv rax, rdiのように書きたかったところですが、そのような2つのレジスタをとる除算命令はx86-64には存在しません。その代わりに、idivは暗黙のうちにRDXとRAXを取って、それを合わせたものを128ビット整数とみなして、それを引数のレジスタの64ビットの値で割り、商をRAXに、余りをRDXにセットする、という仕様になっています。cqo命令を使うと、RAXに入っている64ビットの値を128ビットに伸ばしてRDXとRAXにセットすることができるので、上記のコードではidivを呼ぶ前にcqoを呼んでいます。』という書き方なので、実は compilerbook の記述は 64 ビット整数の割り算のやり方
Rui さんここ意図的にそう残してるんじゃないかなぁと思いますよ
idivl (32 bit の idiv) と idivq (64 bit の idiv) が存在
idivl は EDX:EAX を割る。 idivq は RDX:RAX を割る。Intel syntax ではともに idiv と書かれる
lemolatoon
lemolatoon
オペランドを edi にしたら 32 ビットが呼ばれる?
cltd が通らない
hsjoihs
hsjoihs
Intel syntax だと cdq とかだったかも
かりんとう
かりんとう
割り算する前に符号拡張すればいいのか
Imperi
Imperi
そこらへんのキャストのタイミング、考えるか
hsjoihs
hsjoihs
今までの実装は、64 ビットについては正しいので、long long の割り算をするときにはこれを復活させなきゃいけない
kanataso
kanataso
なんかおかしいけどドーナツ動きました
特定の浮動小数点数の値だけを読めるようなパーサーにしている。godbolt を見ると、float に代入するときに、long でグローバルに置いてある定数から float に移してるので、と
hsjoihs
hsjoihs
Java とかだと、浮動小数点数は基本的に定数テーブルというところに入れてあるけれど、『0.0 をロードする命令』と『1.0 をロードする命令』は JVM に組み込みで入ってる
kanataso
kanataso
lemolatoon
lemolatoon
またセグフォしてしまった
hsjoihs
hsjoihs
おやおや
lemolatoon
lemolatoon
比較演算子がバグってそう。ドーナツのおかげで気づけた
hsjoihs
hsjoihs
頑張って整数演算に書き直した甲斐があった
season1618 さんの https://trap.jp/post/1638/ の、『Cコンパイラをこの機会に作ってみることにしました。一応情報系ということで人生で一回は作ってみたいなあと思っていたので。』の部分本当に好きなんだよな
Imperi
Imperi
typedef って変数と同様のスコープ持つからめんどい。後回しにするかなぁ
hsjoihs
hsjoihs
私は typedef 作ってないし struct と union はトップレベルでしか定義できません
ファイルスコープ作ってたっけ?作ってない気がしてきた
saiten
saiten
楽しそうなことやってますね
hsjoihs
hsjoihs
C 言語というものを知っていますか?gcc とかを使ったことがありますか?それが C コンパイラであり、C コンパイラを書いてます
Imperi
Imperi
IOCCC ってところに出てた、ドーナツという作品を、自作のコンパイラで動かそうとしている
hsjoihs
hsjoihs
今みなさんがやってるのは、私が必死こいて浮動小数点数を取り除いて全部整数でできるようにしたバージョンです
ここにあります
                   int putchar();void*
               memset();int m(int a,int b)
            {return (a*b+5000)/10000;}void a(
          int*c,int*s,int d,int t){int k=m(*c,d
        )-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
      ;}int usleep();int printf();int main(){int z[
     1760];char b[1760];printf("\e[2J");int s=10000;
    int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
   ,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
  0,7040);int l=0;int p           =s;for(int i=0;i<88;i
  ++,a(&p,&l,9974+i%                 2,714)){int w=0;int
 e=s;for(int j=0;j<                   314;j++,a(&e,&w,s-
 2,200)){int f=p+2                     *s;int g=s*s/(m(m
(w,f),r)+m(l,q)+5*                     s);int t=m(m(w,q),
f)-m(l,r);int x=40                     +30*m(g,m(m(e,u),f
 )-m(t,v))/s;int y                     =12+15*m(g,m(m(e, 
 v),f)+m(t,u))/s;int                   o=x+80*y;int N=8* 
  (m(m(l,r)-m(m(w,q)                ,p),u)-m(m(w,r),p)-  
  m(l,q)-m(m(e,v),p))/s           ;if(y>0&&g>z[o]&&y<22  
   &&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]+0
    ;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
     (k%80?b[k]:10);printf("Author: @a1k0n. Rewritt"
      "en by @hsjoihs so that it works without flo"
        );https://github.com/hsjoihs/c-compiler/
          printf("ating types.\nNote that roun"
            "ding errors gradually reduce th"
               "e donut's size.\n");usleep
                   (50000);}return 0;}
実は https://~~ は、https: というラベルと、 //~~ というコメントに分かれるので、ソースコード中に直に書けます
kanataso
kanataso
goto 作ってないなぁ
float donut も動いてます
lemolatoon
lemolatoon
すごい
             k;double sin()
         ,cos();main(){float A=
       0,B=0,i,j,z[1760];char b[
     1760];printf("\x1b[2J");for(;;
  ){memset(b,32,1760);memset(z,0,7040)
  ;for(j=0;6.28>j;j+=0.07)for(i=0;6.28
 >i;i+=0.02){float c=sin(i),d=cos(j),e=
 sin(A),f=sin(j),g=cos(A),h=d+2,D=1/(c*
 h*e+f*g+5),l=cos      (i),m=cos(B),n=s\
in(B),t=c*h*g-f*        e;int x=40+30*D*
(l*h*m-t*n),y=            12+15*D*(l*h*n
+t*m),o=x+80*y,          N=8*((f*e-c*d*g
 )*m-c*d*e-f*g-l        *d*n);if(22>y&&
 y>0&&x>0&&80>x&&D>z[o]){z[o]=D;;;b[o]=
 ".,-~:;=!*#$@"[N>0?N:0];}}/*#****!!-*/
  printf("\x1b[H");for(k=0;1761>k;k++)
   putchar(k%80?b[k]:10);A+=0.04;B+=
     0.02;}}/*****####*******!!=;:~
       ~::==!!!**********!!!==::-
         .,~~;;;========;;;:~-.
             ..,--------,*/
hsjoihs
hsjoihs
元ネタの double ドーナツですね
Imperi
Imperi
ABI convention を見てぶっ倒れそうになった。とくに struct に double 入ってるとき
hsjoihs
hsjoihs
だから double 実装したくないんだよな
Imperi
Imperi
これは浮動小数点数だいぶ先だなぁ。というか構造体の値渡しを実装したくない
hsjoihs
hsjoihs
構造体を返すのは実装しましたが、値渡しは小さい構造体についてはサポートしてないです
さてみなさんのコンパイラはいまどんな感じですか?
lemolatoon
lemolatoon
いたるところで符号拡張してないことに気づいたので、やっている
kanataso
kanataso
x86-64 の float 対応したので、RISC-V も float 対応させます。SL コマンドはなぜ未だにおかしいのか
lemolatoon
lemolatoon
ターミナルの問題という可能性は?
kanataso
kanataso
そんなことはないんですよ
hsjoihs
hsjoihs
kanatasoさんのところのバグった sl 大好きなんだよな。正常な sl はいつでも簡単に見ることができるけど、こわれた sl は C コンパイラを自作してこそかもしれない
あんこ
あんこ
二分探索という手があるのを思いだした
hsjoihs
hsjoihs
フランケンコンパイル上の二分探索は、二つに分けると両方からバグが消えたりするので本当に厄介だけど、バグを保ちつつ少しずつ小さくできればかなり進展になる
実は二分探索は、
・『AとB』の中にあれば『Aの中にある』または『Bの中にある』だ
という不変条件を前提とした探索なんですね
Imperi
Imperi
初めて指数探索知ったとき、『二分探索よりこっちのほうがいいじゃん』って思ったけど。上界が分かってなくても使える。start から 1 個先は満たしてるか・2個先は満たしてるか・4個先は満たしているか・………を調べる。上界が分かってなくてもつかえる
kanataso
kanataso
TCP のネーグルのアルゴリズムみたい………と思ったけどこれじゃないな
kgtkr
kgtkr
覗きにきました()
saiten
saiten
(このチャンネルのログを)一番最初から読んでるんですけど、どこを参考にして読んでるんですか?
hsjoihs
hsjoihs
saiten
saiten
やっぱりこれか
hsjoihs
hsjoihs
C コンパイラゼミの歴史の話をすると、2018年に植山類さんと hikalium さんの二人で開講されたゼミで、初年度のときのラフスケッチ資料に大幅に加筆したもの。後半部分を見ると、ラフスケッチであることがよくわかる
私はその 2018 年のときの受講生です
C コンパイラに興味がありましたら是非。ちなみに、一つ言っておくと、最初の方かなり丁寧にコードとか示されてるのでこれを写経する人がとても多く、それをダメとは全く言いませんが、一方で『本当にこう書くしかないのか?』『こうも書けるのでは?』とか考えつつ、自分ならではの試行錯誤をすると、さらに実りがあると思います
『始めたいです』と思ったら是非連絡頂けると
この本は C を C で書いてますけど、別に TypeScript とかで C コンパイラを書いてもいいわけで。自由にやっていきましょう
tkr、TypeScript で C コンパイラ書かな~い?
kgtkr
kgtkr
いやだ、、
TSはきつい(Cよりはいいかも)
hsjoihs
hsjoihs
なんと C よりはいい
saiten
saiten
C が一番いいのでは?
hsjoihs
hsjoihs
C は普通に不便な言語ですよ?
Imperi
Imperi
かゆいところには全然手が届いていない。ただしセルフホストしたい場合は C で書くことになる
kgtkr
kgtkr
Cコンパイラセルフホストはかなりの人がやっているので, ネットに落ちてる人が書いたCコンパイラをコンパイルするを最初の目標にすればいいかなーRustでやるかなーの気持ち
saiten
saiten
セルフホストというのは、自作 C コンパイラを自作 C コンパイラでコンパイル?
hsjoihs
hsjoihs
そうです
kgtkr
kgtkr
面白そう(地獄そう)
hsjoihs
hsjoihs
kgtkr のその問題設計とてもいいと思います。A さんのコンパイラはセルフホストしているが、tkr のコンパイルでコンパイルした A さんのコンパイラはセルフホストに失敗する、とかが発生するととても楽しいと思います
kgtkr
kgtkr
面白そう(地獄そう)
saiten
saiten
えっと………どういうことだ
hsjoihs
hsjoihs
ややこしいですよねこれ
セルフホストというのはヤバくて、
saiten
saiten
この『世代』というのは?
hsjoihs
hsjoihs
コンパイラを作っているので、ソースコードをその自作コンパイラでコンパイルできる
すると、『foo.c が gcc に喰われて出てきた foo.s』で作った『第一世代』と、その第一世代が foo.c を喰って出てきた foo.s で作られた『第二世代』、その第二世代が foo.c を喰って出てきた foo.s で作られた『第三世代』、………と世代を重ねることができる
kgtkr
kgtkr
セルフホストでいうとwasmのインタプリタはRust(やwasmの出力に対応している好きな言語)で書きつつセルフホストできるのでかなり楽しかった
hsjoihs
hsjoihs
たしかにね
wasm へのコンパイラとか書いてみたらどうよ
TumoiYorozu
TumoiYorozu
おはようございます🌞【時刻: 01:31】
speed
speed
今は何が実装されていますか
hsjoihs
hsjoihs
C コンパイラです
saiten
saiten
C++ でも C コンパイラ書ける?
hsjoihs
hsjoihs
はいもちろん。普通の言語ならまあ C コンパイラを書けないということはないでしょう
TumoiYorozu
TumoiYorozu
セルフホスト目指さないならアリっぽい
hsjoihs
hsjoihs
そうですね。セルフホストは面白い(かつ分かりやすい)ゴールポストではあるけど、それだけが C コンパイラ自作というわけでは全くないので
Imperi
Imperi
enum を一発で通した
hsjoihs
hsjoihs
お、imperi さん C コンパイラセルフホスト狙います?
お、kanatasoさんのもとで(小さい方の)sl コマンドが走ってる
kanataso
kanataso
先頭車両だけ縦に回転してる
hsjoihs
hsjoihs
ぜったいおもろいので動画撮影しておいて欲しい
Imperi
Imperi
いまは declarator のネストを書いていたけど、一度構文解析してると二重配列とかも構文解析の時に普通にパースして意味解析で順序だけ考慮すればいいから、順番が『逆になる』現象が解決しやすくなるなぁ、と
kanataso
kanataso
今までは回転する sl があるだけだったが、やっとデバッグの端緒が掴め始めた
lemolatoon
lemolatoon
ドーナツ動いた!比較演算もマイナスの時にこわれていて、型のサイズを見るようにした
hsjoihs
hsjoihs
いやぁドーナツめちゃめちゃいいサンプルになったな。時間掛けて移植してほんとによかった
ゆーて 75 分ぐらいで整数への移植終わらせることができたのか
ゆみや
ゆみや
人が作業ライブしてるのを見るとこちらもなんかやる気が出てくる
hsjoihs
hsjoihs
そうよね~
ゆみや
ゆみや
(深夜とはいえ他コースの作業の様子に別コースの人間がいていいのだろうか、と思いつつも、でもまあ他にも別コースの人々来てるし別にええか!という気持ちもある)
hsjoihs
hsjoihs
C コンパイラゼミは秘匿情報が皆無なゼミなので。まあそもそも L3 以外にも C コンパイラ書いてる受講生がおるので……
kanataso
kanataso
原因が判明しました。array[5][3] が array[3][5] として扱われてた
Imperi
Imperi
二重配列が順番逆になるの、よくない。パース難しすぎる
パースする順番とネストさせる順番が逆なのなぁ
hsjoihs
hsjoihs
私はよく『靴下を裏返したような』って言ってますけど。 C の型宣言
Imperi
Imperi
const は捨てよう
hsjoihs
hsjoihs
const は全無視するといいですよ
ゆみや
ゆみや
(一昨日は進捗の遅れを取り戻そうと5時まで電子回路のデバッグしてたけど、やはり焦って徹夜で作業するのは非効率だと実感)
(でも趣味をやっている時の徹夜は楽しいんですよね)
(そのためしばしば生活習慣が崩壊し、怒られが発生しがち)
hsjoihs
hsjoihs
『趣味をやっている時の徹夜は楽しいんですよね』ほんとそれ。徹夜とかカフェイン大量摂取とか、作業効率は下がるけど話のペースは上がるんだよな
lowlayer-girls に出演して、あの枠で社会言語学の話をしていたときも、カフェイン大量摂取をしていた
kanataso
kanataso
SL 正しく動いた!
ゆみや
ゆみや
(めでたい)
hsjoihs
hsjoihs
単純なゲームのコンパイル、やっぱいいですね
Imperi
Imperi
『ここまでは OK なんだな』という精神安定剤。なんか機能足すたびに make donut してる
kanataso
kanataso
セルフホストしてからは、なにか機能追加するたびにセルフホストできるかを確認してるなぁ
hsjoihs
hsjoihs
私も make test_all 作ってますね
kanataso
kanataso
type-specifier 周りにそろそろ向き合う頃かもしれない
Imperi
Imperi
long long とか short int とかあるからつらい
hsjoihs
hsjoihs
キーワードの数を増やしたくなかったというのは分かりますけどねぇ
saiten
saiten
なんか正しいのよりも遥かに単調なコードが出力されてしまい、誤りっぽい
hsjoihs
hsjoihs
じゃあ私のコンパイラのコレクションに足しますかね。ふつうコンパイラコレクションと言ったら別の概念を指すけど、私はコンパイラをコレクションしてるんだよな
Imperi
Imperi
宣言で declarator が複数あったときって評価順は未定義ですか?
hsjoihs
hsjoihs
そういうのは、『未定義』ではなく『未規定』と言います
あんこ
あんこ
関数の引数の順序も
hsjoihs
hsjoihs
未規定です
C++ は 17 で評価順序の規定が増えましたよね
Imperi
Imperi
あー、『関数は引数より先に評価されると規定されました』か
hsjoihs
hsjoihs
そうそう、indeterminately sequenced という単語もできましたよね
Imperi
Imperi
あんこ
あんこ
hsjoihs
hsjoihs
__STDC__ でヘッダを代用するのは便利ですよ
あんこ
あんこ
これはなんですか?
hsjoihs
hsjoihs
これは、『規格準拠している C コンパイラは、このマクロを 1 として定義している』というマクロです
#ifdef __STDC__
#include <stdio.h>
#include <stdlib.h>
#else
int printf();
#endif
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif

#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
             struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
お、kanatasoさんは IOCCC のフライトシミュレータをやろうとしてるのですね
ゆみや
ゆみや
(離脱します。hsjoihs氏とL3の受講生の方々お疲れ様です) 【時刻: 03:11】
hsjoihs
hsjoihs
33e-3は標準にある書き方のはずですけど。ちょっと資料持ってきますね
ちなみにこれで 0xe-0xe が落ちるという話がありますね
Imperi
Imperi
hsjoihs
hsjoihs
解散!【時刻: 03:36】
起床【時刻: 06:58】
起床2 【時刻: 09:47】
lemolatoon
lemolatoon
発表って結局どうするんでしょう?
hsjoihs
hsjoihs
そうなんですよね。結局決めてなかった
私の感想としては、発表に出せるおもしろ話が一番多いのはkanatasoさんだとは思います
(L3受講生それぞれに「この人に発表してもらいたい」を投票してもらう、とか考えてたけどすっかりやり忘れてた………)
lemolatoon
lemolatoon
riscv周りの話とかうけがよさそう
hsjoihs
hsjoihs
RISC-V、バグった sl、漢字しか出なくなったターミナル、とかいろいろあるので、私としてはkanatasoさんが全体発表するのが一番おもろいのかな、と思います。他のみなさんはどう考えますか
kanataso
kanataso
mikiken
mikiken
バグったsl大好きなのでkanatasoさん推しです
lemolatoon
lemolatoon
バグったslと治ったslどっちもいけますよねたしか
かりんとう
かりんとう
荒ぶるSLをキャンプ内に布教して欲しいので推しです。
hsjoihs
hsjoihs
@あんこ (C受講生) codegen.c で、switch 文のところにある fprintf(stderr, "%d\n", stderr); ってどういう意図でここに書いてます?
あと、parse.c に printf(stderr, "if end\n"); ってありますけど、 fprintf の誤りかと。
セルフホスト、fprintf(stderr, ...) でセグフォしてるなぁ。キャストで stderr を作っているところを一旦差し戻すかぁ
@あんこ (C受講生)
調査報告:セルフホストすると、変数定義にまつわるテーブルが死ぬらしい
typedef int size_t; とか #define size_t int とかだけでなく、for 文のループカウンタすら「その変数は宣言されていません」を吐く
kanatasoさん、発表中にドーナツのソースを出すことがあったら、芸術点の高いこっちの方で表示して頂きたいです
                   int putchar();void*
               memset();int m(int a,int b)
            {return (a*b+5000)/10000;}void a(
          int*c,int*s,int d,int t){int k=m(*c,d
        )-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
      ;}int usleep();int printf();int main(){int z[
     1760];char b[1760];printf("\e[2J");int s=10000;
    int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
   ,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
  0,7040);int l=0;int p           =s;for(int i=0;i<88;i
  ++,a(&p,&l,9974+i%                 2,714)){int w=0;int
 e=s;for(int j=0;j<                   314;j++,a(&e,&w,s-
 2,200)){int f=p+2                     *s;int g=s*s/(m(m
(w,f),r)+m(l,q)+5*                     s);int t=m(m(w,q),
f)-m(l,r);int x=40                     +30*m(g,m(m(e,u),f
 )-m(t,v))/s;int y                     =12+15*m(g,m(m(e, 
 v),f)+m(t,u))/s;int                   o=x+80*y;int N=8* 
  (m(m(l,r)-m(m(w,q)                ,p),u)-m(m(w,r),p)-  
  m(l,q)-m(m(e,v),p))/s           ;if(y>0&&g>z[o]&&y<22  
   &&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]+0
    ;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
     (k%80?b[k]:10);printf("Author: @a1k0n. Rewritt"
      "en by @hsjoihs so that it works without flo"
        );https://github.com/hsjoihs/c-compiler/
          printf("ating types.\nNote that roun"
            "ding errors gradually reduce th"
               "e donut's size.\n");usleep
                   (50000);}return 0;}
れもん
れもん
L3の裏でホワイトボードに書かれていた様々です
uint256_t
uint256_t
本当にお疲れ様でした。
lemolatoon
lemolatoon
お疲れ様でした。すごい充実してました!!
ありがとうございました!
mikiken
mikiken
ありがとうございました!!(アフターイベントまでにコンパイラの進捗生めるように頑張ります)
かりんとう
かりんとう
皆さんお疲れさまでした。キャンプ中だけでも30時間コンパイラ開発で密度が高い時間を過ごせました!今日でキャンプが終了なのは名残惜しいですが、事前学習から今日までありがとうございました!(近日中にブログ書けたらいいなと思っています)
tamaron
tamaron
お疲れ様でした!
hsjoihs
hsjoihs
フランケンコンパイルはどんな感じですか?
あんこ
あんこ
gcc で通ってセルフコンパイルが通らない
hsjoihs
hsjoihs
まだ原因究明には程遠い、と
信頼できない部分をなるべく削ること、がやりたいので、『第2と第3の diff がある = バグってる』かつ、『セルフホストしてる部品がなるべく小さい』を作りたい、ということ
あんこ
あんこ
そうですね
hsjoihs
hsjoihs
なので、最初は『ファイル一個だけをセルフコンパイル』をやって、それでできなかったら『不ファイル2個だけをセルフコンパイル』とやって、みたいなことができるんですが、前から言ってる通り、この手法の強みって diff 見ればバグの存在が保証できる。つまり自動化できる。私は手作業でやったけど
あんこ
あんこ
睡眠が足りなかったので寝ようと思います
hsjoihs
hsjoihs
寝て起きてコードをみたらあんこさんのやつに気になる箇所結構あったので、寝て起きてみるといいかもしれません
mikiken
mikiken
まさにこの『int のサイズ変更』で詰まってる
hsjoihs
hsjoihs
intel syntax であることが混乱の一因な気もしますけどねぇ
AT&T はそもそも命令名の後に長さを表す文字がつく。movl vs. movq とか
mikiken
mikiken
配列を宣言するときに、メモリ確保するという話で int とポインタのサイズを変更しないとなぁと修正しようとしている
hsjoihs
hsjoihs
添字演算子自体は配列と無関係に実装できる(a[b] を (*((a)+(b))) に展開するだけ)ので、先にやるのもアリです
mikiken
mikiken
16バイトスタックアラインも絡んでくるから余計にめんどい
saiten
saiten
C コンパイラ自作で詰まったら l3-random に投げていいんですか?
hsjoihs
hsjoihs
投げていいですし、 @hsjoihs (L3講師) とか @L3ゼミ受講生 とかメンション付けて投げてもいいですよ。そもそも受講生の方が C コンパイラ自作についてフレッシュな記憶をお持ちですし
あんこ
あんこ
#include ができていないのを解決したいが、複雑であり難しい
えっここでは見えてるの?
hsjoihs
hsjoihs
名前が見えてたり見えてなかったりするんですか?
あんこ
あんこ
そう、トークンのなにかがおかしい
これを do-while にすればいいのか
hsjoihs
hsjoihs
お、バグ分かりました?
あんこ
あんこ
include したときに EOF まで include してしまい、include 直後でファイルが終端してしまっていた
kgtkr
kgtkr
プリプロセッサまで自作している?
れもん
れもん
includeとかカッコ無しdefineくらいなら人々入れがちというイメージ
kgtkr
kgtkr
なるほどー
Powered by PseudoRoku, which is designed by hsjoihs. Feel free to report any issues.